|
From: Nao N. <nao...@hi...> - 2011-02-25 15:31:05
|
Add a SCSI option for persistent name support. About the persistent name idea, please refer below mail. http://linux.derkeiler.com/Mailing-Lists/Kernel/2010-10/msg03007.html Device names(e.g. sda) are assigned in the order of logical unit recognizing. the new option can assigne device name regardless of the order of logical unit recognizing. If using this option, add the following kernel parameter. scsi_mod.persistent_name=1 Also, add the following udev rules. SUBSYSTEM=="scsi_unnamed", ATTR{byid}=="xxx", PROGRAM="echo -n sda > /sys/%p/device_name Signed-off-by: Nao Nishijima <nao...@hi...> --- drivers/scsi/Makefile | 1 drivers/scsi/scsi_sysfs.c | 6 + drivers/scsi/scsi_unnamed.c | 188 +++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/scsi_unnamed.h | 4 + 4 files changed, 199 insertions(+), 0 deletions(-) create mode 100644 drivers/scsi/scsi_unnamed.c create mode 100644 drivers/scsi/scsi_unnamed.h diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 2e9a87e..939fd52 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -166,6 +166,7 @@ scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o scsi_mod-y += scsi_trace.o scsi_mod-$(CONFIG_PM_OPS) += scsi_pm.o +scsi_mod-y += scsi_unnamed.o scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 490ce21..30eb955 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -22,6 +22,7 @@ #include "scsi_priv.h" #include "scsi_logging.h" +#include "scsi_unnamed.h" static struct device_type scsi_dev_type; @@ -393,6 +394,10 @@ int scsi_sysfs_register(void) { int error; + error = scsi_unnamed_init(&scsi_bus_type); + if (error < 0) + return error; + error = bus_register(&scsi_bus_type); if (!error) { error = class_register(&sdev_class); @@ -407,6 +412,7 @@ void scsi_sysfs_unregister(void) { class_unregister(&sdev_class); bus_unregister(&scsi_bus_type); + scsi_unnamed_exit(); } /* diff --git a/drivers/scsi/scsi_unnamed.c b/drivers/scsi/scsi_unnamed.c new file mode 100644 index 0000000..330a52f --- /dev/null +++ b/drivers/scsi/scsi_unnamed.c @@ -0,0 +1,188 @@ +/* + * SCSI unnamed module + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/kobject.h> +#include <linux/kdev_t.h> +#include <linux/sysdev.h> +#include <linux/list.h> + +#include <scsi/scsi_driver.h> +#include <scsi/scsi_device.h> + +#define MAX_BUFFER_LEN 256 +#define DISK_NAME_LEN 32 + +static LIST_HEAD(su_list); +static int persistent_name; + +MODULE_PARM_DESC(persistent_name, "SCSI unnamed device support"); +module_param(persistent_name, bool, 0644); + +static struct class su_sysfs_class = { + .name = "scsi_unnamed", +}; + +struct scsi_unnamed { + struct list_head list; + struct device dev; + char byid[MAX_BUFFER_LEN]; + char device_name[DISK_NAME_LEN]; +}; + +#define to_scsi_unnamed(d) \ + container_of(d, struct scsi_unnamed, dev) + +static int get_byid(struct scsi_device *sdev, char *byid) +{ + char *buf; + unsigned int len; + + buf = kmalloc(MAX_BUFFER_LEN, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (scsi_get_vpd_page(sdev, 0x83, buf, MAX_BUFFER_LEN)) { + kfree(buf); + return -EINVAL; + } + + /* need some check. TBD */ + len = ((unsigned char *)buf)[7]; + memcpy(byid, strim(buf + 8), len); + kfree(buf); + + return 0; +} + +static int probe_again(struct device_driver *drv, void *data) +{ + struct device *dev = data; + + if (drv->probe) + return drv->probe(dev); + + return -EINVAL; +} + +static ssize_t byid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", + container_of(dev, struct scsi_unnamed, dev)->byid); +} + +static ssize_t device_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", to_scsi_unnamed(dev)->device_name); +} + +static ssize_t device_name_store(struct device *dev, + struct device_attribute *attr, char *buf, size_t count) +{ + struct scsi_unnamed *su = to_scsi_unnamed(dev); + struct scsi_unnamed *tmp; + int ret = 0; + + BUG_ON(su == NULL); + + if (su->device_name[0] != '\0' || count >= DISK_NAME_LEN) + return -EINVAL; + + list_for_each_entry(tmp, &su_list, list) { + if (strcmp(tmp->device_name, buf) == 0) { + printk(KERN_NOTICE "duplicate name!\n"); + return -EINVAL; + } + } + + strncpy(su->device_name, buf, DISK_NAME_LEN); + + dev->parent->init_name = su->device_name; + + ret = bus_for_each_drv(dev->parent->bus, NULL, dev->parent, + probe_again); + + return count; +} + +static DEVICE_ATTR(device_name, S_IRUGO|S_IWUSR, device_name_show, + device_name_store); +static DEVICE_ATTR(byid, S_IRUGO|S_IWUSR, byid_show, NULL); + +int scsi_unnamed_probe(struct device *dev) +{ + struct scsi_unnamed *su; + struct scsi_device *sdev = to_scsi_device(dev); + int ret = -EINVAL; + static int i; + + su = kzalloc(sizeof(*su), GFP_KERNEL); + if (!su) + return -ENOMEM; + + list_add(&su->list, &su_list); + device_initialize(&su->dev); + su->dev.parent = dev; + su->dev.class = &su_sysfs_class; + dev_set_name(&su->dev, "su%d", i++); + + if (sdev->type == TYPE_DISK) + ret = get_byid(sdev, su->byid); + if (ret < 0) + strncpy(su->byid, dev_name(dev), MAX_BUFFER_LEN); + + ret = device_add(&su->dev); + if (ret) + return ret; + + ret = device_create_file(&su->dev, &dev_attr_device_name); + if (ret) + return -ENODEV; + + ret = device_create_file(&su->dev, &dev_attr_byid); + if (ret) + return -ENODEV; + + return 0; +} + +int check_device_name_prefix(const char *prefix, struct device *dev) +{ + if (persistent_name && strncmp(prefix, dev_name(dev), 2)) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(check_device_name_prefix); + +int copy_persistent_name(char *disk_name, struct device *dev) +{ + if (persistent_name) { + sprintf(disk_name, "%s", dev->init_name); + return 1; + } + return 0; +} +EXPORT_SYMBOL(copy_persistent_name); + +int scsi_unnamed_init(struct bus_type *bus) +{ + if (persistent_name) { + bus->probe = scsi_unnamed_probe; + return class_register(&su_sysfs_class); + } + return 0; +} +EXPORT_SYMBOL(scsi_unnamed_init); + +void scsi_unnamed_exit(void) +{ + if (persistent_name) + class_unregister(&su_sysfs_class); +} +EXPORT_SYMBOL(scsi_unnamed_exit); diff --git a/drivers/scsi/scsi_unnamed.h b/drivers/scsi/scsi_unnamed.h new file mode 100644 index 0000000..3cf858c --- /dev/null +++ b/drivers/scsi/scsi_unnamed.h @@ -0,0 +1,4 @@ +extern int check_device_name_prefix(char *, struct device *); +extern int copy_persistent_name(char *, struct device *); +extern int scsi_unnamed_init(struct bus_type *); +extern void scsi_unnamed_exit(void); |