Merge branch 'upstream' of git://git.infradead.org/users/pcmoore/audit
[deliverable/linux.git] / drivers / scsi / scsi_lib.c
index dd8ad2a44510cae1b97260b7d64fc65e7770db53..fa6b2c4eb7a2b1dc620bcf22db3abf1001a7f3d3 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/scatterlist.h>
 #include <linux/blk-mq.h>
 #include <linux/ratelimit.h>
+#include <asm/unaligned.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -3154,3 +3155,190 @@ void sdev_enable_disk_events(struct scsi_device *sdev)
        atomic_dec(&sdev->disk_events_disable_depth);
 }
 EXPORT_SYMBOL(sdev_enable_disk_events);
+
+/**
+ * scsi_vpd_lun_id - return a unique device identification
+ * @sdev: SCSI device
+ * @id:   buffer for the identification
+ * @id_len:  length of the buffer
+ *
+ * Copies a unique device identification into @id based
+ * on the information in the VPD page 0x83 of the device.
+ * The string will be formatted as a SCSI name string.
+ *
+ * Returns the length of the identification or error on failure.
+ * If the identifier is longer than the supplied buffer the actual
+ * identifier length is returned and the buffer is not zero-padded.
+ */
+int scsi_vpd_lun_id(struct scsi_device *sdev, char *id, size_t id_len)
+{
+       u8 cur_id_type = 0xff;
+       u8 cur_id_size = 0;
+       unsigned char *d, *cur_id_str;
+       unsigned char __rcu *vpd_pg83;
+       int id_size = -EINVAL;
+
+       rcu_read_lock();
+       vpd_pg83 = rcu_dereference(sdev->vpd_pg83);
+       if (!vpd_pg83) {
+               rcu_read_unlock();
+               return -ENXIO;
+       }
+
+       /*
+        * Look for the correct descriptor.
+        * Order of preference for lun descriptor:
+        * - SCSI name string
+        * - NAA IEEE Registered Extended
+        * - EUI-64 based 16-byte
+        * - EUI-64 based 12-byte
+        * - NAA IEEE Registered
+        * - NAA IEEE Extended
+        * as longer descriptors reduce the likelyhood
+        * of identification clashes.
+        */
+
+       /* The id string must be at least 20 bytes + terminating NULL byte */
+       if (id_len < 21) {
+               rcu_read_unlock();
+               return -EINVAL;
+       }
+
+       memset(id, 0, id_len);
+       d = vpd_pg83 + 4;
+       while (d < vpd_pg83 + sdev->vpd_pg83_len) {
+               /* Skip designators not referring to the LUN */
+               if ((d[1] & 0x30) != 0x00)
+                       goto next_desig;
+
+               switch (d[1] & 0xf) {
+               case 0x2:
+                       /* EUI-64 */
+                       if (cur_id_size > d[3])
+                               break;
+                       /* Prefer NAA IEEE Registered Extended */
+                       if (cur_id_type == 0x3 &&
+                           cur_id_size == d[3])
+                               break;
+                       cur_id_size = d[3];
+                       cur_id_str = d + 4;
+                       cur_id_type = d[1] & 0xf;
+                       switch (cur_id_size) {
+                       case 8:
+                               id_size = snprintf(id, id_len,
+                                                  "eui.%8phN",
+                                                  cur_id_str);
+                               break;
+                       case 12:
+                               id_size = snprintf(id, id_len,
+                                                  "eui.%12phN",
+                                                  cur_id_str);
+                               break;
+                       case 16:
+                               id_size = snprintf(id, id_len,
+                                                  "eui.%16phN",
+                                                  cur_id_str);
+                               break;
+                       default:
+                               cur_id_size = 0;
+                               break;
+                       }
+                       break;
+               case 0x3:
+                       /* NAA */
+                       if (cur_id_size > d[3])
+                               break;
+                       cur_id_size = d[3];
+                       cur_id_str = d + 4;
+                       cur_id_type = d[1] & 0xf;
+                       switch (cur_id_size) {
+                       case 8:
+                               id_size = snprintf(id, id_len,
+                                                  "naa.%8phN",
+                                                  cur_id_str);
+                               break;
+                       case 16:
+                               id_size = snprintf(id, id_len,
+                                                  "naa.%16phN",
+                                                  cur_id_str);
+                               break;
+                       default:
+                               cur_id_size = 0;
+                               break;
+                       }
+                       break;
+               case 0x8:
+                       /* SCSI name string */
+                       if (cur_id_size + 4 > d[3])
+                               break;
+                       /* Prefer others for truncated descriptor */
+                       if (cur_id_size && d[3] > id_len)
+                               break;
+                       cur_id_size = id_size = d[3];
+                       cur_id_str = d + 4;
+                       cur_id_type = d[1] & 0xf;
+                       if (cur_id_size >= id_len)
+                               cur_id_size = id_len - 1;
+                       memcpy(id, cur_id_str, cur_id_size);
+                       /* Decrease priority for truncated descriptor */
+                       if (cur_id_size != id_size)
+                               cur_id_size = 6;
+                       break;
+               default:
+                       break;
+               }
+next_desig:
+               d += d[3] + 4;
+       }
+       rcu_read_unlock();
+
+       return id_size;
+}
+EXPORT_SYMBOL(scsi_vpd_lun_id);
+
+/*
+ * scsi_vpd_tpg_id - return a target port group identifier
+ * @sdev: SCSI device
+ *
+ * Returns the Target Port Group identifier from the information
+ * froom VPD page 0x83 of the device.
+ *
+ * Returns the identifier or error on failure.
+ */
+int scsi_vpd_tpg_id(struct scsi_device *sdev, int *rel_id)
+{
+       unsigned char *d;
+       unsigned char __rcu *vpd_pg83;
+       int group_id = -EAGAIN, rel_port = -1;
+
+       rcu_read_lock();
+       vpd_pg83 = rcu_dereference(sdev->vpd_pg83);
+       if (!vpd_pg83) {
+               rcu_read_unlock();
+               return -ENXIO;
+       }
+
+       d = sdev->vpd_pg83 + 4;
+       while (d < sdev->vpd_pg83 + sdev->vpd_pg83_len) {
+               switch (d[1] & 0xf) {
+               case 0x4:
+                       /* Relative target port */
+                       rel_port = get_unaligned_be16(&d[6]);
+                       break;
+               case 0x5:
+                       /* Target port group */
+                       group_id = get_unaligned_be16(&d[6]);
+                       break;
+               default:
+                       break;
+               }
+               d += d[3] + 4;
+       }
+       rcu_read_unlock();
+
+       if (group_id >= 0 && rel_id && rel_port != -1)
+               *rel_id = rel_port;
+
+       return group_id;
+}
+EXPORT_SYMBOL(scsi_vpd_tpg_id);
This page took 0.060139 seconds and 5 git commands to generate.