diff options
Diffstat (limited to 'drivers/scsi/scsi_sysfs.c')
| -rw-r--r-- | drivers/scsi/scsi_sysfs.c | 306 | 
1 files changed, 196 insertions, 110 deletions
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 40c639491b2..074e8cc3095 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -281,6 +281,58 @@ exit_store_host_reset:  static DEVICE_ATTR(host_reset, S_IWUSR, NULL, store_host_reset); +static ssize_t +show_shost_eh_deadline(struct device *dev, +		      struct device_attribute *attr, char *buf) +{ +	struct Scsi_Host *shost = class_to_shost(dev); + +	if (shost->eh_deadline == -1) +		return snprintf(buf, strlen("off") + 2, "off\n"); +	return sprintf(buf, "%u\n", shost->eh_deadline / HZ); +} + +static ssize_t +store_shost_eh_deadline(struct device *dev, struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct Scsi_Host *shost = class_to_shost(dev); +	int ret = -EINVAL; +	unsigned long deadline, flags; + +	if (shost->transportt && +	    (shost->transportt->eh_strategy_handler || +	     !shost->hostt->eh_host_reset_handler)) +		return ret; + +	if (!strncmp(buf, "off", strlen("off"))) +		deadline = -1; +	else { +		ret = kstrtoul(buf, 10, &deadline); +		if (ret) +			return ret; +		if (deadline * HZ > UINT_MAX) +			return -EINVAL; +	} + +	spin_lock_irqsave(shost->host_lock, flags); +	if (scsi_host_in_recovery(shost)) +		ret = -EBUSY; +	else { +		if (deadline == -1) +			shost->eh_deadline = -1; +		else +			shost->eh_deadline = deadline * HZ; + +		ret = count; +	} +	spin_unlock_irqrestore(shost->host_lock, flags); + +	return ret; +} + +static DEVICE_ATTR(eh_deadline, S_IRUGO | S_IWUSR, show_shost_eh_deadline, store_shost_eh_deadline); +  shost_rd_attr(unique_id, "%u\n");  shost_rd_attr(host_busy, "%hu\n");  shost_rd_attr(cmd_per_lun, "%hd\n"); @@ -308,6 +360,7 @@ static struct attribute *scsi_sysfs_shost_attrs[] = {  	&dev_attr_prot_capabilities.attr,  	&dev_attr_prot_guard_type.attr,  	&dev_attr_host_reset.attr, +	&dev_attr_eh_deadline.attr,  	NULL  }; @@ -332,17 +385,14 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)  {  	struct scsi_device *sdev;  	struct device *parent; -	struct scsi_target *starget;  	struct list_head *this, *tmp;  	unsigned long flags;  	sdev = container_of(work, struct scsi_device, ew.work);  	parent = sdev->sdev_gendev.parent; -	starget = to_scsi_target(parent);  	spin_lock_irqsave(sdev->host->host_lock, flags); -	starget->reap_ref++;  	list_del(&sdev->siblings);  	list_del(&sdev->same_target_siblings);  	list_del(&sdev->starved_entry); @@ -362,8 +412,8 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)  	/* NULL queue means the device can't be used */  	sdev->request_queue = NULL; -	scsi_target_reap(scsi_target(sdev)); - +	kfree(sdev->vpd_pg83); +	kfree(sdev->vpd_pg80);  	kfree(sdev->inquiry);  	kfree(sdev); @@ -528,7 +578,7 @@ static int scsi_sdev_check_buf_bit(const char *buf)   * Create the actual show/store functions and data structures.   */  sdev_rd_attr (device_blocked, "%d\n"); -sdev_rd_attr (queue_depth, "%d\n"); +sdev_rd_attr (device_busy, "%d\n");  sdev_rd_attr (type, "%d\n");  sdev_rd_attr (scsi_level, "%d\n");  sdev_rd_attr (vendor, "%.8s\n"); @@ -597,23 +647,12 @@ store_rescan_field (struct device *dev, struct device_attribute *attr,  }  static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); -static void sdev_store_delete_callback(struct device *dev) -{ -	scsi_remove_device(to_scsi_device(dev)); -} -  static ssize_t  sdev_store_delete(struct device *dev, struct device_attribute *attr,  		  const char *buf, size_t count)  { -	int rc; - -	/* An attribute cannot be unregistered by one of its own methods, -	 * so we have to use this roundabout approach. -	 */ -	rc = device_schedule_callback(dev, sdev_store_delete_callback); -	if (rc) -		count = rc; +	if (device_remove_file_self(dev, attr)) +		scsi_remove_device(to_scsi_device(dev));  	return count;  };  static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); @@ -671,10 +710,64 @@ show_queue_type_field(struct device *dev, struct device_attribute *attr,  	return snprintf(buf, 20, "%s\n", name);  } -static DEVICE_ATTR(queue_type, S_IRUGO, show_queue_type_field, NULL); +static ssize_t +store_queue_type_field(struct device *dev, struct device_attribute *attr, +		       const char *buf, size_t count) +{ +	struct scsi_device *sdev = to_scsi_device(dev); +	struct scsi_host_template *sht = sdev->host->hostt; +	int tag_type = 0, retval; +	int prev_tag_type = scsi_get_tag_type(sdev); + +	if (!sdev->tagged_supported || !sht->change_queue_type) +		return -EINVAL; + +	if (strncmp(buf, "ordered", 7) == 0) +		tag_type = MSG_ORDERED_TAG; +	else if (strncmp(buf, "simple", 6) == 0) +		tag_type = MSG_SIMPLE_TAG; +	else if (strncmp(buf, "none", 4) != 0) +		return -EINVAL; + +	if (tag_type == prev_tag_type) +		return count; + +	retval = sht->change_queue_type(sdev, tag_type); +	if (retval < 0) +		return retval; + +	return count; +} + +static DEVICE_ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field, +		   store_queue_type_field); + +#define sdev_vpd_pg_attr(_page)						\ +static ssize_t							\ +show_vpd_##_page(struct file *filp, struct kobject *kobj,	\ +		 struct bin_attribute *bin_attr,			\ +		 char *buf, loff_t off, size_t count)			\ +{									\ +	struct device *dev = container_of(kobj, struct device, kobj);	\ +	struct scsi_device *sdev = to_scsi_device(dev);			\ +	if (!sdev->vpd_##_page)						\ +		return -EINVAL;						\ +	return memory_read_from_buffer(buf, count, &off,		\ +				       sdev->vpd_##_page,		\ +				       sdev->vpd_##_page##_len);	\ +}									\ +static struct bin_attribute dev_attr_vpd_##_page = {		\ +	.attr =	{.name = __stringify(vpd_##_page), .mode = S_IRUGO },	\ +	.size = 0,							\ +	.read = show_vpd_##_page,					\ +}; + +sdev_vpd_pg_attr(pg83); +sdev_vpd_pg_attr(pg80);  static ssize_t -show_iostat_counterbits(struct device *dev, struct device_attribute *attr, 				char *buf) +show_iostat_counterbits(struct device *dev, struct device_attribute *attr, +			char *buf)  {  	return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8);  } @@ -745,45 +838,9 @@ DECLARE_EVT(soft_threshold_reached, SOFT_THRESHOLD_REACHED_REPORTED)  DECLARE_EVT(mode_parameter_change_reported, MODE_PARAMETER_CHANGE_REPORTED)  DECLARE_EVT(lun_change_reported, LUN_CHANGE_REPORTED) -/* Default template for device attributes.  May NOT be modified */ -static struct attribute *scsi_sdev_attrs[] = { -	&dev_attr_device_blocked.attr, -	&dev_attr_type.attr, -	&dev_attr_scsi_level.attr, -	&dev_attr_vendor.attr, -	&dev_attr_model.attr, -	&dev_attr_rev.attr, -	&dev_attr_rescan.attr, -	&dev_attr_delete.attr, -	&dev_attr_state.attr, -	&dev_attr_timeout.attr, -	&dev_attr_eh_timeout.attr, -	&dev_attr_iocounterbits.attr, -	&dev_attr_iorequest_cnt.attr, -	&dev_attr_iodone_cnt.attr, -	&dev_attr_ioerr_cnt.attr, -	&dev_attr_modalias.attr, -	REF_EVT(media_change), -	REF_EVT(inquiry_change_reported), -	REF_EVT(capacity_change_reported), -	REF_EVT(soft_threshold_reached), -	REF_EVT(mode_parameter_change_reported), -	REF_EVT(lun_change_reported), -	NULL -}; - -static struct attribute_group scsi_sdev_attr_group = { -	.attrs =	scsi_sdev_attrs, -}; - -static const struct attribute_group *scsi_sdev_attr_groups[] = { -	&scsi_sdev_attr_group, -	NULL -}; -  static ssize_t -sdev_store_queue_depth_rw(struct device *dev, struct device_attribute *attr, -			  const char *buf, size_t count) +sdev_store_queue_depth(struct device *dev, struct device_attribute *attr, +		       const char *buf, size_t count)  {  	int depth, retval;  	struct scsi_device *sdev = to_scsi_device(dev); @@ -806,10 +863,10 @@ sdev_store_queue_depth_rw(struct device *dev, struct device_attribute *attr,  	return count;  } +sdev_show_function(queue_depth, "%d\n"); -static struct device_attribute sdev_attr_queue_depth_rw = -	__ATTR(queue_depth, S_IRUGO | S_IWUSR, sdev_show_queue_depth, -	       sdev_store_queue_depth_rw); +static DEVICE_ATTR(queue_depth, S_IRUGO | S_IWUSR, sdev_show_queue_depth, +		   sdev_store_queue_depth);  static ssize_t  sdev_show_queue_ramp_up_period(struct device *dev, @@ -837,40 +894,79 @@ sdev_store_queue_ramp_up_period(struct device *dev,  	return period;  } -static struct device_attribute sdev_attr_queue_ramp_up_period = -	__ATTR(queue_ramp_up_period, S_IRUGO | S_IWUSR, -	       sdev_show_queue_ramp_up_period, -	       sdev_store_queue_ramp_up_period); +static DEVICE_ATTR(queue_ramp_up_period, S_IRUGO | S_IWUSR, +		   sdev_show_queue_ramp_up_period, +		   sdev_store_queue_ramp_up_period); -static ssize_t -sdev_store_queue_type_rw(struct device *dev, struct device_attribute *attr, -			 const char *buf, size_t count) +static umode_t scsi_sdev_attr_is_visible(struct kobject *kobj, +					 struct attribute *attr, int i)  { +	struct device *dev = container_of(kobj, struct device, kobj);  	struct scsi_device *sdev = to_scsi_device(dev); -	struct scsi_host_template *sht = sdev->host->hostt; -	int tag_type = 0, retval; -	int prev_tag_type = scsi_get_tag_type(sdev); -	if (!sdev->tagged_supported || !sht->change_queue_type) -		return -EINVAL; -	if (strncmp(buf, "ordered", 7) == 0) -		tag_type = MSG_ORDERED_TAG; -	else if (strncmp(buf, "simple", 6) == 0) -		tag_type = MSG_SIMPLE_TAG; -	else if (strncmp(buf, "none", 4) != 0) -		return -EINVAL; +	if (attr == &dev_attr_queue_depth.attr && +	    !sdev->host->hostt->change_queue_depth) +		return S_IRUGO; -	if (tag_type == prev_tag_type) -		return count; +	if (attr == &dev_attr_queue_ramp_up_period.attr && +	    !sdev->host->hostt->change_queue_depth) +		return 0; -	retval = sht->change_queue_type(sdev, tag_type); -	if (retval < 0) -		return retval; +	if (attr == &dev_attr_queue_type.attr && +	    !sdev->host->hostt->change_queue_type) +		return S_IRUGO; -	return count; +	return attr->mode;  } +/* Default template for device attributes.  May NOT be modified */ +static struct attribute *scsi_sdev_attrs[] = { +	&dev_attr_device_blocked.attr, +	&dev_attr_type.attr, +	&dev_attr_scsi_level.attr, +	&dev_attr_device_busy.attr, +	&dev_attr_vendor.attr, +	&dev_attr_model.attr, +	&dev_attr_rev.attr, +	&dev_attr_rescan.attr, +	&dev_attr_delete.attr, +	&dev_attr_state.attr, +	&dev_attr_timeout.attr, +	&dev_attr_eh_timeout.attr, +	&dev_attr_iocounterbits.attr, +	&dev_attr_iorequest_cnt.attr, +	&dev_attr_iodone_cnt.attr, +	&dev_attr_ioerr_cnt.attr, +	&dev_attr_modalias.attr, +	&dev_attr_queue_depth.attr, +	&dev_attr_queue_type.attr, +	&dev_attr_queue_ramp_up_period.attr, +	REF_EVT(media_change), +	REF_EVT(inquiry_change_reported), +	REF_EVT(capacity_change_reported), +	REF_EVT(soft_threshold_reached), +	REF_EVT(mode_parameter_change_reported), +	REF_EVT(lun_change_reported), +	NULL +}; + +static struct bin_attribute *scsi_sdev_bin_attrs[] = { +	&dev_attr_vpd_pg83, +	&dev_attr_vpd_pg80, +	NULL +}; +static struct attribute_group scsi_sdev_attr_group = { +	.attrs =	scsi_sdev_attrs, +	.bin_attrs =	scsi_sdev_bin_attrs, +	.is_visible =	scsi_sdev_attr_is_visible, +}; + +static const struct attribute_group *scsi_sdev_attr_groups[] = { +	&scsi_sdev_attr_group, +	NULL +}; +  static int scsi_target_add(struct scsi_target *starget)  {  	int error; @@ -893,10 +989,6 @@ static int scsi_target_add(struct scsi_target *starget)  	return 0;  } -static struct device_attribute sdev_attr_queue_type_rw = -	__ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field, -	       sdev_store_queue_type_rw); -  /**   * scsi_sysfs_add_sdev - add scsi device to sysfs   * @sdev:	scsi_device to add @@ -950,25 +1042,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)  	transport_add_device(&sdev->sdev_gendev);  	sdev->is_visible = 1; -	/* create queue files, which may be writable, depending on the host */ -	if (sdev->host->hostt->change_queue_depth) { -		error = device_create_file(&sdev->sdev_gendev, -					   &sdev_attr_queue_depth_rw); -		error = device_create_file(&sdev->sdev_gendev, -					   &sdev_attr_queue_ramp_up_period); -	} -	else -		error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth); -	if (error) -		return error; - -	if (sdev->host->hostt->change_queue_type) -		error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_type_rw); -	else -		error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_type); -	if (error) -		return error; -  	error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL);  	if (error) @@ -1018,6 +1091,13 @@ void __scsi_remove_device(struct scsi_device *sdev)  		sdev->host->hostt->slave_destroy(sdev);  	transport_destroy_device(dev); +	/* +	 * Paired with the kref_get() in scsi_sysfs_initialize().  We have +	 * remoed sysfs visibility from the device, so make the target +	 * invisible if this was the last device underneath it. +	 */ +	scsi_target_reap(scsi_target(sdev)); +  	put_device(dev);  } @@ -1080,7 +1160,7 @@ void scsi_remove_target(struct device *dev)  			continue;  		if (starget->dev.parent == dev || &starget->dev == dev) {  			/* assuming new targets arrive at the end */ -			starget->reap_ref++; +			kref_get(&starget->reap_ref);  			spin_unlock_irqrestore(shost->host_lock, flags);  			if (last)  				scsi_target_reap(last); @@ -1164,6 +1244,12 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev)  	list_add_tail(&sdev->same_target_siblings, &starget->devices);  	list_add_tail(&sdev->siblings, &shost->__devices);  	spin_unlock_irqrestore(shost->host_lock, flags); +	/* +	 * device can now only be removed via __scsi_remove_device() so hold +	 * the target.  Target will be held in CREATED state until something +	 * beneath it becomes visible (in which case it moves to RUNNING) +	 */ +	kref_get(&starget->reap_ref);  }  int scsi_is_sdev_device(const struct device *dev)  | 
