diff options
Diffstat (limited to 'drivers/scsi/scsi_sysfs.c')
| -rw-r--r-- | drivers/scsi/scsi_sysfs.c | 43 | 
1 files changed, 31 insertions, 12 deletions
| diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index d6984df71f1c..f796bd61f3f0 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -114,7 +114,7 @@ static int check_set(unsigned long long *val, char *src)  {  	char *last; -	if (strncmp(src, "-", 20) == 0) { +	if (strcmp(src, "-") == 0) {  		*val = SCAN_WILD_CARD;  	} else {  		/* @@ -303,6 +303,8 @@ store_host_reset(struct device *dev, struct device_attribute *attr,  	if (sht->host_reset)  		ret = sht->host_reset(shost, type); +	else +		ret = -EOPNOTSUPP;  exit_store_host_reset:  	if (ret == 0) @@ -426,6 +428,7 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)  	struct scsi_device *sdev;  	struct device *parent;  	struct list_head *this, *tmp; +	struct scsi_vpd *vpd_pg80 = NULL, *vpd_pg83 = NULL;  	unsigned long flags;  	sdev = container_of(work, struct scsi_device, ew.work); @@ -454,8 +457,17 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)  	/* NULL queue means the device can't be used */  	sdev->request_queue = NULL; -	kfree(sdev->vpd_pg83); -	kfree(sdev->vpd_pg80); +	mutex_lock(&sdev->inquiry_mutex); +	rcu_swap_protected(sdev->vpd_pg80, vpd_pg80, +			   lockdep_is_held(&sdev->inquiry_mutex)); +	rcu_swap_protected(sdev->vpd_pg83, vpd_pg83, +			   lockdep_is_held(&sdev->inquiry_mutex)); +	mutex_unlock(&sdev->inquiry_mutex); + +	if (vpd_pg83) +		kfree_rcu(vpd_pg83, rcu); +	if (vpd_pg80) +		kfree_rcu(vpd_pg80, rcu);  	kfree(sdev->inquiry);  	kfree(sdev); @@ -793,15 +805,16 @@ show_vpd_##_page(struct file *filp, struct kobject *kobj,	\  {									\  	struct device *dev = container_of(kobj, struct device, kobj);	\  	struct scsi_device *sdev = to_scsi_device(dev);			\ -	int ret;							\ -	if (!sdev->vpd_##_page)						\ -		return -EINVAL;						\ +	struct scsi_vpd *vpd_page;					\ +	int ret = -EINVAL;						\ +									\  	rcu_read_lock();						\ -	ret = memory_read_from_buffer(buf, count, &off,			\ -				      rcu_dereference(sdev->vpd_##_page), \ -				       sdev->vpd_##_page##_len);	\ +	vpd_page = rcu_dereference(sdev->vpd_##_page);			\ +	if (vpd_page)							\ +		ret = memory_read_from_buffer(buf, count, &off,		\ +				vpd_page->data, vpd_page->len);		\  	rcu_read_unlock();						\ -	return ret;						\ +	return ret;							\  }									\  static struct bin_attribute dev_attr_vpd_##_page = {		\  	.attr =	{.name = __stringify(vpd_##_page), .mode = S_IRUGO },	\ @@ -1363,13 +1376,19 @@ static void __scsi_remove_target(struct scsi_target *starget)  	spin_lock_irqsave(shost->host_lock, flags);   restart:  	list_for_each_entry(sdev, &shost->__devices, siblings) { +		/* +		 * We cannot call scsi_device_get() here, as +		 * we might've been called from rmmod() causing +		 * scsi_device_get() to fail the module_is_live() +		 * check. +		 */  		if (sdev->channel != starget->channel ||  		    sdev->id != starget->id || -		    scsi_device_get(sdev)) +		    !get_device(&sdev->sdev_gendev))  			continue;  		spin_unlock_irqrestore(shost->host_lock, flags);  		scsi_remove_device(sdev); -		scsi_device_put(sdev); +		put_device(&sdev->sdev_gendev);  		spin_lock_irqsave(shost->host_lock, flags);  		goto restart;  	} |