fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / scsi / scsi_sysfs.c
index a8855d3..259c90c 100644 (file)
@@ -6,7 +6,6 @@
  * Created to pull SCSI mid layer sysfs routines into one file.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/blkdev.h>
 #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"
 #include "scsi_logging.h"
 
-static struct {
+static const struct {
        enum scsi_device_state  value;
        char                    *name;
 } sdev_states[] = {
@@ -30,6 +30,7 @@ static struct {
        { SDEV_DEL, "deleted" },
        { SDEV_QUIESCE, "quiesce" },
        { SDEV_OFFLINE, "offline" },
+       { SDEV_BLOCK,   "blocked" },
 };
 
 const char *scsi_device_state_name(enum scsi_device_state state)
@@ -37,7 +38,7 @@ const char *scsi_device_state_name(enum scsi_device_state state)
        int i;
        char *name = NULL;
 
-       for (i = 0; i < sizeof(sdev_states)/sizeof(sdev_states[0]); i++) {
+       for (i = 0; i < ARRAY_SIZE(sdev_states); i++) {
                if (sdev_states[i].value == state) {
                        name = sdev_states[i].name;
                        break;
@@ -46,6 +47,32 @@ const char *scsi_device_state_name(enum scsi_device_state state)
        return name;
 }
 
+static const struct {
+       enum scsi_host_state    value;
+       char                    *name;
+} shost_states[] = {
+       { SHOST_CREATED, "created" },
+       { SHOST_RUNNING, "running" },
+       { SHOST_CANCEL, "cancel" },
+       { SHOST_DEL, "deleted" },
+       { SHOST_RECOVERY, "recovery" },
+       { SHOST_CANCEL_RECOVERY, "cancel/recovery" },
+       { SHOST_DEL_RECOVERY, "deleted/recovery", },
+};
+const char *scsi_host_state_name(enum scsi_host_state state)
+{
+       int i;
+       char *name = NULL;
+
+       for (i = 0; i < ARRAY_SIZE(shost_states); i++) {
+               if (shost_states[i].value == state) {
+                       name = shost_states[i].name;
+                       break;
+               }
+       }
+       return name;
+}
+
 static int check_set(unsigned int *val, char *src)
 {
        char *last;
@@ -78,7 +105,10 @@ static int scsi_scan(struct Scsi_Host *shost, const char *str)
                return -EINVAL;
        if (check_set(&lun, s3))
                return -EINVAL;
-       res = scsi_scan_host_selected(shost, channel, id, lun, 1);
+       if (shost->transportt->user_scan)
+               res = shost->transportt->user_scan(shost, channel, id, lun);
+       else
+               res = scsi_scan_host_selected(shost, channel, id, lun, 1);
        return res;
 }
 
@@ -122,9 +152,47 @@ static ssize_t store_scan(struct class_device *class_dev, const char *buf,
 };
 static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan);
 
+static ssize_t
+store_shost_state(struct class_device *class_dev, const char *buf, size_t count)
+{
+       int i;
+       struct Scsi_Host *shost = class_to_shost(class_dev);
+       enum scsi_host_state state = 0;
+
+       for (i = 0; i < ARRAY_SIZE(shost_states); i++) {
+               const int len = strlen(shost_states[i].name);
+               if (strncmp(shost_states[i].name, buf, len) == 0 &&
+                  buf[len] == '\n') {
+                       state = shost_states[i].value;
+                       break;
+               }
+       }
+       if (!state)
+               return -EINVAL;
+
+       if (scsi_host_set_state(shost, state))
+               return -EINVAL;
+       return count;
+}
+
+static ssize_t
+show_shost_state(struct class_device *class_dev, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(class_dev);
+       const char *name = scsi_host_state_name(shost->shost_state);
+
+       if (!name)
+               return -EINVAL;
+
+       return snprintf(buf, 20, "%s\n", name);
+}
+
+static CLASS_DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_shost_state, store_shost_state);
+
 shost_rd_attr(unique_id, "%u\n");
 shost_rd_attr(host_busy, "%hu\n");
 shost_rd_attr(cmd_per_lun, "%hd\n");
+shost_rd_attr(can_queue, "%hd\n");
 shost_rd_attr(sg_tablesize, "%hu\n");
 shost_rd_attr(unchecked_isa_dma, "%d\n");
 shost_rd_attr2(proc_name, hostt->proc_name, "%s\n");
@@ -133,10 +201,12 @@ static struct class_device_attribute *scsi_sysfs_shost_attrs[] = {
        &class_device_attr_unique_id,
        &class_device_attr_host_busy,
        &class_device_attr_cmd_per_lun,
+       &class_device_attr_can_queue,
        &class_device_attr_sg_tablesize,
        &class_device_attr_unchecked_isa_dma,
        &class_device_attr_proc_name,
        &class_device_attr_scan,
+       &class_device_attr_state,
        NULL
 };
 
@@ -148,33 +218,51 @@ static void scsi_device_cls_release(struct class_device *class_dev)
        put_device(&sdev->sdev_gendev);
 }
 
-void scsi_device_dev_release(struct device *dev)
+static void scsi_device_dev_release_usercontext(struct work_struct *work)
 {
        struct scsi_device *sdev;
        struct device *parent;
+       struct scsi_target *starget;
        unsigned long flags;
 
-       parent = dev->parent;
-       sdev = to_scsi_device(dev);
+       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);
-       if (sdev->single_lun && --sdev->sdev_target->starget_refcnt == 0)
-               kfree(sdev->sdev_target);
        spin_unlock_irqrestore(sdev->host->host_lock, flags);
 
-       if (sdev->request_queue)
+       if (sdev->request_queue) {
+               sdev->request_queue->queuedata = NULL;
+               /* user context needed to free queue */
                scsi_free_queue(sdev->request_queue);
+               /* temporary expedient, try to catch use of queue lock
+                * after free of sdev */
+               sdev->request_queue = NULL;
+       }
+
+       scsi_target_reap(scsi_target(sdev));
 
        kfree(sdev->inquiry);
        kfree(sdev);
 
-       put_device(parent);
+       if (parent)
+               put_device(parent);
+}
+
+static void scsi_device_dev_release(struct device *dev)
+{
+       struct scsi_device *sdp = to_scsi_device(dev);
+       execute_in_process_context(scsi_device_dev_release_usercontext,
+                                  &sdp->ew);
 }
 
-struct class sdev_class = {
+static struct class sdev_class = {
        .name           = "scsi_device",
        .release        = scsi_device_cls_release,
 };
@@ -183,12 +271,45 @@ struct class sdev_class = {
 static int scsi_bus_match(struct device *dev, struct device_driver *gendrv)
 {
        struct scsi_device *sdp = to_scsi_device(dev);
+       if (sdp->no_uld_attach)
+               return 0;
        return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
 }
 
+static int scsi_bus_suspend(struct device * dev, pm_message_t state)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       struct scsi_host_template *sht = sdev->host->hostt;
+       int err;
+
+       err = scsi_device_quiesce(sdev);
+       if (err)
+               return err;
+
+       if (sht->suspend)
+               err = sht->suspend(sdev, state);
+
+       return err;
+}
+
+static int scsi_bus_resume(struct device * dev)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       struct scsi_host_template *sht = sdev->host->hostt;
+       int err = 0;
+
+       if (sht->resume)
+               err = sht->resume(sdev);
+
+       scsi_device_resume(sdev);
+       return err;
+}
+
 struct bus_type scsi_bus_type = {
         .name          = "scsi",
         .match         = scsi_bus_match,
+       .suspend        = scsi_bus_suspend,
+       .resume         = scsi_bus_resume,
 };
 
 int scsi_sysfs_register(void)
@@ -217,7 +338,7 @@ void scsi_sysfs_unregister(void)
  */
 #define sdev_show_function(field, format_string)                               \
 static ssize_t                                                         \
-sdev_show_##field (struct device *dev, char *buf)                              \
+sdev_show_##field (struct device *dev, struct device_attribute *attr, char *buf)                               \
 {                                                                      \
        struct scsi_device *sdev;                                       \
        sdev = to_scsi_device(dev);                                     \
@@ -241,7 +362,7 @@ static DEVICE_ATTR(field, S_IRUGO, sdev_show_##field, NULL);
        sdev_show_function(field, format_string)                                \
                                                                        \
 static ssize_t                                                         \
-sdev_store_##field (struct device *dev, const char *buf, size_t count) \
+sdev_store_##field (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)  \
 {                                                                      \
        struct scsi_device *sdev;                                       \
        sdev = to_scsi_device(dev);                                     \
@@ -261,7 +382,7 @@ static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, sdev_show_##field, sdev_store_##fie
        sdev_show_function(field, "%d\n")                                       \
                                                                        \
 static ssize_t                                                         \
-sdev_store_##field (struct device *dev, const char *buf, size_t count) \
+sdev_store_##field (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)  \
 {                                                                      \
        int ret;                                                        \
        struct scsi_device *sdev;                                       \
@@ -304,7 +425,7 @@ sdev_rd_attr (model, "%.16s\n");
 sdev_rd_attr (rev, "%.4s\n");
 
 static ssize_t
-sdev_show_timeout (struct device *dev, char *buf)
+sdev_show_timeout (struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct scsi_device *sdev;
        sdev = to_scsi_device(dev);
@@ -312,7 +433,7 @@ sdev_show_timeout (struct device *dev, char *buf)
 }
 
 static ssize_t
-sdev_store_timeout (struct device *dev, const char *buf, size_t count)
+sdev_store_timeout (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct scsi_device *sdev;
        int timeout;
@@ -324,14 +445,14 @@ sdev_store_timeout (struct device *dev, const char *buf, size_t count)
 static DEVICE_ATTR(timeout, S_IRUGO | S_IWUSR, sdev_show_timeout, sdev_store_timeout);
 
 static ssize_t
-store_rescan_field (struct device *dev, const char *buf, size_t count) 
+store_rescan_field (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        scsi_rescan_device(dev);
        return count;
 }
 static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field);
 
-static ssize_t sdev_store_delete(struct device *dev, const char *buf,
+static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf,
                                 size_t count)
 {
        scsi_remove_device(to_scsi_device(dev));
@@ -340,13 +461,13 @@ static ssize_t sdev_store_delete(struct device *dev, const char *buf,
 static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete);
 
 static ssize_t
-store_state_field(struct device *dev, const char *buf, size_t count)
+store_state_field(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        int i;
        struct scsi_device *sdev = to_scsi_device(dev);
        enum scsi_device_state state = 0;
 
-       for (i = 0; i < sizeof(sdev_states)/sizeof(sdev_states[0]); i++) {
+       for (i = 0; i < ARRAY_SIZE(sdev_states); i++) {
                const int len = strlen(sdev_states[i].name);
                if (strncmp(sdev_states[i].name, buf, len) == 0 &&
                   buf[len] == '\n') {
@@ -363,7 +484,7 @@ store_state_field(struct device *dev, const char *buf, size_t count)
 }
 
 static ssize_t
-show_state_field(struct device *dev, char *buf)
+show_state_field(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct scsi_device *sdev = to_scsi_device(dev);
        const char *name = scsi_device_state_name(sdev->sdev_state);
@@ -374,13 +495,52 @@ 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, struct device_attribute *attr, 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);
+
+static ssize_t
+show_iostat_counterbits(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8);
+}
+
+static DEVICE_ATTR(iocounterbits, S_IRUGO, show_iostat_counterbits, NULL);
+
+#define show_sdev_iostat(field)                                                \
+static ssize_t                                                         \
+show_iostat_##field(struct device *dev, struct device_attribute *attr, char *buf)                      \
+{                                                                      \
+       struct scsi_device *sdev = to_scsi_device(dev);                 \
+       unsigned long long count = atomic_read(&sdev->field);           \
+       return snprintf(buf, 20, "0x%llx\n", count);                    \
+}                                                                      \
+static DEVICE_ATTR(field, S_IRUGO, show_iostat_##field, NULL)
+
+show_sdev_iostat(iorequest_cnt);
+show_sdev_iostat(iodone_cnt);
+show_sdev_iostat(ioerr_cnt);
 
 
 /* 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,
@@ -390,9 +550,84 @@ static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
        &dev_attr_delete,
        &dev_attr_state,
        &dev_attr_timeout,
+       &dev_attr_iocounterbits,
+       &dev_attr_iorequest_cnt,
+       &dev_attr_iodone_cnt,
+       &dev_attr_ioerr_cnt,
        NULL
 };
 
+static ssize_t sdev_store_queue_depth_rw(struct device *dev, struct device_attribute *attr, 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, 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 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,
@@ -439,7 +674,6 @@ static int attr_add(struct device *dev, struct device_attribute *attr)
  **/
 int scsi_sysfs_add_sdev(struct scsi_device *sdev)
 {
-       struct class_device_attribute **attrs;
        int error, i;
 
        if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0)
@@ -447,35 +681,25 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
 
        error = device_add(&sdev->sdev_gendev);
        if (error) {
+               put_device(sdev->sdev_gendev.parent);
                printk(KERN_INFO "error 1\n");
                return error;
        }
-
        error = class_device_add(&sdev->sdev_classdev);
        if (error) {
                printk(KERN_INFO "error 2\n");
                goto clean_device;
        }
+
        /* take a reference for the sdev_classdev; this is
         * released by the sdev_class .release */
        get_device(&sdev->sdev_gendev);
-
-       if (sdev->transport_classdev.class) {
-               error = class_device_add(&sdev->transport_classdev);
-               if (error)
-                       goto clean_device2;
-               /* take a reference for the transport_classdev; this
-                * is released by the transport_class .release */
-               get_device(&sdev->sdev_gendev);
-               
-       }
-
        if (sdev->host->hostt->sdev_attrs) {
                for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) {
                        error = attr_add(&sdev->sdev_gendev,
                                        sdev->host->hostt->sdev_attrs[i]);
                        if (error) {
-                               scsi_remove_device(sdev);
+                               __scsi_remove_device(sdev);
                                goto out;
                        }
                }
@@ -484,61 +708,114 @@ 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);
+                               __scsi_remove_device(sdev);
                                goto out;
                        }
                }
        }
 
-       if (sdev->transport_classdev.class) {
-               attrs = sdev->host->transportt->attrs;
-               for (i = 0; attrs[i]; i++) {
-                       error = class_device_create_file(&sdev->transport_classdev,
-                                                        attrs[i]);
-                       if (error) {
-                               scsi_remove_device(sdev);
-                               goto out;
-                       }
-               }
-       }
-
+       transport_add_device(&sdev->sdev_gendev);
  out:
        return error;
 
- clean_device2:
-       class_device_del(&sdev->sdev_classdev);
  clean_device:
        scsi_device_set_state(sdev, SDEV_CANCEL);
 
        device_del(&sdev->sdev_gendev);
+       transport_destroy_device(&sdev->sdev_gendev);
        put_device(&sdev->sdev_gendev);
 
        return error;
 }
 
-/**
- * scsi_remove_device - unregister a device from the scsi bus
- * @sdev:      scsi_device to unregister
- **/
-void scsi_remove_device(struct scsi_device *sdev)
+void __scsi_remove_device(struct scsi_device *sdev)
 {
+       struct device *dev = &sdev->sdev_gendev;
+
        if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
                return;
 
        class_device_unregister(&sdev->sdev_classdev);
-       if (sdev->transport_classdev.class)
-               class_device_unregister(&sdev->transport_classdev);
-       device_del(&sdev->sdev_gendev);
+       transport_remove_device(dev);
+       device_del(dev);
        scsi_device_set_state(sdev, SDEV_DEL);
        if (sdev->host->hostt->slave_destroy)
                sdev->host->hostt->slave_destroy(sdev);
-       if (sdev->host->transportt->cleanup)
-               sdev->host->transportt->cleanup(sdev);
-       put_device(&sdev->sdev_gendev);
+       transport_destroy_device(dev);
+       put_device(dev);
+}
+
+/**
+ * scsi_remove_device - unregister a device from the scsi bus
+ * @sdev:      scsi_device to unregister
+ **/
+void scsi_remove_device(struct scsi_device *sdev)
+{
+       struct Scsi_Host *shost = sdev->host;
+
+       mutex_lock(&shost->scan_mutex);
+       __scsi_remove_device(sdev);
+       mutex_unlock(&shost->scan_mutex);
+}
+EXPORT_SYMBOL(scsi_remove_device);
+
+void __scsi_remove_target(struct scsi_target *starget)
+{
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       unsigned long flags;
+       struct scsi_device *sdev;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       starget->reap_ref++;
+ restart:
+       list_for_each_entry(sdev, &shost->__devices, siblings) {
+               if (sdev->channel != starget->channel ||
+                   sdev->id != starget->id ||
+                   sdev->sdev_state == SDEV_DEL)
+                       continue;
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               scsi_remove_device(sdev);
+               spin_lock_irqsave(shost->host_lock, flags);
+               goto restart;
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+       scsi_target_reap(starget);
+}
+
+static int __remove_child (struct device * dev, void * data)
+{
+       if (scsi_is_target_device(dev))
+               __scsi_remove_target(to_scsi_target(dev));
+       return 0;
+}
+
+/**
+ * scsi_remove_target - try to remove a target and all its devices
+ * @dev: generic starget or parent of generic stargets to be removed
+ *
+ * Note: This is slightly racy.  It is possible that if the user
+ * requests the addition of another device then the target won't be
+ * removed.
+ */
+void scsi_remove_target(struct device *dev)
+{
+       struct device *rdev;
+
+       if (scsi_is_target_device(dev)) {
+               __scsi_remove_target(to_scsi_target(dev));
+               return;
+       }
+
+       rdev = get_device(dev);
+       device_for_each_child(dev, NULL, __remove_child);
+       put_device(rdev);
 }
+EXPORT_SYMBOL(scsi_remove_target);
 
 int scsi_register_driver(struct device_driver *drv)
 {
@@ -546,6 +823,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)
 {
@@ -553,6 +831,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(
@@ -620,9 +899,43 @@ int scsi_sysfs_add_host(struct Scsi_Host *shost)
                }
        }
 
+       transport_register_device(&shost->shost_gendev);
        return 0;
 }
 
+void scsi_sysfs_device_initialize(struct scsi_device *sdev)
+{
+       unsigned long flags;
+       struct Scsi_Host *shost = sdev->host;
+       struct scsi_target  *starget = sdev->sdev_target;
+
+       device_initialize(&sdev->sdev_gendev);
+       sdev->sdev_gendev.bus = &scsi_bus_type;
+       sdev->sdev_gendev.release = scsi_device_dev_release;
+       sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d",
+               sdev->host->host_no, sdev->channel, sdev->id,
+               sdev->lun);
+       
+       class_device_initialize(&sdev->sdev_classdev);
+       sdev->sdev_classdev.dev = &sdev->sdev_gendev;
+       sdev->sdev_classdev.class = &sdev_class;
+       snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE,
+                "%d:%d:%d:%d", sdev->host->host_no,
+                sdev->channel, sdev->id, sdev->lun);
+       sdev->scsi_level = SCSI_2;
+       transport_setup_device(&sdev->sdev_gendev);
+       spin_lock_irqsave(shost->host_lock, flags);
+       list_add_tail(&sdev->same_target_siblings, &starget->devices);
+       list_add_tail(&sdev->siblings, &shost->__devices);
+       spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
+int scsi_is_sdev_device(const struct device *dev)
+{
+       return dev->release == scsi_device_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_sdev_device);
+
 /* A blank transport template that is used in drivers that don't
  * yet implement Transport Attributes */
-struct scsi_transport_template blank_transport_template = { NULL, };
+struct scsi_transport_template blank_transport_template = { { { {NULL, }, }, }, };