- patches.fixes/patch-2.6.11-rc1: 2.6.11-rc1.
[linux-flexiantxendom0-3.2.10.git] / drivers / scsi / scsi_sysfs.c
index 79e47aa..1fc2962 100644 (file)
@@ -15,6 +15,7 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
 #include <scsi/scsi_transport.h>
 
 #include "scsi_priv.h"
@@ -169,7 +170,10 @@ void scsi_device_dev_release(struct device *dev)
 
        if (delete) {
                struct scsi_target *starget = to_scsi_target(parent);
+               struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
                if (!starget->create) {
+                       if (shost->transportt->target_destroy)
+                               shost->transportt->target_destroy(starget);
                        device_del(parent);
                        if (starget->transport_classdev.class)
                                class_device_unregister(&starget->transport_classdev);
@@ -388,13 +392,30 @@ show_state_field(struct device *dev, char *buf)
        return snprintf(buf, 20, "%s\n", name);
 }
 
-DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field);
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field);
+
+static ssize_t
+show_queue_type_field(struct device *dev, char *buf)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       const char *name = "none";
+
+       if (sdev->ordered_tags)
+               name = "ordered";
+       else if (sdev->simple_tags)
+               name = "simple";
+
+       return snprintf(buf, 20, "%s\n", name);
+}
+
+static DEVICE_ATTR(queue_type, S_IRUGO, show_queue_type_field, NULL);
 
 
 /* Default template for device attributes.  May NOT be modified */
 static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
        &dev_attr_device_blocked,
        &dev_attr_queue_depth,
+       &dev_attr_queue_type,
        &dev_attr_type,
        &dev_attr_scsi_level,
        &dev_attr_vendor,
@@ -407,6 +428,77 @@ static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
        NULL
 };
 
+static ssize_t sdev_store_queue_depth_rw(struct device *dev, const char *buf,
+                                        size_t count)
+{
+       int depth, retval;
+       struct scsi_device *sdev = to_scsi_device(dev);
+       struct scsi_host_template *sht = sdev->host->hostt;
+
+       if (!sht->change_queue_depth)
+               return -EINVAL;
+
+       depth = simple_strtoul(buf, NULL, 0);
+
+       if (depth < 1)
+               return -EINVAL;
+
+       retval = sht->change_queue_depth(sdev, depth);
+       if (retval < 0)
+               return retval;
+
+       return count;
+}
+
+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 ssize_t sdev_store_queue_type_rw(struct device *dev, 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 struct device_attribute sdev_attr_queue_type_rw =
+       __ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field,
+              sdev_store_queue_type_rw);
+
+static struct device_attribute *attr_changed_internally(
+               struct Scsi_Host *shost,
+               struct device_attribute * attr)
+{
+       if (!strcmp("queue_depth", attr->attr.name)
+           && shost->hostt->change_queue_depth)
+               return &sdev_attr_queue_depth_rw;
+       else if (!strcmp("queue_type", attr->attr.name)
+           && shost->hostt->change_queue_type)
+               return &sdev_attr_queue_type_rw;
+       return attr;
+}
+
 
 static struct device_attribute *attr_overridden(
                struct device_attribute **attrs,
@@ -547,8 +639,10 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
        for (i = 0; scsi_sysfs_sdev_attrs[i]; i++) {
                if (!attr_overridden(sdev->host->hostt->sdev_attrs,
                                        scsi_sysfs_sdev_attrs[i])) {
-                       error = device_create_file(&sdev->sdev_gendev,
-                                       scsi_sysfs_sdev_attrs[i]);
+                       struct device_attribute * attr = 
+                               attr_changed_internally(sdev->host, 
+                                                       scsi_sysfs_sdev_attrs[i]);
+                       error = device_create_file(&sdev->sdev_gendev, attr);
                        if (error) {
                                scsi_remove_device(sdev);
                                goto out;
@@ -601,11 +695,14 @@ void scsi_remove_device(struct scsi_device *sdev)
        scsi_device_set_state(sdev, SDEV_DEL);
        if (sdev->host->hostt->slave_destroy)
                sdev->host->hostt->slave_destroy(sdev);
+       if (sdev->host->transportt->device_destroy)
+               sdev->host->transportt->device_destroy(sdev);
        put_device(&sdev->sdev_gendev);
 
 out:
        up(&shost->scan_mutex);
 }
+EXPORT_SYMBOL(scsi_remove_device);
 
 int scsi_register_driver(struct device_driver *drv)
 {
@@ -613,6 +710,7 @@ int scsi_register_driver(struct device_driver *drv)
 
        return driver_register(drv);
 }
+EXPORT_SYMBOL(scsi_register_driver);
 
 int scsi_register_interface(struct class_interface *intf)
 {
@@ -620,6 +718,7 @@ int scsi_register_interface(struct class_interface *intf)
 
        return class_interface_register(intf);
 }
+EXPORT_SYMBOL(scsi_register_interface);
 
 
 static struct class_device_attribute *class_attr_overridden(
@@ -708,6 +807,14 @@ int scsi_sysfs_add_host(struct Scsi_Host *shost)
                        if (error)
                                return error;
                }
+
+               if (shost->transportt->host_statistics) {
+                       error = sysfs_create_group(
+                                       &shost->transport_classdev.kobj,
+                                       shost->transportt->host_statistics);
+                       if (error)
+                               return error;
+               }
        }
 
        return 0;