From ed062e6e6f750eaed546e6e54965b0b4fec24d7b Mon Sep 17 00:00:00 2001 From: George Kennedy Date: Mon, 15 May 2017 07:43:56 -0700 Subject: [PATCH] SPARC64: Add Linux vds driver Device ID support for Solaris guest boot 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 Reviewed-by: Alexandre Chartre Orabug: 25836231 Signed-off-by: Allen Pais --- drivers/block/vds/Makefile | 2 +- drivers/block/vds/vds.h | 4 + drivers/block/vds/vds_devid.c | 381 ++++++++++++++++++++++++++++++++++ drivers/block/vds/vds_devid.h | 133 ++++++++++++ drivers/block/vds/vds_efi.c | 48 ++++- drivers/block/vds/vds_io.c | 77 +++++++ drivers/block/vds/vds_io.h | 3 +- drivers/block/vds/vds_main.c | 30 ++- drivers/block/vds/vds_vtoc.c | 16 ++ 9 files changed, 685 insertions(+), 9 deletions(-) create mode 100644 drivers/block/vds/vds_devid.c create mode 100644 drivers/block/vds/vds_devid.h diff --git a/drivers/block/vds/Makefile b/drivers/block/vds/Makefile index 102f0766dc30..0efc1b4b0ad5 100644 --- a/drivers/block/vds/Makefile +++ b/drivers/block/vds/Makefile @@ -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 diff --git a/drivers/block/vds/vds.h b/drivers/block/vds/vds.h index fb249c638a01..676409420dac 100644 --- a/drivers/block/vds/vds.h +++ b/drivers/block/vds/vds.h @@ -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 index 000000000000..fd7de19e3135 --- /dev/null +++ b/drivers/block/vds/vds_devid.c @@ -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 index 000000000000..594dbe981b20 --- /dev/null +++ b/drivers/block/vds/vds_devid.h @@ -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); diff --git a/drivers/block/vds/vds_efi.c b/drivers/block/vds/vds_efi.c index 43c929c4390c..be61a041c103 100644 --- a/drivers/block/vds/vds_efi.c +++ b/drivers/block/vds/vds_efi.c @@ -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 @@ -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; diff --git a/drivers/block/vds/vds_io.c b/drivers/block/vds/vds_io.c index b0e397c31e36..de394d180a89 100644 --- a/drivers/block/vds/vds_io.c +++ b/drivers/block/vds/vds_io.c @@ -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); } diff --git a/drivers/block/vds/vds_io.h b/drivers/block/vds/vds_io.h index 643eb6f4556a..6492baaefe13 100644 --- a/drivers/block/vds/vds_io.h +++ b/drivers/block/vds/vds_io.h @@ -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); diff --git a/drivers/block/vds/vds_main.c b/drivers/block/vds/vds_main.c index 4d94471e8585..256a65371c67 100644 --- a/drivers/block/vds/vds_main.c +++ b/drivers/block/vds/vds_main.c @@ -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) diff --git a/drivers/block/vds/vds_vtoc.c b/drivers/block/vds/vds_vtoc.c index d7963a68071d..9f3522739620 100644 --- a/drivers/block/vds/vds_vtoc.c +++ b/drivers/block/vds/vds_vtoc.c @@ -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; + } + } } /* -- 2.50.1