Merge branch 'jejb-scsi' into misc
[cascardo/linux.git] / drivers / scsi / scsi_sysfs.c
index 21930c9..4f18a85 100644 (file)
@@ -17,6 +17,7 @@
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dh.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_driver.h>
 
@@ -760,11 +761,15 @@ 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;                                         \
-       return memory_read_from_buffer(buf, count, &off,                \
-                                      sdev->vpd_##_page,               \
+       rcu_read_lock();                                                \
+       ret = memory_read_from_buffer(buf, count, &off,                 \
+                                     rcu_dereference(sdev->vpd_##_page), \
                                       sdev->vpd_##_page##_len);        \
+       rcu_read_unlock();                                              \
+       return ret;                                             \
 }                                                                      \
 static struct bin_attribute dev_attr_vpd_##_page = {           \
        .attr = {.name = __stringify(vpd_##_page), .mode = S_IRUGO },   \
@@ -900,6 +905,76 @@ sdev_show_function(queue_depth, "%d\n");
 static DEVICE_ATTR(queue_depth, S_IRUGO | S_IWUSR, sdev_show_queue_depth,
                   sdev_store_queue_depth);
 
+static ssize_t
+sdev_show_wwid(struct device *dev, struct device_attribute *attr,
+                   char *buf)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       ssize_t count;
+
+       count = scsi_vpd_lun_id(sdev, buf, PAGE_SIZE);
+       if (count > 0) {
+               buf[count] = '\n';
+               count++;
+       }
+       return count;
+}
+static DEVICE_ATTR(wwid, S_IRUGO, sdev_show_wwid, NULL);
+
+#ifdef CONFIG_SCSI_DH
+static ssize_t
+sdev_show_dh_state(struct device *dev, struct device_attribute *attr,
+                  char *buf)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+
+       if (!sdev->handler)
+               return snprintf(buf, 20, "detached\n");
+
+       return snprintf(buf, 20, "%s\n", sdev->handler->name);
+}
+
+static ssize_t
+sdev_store_dh_state(struct device *dev, struct device_attribute *attr,
+                   const char *buf, size_t count)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       int err = -EINVAL;
+
+       if (sdev->sdev_state == SDEV_CANCEL ||
+           sdev->sdev_state == SDEV_DEL)
+               return -ENODEV;
+
+       if (!sdev->handler) {
+               /*
+                * Attach to a device handler
+                */
+               err = scsi_dh_attach(sdev->request_queue, buf);
+       } else if (!strncmp(buf, "activate", 8)) {
+               /*
+                * Activate a device handler
+                */
+               if (sdev->handler->activate)
+                       err = sdev->handler->activate(sdev, NULL, NULL);
+               else
+                       err = 0;
+       } else if (!strncmp(buf, "detach", 6)) {
+               /*
+                * Detach from a device handler
+                */
+               sdev_printk(KERN_WARNING, sdev,
+                           "can't detach handler %s.\n",
+                           sdev->handler->name);
+               err = -EINVAL;
+       }
+
+       return err < 0 ? err : count;
+}
+
+static DEVICE_ATTR(dh_state, S_IRUGO | S_IWUSR, sdev_show_dh_state,
+                  sdev_store_dh_state);
+#endif
+
 static ssize_t
 sdev_show_queue_ramp_up_period(struct device *dev,
                               struct device_attribute *attr,
@@ -969,6 +1044,10 @@ static struct attribute *scsi_sdev_attrs[] = {
        &dev_attr_modalias.attr,
        &dev_attr_queue_depth.attr,
        &dev_attr_queue_type.attr,
+       &dev_attr_wwid.attr,
+#ifdef CONFIG_SCSI_DH
+       &dev_attr_dh_state.attr,
+#endif
        &dev_attr_queue_ramp_up_period.attr,
        REF_EVT(media_change),
        REF_EVT(inquiry_change_reported),
@@ -1058,11 +1137,12 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
        }
 
        error = scsi_dh_add_device(sdev);
-       if (error) {
+       if (error)
+               /*
+                * device_handler is optional, so any error can be ignored
+                */
                sdev_printk(KERN_INFO, sdev,
                                "failed to add device handler: %d\n", error);
-               return error;
-       }
 
        device_enable_async_suspend(&sdev->sdev_dev);
        error = device_add(&sdev->sdev_dev);