[PATCH 1/3] scsi_dh: Add modalias support for SCSI targets

Chandra Seetharaman
2009-03-18T01:33:25Z

From: Peter Jones <pjones@redhat.com>

This patch allows the use of modaliases on scsi targets to correctly
load scsi device handler modules when the devices are found.

Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>

---
drivers/scsi/scsi_sysfs.c | 55 +++++++++++++++++++++++++++++++++++++++-
include/linux/mod_devicetable.h | 6 ++++
include/linux/string_helpers.h | 2 +
include/scsi/scsi.h | 1 +
include/scsi/scsi_device.h | 9 +-----
lib/string_helpers.c | 32 +++++++++++++++++++++++
scripts/mod/file2alias.c | 38 ++++++++++++++++++++++++++++
7 files changed, 134 insertions(+), 9 deletions(-)

Index: linux-2.6.28/drivers/scsi/scsi_sysfs.c
===================================================================
--- linux-2.6.28.orig/drivers/scsi/scsi_sysfs.c
+++ linux-2.6.28/drivers/scsi/scsi_sysfs.c
@@ -10,6 +10,7 @@
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/device.h>
+#include <linux/string_helpers.h>

#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -362,16 +363,63 @@ static int scsi_bus_match(struct device
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", 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;
}

@@ -697,8 +745,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);

Index: linux-2.6.28/include/linux/mod_devicetable.h
===================================================================
--- linux-2.6.28.orig/include/linux/mod_devicetable.h
+++ linux-2.6.28/include/linux/mod_devicetable.h
@@ -454,4 +454,10 @@ struct dmi_system_id {

#define DMI_MATCH(a, b) { a, b }

+struct scsi_dh_device_id {
+ unsigned char type;
+ char vendor[9];
+ char model[17];
+};
+
#endif /* LINUX_MOD_DEVICETABLE_H */
Index: linux-2.6.28/include/linux/string_helpers.h
===================================================================
--- linux-2.6.28.orig/include/linux/string_helpers.h
+++ linux-2.6.28/include/linux/string_helpers.h
@@ -13,4 +13,6 @@ enum string_size_units {
int string_get_size(u64 size, enum string_size_units units,
char *buf, int len);

+unsigned char *string_to_hex(const unsigned char *s);
+
#endif
Index: linux-2.6.28/include/scsi/scsi.h
===================================================================
--- linux-2.6.28.orig/include/scsi/scsi.h
+++ linux-2.6.28/include/scsi/scsi.h
@@ -264,6 +264,7 @@ static inline int scsi_status_is_good(in
#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
#define TYPE_RBC 0x0e
#define TYPE_NO_LUN 0x7f
+#define TYPE_ANY 0xff

/* SCSI protocols; these are taken from SPC-3 section 7.5 */
enum scsi_protocol {
Index: linux-2.6.28/include/scsi/scsi_device.h
===================================================================
--- linux-2.6.28.orig/include/scsi/scsi_device.h
+++ linux-2.6.28/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>
@@ -169,11 +170,6 @@ struct scsi_device {
unsigned long sdev_data[0];
} __attribute__((aligned(sizeof(unsigned long))));

-struct scsi_dh_devlist {
- char *vendor;
- char *model;
-};
-
struct scsi_device_handler {
/* Used by the infrastructure */
struct list_head list; /* list of scsi_device_handlers */
@@ -181,7 +177,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 *);
@@ -456,6 +452,5 @@ static inline int scsi_device_protection

#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 */
Index: linux-2.6.28/lib/string_helpers.c
===================================================================
--- linux-2.6.28.orig/lib/string_helpers.c
+++ linux-2.6.28/lib/string_helpers.c
@@ -66,3 +66,35 @@ int string_get_size(u64 size, const enum
return 0;
}
EXPORT_SYMBOL(string_get_size);
+
+/**
+ * string_to_hex - convert a string to a series of hexidecimal values
+ * @s: The string to operate on
+ *
+ * This function returns a GFP_KERNEL allocated buffer filled with
+ * the hexidecimal representation of the value of each character in @s .
+ * Returns a pointer to the allocated string on success and NULL on error,
+ * and the returned string is zero terminated.
+ *
+ */
+unsigned char *string_to_hex(const unsigned char *s)
+{
+ unsigned char *ret, *ptr;
+ static const unsigned char *hex = "0123456789ABCDEF";
+ int len;
+
+ len = strlen(s);
+
+ ret = ptr = kmalloc(len * 2 + 1, GFP_KERNEL);
+ if (!ret)
+ return NULL;
+
+ for (; *s; s++) {
+ *ptr++ = hex[(*s & 0xf0)>>4];
+ *ptr++ = hex[*s & 0x0f];
+ }
+ *ptr = '\0';
+
+ return ret;
+}
+EXPORT_SYMBOL(string_to_hex);
Index: linux-2.6.28/scripts/mod/file2alias.c
===================================================================
--- linux-2.6.28.orig/scripts/mod/file2alias.c
+++ linux-2.6.28/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)
{
@@ -710,6 +726,23 @@ static int do_dmi_entry(const char *file
strcat(alias, ":");
return 1;
}
+
+/* Looks like: scsi:t-NvSmS */
+/* defining TYPE_ANY here is a gross hack to avoid moving all the scsi.h
+ * TYPE_ definitions into mod_devicetable.h */
+#define TYPE_ANY 0xff
+static int do_scsi_entry(const char *filename,
+ struct scsi_dh_device_id *id, char *alias)
+{
+ strcpy(alias, "scsi:");
+ ADD(alias, "t-", id->type != TYPE_ANY, 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)
{
@@ -736,6 +769,7 @@ static void do_table(void *symval, unsig
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);
@@ -849,6 +883,10 @@ void handle_moddevtable(struct module *m
do_table(symval, sym->st_size,
sizeof(struct dmi_system_id), "dmi",
do_dmi_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);
}