diff options
Diffstat (limited to 'drivers/scsi/device_handler/scsi_dh.c')
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 168 | 
1 files changed, 84 insertions, 84 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 6fae3d285ae..33e422e7583 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -22,19 +22,12 @@   */  #include <linux/slab.h> +#include <linux/module.h>  #include <scsi/scsi_dh.h>  #include "../scsi_priv.h" -struct scsi_dh_devinfo_list { -	struct list_head node; -	char vendor[9]; -	char model[17]; -	struct scsi_device_handler *handler; -}; -  static DEFINE_SPINLOCK(list_lock);  static LIST_HEAD(scsi_dh_list); -static LIST_HEAD(scsi_dh_dev_list);  static struct scsi_device_handler *get_device_handler(const char *name)  { @@ -51,43 +44,29 @@ static struct scsi_device_handler *get_device_handler(const char *name)  	return found;  } - +/* + * device_handler_match_function - Match a device handler to a device + * @sdev - SCSI device to be tested + * + * Tests @sdev against the match function of all registered device_handler. + * Returns the found device handler or NULL if not found. + */  static struct scsi_device_handler * -scsi_dh_cache_lookup(struct scsi_device *sdev) +device_handler_match_function(struct scsi_device *sdev)  { -	struct scsi_dh_devinfo_list *tmp; -	struct scsi_device_handler *found_dh = NULL; +	struct scsi_device_handler *tmp_dh, *found_dh = NULL;  	spin_lock(&list_lock); -	list_for_each_entry(tmp, &scsi_dh_dev_list, node) { -		if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) && -		    !strncmp(sdev->model, tmp->model, strlen(tmp->model))) { -			found_dh = tmp->handler; +	list_for_each_entry(tmp_dh, &scsi_dh_list, list) { +		if (tmp_dh->match && tmp_dh->match(sdev)) { +			found_dh = tmp_dh;  			break;  		}  	}  	spin_unlock(&list_lock); -  	return found_dh;  } -static int scsi_dh_handler_lookup(struct scsi_device_handler *scsi_dh, -				  struct scsi_device *sdev) -{ -	int i, found = 0; - -	for(i = 0; scsi_dh->devlist[i].vendor; i++) { -		if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor, -			     strlen(scsi_dh->devlist[i].vendor)) && -		    !strncmp(sdev->model, scsi_dh->devlist[i].model, -			     strlen(scsi_dh->devlist[i].model))) { -			found = 1; -			break; -		} -	} -	return found; -} -  /*   * device_handler_match - Attach a device handler to a device   * @scsi_dh - The device handler to match against or NULL @@ -101,42 +80,12 @@ static struct scsi_device_handler *  device_handler_match(struct scsi_device_handler *scsi_dh,  		     struct scsi_device *sdev)  { -	struct scsi_device_handler *found_dh = NULL; -	struct scsi_dh_devinfo_list *tmp; +	struct scsi_device_handler *found_dh; -	found_dh = scsi_dh_cache_lookup(sdev); -	if (found_dh) -		return found_dh; +	found_dh = device_handler_match_function(sdev); -	if (scsi_dh) { -		if (scsi_dh_handler_lookup(scsi_dh, sdev)) -			found_dh = scsi_dh; -	} else { -		struct scsi_device_handler *tmp_dh; - -		spin_lock(&list_lock); -		list_for_each_entry(tmp_dh, &scsi_dh_list, list) { -			if (scsi_dh_handler_lookup(tmp_dh, sdev)) -				found_dh = tmp_dh; -		} -		spin_unlock(&list_lock); -	} - -	if (found_dh) { /* If device is found, add it to the cache */ -		tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); -		if (tmp) { -			strncpy(tmp->vendor, sdev->vendor, 8); -			strncpy(tmp->model, sdev->model, 16); -			tmp->vendor[8] = '\0'; -			tmp->model[16] = '\0'; -			tmp->handler = found_dh; -			spin_lock(&list_lock); -			list_add(&tmp->node, &scsi_dh_dev_list); -			spin_unlock(&list_lock); -		} else { -			found_dh = NULL; -		} -	} +	if (scsi_dh && found_dh != scsi_dh) +		found_dh = NULL;  	return found_dh;  } @@ -207,6 +156,10 @@ store_dh_state(struct device *dev, struct device_attribute *attr,  	struct scsi_device_handler *scsi_dh;  	int err = -EINVAL; +	if (sdev->sdev_state == SDEV_CANCEL || +	    sdev->sdev_state == SDEV_DEL) +		return -ENODEV; +  	if (!sdev->scsi_dh_data) {  		/*  		 * Attach to a device handler @@ -373,12 +326,14 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data)   */  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); @@ -395,7 +350,6 @@ EXPORT_SYMBOL_GPL(scsi_register_device_handler);   */  int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)  { -	struct scsi_dh_devinfo_list *tmp, *pos;  	if (!get_device_handler(scsi_dh->name))  		return -ENODEV; @@ -405,12 +359,6 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)  	spin_lock(&list_lock);  	list_del(&scsi_dh->list); -	list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) { -		if (pos->handler == scsi_dh) { -			list_del(&pos->node); -			kfree(pos); -		} -	}  	spin_unlock(&list_lock);  	printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); @@ -437,21 +385,39 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)  	unsigned long flags;  	struct scsi_device *sdev;  	struct scsi_device_handler *scsi_dh = NULL; +	struct device *dev = NULL;  	spin_lock_irqsave(q->queue_lock, flags);  	sdev = q->queuedata; -	if (sdev && sdev->scsi_dh_data) +	if (!sdev) { +		spin_unlock_irqrestore(q->queue_lock, flags); +		err = SCSI_DH_NOSYS; +		if (fn) +			fn(data, err); +		return err; +	} + +	if (sdev->scsi_dh_data)  		scsi_dh = sdev->scsi_dh_data->scsi_dh; -	if (!scsi_dh || !get_device(&sdev->sdev_gendev)) +	dev = get_device(&sdev->sdev_gendev); +	if (!scsi_dh || !dev || +	    sdev->sdev_state == SDEV_CANCEL || +	    sdev->sdev_state == SDEV_DEL)  		err = SCSI_DH_NOSYS; +	if (sdev->sdev_state == SDEV_OFFLINE) +		err = SCSI_DH_DEV_OFFLINED;  	spin_unlock_irqrestore(q->queue_lock, flags); -	if (err) -		return err; +	if (err) { +		if (fn) +			fn(data, err); +		goto out; +	}  	if (scsi_dh->activate)  		err = scsi_dh->activate(sdev, fn, data); -	put_device(&sdev->sdev_gendev); +out: +	put_device(dev);  	return err;  }  EXPORT_SYMBOL_GPL(scsi_dh_activate); @@ -501,8 +467,9 @@ int scsi_dh_handler_exist(const char *name)  EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);  /* - * scsi_dh_handler_attach - Attach device handler - * @sdev - sdev the handler should be attached to + * scsi_dh_attach - Attach device handler + * @q - Request queue that is associated with the scsi_device + *      the handler should be attached to   * @name - name of the handler to attach   */  int scsi_dh_attach(struct request_queue *q, const char *name) @@ -531,8 +498,9 @@ int scsi_dh_attach(struct request_queue *q, const char *name)  EXPORT_SYMBOL_GPL(scsi_dh_attach);  /* - * scsi_dh_handler_detach - Detach device handler - * @sdev - sdev the handler should be detached from + * scsi_dh_detach - Detach device handler + * @q - Request queue that is associated with the scsi_device + *      the handler should be detached from   *   * This function will detach the device handler only   * if the sdev is not part of the internal list, ie @@ -561,6 +529,38 @@ void scsi_dh_detach(struct request_queue *q)  }  EXPORT_SYMBOL_GPL(scsi_dh_detach); +/* + * scsi_dh_attached_handler_name - Get attached device handler's name + * @q - Request queue that is associated with the scsi_device + *      that may have a device handler attached + * @gfp - the GFP mask used in the kmalloc() call when allocating memory + * + * Returns name of attached handler, NULL if no handler is attached. + * Caller must take care to free the returned string. + */ +const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) +{ +	unsigned long flags; +	struct scsi_device *sdev; +	const char *handler_name = NULL; + +	spin_lock_irqsave(q->queue_lock, flags); +	sdev = q->queuedata; +	if (!sdev || !get_device(&sdev->sdev_gendev)) +		sdev = NULL; +	spin_unlock_irqrestore(q->queue_lock, flags); + +	if (!sdev) +		return NULL; + +	if (sdev->scsi_dh_data) +		handler_name = kstrdup(sdev->scsi_dh_data->scsi_dh->name, gfp); + +	put_device(&sdev->sdev_gendev); +	return handler_name; +} +EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name); +  static struct notifier_block scsi_dh_nb = {  	.notifier_call = scsi_dh_notifier  };  | 
