NVMe: Update SCSI Inquiry VPD 83h translation
authorKeith Busch <keith.busch@intel.com>
Thu, 19 Feb 2015 20:42:14 +0000 (13:42 -0700)
committerKeith Busch <keith.busch@intel.com>
Thu, 19 Feb 2015 23:15:35 +0000 (16:15 -0700)
The original translation created collisions on Inquiry VPD 83 for many
existing devices. Newer specifications provide other ways to translate
based on the device's version can be used to create unique identifiers.

Version 1.1 provides an EUI64 field that uniquely identifies each
namespace, and 1.2 added the longer NGUID field for the same reason.
Both follow the IEEE EUI format and readily translate to the SCSI device
identification EUI designator type 2h. For devices implementing either,
the translation will use this type, defaulting to the EUI64 8-byte type if
implemented then NGUID's 16 byte version if not. If neither are provided,
the 1.0 translation is used, and is updated to use the SCSI String format
to guarantee a unique identifier.

Knowing when to use the new fields depends on the nvme controller's
revision. The NVME_VS macro was not decoding this correctly, so that is
fixed in this patch and moved to a more appropriate place.

Since the Identify Namespace structure required an update for the NGUID
field, this patch adds the remaining new 1.2 fields to the structure.

Signed-off-by: Keith Busch <keith.busch@intel.com>
drivers/block/nvme-scsi.c
include/linux/nvme.h
include/uapi/linux/nvme.h

index 5e78568026c339da939a33acd54cbd80891c5a10..5cc907648742d8d0de96b19587ba45b5bdeb180c 100644 (file)
@@ -779,10 +779,8 @@ static int nvme_trans_device_id_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
        struct nvme_dev *dev = ns->dev;
        dma_addr_t dma_addr;
        void *mem;
-       struct nvme_id_ctrl *id_ctrl;
        int res = SNTI_TRANSLATION_SUCCESS;
        int nvme_sc;
-       u8 ieee[4];
        int xfer_len;
        __be32 tmp_id = cpu_to_be32(ns->ns_id);
 
@@ -793,46 +791,60 @@ static int nvme_trans_device_id_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
                goto out_dma;
        }
 
-       /* nvme controller identify */
-       nvme_sc = nvme_identify(dev, 0, 1, dma_addr);
-       res = nvme_trans_status_code(hdr, nvme_sc);
-       if (res)
-               goto out_free;
-       if (nvme_sc) {
-               res = nvme_sc;
-               goto out_free;
-       }
-       id_ctrl = mem;
-
-       /* Since SCSI tried to save 4 bits... [SPC-4(r34) Table 591] */
-       ieee[0] = id_ctrl->ieee[0] << 4;
-       ieee[1] = id_ctrl->ieee[0] >> 4 | id_ctrl->ieee[1] << 4;
-       ieee[2] = id_ctrl->ieee[1] >> 4 | id_ctrl->ieee[2] << 4;
-       ieee[3] = id_ctrl->ieee[2] >> 4;
-
-       memset(inq_response, 0, STANDARD_INQUIRY_LENGTH);
+       memset(inq_response, 0, alloc_len);
        inq_response[1] = INQ_DEVICE_IDENTIFICATION_PAGE;    /* Page Code */
-       inq_response[3] = 20;      /* Page Length */
-       /* Designation Descriptor start */
-       inq_response[4] = 0x01;    /* Proto ID=0h | Code set=1h */
-       inq_response[5] = 0x03;    /* PIV=0b | Asso=00b | Designator Type=3h */
-       inq_response[6] = 0x00;    /* Rsvd */
-       inq_response[7] = 16;      /* Designator Length */
-       /* Designator start */
-       inq_response[8] = 0x60 | ieee[3]; /* NAA=6h | IEEE ID MSB, High nibble*/
-       inq_response[9] = ieee[2];        /* IEEE ID */
-       inq_response[10] = ieee[1];       /* IEEE ID */
-       inq_response[11] = ieee[0];       /* IEEE ID| Vendor Specific ID... */
-       inq_response[12] = (dev->pci_dev->vendor & 0xFF00) >> 8;
-       inq_response[13] = (dev->pci_dev->vendor & 0x00FF);
-       inq_response[14] = dev->serial[0];
-       inq_response[15] = dev->serial[1];
-       inq_response[16] = dev->model[0];
-       inq_response[17] = dev->model[1];
-       memcpy(&inq_response[18], &tmp_id, sizeof(u32));
-       /* Last 2 bytes are zero */
+       if (readl(&dev->bar->vs) >= NVME_VS(1, 1)) {
+               struct nvme_id_ns *id_ns = mem;
+               void *eui = id_ns->eui64;
+               int len = sizeof(id_ns->eui64);
 
-       xfer_len = min(alloc_len, STANDARD_INQUIRY_LENGTH);
+               nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
+               res = nvme_trans_status_code(hdr, nvme_sc);
+               if (res)
+                       goto out_free;
+               if (nvme_sc) {
+                       res = nvme_sc;
+                       goto out_free;
+               }
+
+               if (readl(&dev->bar->vs) >= NVME_VS(1, 2)) {
+                       if (bitmap_empty(eui, len * 8)) {
+                               eui = id_ns->nguid;
+                               len = sizeof(id_ns->nguid);
+                       }
+               }
+               if (bitmap_empty(eui, len * 8))
+                       goto scsi_string;
+
+               inq_response[3] = 4 + len; /* Page Length */
+               /* Designation Descriptor start */
+               inq_response[4] = 0x01;    /* Proto ID=0h | Code set=1h */
+               inq_response[5] = 0x02;    /* PIV=0b | Asso=00b | Designator Type=2h */
+               inq_response[6] = 0x00;    /* Rsvd */
+               inq_response[7] = len;     /* Designator Length */
+               memcpy(&inq_response[8], eui, len);
+       } else {
+ scsi_string:
+               if (alloc_len < 72) {
+                       res = nvme_trans_completion(hdr,
+                                       SAM_STAT_CHECK_CONDITION,
+                                       ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
+                                       SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
+                       goto out_free;
+               }
+               inq_response[3] = 0x48;    /* Page Length */
+               /* Designation Descriptor start */
+               inq_response[4] = 0x03;    /* Proto ID=0h | Code set=3h */
+               inq_response[5] = 0x08;    /* PIV=0b | Asso=00b | Designator Type=8h */
+               inq_response[6] = 0x00;    /* Rsvd */
+               inq_response[7] = 0x44;    /* Designator Length */
+
+               sprintf(&inq_response[8], "%04x", dev->pci_dev->vendor);
+               memcpy(&inq_response[12], dev->model, sizeof(dev->model));
+               sprintf(&inq_response[52], "%04x", tmp_id);
+               memcpy(&inq_response[56], dev->serial, sizeof(dev->serial));
+       }
+       xfer_len = alloc_len;
        res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
 
  out_free:
@@ -2222,7 +2234,7 @@ static int nvme_trans_inquiry(struct nvme_ns *ns, struct sg_io_hdr *hdr,
        page_code = GET_INQ_PAGE_CODE(cmd);
        alloc_len = GET_INQ_ALLOC_LENGTH(cmd);
 
-       inq_response = kmalloc(STANDARD_INQUIRY_LENGTH, GFP_KERNEL);
+       inq_response = kmalloc(alloc_len, GFP_KERNEL);
        if (inq_response == NULL) {
                res = -ENOMEM;
                goto out_mem;
index cca264db24785bc9648ac70c0dbd1dd43dfe36cf..1f062a9e521d4517404982472cf0cd183ae3f1e4 100644 (file)
@@ -62,8 +62,6 @@ enum {
        NVME_CSTS_SHST_MASK     = 3 << 2,
 };
 
-#define NVME_VS(major, minor)  (major << 16 | minor)
-
 extern unsigned char nvme_io_timeout;
 #define NVME_IO_TIMEOUT        (nvme_io_timeout * HZ)
 
index 406bfc95652c49d6fdcd85a15b24a63ca3d9fed6..aef9a81b2d75b9524000f3b1b1bf858464e575e2 100644 (file)
@@ -115,7 +115,13 @@ struct nvme_id_ns {
        __le16                  nawun;
        __le16                  nawupf;
        __le16                  nacwu;
-       __u8                    rsvd40[80];
+       __le16                  nabsn;
+       __le16                  nabo;
+       __le16                  nabspf;
+       __u16                   rsvd46;
+       __le64                  nvmcap[2];
+       __u8                    rsvd64[40];
+       __u8                    nguid[16];
        __u8                    eui64[8];
        struct nvme_lbaf        lbaf[16];
        __u8                    rsvd192[192];
@@ -565,6 +571,8 @@ struct nvme_passthru_cmd {
        __u32   result;
 };
 
+#define NVME_VS(major, minor) (((major) << 16) | ((minor) << 8))
+
 #define nvme_admin_cmd nvme_passthru_cmd
 
 #define NVME_IOCTL_ID          _IO('N', 0x40)