[SCSI] scsi_sysfs: Implement 'is_visible' callback
authorHannes Reinecke <hare@suse.de>
Sat, 15 Mar 2014 08:51:47 +0000 (09:51 +0100)
committerJames Bottomley <JBottomley@Parallels.com>
Wed, 19 Mar 2014 22:22:39 +0000 (15:22 -0700)
Instead of modifying attributes after the device has been created
we should be using the 'is_visible' callback to avoid races.

Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/scsi_sysfs.c

index 8dab6ddf822266ceae113c8e43f3f3a90f295821..85098222a9e8f3c4c16f5b9d0e253893027e5b42 100644 (file)
@@ -576,7 +576,6 @@ 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");
@@ -720,7 +719,37 @@ 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);
 
 static ssize_t
 show_iostat_counterbits(struct device *dev, struct device_attribute *attr,                             char *buf)
@@ -794,46 +823,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_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,
-       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);
@@ -856,10 +848,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,
@@ -887,40 +879,73 @@ 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 attribute_group scsi_sdev_attr_group = {
+       .attrs =        scsi_sdev_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;
@@ -943,10 +968,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
@@ -1000,25 +1021,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)