#include <linux/completion.h>
 #include <linux/transport_class.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 void scsi_remove_host(struct Scsi_Host *shost)
 {
        unsigned long flags;
+
        mutex_lock(&shost->scan_mutex);
        spin_lock_irqsave(shost->host_lock, flags);
        if (scsi_host_set_state(shost, SHOST_CANCEL))
                        return;
                }
        spin_unlock_irqrestore(shost->host_lock, flags);
+
+       scsi_autopm_get_host(shost);
        scsi_forget_host(shost);
        mutex_unlock(&shost->scan_mutex);
        scsi_proc_host_rm(shost);
                shost->shost_gendev.parent = dev ? dev : &platform_bus;
        shost->dma_dev = dma_dev;
 
-       device_enable_async_suspend(&shost->shost_gendev);
-
        error = device_add(&shost->shost_gendev);
        if (error)
                goto out;
 
+       pm_runtime_set_active(&shost->shost_gendev);
+       pm_runtime_enable(&shost->shost_gendev);
+       device_enable_async_suspend(&shost->shost_gendev);
+
        scsi_host_set_state(shost, SHOST_RUNNING);
        get_device(shost->shost_gendev.parent);
 
 
                 * what we need to do to get it up and online again (if we can).
                 * If we fail, we end up taking the thing offline.
                 */
+               if (scsi_autopm_get_host(shost) != 0) {
+                       SCSI_LOG_ERROR_RECOVERY(1,
+                               printk(KERN_ERR "Error handler scsi_eh_%d "
+                                               "unable to autoresume\n",
+                                               shost->host_no));
+                       continue;
+               }
+
                if (shost->transportt->eh_strategy_handler)
                        shost->transportt->eh_strategy_handler(shost);
                else
                 * which are still online.
                 */
                scsi_restart_operations(shost);
+               scsi_autopm_put_host(shost);
                set_current_state(TASK_INTERRUPTIBLE);
        }
        __set_current_state(TASK_RUNNING);
 int
 scsi_reset_provider(struct scsi_device *dev, int flag)
 {
-       struct scsi_cmnd *scmd = scsi_get_command(dev, GFP_KERNEL);
+       struct scsi_cmnd *scmd;
        struct Scsi_Host *shost = dev->host;
        struct request req;
        unsigned long flags;
        int rtn;
 
+       if (scsi_autopm_get_host(shost) < 0)
+               return FAILED;
+
+       scmd = scsi_get_command(dev, GFP_KERNEL);
        blk_rq_init(NULL, &req);
        scmd->request = &req;
 
        scsi_run_host_queues(shost);
 
        scsi_next_command(scmd);
+       scsi_autopm_put_host(shost);
        return rtn;
 }
 EXPORT_SYMBOL(scsi_reset_provider);
 
 
        if (scsi_is_sdev_device(dev))
                err = scsi_dev_type_resume(dev);
+
+       if (err == 0) {
+               pm_runtime_disable(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+       }
        return err;
 }
 
 
 #endif /* CONFIG_PM_SLEEP */
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int scsi_runtime_suspend(struct device *dev)
+{
+       int err = 0;
+
+       dev_dbg(dev, "scsi_runtime_suspend\n");
+       if (scsi_is_sdev_device(dev)) {
+               err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND);
+               if (err == -EAGAIN)
+                       pm_schedule_suspend(dev, jiffies_to_msecs(
+                               round_jiffies_up_relative(HZ/10)));
+       }
+
+       /* Insert hooks here for targets, hosts, and transport classes */
+
+       return err;
+}
+
+static int scsi_runtime_resume(struct device *dev)
+{
+       int err = 0;
+
+       dev_dbg(dev, "scsi_runtime_resume\n");
+       if (scsi_is_sdev_device(dev))
+               err = scsi_dev_type_resume(dev);
+
+       /* Insert hooks here for targets, hosts, and transport classes */
+
+       return err;
+}
+
+static int scsi_runtime_idle(struct device *dev)
+{
+       int err;
+
+       dev_dbg(dev, "scsi_runtime_idle\n");
+
+       /* Insert hooks here for targets, hosts, and transport classes */
+
+       if (scsi_is_sdev_device(dev))
+               err = pm_schedule_suspend(dev, 100);
+       else
+               err = pm_runtime_suspend(dev);
+       return err;
+}
+
+int scsi_autopm_get_device(struct scsi_device *sdev)
+{
+       int     err;
+
+       err = pm_runtime_get_sync(&sdev->sdev_gendev);
+       if (err < 0)
+               pm_runtime_put_sync(&sdev->sdev_gendev);
+       else if (err > 0)
+               err = 0;
+       return err;
+}
+EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
+
+void scsi_autopm_put_device(struct scsi_device *sdev)
+{
+       pm_runtime_put_sync(&sdev->sdev_gendev);
+}
+EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
+
+void scsi_autopm_get_target(struct scsi_target *starget)
+{
+       pm_runtime_get_sync(&starget->dev);
+}
+
+void scsi_autopm_put_target(struct scsi_target *starget)
+{
+       pm_runtime_put_sync(&starget->dev);
+}
+
+int scsi_autopm_get_host(struct Scsi_Host *shost)
+{
+       int     err;
+
+       err = pm_runtime_get_sync(&shost->shost_gendev);
+       if (err < 0)
+               pm_runtime_put_sync(&shost->shost_gendev);
+       else if (err > 0)
+               err = 0;
+       return err;
+}
+
+void scsi_autopm_put_host(struct Scsi_Host *shost)
+{
+       pm_runtime_put_sync(&shost->shost_gendev);
+}
+
+#else
+
+#define scsi_runtime_suspend   NULL
+#define scsi_runtime_resume    NULL
+#define scsi_runtime_idle      NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
 const struct dev_pm_ops scsi_bus_pm_ops = {
        .suspend =              scsi_bus_suspend,
        .resume =               scsi_bus_resume_common,
        .thaw =                 scsi_bus_resume_common,
        .poweroff =             scsi_bus_poweroff,
        .restore =              scsi_bus_resume_common,
+       .runtime_suspend =      scsi_runtime_suspend,
+       .runtime_resume =       scsi_runtime_resume,
+       .runtime_idle =         scsi_runtime_idle,
 };
 
 struct request;
 struct scsi_cmnd;
 struct scsi_device;
+struct scsi_target;
 struct scsi_host_template;
 struct Scsi_Host;
 struct scsi_nl_hdr;
 /* scsi_pm.c */
 #ifdef CONFIG_PM_OPS
 extern const struct dev_pm_ops scsi_bus_pm_ops;
-#else
+#else /* CONFIG_PM_OPS */
 #define scsi_bus_pm_ops                (*NULL)
 #endif
+#ifdef CONFIG_PM_RUNTIME
+extern void scsi_autopm_get_target(struct scsi_target *);
+extern void scsi_autopm_put_target(struct scsi_target *);
+extern int scsi_autopm_get_host(struct Scsi_Host *);
+extern void scsi_autopm_put_host(struct Scsi_Host *);
+#else
+static inline void scsi_autopm_get_target(struct scsi_target *t) {}
+static inline void scsi_autopm_put_target(struct scsi_target *t) {}
+static inline int scsi_autopm_get_host(struct Scsi_Host *h) { return 0; }
+static inline void scsi_autopm_put_host(struct Scsi_Host *h) {}
+#endif /* CONFIG_PM_RUNTIME */
 
 /* 
  * internal scsi timeout functions: for use by mid-layer and transport
 
        starget = scsi_alloc_target(parent, channel, id);
        if (!starget)
                return ERR_PTR(-ENOMEM);
+       scsi_autopm_get_target(starget);
 
        mutex_lock(&shost->scan_mutex);
        if (!shost->async_scan)
                scsi_complete_async_scans();
 
-       if (scsi_host_scan_allowed(shost))
+       if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) {
                scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);
+               scsi_autopm_put_host(shost);
+       }
        mutex_unlock(&shost->scan_mutex);
+       scsi_autopm_put_target(starget);
        scsi_target_reap(starget);
        put_device(&starget->dev);
 
        starget = scsi_alloc_target(parent, channel, id);
        if (!starget)
                return;
+       scsi_autopm_get_target(starget);
 
        if (lun != SCAN_WILD_CARD) {
                /*
        }
 
  out_reap:
+       scsi_autopm_put_target(starget);
        /* now determine if the target has any children at all
         * and if not, nuke it */
        scsi_target_reap(starget);
        if (!shost->async_scan)
                scsi_complete_async_scans();
 
-       if (scsi_host_scan_allowed(shost))
+       if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) {
                __scsi_scan_target(parent, channel, id, lun, rescan);
+               scsi_autopm_put_host(shost);
+       }
        mutex_unlock(&shost->scan_mutex);
 }
 EXPORT_SYMBOL(scsi_scan_target);
        if (!shost->async_scan)
                scsi_complete_async_scans();
 
-       if (scsi_host_scan_allowed(shost)) {
+       if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) {
                if (channel == SCAN_WILD_CARD)
                        for (channel = 0; channel <= shost->max_channel;
                             channel++)
                                                  rescan);
                else
                        scsi_scan_channel(shost, channel, id, lun, rescan);
+               scsi_autopm_put_host(shost);
        }
        mutex_unlock(&shost->scan_mutex);
 
 static int do_scan_async(void *_data)
 {
        struct async_scan_data *data = _data;
-       do_scsi_scan_host(data->shost);
+       struct Scsi_Host *shost = data->shost;
+
+       do_scsi_scan_host(shost);
        scsi_finish_async_scan(data);
+       scsi_autopm_put_host(shost);
        return 0;
 }
 
 
        if (strncmp(scsi_scan_type, "none", 4) == 0)
                return;
+       if (scsi_autopm_get_host(shost) < 0)
+               return;
 
        data = scsi_prep_async_scan(shost);
        if (!data) {
                do_scsi_scan_host(shost);
+               scsi_autopm_put_host(shost);
                return;
        }
 
        p = kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no);
        if (IS_ERR(p))
                do_scan_async(data);
+       /* scsi_autopm_put_host(shost) is called in do_scan_async() */
 }
 EXPORT_SYMBOL(scsi_scan_host);
 
 
 #include <linux/init.h>
 #include <linux/blkdev.h>
 #include <linux/device.h>
+#include <linux/pm_runtime.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
        if (starget->state != STARGET_CREATED)
                return 0;
 
-       device_enable_async_suspend(&starget->dev);
-
        error = device_add(&starget->dev);
        if (error) {
                dev_err(&starget->dev, "target device_add failed, error %d\n", error);
        transport_add_device(&starget->dev);
        starget->state = STARGET_RUNNING;
 
+       pm_runtime_set_active(&starget->dev);
+       pm_runtime_enable(&starget->dev);
+       device_enable_async_suspend(&starget->dev);
+
        return 0;
 }
 
                return error;
 
        transport_configure_device(&starget->dev);
+
        device_enable_async_suspend(&sdev->sdev_gendev);
+       scsi_autopm_get_target(starget);
+       pm_runtime_set_active(&sdev->sdev_gendev);
+       pm_runtime_forbid(&sdev->sdev_gendev);
+       pm_runtime_enable(&sdev->sdev_gendev);
+       scsi_autopm_put_target(starget);
+
+       /* The following call will keep sdev active indefinitely, until
+        * its driver does a corresponding scsi_autopm_pm_device().  Only
+        * drivers supporting autosuspend will do this.
+        */
+       scsi_autopm_get_device(sdev);
+
        error = device_add(&sdev->sdev_gendev);
        if (error) {
                printk(KERN_INFO "error 1\n");
 
        if (retval)
                goto sg_put;
 
+       retval = scsi_autopm_get_device(sdp->device);
+       if (retval)
+               goto sdp_put;
+
        if (!((flags & O_NONBLOCK) ||
              scsi_block_when_processing_errors(sdp->device))) {
                retval = -ENXIO;
        }
        retval = 0;
 error_out:
-       if (retval)
+       if (retval) {
+               scsi_autopm_put_device(sdp->device);
+sdp_put:
                scsi_device_put(sdp->device);
+       }
 sg_put:
        if (sdp)
                sg_put_dev(sdp);
        sdp->exclude = 0;
        wake_up_interruptible(&sdp->o_excl_wait);
 
+       scsi_autopm_put_device(sdp->device);
        kref_put(&sfp->f_ref, sg_remove_sfp);
        return 0;
 }
 
                            struct scsi_sense_hdr *, int timeout, int retries,
                            int *resid);
 
+#ifdef CONFIG_PM_RUNTIME
+extern int scsi_autopm_get_device(struct scsi_device *);
+extern void scsi_autopm_put_device(struct scsi_device *);
+#else
+static inline int scsi_autopm_get_device(struct scsi_device *d) { return 0; }
+static inline void scsi_autopm_put_device(struct scsi_device *d) {}
+#endif /* CONFIG_PM_RUNTIME */
+
 static inline int __must_check scsi_device_reprobe(struct scsi_device *sdev)
 {
        return device_reprobe(&sdev->sdev_gendev);