From 765cbc6dad16b87724803e359d6be792ddf08614 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Thu, 17 Jul 2008 16:52:51 -0700 Subject: [SCSI] scsi_dh: Implement common device table handling Instead of having each and every driver implement its own device table scanning code we should rather implement a common routine and scan the device tables there. This allows us also to implement a general notifier chain callback for all device handler instead for one per handler. [sekharan: Fix rejections caused by conflicting bug fix] Signed-off-by: Hannes Reinecke Signed-off-by: Chandra Seetharaman Signed-off-by: James Bottomley --- drivers/scsi/device_handler/scsi_dh.c | 202 ++++++++++++++++++++++++++++------ 1 file changed, 170 insertions(+), 32 deletions(-) (limited to 'drivers/scsi/device_handler/scsi_dh.c') diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index ab6c21cd9689..3f798171ed69 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -33,7 +33,7 @@ static struct scsi_device_handler *get_device_handler(const char *name) spin_lock(&list_lock); list_for_each_entry(tmp, &scsi_dh_list, list) { - if (!strcmp(tmp->name, name)) { + if (!strncmp(tmp->name, name, strlen(tmp->name))) { found = tmp; break; } @@ -42,50 +42,172 @@ static struct scsi_device_handler *get_device_handler(const char *name) return found; } -static int scsi_dh_notifier_add(struct device *dev, void *data) +static int device_handler_match(struct scsi_device_handler *tmp, + struct scsi_device *sdev) { - struct scsi_device_handler *scsi_dh = data; + int i; + + for(i = 0; tmp->devlist[i].vendor; i++) { + if (!strncmp(sdev->vendor, tmp->devlist[i].vendor, + strlen(tmp->devlist[i].vendor)) && + !strncmp(sdev->model, tmp->devlist[i].model, + strlen(tmp->devlist[i].model))) { + return 1; + } + } - scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev); return 0; } /* - * scsi_register_device_handler - register a device handler personality - * module. - * @scsi_dh - device handler to be registered. + * scsi_dh_handler_attach - Attach a device handler to a device + * @sdev - SCSI device the device handler should attach to + * @scsi_dh - The device handler to attach + */ +static int scsi_dh_handler_attach(struct scsi_device *sdev, + struct scsi_device_handler *scsi_dh) +{ + int err = 0; + + if (sdev->scsi_dh_data) { + if (sdev->scsi_dh_data->scsi_dh != scsi_dh) + err = -EBUSY; + } else if (scsi_dh->attach) + err = scsi_dh->attach(sdev); + + return err; +} + +/* + * scsi_dh_handler_detach - Detach a device handler from a device + * @sdev - SCSI device the device handler should be detached from + * @scsi_dh - Device handler to be detached * - * Returns 0 on success, -EBUSY if handler already registered. + * Detach from a device handler. If a device handler is specified, + * only detach if the currently attached handler is equal to it. */ -int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) +static void scsi_dh_handler_detach(struct scsi_device *sdev, + struct scsi_device_handler *scsi_dh) { - int ret = -EBUSY; - struct scsi_device_handler *tmp; + if (!sdev->scsi_dh_data) + return; - tmp = get_device_handler(scsi_dh->name); - if (tmp) - goto done; + if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh) + return; - ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb); + if (!scsi_dh) + scsi_dh = sdev->scsi_dh_data->scsi_dh; + + if (scsi_dh && scsi_dh->detach) + scsi_dh->detach(sdev); +} + +/* + * scsi_dh_notifier - notifier chain callback + */ +static int scsi_dh_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct scsi_device *sdev; + int err = 0; + struct scsi_device_handler *tmp, *devinfo = NULL; + + if (!scsi_is_sdev_device(dev)) + return 0; + + sdev = to_scsi_device(dev); - bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); spin_lock(&list_lock); - list_add(&scsi_dh->list, &scsi_dh_list); + list_for_each_entry(tmp, &scsi_dh_list, list) { + if (device_handler_match(tmp, sdev)) { + devinfo = tmp; + break; + } + } spin_unlock(&list_lock); -done: - return ret; + if (!devinfo) + goto out; + + if (action == BUS_NOTIFY_ADD_DEVICE) { + err = scsi_dh_handler_attach(sdev, devinfo); + } else if (action == BUS_NOTIFY_DEL_DEVICE) { + scsi_dh_handler_detach(sdev, NULL); + } +out: + return err; } -EXPORT_SYMBOL_GPL(scsi_register_device_handler); +/* + * scsi_dh_notifier_add - Callback for scsi_register_device_handler + */ +static int scsi_dh_notifier_add(struct device *dev, void *data) +{ + struct scsi_device_handler *scsi_dh = data; + struct scsi_device *sdev; + + if (!scsi_is_sdev_device(dev)) + return 0; + + if (!get_device(dev)) + return 0; + + sdev = to_scsi_device(dev); + + if (device_handler_match(scsi_dh, sdev)) + scsi_dh_handler_attach(sdev, scsi_dh); + + put_device(dev); + + return 0; +} + +/* + * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler + */ static int scsi_dh_notifier_remove(struct device *dev, void *data) { struct scsi_device_handler *scsi_dh = data; + struct scsi_device *sdev; + + if (!scsi_is_sdev_device(dev)) + return 0; + + if (!get_device(dev)) + return 0; + + sdev = to_scsi_device(dev); + + scsi_dh_handler_detach(sdev, scsi_dh); + + put_device(dev); - scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev); return 0; } +/* + * scsi_register_device_handler - register a device handler personality + * module. + * @scsi_dh - device handler to be registered. + * + * Returns 0 on success, -EBUSY if handler already registered. + */ +int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) +{ + if (get_device_handler(scsi_dh->name)) + return -EBUSY; + + spin_lock(&list_lock); + list_add(&scsi_dh->list, &scsi_dh_list); + spin_unlock(&list_lock); + bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); + printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); + + return SCSI_DH_OK; +} +EXPORT_SYMBOL_GPL(scsi_register_device_handler); + /* * scsi_unregister_device_handler - register a device handler personality * module. @@ -95,23 +217,18 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data) */ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) { - int ret = -ENODEV; - struct scsi_device_handler *tmp; - - tmp = get_device_handler(scsi_dh->name); - if (!tmp) - goto done; - - ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb); + if (!get_device_handler(scsi_dh->name)) + return -ENODEV; bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, - scsi_dh_notifier_remove); + scsi_dh_notifier_remove); + spin_lock(&list_lock); list_del(&scsi_dh->list); spin_unlock(&list_lock); + printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); -done: - return ret; + return SCSI_DH_OK; } EXPORT_SYMBOL_GPL(scsi_unregister_device_handler); @@ -157,6 +274,27 @@ int scsi_dh_handler_exist(const char *name) } EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); +static struct notifier_block scsi_dh_nb = { + .notifier_call = scsi_dh_notifier +}; + +static int __init scsi_dh_init(void) +{ + int r; + + r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); + + return r; +} + +static void __exit scsi_dh_exit(void) +{ + bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); +} + +module_init(scsi_dh_init); +module_exit(scsi_dh_exit); + MODULE_DESCRIPTION("SCSI device handler"); MODULE_AUTHOR("Chandra Seetharaman "); MODULE_LICENSE("GPL"); -- cgit v1.2.3