--- /dev/null
+Subject: Add TGPS setting to scsi devices
+From: Hannes Reinecke <hare@suse.de>
+
+Some multipath-capable storage arrays are capable of running
+in compatible mode, ie supporting both the original vendor-specific
+failover mode and the SPC-3 compliant ALUA mode.
+This patch stores the TGPS setting in the sdev so that we can directly
+match onto it and select the correct device handler automatically.
+And we can save code in the ALUA device handler.
+
+Signed-off-by: Hannes Reinecke <hare@suse.de>
+
+---
+ drivers/scsi/device_handler/scsi_dh.c | 9 ++-
+ drivers/scsi/device_handler/scsi_dh_alua.c | 67 +++-------------------------
+ drivers/scsi/device_handler/scsi_dh_emc.c | 8 +--
+ drivers/scsi/device_handler/scsi_dh_hp_sw.c | 10 ++--
+ drivers/scsi/device_handler/scsi_dh_rdac.c | 34 +++++++-------
+ drivers/scsi/scsi_scan.c | 1
+ drivers/scsi/scsi_sysfs.c | 2
+ include/scsi/scsi_device.h | 4 +
+ 8 files changed, 48 insertions(+), 87 deletions(-)
+
+--- a/drivers/scsi/device_handler/scsi_dh_alua.c
++++ b/drivers/scsi/device_handler/scsi_dh_alua.c
+@@ -118,43 +118,6 @@ static struct request *get_alua_req(stru
+ }
+
+ /*
+- * submit_std_inquiry - Issue a standard INQUIRY command
+- * @sdev: sdev the command should be send to
+- */
+-static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
+-{
+- struct request *rq;
+- int err = SCSI_DH_RES_TEMP_UNAVAIL;
+-
+- rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ);
+- if (!rq)
+- goto done;
+-
+- /* Prepare the command. */
+- rq->cmd[0] = INQUIRY;
+- rq->cmd[1] = 0;
+- rq->cmd[2] = 0;
+- rq->cmd[4] = ALUA_INQUIRY_SIZE;
+- rq->cmd_len = COMMAND_SIZE(INQUIRY);
+-
+- rq->sense = h->sense;
+- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+- rq->sense_len = h->senselen = 0;
+-
+- err = blk_execute_rq(rq->q, NULL, rq, 1);
+- if (err == -EIO) {
+- sdev_printk(KERN_INFO, sdev,
+- "%s: std inquiry failed with %x\n",
+- ALUA_DH_NAME, rq->errors);
+- h->senselen = rq->sense_len;
+- err = SCSI_DH_IO;
+- }
+- blk_put_request(rq);
+-done:
+- return err;
+-}
+-
+-/*
+ * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command
+ * @sdev: sdev the command should be sent to
+ */
+@@ -281,23 +244,19 @@ done:
+ }
+
+ /*
+- * alua_std_inquiry - Evaluate standard INQUIRY command
++ * alua_check_tgps - Evaluate TGPS setting
+ * @sdev: device to be checked
+ *
+- * Just extract the TPGS setting to find out if ALUA
++ * Just examine the TPGS setting of the device to find out if ALUA
+ * is supported.
+ */
+-static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h)
++static int alua_check_tgps(struct scsi_device *sdev, struct alua_dh_data *h)
+ {
+- int err;
+-
+- err = submit_std_inquiry(sdev, h);
+-
+- if (err != SCSI_DH_OK)
+- return err;
++ int err = SCSI_DH_OK;
+
+ /* Check TPGS setting */
+- h->tpgs = (h->inq[5] >> 4) & 0x3;
++ h->tpgs = sdev->tgps;
++
+ switch (h->tpgs) {
+ case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT:
+ sdev_printk(KERN_INFO, sdev,
+@@ -609,7 +568,7 @@ static int alua_initialize(struct scsi_d
+ {
+ int err;
+
+- err = alua_std_inquiry(sdev, h);
++ err = alua_check_tgps(sdev, h);
+ if (err != SCSI_DH_OK)
+ goto out;
+
+@@ -674,16 +633,8 @@ static int alua_prep_fn(struct scsi_devi
+ }
+
+ static const struct scsi_dh_devlist alua_dev_list[] = {
+- {"HP", "MSA VOLUME" },
+- {"HP", "HSV101" },
+- {"HP", "HSV111" },
+- {"HP", "HSV200" },
+- {"HP", "HSV210" },
+- {"HP", "HSV300" },
+- {"IBM", "2107900" },
+- {"IBM", "2145" },
+- {"Pillar", "Axiom" },
+- {NULL, NULL}
++ {"", "", 3 },
++ {NULL, NULL, 0}
+ };
+
+ static int alua_bus_attach(struct scsi_device *sdev);
+--- a/drivers/scsi/device_handler/scsi_dh.c
++++ b/drivers/scsi/device_handler/scsi_dh.c
+@@ -28,6 +28,7 @@ struct scsi_dh_devinfo_list {
+ struct list_head node;
+ char vendor[9];
+ char model[17];
++ char tgps;
+ struct scsi_device_handler *handler;
+ };
+
+@@ -60,7 +61,8 @@ scsi_dh_cache_lookup(struct scsi_device
+ spin_lock(&list_lock);
+ list_for_each_entry(tmp, &scsi_dh_dev_list, node) {
+ if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) &&
+- !strncmp(sdev->model, tmp->model, strlen(tmp->model))) {
++ !strncmp(sdev->model, tmp->model, strlen(tmp->model)) &&
++ (!tmp->tgps || (sdev->tgps & tmp->tgps) != 0)) {
+ found_dh = tmp->handler;
+ break;
+ }
+@@ -79,7 +81,9 @@ static int scsi_dh_handler_lookup(struct
+ if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor,
+ strlen(scsi_dh->devlist[i].vendor)) &&
+ !strncmp(sdev->model, scsi_dh->devlist[i].model,
+- strlen(scsi_dh->devlist[i].model))) {
++ strlen(scsi_dh->devlist[i].model)) &&
++ (!scsi_dh->devlist[i].tgps ||
++ (sdev->tgps & scsi_dh->devlist[i].tgps) != 0)) {
+ found = 1;
+ break;
+ }
+@@ -128,6 +132,7 @@ device_handler_match(struct scsi_device_
+ strncpy(tmp->model, sdev->model, 16);
+ tmp->vendor[8] = '\0';
+ tmp->model[16] = '\0';
++ tmp->tgps = sdev->tgps;
+ tmp->handler = found_dh;
+ spin_lock(&list_lock);
+ list_add(&tmp->node, &scsi_dh_dev_list);
+--- a/drivers/scsi/device_handler/scsi_dh_emc.c
++++ b/drivers/scsi/device_handler/scsi_dh_emc.c
+@@ -563,10 +563,10 @@ done:
+ }
+
+ static const struct scsi_dh_devlist clariion_dev_list[] = {
+- {"DGC", "RAID"},
+- {"DGC", "DISK"},
+- {"DGC", "VRAID"},
+- {NULL, NULL},
++ {"DGC", "RAID", 0},
++ {"DGC", "DISK", 0},
++ {"DGC", "VRAID", 0},
++ {NULL, NULL, 0},
+ };
+
+ static int clariion_bus_attach(struct scsi_device *sdev);
+--- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c
++++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
+@@ -283,11 +283,11 @@ static int hp_sw_activate(struct scsi_de
+ }
+
+ static const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
+- {"COMPAQ", "MSA1000 VOLUME"},
+- {"COMPAQ", "HSV110"},
+- {"HP", "HSV100"},
+- {"DEC", "HSG80"},
+- {NULL, NULL},
++ {"COMPAQ", "MSA1000 VOLUME", 0},
++ {"COMPAQ", "HSV110", 0},
++ {"HP", "HSV100", 0},
++ {"DEC", "HSG80", 0},
++ {NULL, NULL, 0},
+ };
+
+ static int hp_sw_bus_attach(struct scsi_device *sdev);
+--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
++++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
+@@ -583,23 +583,23 @@ static int rdac_check_sense(struct scsi_
+ }
+
+ static const struct scsi_dh_devlist rdac_dev_list[] = {
+- {"IBM", "1722"},
+- {"IBM", "1724"},
+- {"IBM", "1726"},
+- {"IBM", "1742"},
+- {"IBM", "1814"},
+- {"IBM", "1815"},
+- {"IBM", "1818"},
+- {"IBM", "3526"},
+- {"SGI", "TP9400"},
+- {"SGI", "TP9500"},
+- {"SGI", "IS"},
+- {"STK", "OPENstorage D280"},
+- {"SUN", "CSM200_R"},
+- {"SUN", "LCSM100_F"},
+- {"DELL", "MD3000"},
+- {"DELL", "MD3000i"},
+- {NULL, NULL},
++ {"IBM", "1722", 0},
++ {"IBM", "1724", 0},
++ {"IBM", "1726", 0},
++ {"IBM", "1742", 0},
++ {"IBM", "1814", 0},
++ {"IBM", "1815", 0},
++ {"IBM", "1818", 0},
++ {"IBM", "3526", 0},
++ {"SGI", "TP9400", 0},
++ {"SGI", "TP9500", 0},
++ {"SGI", "IS", 0},
++ {"STK", "OPENstorage D280", 0},
++ {"SUN", "CSM200_R", 0},
++ {"SUN", "LCSM100_F", 0},
++ {"DELL", "MD3000", 0},
++ {"DELL", "MD3000i", 0},
++ {NULL, NULL, 0},
+ };
+
+ static int rdac_bus_attach(struct scsi_device *sdev);
+--- a/drivers/scsi/scsi_scan.c
++++ b/drivers/scsi/scsi_scan.c
+@@ -821,6 +821,7 @@ static int scsi_add_lun(struct scsi_devi
+ sdev->inq_periph_qual = (inq_result[0] >> 5) & 7;
+ sdev->lockable = sdev->removable;
+ sdev->soft_reset = (inq_result[7] & 1) && ((inq_result[3] & 7) == 2);
++ sdev->tgps = (inq_result[5] >> 4) & 3;
+
+ if (sdev->scsi_level >= SCSI_3 ||
+ (sdev->inquiry_len > 56 && inq_result[56] & 0x04))
+--- a/drivers/scsi/scsi_sysfs.c
++++ b/drivers/scsi/scsi_sysfs.c
+@@ -543,6 +543,7 @@ sdev_rd_attr (scsi_level, "%d\n");
+ sdev_rd_attr (vendor, "%.8s\n");
+ sdev_rd_attr (model, "%.16s\n");
+ sdev_rd_attr (rev, "%.4s\n");
++sdev_rd_attr (tgps, "%d\n");
+
+ /*
+ * TODO: can we make these symlinks to the block layer ones?
+@@ -728,6 +729,7 @@ static struct attribute *scsi_sdev_attrs
+ &dev_attr_vendor.attr,
+ &dev_attr_model.attr,
+ &dev_attr_rev.attr,
++ &dev_attr_tgps.attr,
+ &dev_attr_rescan.attr,
+ &dev_attr_delete.attr,
+ &dev_attr_state.attr,
+--- a/include/scsi/scsi_device.h
++++ b/include/scsi/scsi_device.h
+@@ -96,7 +96,8 @@ struct scsi_device {
+ void *hostdata; /* available to low-level driver */
+ char type;
+ char scsi_level;
+- char inq_periph_qual; /* PQ from INQUIRY data */
++ char inq_periph_qual; /* PQ from INQUIRY data */
++ char tgps; /* Target port group support */
+ unsigned char inquiry_len; /* valid bytes in 'inquiry' */
+ unsigned char * inquiry; /* INQUIRY response data */
+ const char * vendor; /* [back_compat] point into 'inquiry' ... */
+@@ -174,6 +175,7 @@ struct scsi_device {
+ struct scsi_dh_devlist {
+ char *vendor;
+ char *model;
++ char tgps;
+ };
+
+ struct scsi_device_handler {