chiark / gitweb /
scsi_id: Export WWN and Unit Serial Number
authorDavid Zeuthen <davidz@redhat.com>
Sat, 21 Nov 2009 01:21:58 +0000 (20:21 -0500)
committerKay Sievers <kay.sievers@vrfy.org>
Mon, 23 Nov 2009 20:38:21 +0000 (21:38 +0100)
Since the Unit Serial Number and the WWN are useful bits of
information export them as properties in the udev database. These
bits of information are often printed on the physical disk, see

http://people.freedesktop.org/~david/FUJITSU-MAY2036RC-sas-disk-picture.jpg

and displaying them separately in the UI (or in /dev/disk) might help
users identify the physical disk. This patch exports these bits of
information as ID_WWN and ID_SCSI_SERIAL.

Note that ata_id also use the ID_WWN property since commit
66094a4a7fc1d303e80785d586800eae9841502b (2009-11-04).

Also print out ID_SCSI=1 so users of the udev database knows the disk
speaks SCSI.

Here's the scsi_id output from one of my SAS disks with these changes:

 ID_SCSI=1
 ID_VENDOR=FUJITSU
 ID_VENDOR_ENC=FUJITSU\x20
 ID_MODEL=MAY2036RC
 ID_MODEL_ENC=MAY2036RC\x20\x20\x20\x20\x20\x20\x20
 ID_REVISION=0103
 ID_TYPE=disk
 ID_SERIAL=3500000e01b83f520
 ID_SERIAL_SHORT=500000e01b83f520
 ID_WWN=0x500000e01b83f520
 ID_SCSI_SERIAL=B3G1P8500RWT

Unfortunately we can't overload ID_SERIAL for two reasons

 1. ID_SERIAL (and ID_SERIAL_SHORT) exported by scsi_id isn't really
    the unit serial number (as defined by SCSI) - it is sometimes the
    WWN (since it is more unique than the Unit Serial Number) and
    complex rules (to make the serial unique) govern what value to
    use.

 2. It would break existing setups if the value of ID_SERIAL changed

Signed-off-by: David Zeuthen <davidz@redhat.com>
extras/scsi_id/scsi_id.c
extras/scsi_id/scsi_id.h
extras/scsi_id/scsi_serial.c

index 72a7257..9c5b2c3 100644 (file)
@@ -549,6 +549,7 @@ static int scsi_id(struct udev *udev, char *maj_min_dev)
        if (export) {
                char serial_str[MAX_SERIAL_LEN];
 
+               printf("ID_SCSI=1\n");
                printf("ID_VENDOR=%s\n", vendor_str);
                printf("ID_VENDOR_ENC=%s\n", vendor_enc_str);
                printf("ID_MODEL=%s\n", model_str);
@@ -563,6 +564,12 @@ static int scsi_id(struct udev *udev, char *maj_min_dev)
                        udev_util_replace_chars(serial_str, NULL);
                        printf("ID_SERIAL_SHORT=%s\n", serial_str);
                }
+               if (dev_scsi.wwn[0] != '\0') {
+                       printf("ID_WWN=0x%s\n", dev_scsi.wwn);
+               }
+               if (dev_scsi.unit_serial_number[0] != '\0') {
+                       printf("ID_SCSI_SERIAL=%s\n", dev_scsi.unit_serial_number);
+               }
                goto out;
        }
 
index 9fcadec..4536cfd 100644 (file)
@@ -39,6 +39,12 @@ struct scsi_id_device {
        char serial[MAX_SERIAL_LEN];
        char serial_short[MAX_SERIAL_LEN];
        int use_sg;
+
+        /* Always from page 0x80 e.g. 'B3G1P8500RWT' - may not be unique */
+        char unit_serial_number[MAX_SERIAL_LEN];
+
+        /* NULs if not set - otherwise hex encoding using lower-case e.g. '50014ee0016eb572' */
+        char wwn[17];
 };
 
 extern int scsi_std_inquiry(struct udev *udev, struct scsi_id_device *dev_scsi, const char *devname);
index ab0ffd6..5b18b22 100644 (file)
@@ -85,11 +85,15 @@ static const char hex_str[]="0123456789abcdef";
 #define SG_ERR_CAT_MEDIA_CHANGED       1       /* interpreted from sense buffer */
 #define SG_ERR_CAT_RESET               2       /* interpreted from sense buffer */
 #define SG_ERR_CAT_TIMEOUT             3
-#define SG_ERR_CAT_RECOVERED           4       /* Successful command after recovered err */
-#define SG_ERR_CAT_NOTSUPPORTED        5       /* Illegal / unsupported command */
+#define SG_ERR_CAT_RECOVERED           4       /* Successful command after recovered err */
+#define SG_ERR_CAT_NOTSUPPORTED                5       /* Illegal / unsupported command */
 #define SG_ERR_CAT_SENSE               98      /* Something else in the sense buffer */
 #define SG_ERR_CAT_OTHER               99      /* Some other error/warning */
 
+static int do_scsi_page80_inquiry(struct udev *udev,
+                                 struct scsi_id_device *dev_scsi, int fd,
+                                 char *serial, char *serial_short, int max_len);
+
 static int sg_err_category_new(struct udev *udev,
                               int scsi_status, int msg_status, int
                               host_status, int driver_status, const
@@ -420,7 +424,7 @@ static int do_scsi_page0_inquiry(struct udev *udev,
                return 1;
        }
        if (buffer[3] > len) {
-               info(udev, "%s: page 0 buffer too long %d\n", dev_scsi->kernel,  buffer[3]);
+               info(udev, "%s: page 0 buffer too long %d\n", dev_scsi->kernel,  buffer[3]);
                return 1;
        }
 
@@ -477,7 +481,8 @@ static int check_fill_0x83_id(struct udev *udev,
                              struct scsi_id_device *dev_scsi,
                              unsigned char *page_83,
                              const struct scsi_id_search_values
-                             *id_search, char *serial, char *serial_short, int max_len)
+                             *id_search, char *serial, char *serial_short, int max_len,
+                              char *wwn)
 {
        int i, j, s, len;
 
@@ -561,6 +566,10 @@ static int check_fill_0x83_id(struct udev *udev,
        }
 
        strcpy(serial_short, &serial[s]);
+
+        if (id_search->id_type == SCSI_ID_NAA && wwn != NULL) {
+                strncpy(wwn, &serial[s], 16);
+        }
        return 0;
 }
 
@@ -591,12 +600,16 @@ static int check_fill_0x83_prespc3(struct udev *udev,
 /* Get device identification VPD page */
 static int do_scsi_page83_inquiry(struct udev *udev,
                                  struct scsi_id_device *dev_scsi, int fd,
-                                 char *serial, char *serial_short, int len)
+                                 char *serial, char *serial_short, int len,
+                                  char *unit_serial_number, char *wwn)
 {
        int retval;
        unsigned int id_ind, j;
        unsigned char page_83[SCSI_INQ_BUFF_LEN];
 
+        /* also pick up the page 80 serial number */
+        do_scsi_page80_inquiry(udev, dev_scsi, fd, NULL, unit_serial_number, MAX_SERIAL_LEN);
+
        memset(page_83, 0, SCSI_INQ_BUFF_LEN);
        retval = scsi_inquiry(udev, dev_scsi, fd, 1, PAGE_83, page_83,
                              SCSI_INQ_BUFF_LEN);
@@ -643,7 +656,8 @@ static int do_scsi_page83_inquiry(struct udev *udev,
                                               serial, serial_short, len);
 
        /*
-        * Search for a match in the prioritized id_search_list.
+        * Search for a match in the prioritized id_search_list - since WWN ids
+         * come first we can pick up the WWN in check_fill_0x83_id().
         */
        for (id_ind = 0;
             id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]);
@@ -656,7 +670,8 @@ static int do_scsi_page83_inquiry(struct udev *udev,
                        retval = check_fill_0x83_id(udev,
                                                    dev_scsi, &page_83[j],
                                                    &id_search_list[id_ind],
-                                                   serial, serial_short, len);
+                                                   serial, serial_short, len,
+                                                    wwn);
                        dbg(udev, "%s id desc %d/%d/%d\n", dev_scsi->kernel,
                                id_search_list[id_ind].id_type,
                                id_search_list[id_ind].naa_type,
@@ -775,15 +790,19 @@ static int do_scsi_page80_inquiry(struct udev *udev,
         * Prepend 'S' to avoid unlikely collision with page 0x83 vendor
         * specific type where we prepend '0' + vendor + model.
         */
-       serial[0] = 'S';
-       ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]);
-       if (ser_ind < 0)
-               return 1;
-       len = buf[3];
-       for (i = 4; i < len + 4; i++, ser_ind++)
-               serial[ser_ind] = buf[i];
-       memcpy(serial_short, &buf[4], len);
-       serial_short[len] = '\0';
+        len = buf[3];
+        if (serial != NULL) {
+                serial[0] = 'S';
+                ser_ind = prepend_vendor_model(udev, dev_scsi, &serial[1]);
+                if (ser_ind < 0)
+                        return 1;
+                for (i = 4; i < len + 4; i++, ser_ind++)
+                        serial[ser_ind] = buf[i];
+        }
+        if (serial_short != NULL) {
+                memcpy(serial_short, &buf[4], len);
+                serial_short[len] = '\0';
+        }
        return 0;
 }
 
@@ -866,7 +885,7 @@ int scsi_get_serial(struct udev *udev,
                        goto completed;
                }
        } else if (page_code == PAGE_83) {
-               if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) {
+               if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn)) {
                        retval = 1;
                        goto completed;
                } else  {
@@ -882,7 +901,7 @@ int scsi_get_serial(struct udev *udev,
                         * conform to pre-SPC3 expectations.
                         */
                        if (retval == 2) {
-                               if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len)) {
+                               if (do_scsi_page83_inquiry(udev, dev_scsi, fd, dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn)) {
                                        retval = 1;
                                        goto completed;
                                } else  {
@@ -922,7 +941,7 @@ int scsi_get_serial(struct udev *udev,
        for (ind = 4; ind <= page0[3] + 3; ind++)
                if (page0[ind] == PAGE_83)
                        if (!do_scsi_page83_inquiry(udev, dev_scsi, fd,
-                                                   dev_scsi->serial, dev_scsi->serial_short, len)) {
+                                                   dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn)) {
                                /*
                                 * Success
                                 */