Subject: Add TGPS setting to scsi devices
From: Hannes Reinecke <hare@suse.de>
Patch-mainline: Not yet

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  |   70 +++-------------------------
 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  |   56 +++++++++++-----------
 drivers/scsi/scsi_scan.c                    |    1 
 drivers/scsi/scsi_sysfs.c                   |    2 
 include/scsi/scsi_device.h                  |    4 +
 8 files changed, 59 insertions(+), 101 deletions(-)

--- 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_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -124,43 +124,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
  */
@@ -332,23 +295,19 @@ static unsigned submit_stpg(struct alua_
 }
 
 /*
- * 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,
@@ -610,7 +569,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;
 
@@ -684,19 +643,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" },
-	{"Intel", "Multi-Flex"},
-	{"NETAPP", "LUN"},
-	{"AIX", "NVDISK"},
-	{NULL, NULL}
+	{"", "", 3 },
+	{NULL, NULL, 0}
 };
 
 static int alua_bus_attach(struct scsi_device *sdev);
--- a/drivers/scsi/device_handler/scsi_dh_emc.c
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -623,10 +623,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
@@ -310,11 +310,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
@@ -744,34 +744,34 @@ 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", "1745"},
-	{"IBM", "1746"},
-	{"IBM", "1814"},
-	{"IBM", "1815"},
-	{"IBM", "1818"},
-	{"IBM", "3526"},
-	{"SGI", "TP9400"},
-	{"SGI", "TP9500"},
-	{"SGI", "IS"},
-	{"STK", "OPENstorage D280"},
-	{"SUN", "CSM200_R"},
-	{"SUN", "LCSM100_I"},
-	{"SUN", "LCSM100_S"},
-	{"SUN", "LCSM100_E"},
-	{"SUN", "LCSM100_F"},
-	{"DELL", "MD3000"},
-	{"DELL", "MD3000i"},
-	{"DELL", "MD32xx"},
-	{"DELL", "MD32xxi"},
-	{"LSI", "INF-01-00"},
-	{"ENGENIO", "INF-01-00"},
-	{"STK", "FLEXLINE 380"},
-	{"SUN", "CSM100_R_FC"},
-	{NULL, NULL},
+	{"IBM", "1722", 0},
+	{"IBM", "1724", 0},
+	{"IBM", "1726", 0},
+	{"IBM", "1742", 0},
+	{"IBM", "1745", 0},
+	{"IBM", "1746", 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_I", 0},
+	{"SUN", "LCSM100_S", 0},
+	{"SUN", "LCSM100_E", 0},
+	{"SUN", "LCSM100_F", 0},
+	{"DELL", "MD3000", 0},
+	{"DELL", "MD3000i", 0},
+	{"DELL", "MD32xx", 0},
+	{"DELL", "MD32xxi", 0},
+	{"LSI", "INF-01-00", 0},
+	{"ENGENIO", "INF-01-00", 0},
+	{"STK", "FLEXLINE 380", 0},
+	{"SUN", "CSM100_R_FC", 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
@@ -837,6 +837,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
@@ -99,7 +99,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' ... */
@@ -176,6 +177,7 @@ struct scsi_device {
 struct scsi_dh_devlist {
 	char *vendor;
 	char *model;
+	char tgps;
 };
 
 typedef void (*activate_complete)(void *, int);
