Skip to content

Commit

Permalink
[SCSI] scsi_dh: Implement common device table handling
Browse files Browse the repository at this point in the history
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 <hare@suse.de>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
  • Loading branch information
Hannes Reinecke authored and James Bottomley committed Jul 26, 2008
1 parent 6d49f63 commit 765cbc6
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 219 deletions.
202 changes: 170 additions & 32 deletions drivers/scsi/device_handler/scsi_dh.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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.
Expand All @@ -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);

Expand Down Expand Up @@ -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 <sekharan@us.ibm.com>");
MODULE_LICENSE("GPL");
Loading

0 comments on commit 765cbc6

Please sign in to comment.