scsi_dh_alua: use unique device id
authorHannes Reinecke <hare@suse.de>
Fri, 19 Feb 2016 08:17:05 +0000 (09:17 +0100)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 24 Feb 2016 02:27:02 +0000 (21:27 -0500)
Use scsi_vpd_lun_id() to assign a unique device identification
to the alua port group structure.

Reviewed-by: Bart Van Assche <bart.vanassche@sandisk.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/device_handler/scsi_dh_alua.c

index e9fb7600075027438f095721bc7c8919e90f594a..0bcd901a643596418e1e690dab925c21971f45a1 100644 (file)
@@ -70,6 +70,8 @@ static DEFINE_SPINLOCK(port_group_lock);
 struct alua_port_group {
        struct kref             kref;
        struct list_head        node;
+       unsigned char           device_id_str[256];
+       int                     device_id_len;
        int                     group_id;
        int                     tpgs;
        int                     state;
@@ -162,6 +164,26 @@ static int submit_stpg(struct scsi_device *sdev, int group_id,
                                      ALUA_FAILOVER_RETRIES, NULL, req_flags);
 }
 
+struct alua_port_group *alua_find_get_pg(char *id_str, size_t id_size,
+                                        int group_id)
+{
+       struct alua_port_group *pg;
+
+       list_for_each_entry(pg, &port_group_list, node) {
+               if (pg->group_id != group_id)
+                       continue;
+               if (pg->device_id_len != id_size)
+                       continue;
+               if (strncmp(pg->device_id_str, id_str, id_size))
+                       continue;
+               if (!kref_get_unless_zero(&pg->kref))
+                       continue;
+               return pg;
+       }
+
+       return NULL;
+}
+
 /*
  * alua_alloc_pg - Allocate a new port_group structure
  * @sdev: scsi device
@@ -174,17 +196,39 @@ static int submit_stpg(struct scsi_device *sdev, int group_id,
 struct alua_port_group *alua_alloc_pg(struct scsi_device *sdev,
                                      int group_id, int tpgs)
 {
-       struct alua_port_group *pg;
+       struct alua_port_group *pg, *tmp_pg;
 
        pg = kzalloc(sizeof(struct alua_port_group), GFP_KERNEL);
        if (!pg)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
+       pg->device_id_len = scsi_vpd_lun_id(sdev, pg->device_id_str,
+                                           sizeof(pg->device_id_str));
+       if (pg->device_id_len <= 0) {
+               /*
+                * Internal error: TPGS supported but no device
+                * identifcation found. Disable ALUA support.
+                */
+               kfree(pg);
+               sdev_printk(KERN_INFO, sdev,
+                           "%s: No device descriptors found\n",
+                           ALUA_DH_NAME);
+               return ERR_PTR(-ENXIO);
+       }
        pg->group_id = group_id;
        pg->tpgs = tpgs;
        pg->state = TPGS_STATE_OPTIMIZED;
        kref_init(&pg->kref);
+
        spin_lock(&port_group_lock);
+       tmp_pg = alua_find_get_pg(pg->device_id_str, pg->device_id_len,
+                                 group_id);
+       if (tmp_pg) {
+               spin_unlock(&port_group_lock);
+               kfree(pg);
+               return tmp_pg;
+       }
+
        list_add(&pg->node, &port_group_list);
        spin_unlock(&port_group_lock);
 
@@ -269,7 +313,7 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h)
        h->group_id = group_id;
 
        sdev_printk(KERN_INFO, sdev,
-                   "%s: port group %02x rel port %02x\n",
+                   "%s: port group %x rel port %x\n",
                    ALUA_DH_NAME, h->group_id, h->rel_port);
 
        return 0;
@@ -597,8 +641,9 @@ static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h)
                goto out;
 
        h->pg = alua_alloc_pg(sdev, h->group_id, tpgs);
-       if (!h->pg) {
-               err = SCSI_DH_NOMEM;
+       if (IS_ERR(h->pg)) {
+               if (PTR_ERR(h->pg) == -ENOMEM)
+                       err = SCSI_DH_NOMEM;
                goto out;
        }
        kref_get(&h->pg->kref);