]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
SPARC64: Add Linux vds driver Device ID support for Solaris guest boot
authorGeorge Kennedy <george.kennedy@oracle.com>
Mon, 15 May 2017 14:43:56 +0000 (07:43 -0700)
committerShannon Nelson <shannon.nelson@oracle.com>
Wed, 31 May 2017 23:43:49 +0000 (16:43 -0700)
Currently, Solaris guest backend disk images cannot be moved from the Device ID
they were created at and still boot. This bug fix adds Solaris Device ID
support to the Linux vds driver to allow a Solaris guest backend disk image to
be moved to a different device ID from where it was created and still boot.

The Linux vds driver support added in this bug is for Solaris disk images
only. In the future, Solaris Device ID support for physical disk backends will
be added to the Linux vds driver as well.

From PSARC/1995/352:
Solaris Device IDs provide a means for identifying a device, independent of the
device's current name or device number. The instance number of a device number
may change across reconfiguration boots, changing the device number (dev_t) for
that device.  Operator errors in recabling can cause devices to swap logical
device names, introducing the potential for data loss.

Signed-off-by: George Kennedy <george.kennedy@oracle.com>
Reviewed-by: Alexandre Chartre <Alexandre.Chartre@oracle.com>
Orabug: 25836231
Signed-off-by: Allen Pais <allen.pais@oracle.com>
drivers/block/vds/Makefile
drivers/block/vds/vds.h
drivers/block/vds/vds_devid.c [new file with mode: 0644]
drivers/block/vds/vds_devid.h [new file with mode: 0644]
drivers/block/vds/vds_efi.c
drivers/block/vds/vds_io.c
drivers/block/vds/vds_io.h
drivers/block/vds/vds_main.c
drivers/block/vds/vds_vtoc.c

index 102f0766dc3014ca914a4895bca4449e2c704b09..0efc1b4b0ad59aae3439ec1132e6fe628e66fb77 100644 (file)
@@ -1,5 +1,5 @@
 obj-$(CONFIG_VDS) := vds.o
 
 vds-y := vds_blk.o vds_efi.o vds_io.o vds_label.o vds_main.o vds_reg.o \
-        vds_vtoc.o
+        vds_vtoc.o vds_devid.o
 
index fb249c638a01edae4f138f208b6e0675ac69fc7c..676409420dacbb2b15878eca30cbbeee280d5b2b 100644 (file)
@@ -58,6 +58,9 @@ struct vds_port {
        struct workqueue_struct *ioq;
        struct list_head        io_list;
        wait_queue_head_t       wait;
+       void                    *devid;
+       int                     efi_rsvd_partnum;
+       umode_t                 mode;
 };
 
 #define        VDS_PORT_SEQ            0x1
@@ -192,6 +195,7 @@ int vds_vtoc_clear(struct vds_port *port);
 #define VDS_DEBUG_BIO          0x200
 #define VDS_DEBUG_FIO          0x400
 #define VDS_DEBUG_BELOCK       0x800
+#define VDS_DEBUG_DEVID                0x1000
 
 extern int vds_dbg;
 extern int vds_dbg_ldc;
diff --git a/drivers/block/vds/vds_devid.c b/drivers/block/vds/vds_devid.c
new file mode 100644 (file)
index 0000000..fd7de19
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * vds_devid.c: LDOM Virtual Disk Server.
+ *
+ * Copyright (C) 2017 Oracle. All rights reserved.
+ */
+
+#include "vds.h"
+#include "vds_io.h"
+#include "vds_devid.h"
+
+static short    devid_gen_number;
+
+static u32 vds_devid_cksum(struct dk_devid *dkdevid)
+{
+       u32 chksum, *ip;
+       int i;
+
+       chksum = 0;
+       ip = (void *)dkdevid;
+       for (i = 0; i < ((DEVID_BLKSIZE - sizeof(int)) / sizeof(int)); i++)
+               chksum ^= ip[i];
+
+       return (chksum);
+}
+
+int vds_devid_valid(struct dk_devid *dkdevid)
+{
+       struct devid_info *id;
+       u16 type;
+       u32 chksum;
+
+       /* validate the revision */
+       if ((dkdevid->dkd_rev_hi != DEVID_REV_MSB) ||
+           (dkdevid->dkd_rev_lo != DEVID_REV_LSB))
+               return DEVID_RET_INVALID;
+
+       /* compute checksum */
+       chksum = vds_devid_cksum(dkdevid);
+
+       /* compare the checksums */
+       if (DEVID_GETCHKSUM(dkdevid) != chksum)
+               return DEVID_RET_INVALID;
+
+       id = (struct devid_info *)dkdevid->dkd_devid;
+
+        if (id->did_magic_hi != DEVID_MAGIC_MSB)
+                return (DEVID_RET_INVALID);
+
+        if (id->did_magic_lo != DEVID_MAGIC_LSB)
+                return (DEVID_RET_INVALID);
+
+        if (id->did_rev_hi != DEVID_REV_MSB)
+                return (DEVID_RET_INVALID);
+
+        if (id->did_rev_lo != DEVID_REV_LSB)
+                return (DEVID_RET_INVALID);
+
+        type = DEVID_GETTYPE(id);
+        if ((type == DEVID_NONE) || (type > DEVID_MAXTYPE))
+                return (DEVID_RET_INVALID);
+
+        return (DEVID_RET_VALID);
+}
+
+/*
+ * Return the sizeof a device id. If called with NULL devid it returns
+ * the amount of space needed to determine the size.
+ */
+size_t vds_devid_sizeof(struct devid_info *id)
+{
+       if (id == NULL)
+               return (sizeof (*id) - sizeof (id->did_id));
+
+       return (sizeof (*id) + DEVID_GETLEN(id) - sizeof (id->did_id));
+}
+
+static int vds_devid_init(u16 devid_type, u16 nbytes, void *id,
+       struct devid_info *i_devid, u32 hostid)
+{
+       int             sz = sizeof (*i_devid) + nbytes - sizeof (char);
+       int             driver_len;
+       const char      *driver_name;
+
+       switch (devid_type) {
+       case DEVID_ENCAP:
+               if (nbytes == 0)
+                       return (-1);
+               if (id == NULL)
+                       return (-1);
+               break;
+       case DEVID_FAB:
+               if (nbytes != 0)
+                       return (-1);
+               if (id != NULL)
+                       return (-1);
+               nbytes = sizeof (int) +
+                   DEVID_TIMEVAL_SIZE + sizeof(short);
+               sz += nbytes;
+               break;
+       default:
+               return (-1);
+       }
+
+       i_devid->did_magic_hi = DEVID_MAGIC_MSB;
+       i_devid->did_magic_lo = DEVID_MAGIC_LSB;
+       i_devid->did_rev_hi = DEVID_REV_MSB;
+       i_devid->did_rev_lo = DEVID_REV_LSB;
+       DEVID_FORMTYPE(i_devid, devid_type);
+       DEVID_FORMLEN(i_devid, nbytes);
+
+       /* Fill in driver name hint */
+       driver_name = "vds";
+       driver_len = strlen(driver_name);
+       if (driver_len > DEVID_HINT_SIZE) {
+               /* Pick up last four characters of driver name */
+               driver_name += driver_len - DEVID_HINT_SIZE;
+               driver_len = DEVID_HINT_SIZE;
+       }
+
+       memcpy(i_devid->did_driver, driver_name, driver_len);
+
+       /* Fill in id field */
+       if (devid_type == DEVID_FAB) {
+               char            *cp;
+               struct          timeval now;
+               short           gen;
+               u32             hi, lo;
+
+               gen = devid_gen_number++;
+
+               cp = i_devid->did_id;
+
+               *cp++ = hibyte(hiword(hostid));
+               *cp++ = lobyte(hiword(hostid));
+               *cp++ = hibyte(loword(hostid));
+               *cp++ = lobyte(loword(hostid));
+
+               do_gettimeofday(&now);
+
+               hi = now.tv_sec;
+               *cp++ = hibyte(hiword(hi));
+               *cp++ = lobyte(hiword(hi));
+               *cp++ = hibyte(loword(hi));
+               *cp++ = lobyte(loword(hi));
+               lo = now.tv_usec;
+               *cp++ = hibyte(hiword(lo));
+               *cp++ = lobyte(hiword(lo));
+               *cp++ = hibyte(loword(lo));
+               *cp++ = lobyte(loword(lo));
+
+               /* fill in the generation number */
+               *cp++ = hibyte(gen);
+               *cp++ = lobyte(gen);
+               vds_devid_dump((u8 *)i_devid, 26, (void *)i_devid,
+                   "vds_devid_init:");
+       } else
+               memcpy(i_devid->did_id, id, nbytes);
+
+       return (0);
+}
+
+void vds_devid_dump(unsigned char *buf, int count, void *address, char *info)
+{
+       int i, j;
+       char bp[256];
+
+       if ((vds_dbg & VDS_DEBUG_DEVID) == 0)
+               return;
+
+       if (info != NULL)
+               pr_warn("%s\n", info);
+
+       memset(bp, 0, 256);
+
+       for (i = j = 0; i < count; i++, j++) {
+               if (j == 16) {
+                       j = 0;
+                       pr_warn("%s\n", bp);
+                       memset(bp, 0, 256);
+               }
+               if (j == 0)
+                       sprintf(&bp[strlen(bp)], "%p: ", address+i);
+               sprintf(&bp[strlen(bp)], "%02x ", buf[i]);
+       }
+       if (j != 0)
+               pr_warn("%s\n", bp);
+}
+
+static int vds_dskimg_get_devid_block(struct vds_port *port, size_t *blkp)
+{
+       struct vio_driver_state *vio = &port->vio;
+       unsigned long long spc, head, cyl;
+
+       vdsdbg(DEVID, "port->label_type=%x\n", port->label_type);
+
+       if (port->label_type == VDS_LABEL_EFI) {
+               vdsdbg(DEVID, "efi_rsvd_partnum=%x\n", port->efi_rsvd_partnum);
+               /*
+                * For an EFI disk, the devid is at the beginning of
+                * the reserved slice
+                */
+               if (port->efi_rsvd_partnum == -1) {
+                       vdsdbg(DEVID, "EFI disk has no reserved slice\n");
+                       return (-ENOSPC);
+               }
+               *blkp = port->part[port->efi_rsvd_partnum].start;
+       } else if (port->label_type == VDS_LABEL_VTOC) {
+               if (port->geom->alt_cyl < 2) {
+                       vdsdbg(DEVID,
+                           "not enough alt cylinders for devid (acyl=%u)\n",
+                           port->geom->alt_cyl);
+                       return (-ENOSPC);
+               }
+
+               /* the devid is in on the track next to the last cylinder */
+               cyl = port->geom->num_cyl + port->geom->alt_cyl - 2;
+               spc = port->geom->num_hd * port->geom->num_sec;
+               head = port->geom->num_hd - 1;
+
+               *blkp = (cyl * (spc - port->geom->apc)) +
+                   (head * port->geom->num_sec) + 1;
+       } else {
+               /* unknown disk label */
+               return (-ENOENT);
+       }
+
+       vdsdbg(DEVID, "devid block: %ld\n", *blkp);
+
+       return 0;
+}
+
+static int vds_dskimg_read_devid(struct vds_port *port,
+       struct devid_info *devid)
+{
+       struct vio_driver_state *vio = &port->vio;
+       struct dk_devid *dkdevid;
+       size_t blk;
+       int sz;
+       int rv = 0;
+
+       rv = vds_dskimg_get_devid_block(port, &blk);
+       if (rv)
+               return rv;
+
+       dkdevid = kzalloc(DEVID_BLKSIZE, GFP_ATOMIC);
+
+       /* get the devid */
+       rv = vds_read(port, (void *)dkdevid, blk, DEVID_BLKSIZE);
+       if (rv) {
+               rv = -EIO;
+               goto done;
+       }
+
+       vds_devid_dump((u8 *)dkdevid, DEVID_BLKSIZE, (void *)dkdevid,
+           "dkdevid:");
+
+       /* validate the device id */
+       if (vds_devid_valid(dkdevid) != 0) {
+               vdsdbg(DEVID, "invalid devid found at block %lu\n", blk);
+               rv = -EINVAL;
+               goto done;
+       }
+
+       vdsdbg(DEVID, "devid read at block %lu\n", blk);
+
+       sz = vds_devid_sizeof((struct devid_info *)dkdevid->dkd_devid);
+       if ((sz > 0) && (sz <= DEVID_BLKSIZE)) {
+               memcpy(devid, dkdevid->dkd_devid, sz);
+               vds_devid_dump((u8 *)devid, sz, (void *)devid, "devid:");
+       }
+       rv = 0;
+done:
+       kfree(dkdevid);
+       return rv;
+
+}
+
+int vds_dskimg_write_devid(struct vds_port *port)
+{
+       struct vio_driver_state *vio = &port->vio;
+       struct dk_devid *dkdevid;
+       struct devid_info *devid = port->devid;
+       u32 chksum;
+       size_t blk;
+       int rv;
+
+       vdsdbg(DEVID, "%s: label_type=%x, devid=%p\n", port->path,
+           port->label_type, devid);
+
+       if (devid == NULL) {
+               /* nothing to write */
+               return 0;
+       }
+
+       rv = vds_dskimg_get_devid_block(port, &blk);
+       if (rv)
+               return -EIO;
+
+       dkdevid = kzalloc(DEVID_BLKSIZE, GFP_ATOMIC);
+
+       /* set revision */
+       dkdevid->dkd_rev_hi = DEVID_REV_MSB;
+       dkdevid->dkd_rev_lo = DEVID_REV_LSB;
+
+       /* copy devid */
+       memcpy(&dkdevid->dkd_devid, devid, vds_devid_sizeof(devid));
+
+       /* compute checksum */
+       chksum = vds_devid_cksum(dkdevid);
+
+       /* set checksum */
+       DEVID_SETCHKSUM(chksum, dkdevid);
+
+       vdsdbg(DEVID, "dkdevid: blk=%ld\n", blk);
+       vds_devid_dump((u8 *)dkdevid, DEVID_BLKSIZE, (void *)dkdevid, NULL);
+
+       /* store the devid */
+       rv = vds_write(port, (void *)dkdevid, blk, DEVID_BLKSIZE);
+       if (rv < 0) {
+               vdsdbg(DEVID, "Error writing devid block at %lu\n", blk);
+               rv = -EIO;
+       } else {
+               vdsdbg(DEVID, "devid written at block %lu\n", blk);
+               rv = 0;
+       }
+
+       kfree(dkdevid);
+       return rv;
+}
+
+int vds_dskimg_init_devid(struct vds_port *port)
+{
+       struct vio_driver_state *vio = &port->vio;
+       int status;
+
+       /* Setup devid for the disk image */
+
+       vdsdbg(DEVID, "%s: label_type=%x\n", port->path, port->label_type);
+
+       if (!S_ISREG(port->mode))       /* Handle disk image only */
+               return 0;
+
+       port->devid = kzalloc(DEVID_BLKSIZE, GFP_KERNEL);
+
+       status = vds_dskimg_read_devid(port, port->devid);
+
+       vdsdbg(DEVID, "read & validate disk image devid, status=%d\n",
+           status);
+       if (status == 0) {
+               /* a valid devid was found */
+               return 0;
+       }
+
+       if (status == -EIO) {
+               /*
+                * There was an error while trying to read the devid.
+                * So this disk image may have a devid but we are
+                * unable to read it.
+                */
+               vdsdbg(DEVID, "cannot read devid\n");
+               kfree(port->devid);
+               port->devid = NULL;
+               return status;
+       }
+
+       /* No valid device id was found so create one. */
+
+       vdsdbg(DEVID, "creating devid\n");
+
+       memset(port->devid, 0, DEVID_BLKSIZE);
+
+       if (vds_devid_init(DEVID_FAB, 0, NULL,
+           (struct devid_info *)port->devid, vds_hostid) != 0) {
+               vdsdbg(DEVID, "fail to create devid\n");
+               kfree(port->devid);
+               port->devid = NULL;
+               return -EINVAL;
+       }
+
+       return 0;
+}
diff --git a/drivers/block/vds/vds_devid.h b/drivers/block/vds/vds_devid.h
new file mode 100644 (file)
index 0000000..594dbe9
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * vds_devid.h: LDOM Virtual Disk Server.
+ *
+ * Copyright (C) 2017 Oracle. All rights reserved.
+ */
+
+/*
+ * Device id types
+ */
+#define        DEVID_NONE              0
+#define        DEVID_SCSI3_WWN         1
+#define        DEVID_SCSI_SERIAL       2
+#define        DEVID_FAB               3
+#define        DEVID_ENCAP             4
+#define        DEVID_ATA_SERIAL        5
+#define        DEVID_SCSI3_VPD_T10     6
+#define        DEVID_SCSI3_VPD_EUI     7
+#define        DEVID_SCSI3_VPD_NAA     8
+#define        DEVID_BLOCK             9
+#define        DEVID_PCI_SERIAL        10
+#define        DEVID_MAXTYPE           10
+
+
+/*
+ * Layout of stored fabricated device id  (on-disk)
+ */
+#define DEVID_BLKSIZE        (512)
+#define DEVID_SIZE           (DEVID_BLKSIZE - ((sizeof(u8) * 7)))
+
+struct dk_devid {
+        u8 dkd_rev_hi;                     /* revision (MSB) */
+        u8 dkd_rev_lo;                     /* revision (LSB) */
+        u8 dkd_flags;                      /* flags (not used yet) */
+       u8 dkd_devid[DEVID_SIZE];          /* devid stored here */
+        u8 dkd_checksum3;                  /* checksum (MSB) */
+        u8 dkd_checksum2;
+        u8 dkd_checksum1;
+        u8 dkd_checksum0;                  /* checksum (LSB) */
+};
+
+#define        DEVID_TIMEVAL_SIZE      8
+
+#ifdef __LITTLE_ENDIAN
+#define lobyte(X)       (((unsigned char *)&(X))[0])
+#define hibyte(X)       (((unsigned char *)&(X))[1])
+#define loword(X)       (((unsigned short *)&(X))[0])
+#define hiword(X)       (((unsigned short *)&(X))[1])
+#endif
+#ifdef __BIG_ENDIAN
+#define lobyte(X)       (((unsigned char *)&(X))[1])
+#define hibyte(X)       (((unsigned char *)&(X))[0])
+#define loword(X)       (((unsigned short *)&(X))[1])
+#define hiword(X)       (((unsigned short *)&(X))[0])
+#endif
+
+#define DEVID_GETCHKSUM(dkd)           \
+       (((dkd)->dkd_checksum3 << 24) + \
+       ((dkd)->dkd_checksum2 << 16) +  \
+       ((dkd)->dkd_checksum1 << 8)  +  \
+       ((dkd)->dkd_checksum0))
+
+#define DEVID_SETCHKSUM(c, dkd)                                        \
+       do {                                                    \
+               (dkd)->dkd_checksum3 = hibyte(hiword((c)));     \
+               (dkd)->dkd_checksum2 = lobyte(hiword((c)));     \
+               (dkd)->dkd_checksum1 = hibyte(loword((c)));     \
+               (dkd)->dkd_checksum0 = lobyte(loword((c)));     \
+       } while (0)
+
+/*
+ * Device id - Internal definition.
+ */
+#define DEVID_MAGIC_MSB         0x69
+#define DEVID_MAGIC_LSB         0x64
+#define DEVID_REV_MSB           0x00
+#define DEVID_REV_LSB           0x01
+#define DEVID_HINT_SIZE         4
+
+struct devid_info {
+        u8 did_magic_hi;                   /* device id magic # (msb) */
+        u8 did_magic_lo;                   /* device id magic # (lsb) */
+        u8 did_rev_hi;                     /* device id revision # (msb) */
+        u8 did_rev_lo;                     /* device id revision # (lsb) */
+        u8 did_type_hi;                    /* device id type (msb) */
+        u8 did_type_lo;                    /* device id type (lsb) */
+        u8 did_len_hi;                     /* length of devid data (msb) */
+        u8 did_len_lo;                     /* length of devid data (lsb) */
+        char    did_driver[DEVID_HINT_SIZE];    /* driver name - HINT */
+        char    did_id[1];                      /* start of device id data */
+};
+
+#define NBBY 8
+
+#define DEVID_GETTYPE(devid)            ((u16) \
+                                            (((devid)->did_type_hi << NBBY) + \
+                                            (devid)->did_type_lo))
+
+#define DEVID_FORMTYPE(devid, type)     (devid)->did_type_hi =          \
+                                            ((type) >> NBBY) & 0xFF;    \
+                                        (devid)->did_type_lo =          \
+                                            (type) & 0xFF;
+
+#define DEVID_GETLEN(devid)             ((u16) \
+                                            (((devid)->did_len_hi << NBBY) + \
+                                            (devid)->did_len_lo))
+
+#define DEVID_FORMLEN(devid, len)       (devid)->did_len_hi =           \
+                                            ((len) >> NBBY) & 0xFF;     \
+                                        (devid)->did_len_lo =           \
+                                            (len) & 0xFF;
+
+#define DEVID_RET_VALID                0
+#define DEVID_RET_INVALID      (-1)
+
+struct efi_uuid {
+        u32        time_low;
+        u16        time_mid;
+        u16        time_hi_and_version;
+        u8         clk_node_addr[8];
+};
+
+typedef struct efi_uuid efi_uuid_t;
+
+#define EFI_RESERVED    { 0x6a945a3b, 0x1dd2, 0x11b2, \
+                            { 0x99, 0xa6, 0x08, 0x00, 0x20, 0x73, 0x66, 0x31 } }
+
+int vds_dskimg_init_devid(struct vds_port *port);
+int vds_dskimg_write_devid(struct vds_port *port);
+size_t vds_devid_sizeof(struct devid_info *id);
+void vds_devid_dump(unsigned char *buf, int count, void *address, char *info);
+
+extern u32 vds_hostid;
+extern void do_gettimeofday(struct timeval *tv);
index 43c929c4390c14be5b4e847833718ce1e52a63ed..be61a041c103c05f60e9658d9338909112b72a62 100644 (file)
@@ -1,11 +1,13 @@
 /*
  * vds_vtoc.c: LDOM Virtual Disk Server.
  *
- * Copyright (C) 2014 Oracle. All rights reserved.
+ * Copyright (C) 2014, 2017 Oracle. All rights reserved.
  */
 
 #include "vds.h"
 #include "vds_io.h"
+#include "vds_devid.h"
+
 #include <../block/partitions/check.h>
 #include <../block/partitions/efi.h>
 #include <linux/byteorder/generic.h>
@@ -85,10 +87,32 @@ static int vds_efi_check_gpt(struct vio_driver_state *vio,
            (unsigned char *)gpt, le32_to_cpu(gpt->header_size));
 }
 
+static bool efi_guid_found(efi_uuid_t *guidp, efi_uuid_t *uuidp)
+{
+       u32 time_low;
+       u16 time_hi_and_version, time_mid;
+
+       time_low = le32_to_cpu(uuidp->time_low);
+       time_mid = le16_to_cpu(uuidp->time_mid);
+       time_hi_and_version = le16_to_cpu(uuidp->time_hi_and_version);
+
+       if ((guidp->time_low == time_low) && (guidp->time_mid == time_mid) &&
+           (guidp->time_hi_and_version == time_hi_and_version)) {
+
+               if (memcmp(guidp->clk_node_addr, uuidp->clk_node_addr,
+                   sizeof(guidp->clk_node_addr)) == 0) {
+                       return true;
+               }
+       }
+       return false;
+}
+
 static void vds_efi_update_part(struct vds_port *port, gpt_entry *gpe)
 {
        int i;
        u64 start, end;
+       efi_guid_t *guidp;
+       efi_uuid_t efi_reserved = EFI_RESERVED;
 
        vds_label_clear_part(port);
 
@@ -100,6 +124,10 @@ static void vds_efi_update_part(struct vds_port *port, gpt_entry *gpe)
                if (start && end) {
                        port->part[i].start = start;
                        port->part[i].size = end - start + 1;
+
+                       guidp = &gpe[i].partition_type_guid;
+                       if (efi_guid_found((efi_uuid_t *)guidp, &efi_reserved))
+                               port->efi_rsvd_partnum = i;
                }
        }
 }
@@ -113,6 +141,8 @@ static int vds_efi_update(struct vds_port *port, gpt_header *gpt)
        gpt_entry *gpe = NULL;
        struct vio_driver_state *vio = &port->vio;
 
+       port->efi_rsvd_partnum = -1;
+
        /*
         * Validate GPT and update partition info.
         */
@@ -204,7 +234,7 @@ int vds_efi_set(struct vds_port *port, sector_t lba, size_t len, void *data)
                vdsmsg(err, "write EFI label failed: rv=%d\n", err);
        } else if (lba == VDS_EFI_GPT) {
                rv = vds_efi_validate(port);
-               if (rv)
+               if (rv) {
                        /*
                         * To convert from EFI to VTOC, Solaris format(1M)
                         * clears the EFI signature, issues a GETGEOM command
@@ -212,6 +242,20 @@ int vds_efi_set(struct vds_port *port, sector_t lba, size_t len, void *data)
                         * ignore invalid signature errors here just in case.
                         */
                        vdsdbg(IOC, "read EFI label failed: rv=%d\n", rv);
+
+               } else if (S_ISREG(port->mode)) {
+                       /*
+                        * When the disk label changes then the location where
+                        * the devid is stored on a disk image can change.
+                        */
+                       rv = vds_dskimg_write_devid(port);
+                       if (rv) {
+                               /* EFI was set, though devid write failed */
+                               vdsdbg(DEVID,
+                                   "vds_efi_set: fail to write devid: rv=%d\n",
+                                   rv);
+                       }
+               }
        }
 
        return err;
index b0e397c31e36ea70a4cb98b47e830f516a42b759..de394d180a8980151907236716c268de74b89df2 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "vds.h"
 #include "vds_io.h"
+#include "vds_devid.h"
 
 #define        VDS_MAX_XFER_SIZE       (128 * 1024)
 #define        VDS_RETRIES             5
@@ -464,6 +465,54 @@ int vd_op_set_geom(struct vds_io *io)
        return rv;
 }
 
+int vd_op_get_devid(struct vds_io *io)
+{
+       int rv;
+       struct vio_driver_state *vio = io->vio;
+       struct vds_port *port = to_vds_port(vio);
+       struct vio_disk_devid *devid_outp = NULL;
+       struct devid_info *devid;
+       int devid_len;
+
+       /* FIXME - add support for block special */
+       if (!S_ISREG(port->mode))
+               return 0;
+
+       if (port->devid == NULL) {
+               vdsdbg(DEVID, "VD_OP_GET_DEVID devid not found\n");
+               return (-ENXIO);
+       }
+
+       devid_outp = kzalloc(roundup(io->desc->size, 8), GFP_KERNEL);
+       if (devid_outp == NULL)
+               return (-ENOMEM);
+
+       devid = (struct devid_info *)port->devid;
+       vdsdbg(DEVID, "port->devid:");
+       vds_devid_dump((u8 *)devid, 26, (void *)devid, NULL);
+
+       devid_len = DEVID_GETLEN(devid);
+
+       devid_outp->type = DEVID_GETTYPE(devid);
+       devid_outp->len = devid_len;
+       memcpy(devid_outp->id, devid->did_id, devid_len);
+
+       vdsdbg(DEVID, "devid struct: type=0x%x, len=%d\n",
+           devid_outp->type, devid_outp->len);
+       vds_devid_dump((u8 *)devid_outp, sizeof(devid_outp),
+           (void *)devid_outp, "devid_outp:");
+       vds_devid_dump((u8 *)devid_outp->id, devid_outp->len,
+           (void *)devid_outp->id, "devid_outp->id:");
+
+       rv = vds_copy(vio, LDC_COPY_OUT, devid_outp, io->desc, 0, 0);
+
+       vdsdbg(DEVID, "VD_OP_GET_DEVID rv=%d\n", rv);
+
+       kfree(devid_outp);
+
+       return rv;
+}
+
 int vd_op_get_efi(struct vds_io *io)
 {
        int rv;
@@ -653,6 +702,24 @@ done:
        return err;
 }
 
+static int vds_be_init_devid(struct vds_port *port)
+{
+       int rv;
+
+       if (S_ISREG(port->mode)) {
+               rv = vds_dskimg_init_devid(port);
+       } else {
+               /*
+                * We should support devid for other backends. For now,
+                * we only support devid for disk images backed by a
+                * file.
+                */
+               rv = 0;
+       }
+
+       return rv;
+}
+
 /*
  * Backend operations.
  */
@@ -676,6 +743,7 @@ int vds_be_init(struct vds_port *port)
 
        inode = path.dentry->d_inode;
        mode = inode->i_mode;
+       port->mode = mode;
        path_put(&path);
 
        vds_be_wlock(port);
@@ -731,6 +799,13 @@ int vds_be_init(struct vds_port *port)
 
        vds_label_init(port);
 
+       /*
+        * Failure to create a device id is not fatal and does not
+        * prevent the disk image from being attached.
+        */
+       (void) vds_be_init_devid(port);
+       rv = 0;
+
 done:
        if (be_lock == read)
                vds_be_runlock(port);
@@ -752,6 +827,8 @@ void vds_be_fini(struct vds_port *port)
                port->be_ops->fini(port);
                port->be_data = NULL;
        }
+       kfree(port->devid);
+       port->devid = NULL;
        vds_be_wunlock(port);
 }
 
index 643eb6f4556a090b0720e765dec9340077aa253d..6492baaefe13b8230251fabf527c8e4dc1f9684c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * vds_io.h: LDOM Virtual Disk Server.
  *
- * Copyright (C) 2014, 2015 Oracle. All rights reserved.
+ * Copyright (C) 2014, 2017 Oracle. All rights reserved.
  */
 
 struct vds_port;
@@ -67,6 +67,7 @@ int vd_op_get_vtoc(struct vds_io *io);
 int vd_op_set_vtoc(struct vds_io *io);
 int vd_op_get_geom(struct vds_io *io);
 int vd_op_set_geom(struct vds_io *io);
+int vd_op_get_devid(struct vds_io *io);
 int vd_op_get_efi(struct vds_io *io);
 int vd_op_set_efi(struct vds_io *io);
 int vd_op_flush(struct vds_io *io);
index 4d94471e858510b9ea258386787d627f51efb2fc..256a65371c676adcbf4d42f77e38b99b7fa9ee1f 100644 (file)
@@ -21,6 +21,7 @@ MODULE_VERSION(DRV_MOD_VERSION);
                                 1 << VD_OP_SET_VTOC |          \
                                 1 << VD_OP_GET_DISKGEOM |      \
                                 1 << VD_OP_SET_DISKGEOM |      \
+                                1 << VD_OP_GET_DEVID |         \
                                 1 << VD_OP_GET_EFI |           \
                                 1 << VD_OP_SET_EFI |           \
                                 1 << VD_OP_FLUSH)
@@ -68,6 +69,7 @@ int vds_ooo;  /* out of order execution default value */
 int vds_dbg;
 int vds_dbg_ldc;
 int vds_dbg_vio;
+u32 vds_hostid;
 
 module_param(vds_dbg, uint, 0664);
 module_param(vds_dbg_ldc, uint, 0664);
@@ -95,6 +97,7 @@ static struct vds_operation vds_operations[] = {
        { VD_OP_SET_VTOC, WRITE, vd_op_set_vtoc },
        { VD_OP_GET_DISKGEOM, READ, vd_op_get_geom },
        { VD_OP_SET_DISKGEOM, WRITE, vd_op_set_geom },
+       { VD_OP_GET_DEVID, READ, vd_op_get_devid },
        { VD_OP_GET_EFI, READ, vd_op_get_efi },
        { VD_OP_SET_EFI, WRITE, vd_op_set_efi },
        { VD_OP_FLUSH, WRITE, vd_op_flush }
@@ -1077,16 +1080,33 @@ static struct vio_driver vds_port_driver = {
 
 static int __init vds_init(void)
 {
+       struct mdesc_handle *hp;
+       const u64 *val;
+       u64 node;
        int rv;
 
        rv = vds_io_init();
-       if (!rv) {
-               rv = vio_register_driver(&vds_port_driver);
-               if (rv < 0)
-                       vds_io_fini();
+       if (rv)
+               return rv;
+
+       rv = vio_register_driver(&vds_port_driver);
+       if (rv < 0) {
+               vds_io_fini();
+               return rv;
+       }
+       hp = mdesc_grab();
+       node = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform");
+       if (node != MDESC_NODE_NULL) {
+               val = mdesc_get_property(hp, node, "hostid", NULL);
+               if (val != NULL)
+                       vds_hostid = *val;
+               else
+                       pr_warn("vds_init: Can't find hostid property.\n");
+       } else {
+               pr_warn("vds_init: Can't find platform node in machine-description.\n");
        }
 
-       return rv;
+       return 0;
 }
 
 static void __exit vds_exit(void)
index d7963a68071dbef3e6d8030a6d7e20a17a94e20f..9f352273962095eb6d5c3aed1fa508f94b24c809 100644 (file)
@@ -7,6 +7,7 @@
 #include "vds.h"
 #include "vds_io.h"
 #include "vds_vtoc.h"
+#include "vds_devid.h"
 
 /*
  * By Solaris convention, slice/partition 2 represents the entire disk;
@@ -373,6 +374,7 @@ int vds_vtoc_set(struct vds_port *port, struct vio_disk_vtoc *vtoc)
 {
        int i, rv;
        struct dk_label *label;
+       struct vio_driver_state *vio = &port->vio;
 
        rv = vds_vtoc_get_label(port, &label);
        if (!label)
@@ -402,6 +404,20 @@ int vds_vtoc_set(struct vds_port *port, struct vio_disk_vtoc *vtoc)
                port->label_type = VDS_LABEL_VTOC;
                port->npart = label->dkl_vtoc.v_nparts;
                vds_vtoc_update_part(port, label);
+               /*
+                * When the disk label changes then the location where
+                * the devid is stored on a disk image can change.
+                */
+               if (S_ISREG(port->mode)) {
+                       rv = vds_dskimg_write_devid(port);
+                       if (rv) {
+                               vdsdbg(DEVID,
+                                   "vds_vtoc_set: fail to write devid\n");
+
+                               /* vtoc was set, though devid write failed */
+                               rv = 0;
+                       }
+               }
        }
 
        /*