From dcd1f6b3cda93eaaddc4518eaa8fd1abaa4b2bc7 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 26 Aug 2014 03:44:23 +0000 Subject: [PATCH] scst_vdisk: Make EUI-64 and NAA IDs configurable Make the SCSI device identification page (83h) EUI-64 and NAA IDs configurable. If neither the eui64_id nor the naa_id sysfs attribute has been set, export the first eight bytes of the t10_dev_id as an EUI-64 ID. If the NAA ID but not the EUI-64 ID has been set, report the NAA ID only. If both IDs have been set, report both. Signed-off-by: Bart Van Assche git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@5730 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/README | 27 ++++ scst/README_in-tree | 27 ++++ scst/include/scst.h | 19 +++ scst/src/dev_handlers/scst_vdisk.c | 248 ++++++++++++++++++++++++++--- scst/src/scst_lib.c | 17 ++ 5 files changed, 316 insertions(+), 22 deletions(-) diff --git a/scst/README b/scst/README index 110630d72..f2755bd19 100644 --- a/scst/README +++ b/scst/README @@ -1067,6 +1067,33 @@ Each vdisk_fileio's device has the following attributes in that these first eight characters are unique or VMware will consider these devices as identical. + - eui64_id - allows to set the EUI-64 based device identifier in the + SCSI device identification VPD page (83h). This identifier must be 8, + 12 or 16 bytes long and must be specified in hexadecimal format (EUI = + Extended Unique Identifier). A leading "0x" is allowed but is not + required. Writing a newline into this attribute discards the EUI-64 + identifier. If neither eui64_id nor naa_id have been set the first + eight bytes of the t10_dev_id are used as the EUI-64 ID. If naa_id has + been set but eui64_id has not been set no EUI-64 identifier is + reported in the SCSI device identification VPD page. If eui64_id has + been set the value of this attribute is reported as the EUI-64 ID. The + first three bytes of an EUI-64 ID are a so-called organizationally + unique identifier (OUI). The remaining bytes may be choosen by the + organization that owns the OUI. For more information about OUIs, see + also http://standards.ieee.org/develop/regauth/oui/public.html. + + - naa_id - allows to set the NAA ID in the SCSI INQUIRY response (NAA = + Network Address Authority). This identifier must be 8 or 16 bytes long + and must be specified in hex format. A leading "0x" is allowed but is + not required. Writing a newline into this attribute discards the NAA + ID. If this ID is set it is reported in the SCSI VPD device + identification page (83h). More information about NAA identifiers can + be found in the following documents: + * ANSI T11 committee, Fibre Channel Framing and Signaling Interface - 4 + (FC-FS-4) rev 0.50, May 2014 (http://www.t11.org/). + * IETF, RFC 3980 - T11 Network Address Authority (NAA) Naming Format for + iSCSI Node Names, February 2005 (https://tools.ietf.org/html/rfc3980). + - t10_vend_id - Contents of the T10 VENDOR IDENTIFICATION field of the INQUIRY response. The default value for this field is "SCST_BIO" for vdisk_block devices and "SCST_FIO" for vdisk_fileio devices. diff --git a/scst/README_in-tree b/scst/README_in-tree index 18b10cded..658abb10b 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -914,6 +914,33 @@ Each vdisk_fileio's device has the following attributes in created device at creation time based on the device name and scst_vdisk_ID scst_vdisk.ko module parameter (see below). + - eui64_id - allows to set the EUI-64 based device identifier in the + SCSI device identification VPD page (83h). This identifier must be 8, + 12 or 16 bytes long and must be specified in hexadecimal format (EUI = + Extended Unique Identifier). A leading "0x" is allowed but is not + required. Writing a newline into this attribute discards the EUI-64 + identifier. If neither eui64_id nor naa_id have been set the first + eight bytes of the t10_dev_id are used as the EUI-64 ID. If naa_id has + been set but eui64_id has not been set no EUI-64 identifier is + reported in the SCSI device identification VPD page. If eui64_id has + been set the value of this attribute is reported as the EUI-64 ID. The + first three bytes of an EUI-64 ID are a so-called organizationally + unique identifier (OUI). The remaining bytes may be choosen by the + organization that owns the OUI. For more information about OUIs, see + also http://standards.ieee.org/develop/regauth/oui/public.html. + + - naa_id - allows to set the NAA ID in the SCSI INQUIRY response (NAA = + Network Address Authority). This identifier must be 8 or 16 bytes long + and must be specified in hex format. A leading "0x" is allowed but is + not required. Writing a newline into this attribute discards the NAA + ID. If this ID is set it is reported in the SCSI VPD device + identification page (83h). More information about NAA identifiers can + be found in the following documents: + * ANSI T11 committee, Fibre Channel Framing and Signaling Interface - 4 + (FC-FS-4) rev 0.50, May 2014 (http://www.t11.org/). + * IETF, RFC 3980 - T11 Network Address Authority (NAA) Naming Format for + iSCSI Node Names, February 2005 (https://tools.ietf.org/html/rfc3980). + - t10_vend_id - Contents of the T10 VENDOR IDENTIFICATION field of the INQUIRY response. The default value for this field is "SCST_BIO" for vdisk_block devices and "SCST_FIO" for vdisk_fileio devices. diff --git a/scst/include/scst.h b/scst/include/scst.h index b35a4d13f..6056577d7 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -242,6 +242,25 @@ static inline unsigned int queue_max_hw_sectors(struct request_queue *q) #endif #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +extern int hex_to_bin(char ch); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) +/* + * See also "lib: hex2bin converts ascii hexadecimal string to binary" (commit + * dc88e46029486ed475c71fe1bb696d39511ac8fe). + */ +static inline void hex2bin(u8 *dst, const char *src, size_t count) +{ + while (count--) { + *dst = hex_to_bin(*src++) << 4; + *dst += hex_to_bin(*src++); + dst++; + } +} +#endif + #ifndef __list_for_each /* ToDo: cleanup when both are the same for all relevant kernels */ #define __list_for_each list_for_each diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 9eed6eda7..6cf088cfc 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -202,6 +202,10 @@ struct scst_vdisk_dev { char prod_rev_lvl[4 + 1]; char scsi_device_name[256 + 1]; char t10_dev_id[16+8+2]; /* T10 device ID */ + int eui64_id_len; + uint8_t eui64_id[16]; + int naa_id_len; + uint8_t naa_id[16]; char usn[MAX_USN_LEN]; uint8_t inq_vend_specific[MAX_INQ_VEND_SPECIFIC_LEN]; int inq_vend_specific_len; @@ -392,6 +396,14 @@ static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_eui64_id_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_eui64_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdev_sysfs_naa_id_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t vdev_sysfs_naa_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); static ssize_t vdev_sysfs_usn_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); static ssize_t vdev_sysfs_usn_show(struct kobject *kobj, @@ -462,6 +474,12 @@ static struct kobj_attribute vdev_scsi_device_name_attr = static struct kobj_attribute vdev_t10_dev_id_attr = __ATTR(t10_dev_id, S_IWUSR|S_IRUGO, vdev_sysfs_t10_dev_id_show, vdev_sysfs_t10_dev_id_store); +static struct kobj_attribute vdev_eui64_id_attr = + __ATTR(eui64_id, S_IWUSR|S_IRUGO, vdev_sysfs_eui64_id_show, + vdev_sysfs_eui64_id_store); +static struct kobj_attribute vdev_naa_id_attr = + __ATTR(naa_id, S_IWUSR|S_IRUGO, vdev_sysfs_naa_id_show, + vdev_sysfs_naa_id_store); static struct kobj_attribute vdev_usn_attr = __ATTR(usn, S_IWUSR|S_IRUGO, vdev_sysfs_usn_show, vdev_sysfs_usn_store); static struct kobj_attribute vdev_inq_vend_specific_attr = @@ -495,6 +513,8 @@ static const struct attribute *vdisk_fileio_attrs[] = { &vdev_prod_rev_lvl_attr.attr, &vdev_scsi_device_name_attr.attr, &vdev_t10_dev_id_attr.attr, + &vdev_naa_id_attr.attr, + &vdev_eui64_id_attr.attr, &vdev_usn_attr.attr, &vdev_inq_vend_specific_attr.attr, &vdev_zero_copy_attr.attr, @@ -519,6 +539,8 @@ static const struct attribute *vdisk_blockio_attrs[] = { &vdev_prod_rev_lvl_attr.attr, &vdev_scsi_device_name_attr.attr, &vdev_t10_dev_id_attr.attr, + &vdev_naa_id_attr.attr, + &vdev_eui64_id_attr.attr, &vdev_usn_attr.attr, &vdev_inq_vend_specific_attr.attr, &vdisk_tp_attr.attr, @@ -540,6 +562,8 @@ static const struct attribute *vdisk_nullio_attrs[] = { &vdev_prod_rev_lvl_attr.attr, &vdev_scsi_device_name_attr.attr, &vdev_t10_dev_id_attr.attr, + &vdev_naa_id_attr.attr, + &vdev_eui64_id_attr.attr, &vdev_usn_attr.attr, &vdev_inq_vend_specific_attr.attr, &vdisk_rotational_attr.attr, @@ -557,6 +581,8 @@ static const struct attribute *vcdrom_attrs[] = { &vdev_prod_rev_lvl_attr.attr, &vdev_scsi_device_name_attr.attr, &vdev_t10_dev_id_attr.attr, + &vdev_naa_id_attr.attr, + &vdev_eui64_id_attr.attr, &vdev_usn_attr.attr, &vdev_inq_vend_specific_attr.attr, NULL, @@ -569,7 +595,8 @@ static DEFINE_MUTEX(scst_vdisk_mutex); /* * Protects the device attributes t10_vend_id, vend_specific_id, prod_id, - * prod_rev_lvl, scsi_device_name, t10_dev_id, usn and inq_vend_specific. + * prod_rev_lvl, scsi_device_name, t10_dev_id, eui64_id, naa_id, usn and + * inq_vend_specific. */ static DEFINE_RWLOCK(vdisk_serial_rwlock); @@ -3301,8 +3328,9 @@ static int vdisk_usn_vpd(uint8_t *buf, struct scst_cmd *cmd, static int vdisk_dev_id_vpd(uint8_t *buf, struct scst_cmd *cmd, struct scst_vdisk_dev *virt_dev) { - int i, resp_len, num = 4; + int i, eui64_len = 0, naa_len = 0, resp_len, num = 4; uint16_t tg_id; + u8 *eui64_id = NULL, *naa_id = NULL; buf[1] = 0x83; @@ -3366,30 +3394,48 @@ static int vdisk_dev_id_vpd(uint8_t *buf, struct scst_cmd *cmd, num += 4 + buf[num + 3]; } - /* - * IEEE id - */ - buf[num + 0] = 0x01; /* binary */ + read_lock(&vdisk_serial_rwlock); - /* EUI-64 */ - buf[num + 1] = 0x02; - buf[num + 2] = 0x00; - buf[num + 3] = 0x08; + if (virt_dev->eui64_id_len == 0 && virt_dev->naa_id_len == 0) { + /* + * Compatibility mode: export the first eight bytes of the + * t10_dev_id as an EUI-64 ID. This is not entirely standards + * compliant since t10_dev_id contains an ASCII string and the + * first three bytes of an eight-byte EUI-64 ID are a OUI. + */ + eui64_len = 8; + eui64_id = virt_dev->t10_dev_id; + } else { + if (virt_dev->eui64_id_len) { + eui64_len = virt_dev->eui64_id_len; + eui64_id = virt_dev->eui64_id; + } + if (virt_dev->naa_id_len) { + naa_len = virt_dev->naa_id_len; + naa_id = virt_dev->naa_id; + } + } + if (eui64_len) { + buf[num + 0] = 0x01; /* binary */ + buf[num + 1] = 0x02; /* EUI-64 */ + buf[num + 2] = 0x00; /* reserved */ + buf[num + 3] = eui64_len; + memcpy(&buf[num + 4], eui64_id, eui64_len); + num += 4 + eui64_len; + } + if (naa_len) { + buf[num + 0] = 0x01; /* binary */ + buf[num + 1] = 0x03; /* NAA */ + buf[num + 2] = 0x00; /* reserved */ + buf[num + 3] = naa_len; + memcpy(&buf[num + 4], naa_id, naa_len); + num += 4 + naa_len; + } - /* IEEE id */ - buf[num + 4] = virt_dev->t10_dev_id[0]; - buf[num + 5] = virt_dev->t10_dev_id[1]; - buf[num + 6] = virt_dev->t10_dev_id[2]; + read_unlock(&vdisk_serial_rwlock); - /* IEEE ext id */ - buf[num + 7] = virt_dev->t10_dev_id[3]; - buf[num + 8] = virt_dev->t10_dev_id[4]; - buf[num + 9] = virt_dev->t10_dev_id[5]; - buf[num + 10] = virt_dev->t10_dev_id[6]; - buf[num + 11] = virt_dev->t10_dev_id[7]; - num += buf[num + 3]; + resp_len = num - 4; - resp_len = num; put_unaligned_be16(resp_len, &buf[2]); resp_len += 4; @@ -6148,6 +6194,9 @@ static int vdev_create(struct scst_dev_type *devt, sprintf(virt_dev->scsi_device_name, "%.*s", (int)(sizeof(virt_dev->scsi_device_name) - 1), ""); + virt_dev->eui64_id_len = 0; + virt_dev->naa_id_len = 0; + scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx", dev_id_num); TRACE_DBG("usn %s", virt_dev->usn); @@ -7813,6 +7862,161 @@ static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj, return pos; } +static ssize_t vdev_sysfs_eui64_id_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int res = count; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + while (count > 0 && isspace((uint8_t)buf[0])) { + buf++; + count--; + } + while (count > 0 && isspace((uint8_t)buf[count - 1])) + count--; + if (count >= 2 && buf[0] == '0' && buf[1] == 'x') { + buf += 2; + count -= 2; + } + + switch (count) { + case 0: + case 2 * 8: + case 2 * 12: + case 2 * 16: + break; + default: + res = -EINVAL; + goto out; + } + + write_lock(&vdisk_serial_rwlock); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) || \ + defined(CONFIG_SUSE_KERNEL) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 76) + if (hex2bin(virt_dev->eui64_id, buf, count / 2) == 0) + virt_dev->eui64_id_len = count / 2; + else + res = -EINVAL; +#else + memset(virt_dev->eui64_id, 0, sizeof(virt_dev->eui64_id)); + hex2bin(virt_dev->eui64_id, buf, count / 2); + virt_dev->eui64_id_len = count / 2; +#endif + write_unlock(&vdisk_serial_rwlock); + +out: + return res; +} + +static ssize_t vdev_sysfs_eui64_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i, pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + read_lock(&vdisk_serial_rwlock); + if (virt_dev->eui64_id_len) + pos += sprintf(buf + pos, "0x"); + for (i = 0; i < virt_dev->eui64_id_len; i++) + pos += sprintf(buf + pos, "%02x", virt_dev->eui64_id[i]); + pos += sprintf(buf + pos, "\n%s", virt_dev->eui64_id_len ? + SCST_SYSFS_KEY_MARK "\n" : ""); + read_unlock(&vdisk_serial_rwlock); + + return pos; +} + +static ssize_t vdev_sysfs_naa_id_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int res = -EINVAL, c = count; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + while (c > 0 && isspace((uint8_t)buf[0])) { + buf++; + c--; + } + while (c > 0 && isspace((uint8_t)buf[c - 1])) + c--; + if (c >= 2 && buf[0] == '0' && buf[1] == 'x') { + buf += 2; + c -= 2; + } + + switch (c) { + case 0: + case 2 * 8: + if (strchr("1235cCdDeEfF", buf[0])) + break; + else + goto out; + case 2 * 16: + if (strchr("6", buf[0])) + break; + else + goto out; + default: + goto out; + } + + res = count; + + write_lock(&vdisk_serial_rwlock); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) || \ + defined(CONFIG_SUSE_KERNEL) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 76) + if (hex2bin(virt_dev->naa_id, buf, c / 2) == 0) + virt_dev->naa_id_len = c / 2; + else + res = -EINVAL; +#else + memset(virt_dev->naa_id, 0, sizeof(virt_dev->naa_id)); + hex2bin(virt_dev->naa_id, buf, c / 2); + virt_dev->naa_id_len = c / 2; +#endif + write_unlock(&vdisk_serial_rwlock); + +out: + return res; +} + +static ssize_t vdev_sysfs_naa_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i, pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = dev->dh_priv; + + read_lock(&vdisk_serial_rwlock); + if (virt_dev->naa_id_len) + pos += sprintf(buf + pos, "0x"); + for (i = 0; i < virt_dev->naa_id_len; i++) + pos += sprintf(buf + pos, "%02x", virt_dev->naa_id[i]); + pos += sprintf(buf + pos, "\n%s", virt_dev->naa_id_len ? + SCST_SYSFS_KEY_MARK "\n" : ""); + read_unlock(&vdisk_serial_rwlock); + + return pos; +} + static ssize_t vdev_sysfs_usn_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 16109b8fd..ae416cb0a 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -100,6 +100,23 @@ char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap) } #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +/* + * See also "lib: introduce common method to convert hex digits" (commit + * 903788892ea0fc7fcaf7e8e5fac9a77379fc215b). + */ +int hex_to_bin(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + ch = tolower(ch); + if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + return -1; +} +EXPORT_SYMBOL(hex_to_bin); +#endif + #if !((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && defined(SCSI_EXEC_REQ_FIFO_DEFINED)) && !defined(HAVE_SG_COPY) static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg, #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)