]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/scsi/scsi_sysfs.c
Merge branch 'upstream-linus' of git://oss.oracle.com/home/sourcebo/git/ocfs2
[linux-2.6-omap-h63xx.git] / drivers / scsi / scsi_sysfs.c
index dae59d1da07a720b11f2f74b5696263a8d863e3b..a6fde52946d68d1f2c5f943b2380185dc71c34c7 100644 (file)
@@ -21,7 +21,7 @@
 #include "scsi_priv.h"
 #include "scsi_logging.h"
 
-static struct {
+static const struct {
        enum scsi_device_state  value;
        char                    *name;
 } sdev_states[] = {
@@ -48,7 +48,7 @@ const char *scsi_device_state_name(enum scsi_device_state state)
        return name;
 }
 
-static struct {
+static const struct {
        enum scsi_host_state    value;
        char                    *name;
 } shost_states[] = {
@@ -57,6 +57,8 @@ static struct {
        { 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)
 {
@@ -104,7 +106,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;
 }
 
@@ -212,8 +217,9 @@ static void scsi_device_cls_release(struct class_device *class_dev)
        put_device(&sdev->sdev_gendev);
 }
 
-static void scsi_device_dev_release(struct device *dev)
+static void scsi_device_dev_release_usercontext(void *data)
 {
+       struct device *dev = data;
        struct scsi_device *sdev;
        struct device *parent;
        struct scsi_target *starget;
@@ -232,6 +238,7 @@ static void scsi_device_dev_release(struct device *dev)
 
        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 */
@@ -247,6 +254,13 @@ static void scsi_device_dev_release(struct device *dev)
                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, dev,
+                                  &sdp->ew);
+}
+
 static struct class sdev_class = {
        .name           = "scsi_device",
        .release        = scsi_device_cls_release,
@@ -261,9 +275,40 @@ static int scsi_bus_match(struct device *dev, struct device_driver *gendrv)
        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)
@@ -653,7 +698,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
                        error = attr_add(&sdev->sdev_gendev,
                                        sdev->host->hostt->sdev_attrs[i]);
                        if (error) {
-                               scsi_remove_device(sdev);
+                               __scsi_remove_device(sdev);
                                goto out;
                        }
                }
@@ -667,7 +712,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
                                                        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;
                        }
                }
@@ -687,27 +732,34 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
        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 Scsi_Host *shost = sdev->host;
+       struct device *dev = &sdev->sdev_gendev;
 
-       down(&shost->scan_mutex);
        if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
-               goto out;
+               return;
 
        class_device_unregister(&sdev->sdev_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);
-       transport_unregister_device(&sdev->sdev_gendev);
-       put_device(&sdev->sdev_gendev);
-out:
-       up(&shost->scan_mutex);
+       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);
 
@@ -715,17 +767,20 @@ 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, *tmp;
+       struct scsi_device *sdev;
 
        spin_lock_irqsave(shost->host_lock, flags);
        starget->reap_ref++;
-       list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {
+ restart:
+       list_for_each_entry(sdev, &shost->__devices, siblings) {
                if (sdev->channel != starget->channel ||
-                   sdev->id != starget->id)
+                   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);