From cfaa0404d7208d29b10ed54b7a787d3eede9335c Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Fri, 6 May 2011 10:18:58 -0400
Subject: [PATCH 3/6] scsi_dh: Add modalias support for SCSI targets

This patch allows the use of modaliases on scsi targets to correctly
load scsi device handler modules when the devices are found.
---
 drivers/scsi/device_handler/scsi_dh.c       |   25 +++++++--
 drivers/scsi/device_handler/scsi_dh_alua.c  |   34 +++++++------
 drivers/scsi/device_handler/scsi_dh_emc.c   |   12 +++--
 drivers/scsi/device_handler/scsi_dh_hp_sw.c |   14 +++--
 drivers/scsi/device_handler/scsi_dh_rdac.c  |   68 ++++++++++++++-------------
 drivers/scsi/scsi_sysfs.c                   |   55 +++++++++++++++++++++-
 include/linux/mod_devicetable.h             |    8 +++
 include/scsi/scsi_device.h                  |    9 +---
 scripts/mod/file2alias.c                    |   38 +++++++++++++++
 9 files changed, 188 insertions(+), 75 deletions(-)

diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
index 0119b81..473f9e8 100644
--- a/drivers/scsi/device_handler/scsi_dh.c
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -308,6 +308,19 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data)
 	return 0;
 }
 
+static inline int scsi_dh_list_end(const struct scsi_dh_device_id *id)
+{
+	if (id->type != 0)
+		return 0;
+	if (id->flags != 0)
+		return 0;
+	if (id->vendor[0] != '\0')
+		return 0;
+	if (id->model[0] != '\0')
+		return 0;
+	return 1;
+}
+
 /*
  * scsi_register_device_handler - register a device handler personality
  *      module.
@@ -327,10 +340,10 @@ int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
 	list_add(&scsi_dh->list, &scsi_dh_list);
 	spin_unlock(&list_lock);
 
-	for (i = 0; scsi_dh->devlist[i].vendor; i++) {
+	for (i = 0; !scsi_dh_list_end(&scsi_dh->devlist[i]); i++) {
 		scsi_dev_info_list_add_keyed(0,
-					scsi_dh->devlist[i].vendor,
-					scsi_dh->devlist[i].model,
+					(char *)scsi_dh->devlist[i].vendor,
+					(char *)scsi_dh->devlist[i].model,
 					NULL,
 					scsi_dh->idx,
 					SCSI_DEVINFO_DH);
@@ -360,9 +373,9 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
 	bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
 			 scsi_dh_notifier_remove);
 
-	for (i = 0; scsi_dh->devlist[i].vendor; i++) {
-		scsi_dev_info_list_del_keyed(scsi_dh->devlist[i].vendor,
-					     scsi_dh->devlist[i].model,
+	for (i = 0; !scsi_dh_list_end(&scsi_dh->devlist[i]); i++) {
+		scsi_dev_info_list_del_keyed((char *)scsi_dh->devlist[i].vendor,
+					     (char *)scsi_dh->devlist[i].model,
 					     SCSI_DEVINFO_DH);
 	}
 
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 6fec9fe..b565db4 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -720,24 +720,26 @@ static int alua_prep_fn(struct scsi_device *sdev, struct request *req)
 
 }
 
-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"},
-	{"NETAPP", "LUN C-Mode"},
-	{"AIX", "NVDISK"},
-	{"Promise", "VTrak"},
-	{NULL, NULL}
+static const struct scsi_dh_device_id alua_dev_list[] = {
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "HP", "MSA VOLUME" },
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "HP", "HSV101" },
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "HP", "HSV111" },
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "HP", "HSV200" },
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "HP", "HSV210" },
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "HP", "HSV300" },
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "2107900" },
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "2145" },
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "Pillar", "Axiom" },
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "Intel", "Multi-Flex"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "NETAPP", "LUN"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "NETAPP", "LUN C-Mode"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "AIX", "NVDISK"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "Promise", "VTrak"},
+	{0, 0, "", ""}
 };
 
+MODULE_DEVICE_TABLE(scsi_dh, alua_dev_list);
+
 static int alua_bus_attach(struct scsi_device *sdev);
 static void alua_bus_detach(struct scsi_device *sdev);
 
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
index 48441f6..913f254 100644
--- a/drivers/scsi/device_handler/scsi_dh_emc.c
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -621,13 +621,15 @@ done:
 	return result;
 }
 
-static const struct scsi_dh_devlist clariion_dev_list[] = {
-	{"DGC", "RAID"},
-	{"DGC", "DISK"},
-	{"DGC", "VRAID"},
-	{NULL, NULL},
+static const struct scsi_dh_device_id clariion_dev_list[] = {
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DGC", "RAID"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DGC", "DISK"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DGC", "VRAID"},
+	{0, 0, "", ""},
 };
 
+MODULE_DEVICE_TABLE(scsi_dh, clariion_dev_list);
+
 static int clariion_bus_attach(struct scsi_device *sdev);
 static void clariion_bus_detach(struct scsi_device *sdev);
 
diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
index b479f1e..c8bdcdc 100644
--- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c
+++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
@@ -311,14 +311,16 @@ static int hp_sw_activate(struct scsi_device *sdev,
 	return 0;
 }
 
-static const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
-	{"COMPAQ", "MSA1000 VOLUME"},
-	{"COMPAQ", "HSV110"},
-	{"HP", "HSV100"},
-	{"DEC", "HSG80"},
-	{NULL, NULL},
+static const struct scsi_dh_device_id hp_sw_dh_data_list[] = {
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "COMPAQ", "MSA1000 VOLUME"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "COMPAQ", "HSV110"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "HP", "HSV100"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DEC", "HSG80"},
+	{0, 0, "", ""},
 };
 
+MODULE_DEVICE_TABLE(scsi_dh, hp_sw_dh_data_list);
+
 static int hp_sw_bus_attach(struct scsi_device *sdev);
 static void hp_sw_bus_detach(struct scsi_device *sdev);
 
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index 27c9d65..8053fad 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -782,41 +782,43 @@ static int rdac_check_sense(struct scsi_device *sdev,
 	return SCSI_RETURN_NOT_HANDLED;
 }
 
-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"},
-	{"DELL", "MD36xxi"},
-	{"DELL", "MD36xxf"},
-	{"LSI", "INF-01-00"},
-	{"ENGENIO", "INF-01-00"},
-	{"STK", "FLEXLINE 380"},
-	{"SUN", "CSM100_R_FC"},
-	{"SUN", "STK6580_6780"},
-	{"SUN", "SUN_6180"},
-	{NULL, NULL},
+static const struct scsi_dh_device_id rdac_dev_list[] = {
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "1722"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "1724"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "1726"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "1742"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "1745"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "1746"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "1814"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "1815"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "1818"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "IBM", "3526"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SGI", "TP9400"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SGI", "TP9500"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SGI", "IS"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "STK", "OPENstorage D280"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SUN", "CSM200_R"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SUN", "LCSM100_I"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SUN", "LCSM100_S"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SUN", "LCSM100_E"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SUN", "LCSM100_F"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DELL", "MD3000"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DELL", "MD3000i"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DELL", "MD32xx"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DELL", "MD32xxi"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DELL", "MD36xxi"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "DELL", "MD36xxf"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "LSI", "INF-01-00"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "ENGENIO", "INF-01-00"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "STK", "FLEXLINE 380"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SUN", "CSM100_R_FC"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SUN", "STK6580_6780"},
+	{TYPE_DISK, SCSI_DEVICE_ANY_TYPE, "SUN", "SUN_6180"},
+	{0, 0, "", ""},
 };
 
+MODULE_DEVICE_TABLE(scsi_dh, rdac_dev_list);
+
 static int rdac_bus_attach(struct scsi_device *sdev);
 static void rdac_bus_detach(struct scsi_device *sdev);
 
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 612188c..a61c784 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -12,6 +12,7 @@
 #include <linux/blkdev.h>
 #include <linux/device.h>
 #include <linux/pm_runtime.h>
+#include <linux/string_helpers.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
@@ -361,16 +362,63 @@ static int scsi_bus_match(struct device *dev, struct device_driver *gendrv)
 	return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
 }
 
+static ssize_t format_scsi_modalias(struct scsi_device *sdev, char *buffer,
+				    ssize_t len)
+{
+	char vendor[9];
+	char *hex_vendor;
+	char model[17];
+	char *hex_model;
+	int i;
+
+	strncpy(vendor, sdev->vendor, 8);
+	vendor[8] = '\0';
+	for (i = strlen(vendor) - 1; i >= 0; i--) {
+		if (vendor[i] != ' ')
+			break;
+		vendor[i] = '\0';
+	}
+	hex_vendor = string_to_hex(vendor);
+	if (!hex_vendor)
+		return -ENOMEM;
+
+	strncpy(model, sdev->model, 16);
+	model[8] = '\0';
+	for (i = strlen(model) - 1; i >= 0; i--) {
+		if (model[i] != ' ')
+			break;
+		model[i] = '\0';
+	}
+	hex_model = string_to_hex(model);
+	if (!hex_model) {
+		kfree(hex_vendor);
+		return -ENOMEM;
+	}
+
+	i = snprintf(buffer, len, "scsi:t-0x%02Xv%.16sm%.32s\n", sdev->type,
+		 hex_vendor, hex_model);
+	kfree(hex_vendor);
+	kfree(hex_model);
+	return i;
+}
+
 static int scsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct scsi_device *sdev;
+	char buffer[501];
+	int rc;
 
 	if (dev->type != &scsi_dev_type)
 		return 0;
 
 	sdev = to_scsi_device(dev);
 
-	add_uevent_var(env, "MODALIAS=" SCSI_DEVICE_MODALIAS_FMT, sdev->type);
+	buffer[500] = '\0';
+	rc = format_scsi_modalias(sdev, buffer, 500);
+	if (rc < 0)
+		return rc;
+
+	add_uevent_var(env, "MODALIAS=%s", buffer);
 	return 0;
 }
 
@@ -644,8 +692,11 @@ static ssize_t
 sdev_show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct scsi_device *sdev;
+	ssize_t rc;
+
 	sdev = to_scsi_device(dev);
-	return snprintf (buf, 20, SCSI_DEVICE_MODALIAS_FMT "\n", sdev->type);
+	rc = format_scsi_modalias(sdev, buf, 500);
+	return rc;
 }
 static DEVICE_ATTR(modalias, S_IRUGO, sdev_show_modalias, NULL);
 
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index ae28e93..57291e6 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -533,4 +533,12 @@ struct isapnp_device_id {
 	kernel_ulong_t driver_data;	/* data private to the driver */
 };
 
+#define SCSI_DEVICE_ANY_TYPE 1
+struct scsi_dh_device_id {
+	unsigned char type;
+	unsigned char flags;
+	char vendor[9];
+	char model[17];
+};
+
 #endif /* LINUX_MOD_DEVICETABLE_H */
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 8a72a4c..b23c705 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -1,6 +1,7 @@
 #ifndef _SCSI_SCSI_DEVICE_H
 #define _SCSI_SCSI_DEVICE_H
 
+#include <linux/mod_devicetable.h>
 #include <linux/device.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
@@ -176,11 +177,6 @@ struct scsi_device {
 	unsigned long		sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
 
-struct scsi_dh_devlist {
-	char *vendor;
-	char *model;
-};
-
 typedef void (*activate_complete)(void *, int);
 struct scsi_device_handler {
 	/* Used by the infrastructure */
@@ -190,7 +186,7 @@ struct scsi_device_handler {
 	/* Filled by the hardware handler */
 	struct module *module;
 	const char *name;
-	const struct scsi_dh_devlist *devlist;
+	const struct scsi_dh_device_id *devlist;
 	int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
 	int (*attach)(struct scsi_device *);
 	void (*detach)(struct scsi_device *);
@@ -478,6 +474,5 @@ static inline int scsi_device_tpgs(struct scsi_device *sdev)
 
 #define MODULE_ALIAS_SCSI_DEVICE(type) \
 	MODULE_ALIAS("scsi:t-" __stringify(type) "*")
-#define SCSI_DEVICE_MODALIAS_FMT "scsi:t-0x%02x"
 
 #endif /* _SCSI_SCSI_DEVICE_H */
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index e26e2fb..9202cc0 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -51,6 +51,22 @@ do {                                                            \
                 sprintf(str + strlen(str), "*");                \
 } while(0)
 
+#define ADD_HEX_STR(str, sep, cond, field)				\
+do {									\
+	strcat(str, sep);						\
+	if (cond) {							\
+		char * _s = str + strlen(str);				\
+		char * _f = field;					\
+		static const char *_hex = "0123456789ABCDEF";		\
+									\
+		for (; *_f; _f++) {					\
+			*_s++ = _hex[(*_f & 0xf0)>>4];			\
+			*_s++ = _hex[*_f & 0xf];			\
+		}							\
+	} else								\
+		strcat(str, "*");					\
+} while(0)
+
 /* Always end in a wildcard, for future extension */
 static inline void add_wildcard(char *str)
 {
@@ -859,6 +875,23 @@ static int do_isapnp_entry(const char *filename,
 	return 1;
 }
 
+/* Looks like: scsi:t-NvSmS */
+static int do_scsi_entry(const char *filename,
+			struct scsi_dh_device_id *id, char *alias)
+{
+	strcpy(alias, "scsi:");
+	if (id->flags & SCSI_DEVICE_ANY_TYPE) {
+		strcat(alias, "t-*");
+	} else {
+		sprintf(alias + strlen(alias), "t-0x%02X", id->type);
+	}
+	ADD_HEX_STR(alias, "v", id->vendor[0] != '\0', id->vendor);
+	ADD_HEX_STR(alias, "m", id->model[0] != '\0', id->model);
+
+	add_wildcard(alias);
+	return 1;
+}
+
 /* Ignore any prefix, eg. some architectures prepend _ */
 static inline int sym_is(const char *symbol, const char *name)
 {
@@ -885,6 +918,7 @@ static void do_table(void *symval, unsigned long size,
 	size -= id_size;
 
 	for (i = 0; i < size; i += id_size) {
+		memset(alias, '\0', 500);
 		if (do_entry(mod->name, symval+i, alias)) {
 			buf_printf(&mod->dev_table_buf,
 				   "MODULE_ALIAS(\"%s\");\n", alias);
@@ -1022,6 +1056,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
 		do_table(symval, sym->st_size,
 			sizeof(struct isapnp_device_id), "isa",
 			do_isapnp_entry, mod);
+	else if (sym_is(symname, "__mod_scsi_dh_device_table"))
+		do_table(symval, sym->st_size,
+			 sizeof(struct scsi_dh_device_id), "scsi",
+			 do_scsi_entry, mod);
 	free(zeros);
 }
 
-- 
1.7.6.4

