diff options
Diffstat (limited to 'drivers/scsi/scsi_pm.c')
-rw-r--r-- | drivers/scsi/scsi_pm.c | 43 |
1 files changed, 41 insertions, 2 deletions
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index d329f8b12e2..c4670642d02 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -7,6 +7,7 @@ #include <linux/pm_runtime.h> #include <linux/export.h> +#include <linux/async.h> #include <scsi/scsi.h> #include <scsi/scsi_device.h> @@ -49,8 +50,22 @@ static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg) { int err = 0; - if (scsi_is_sdev_device(dev)) + if (scsi_is_sdev_device(dev)) { + /* + * sd is the only high-level SCSI driver to implement runtime + * PM, and sd treats runtime suspend, system suspend, and + * system hibernate identically (but not system freeze). + */ + if (pm_runtime_suspended(dev)) { + if (msg.event == PM_EVENT_SUSPEND || + msg.event == PM_EVENT_HIBERNATE) + return 0; /* already suspended */ + + /* wake up device so that FREEZE will succeed */ + pm_runtime_resume(dev); + } err = scsi_dev_type_suspend(dev, msg); + } return err; } @@ -58,8 +73,17 @@ static int scsi_bus_resume_common(struct device *dev) { int err = 0; - if (scsi_is_sdev_device(dev)) + if (scsi_is_sdev_device(dev)) { + /* + * Parent device may have runtime suspended as soon as + * it is woken up during the system resume. + * + * Resume it on behalf of child. + */ + pm_runtime_get_sync(dev->parent); err = scsi_dev_type_resume(dev); + pm_runtime_put_sync(dev->parent); + } if (err == 0) { pm_runtime_disable(dev); @@ -69,6 +93,19 @@ static int scsi_bus_resume_common(struct device *dev) return err; } +static int scsi_bus_prepare(struct device *dev) +{ + if (scsi_is_sdev_device(dev)) { + /* sd probing uses async_schedule. Wait until it finishes. */ + async_synchronize_full(); + + } else if (scsi_is_host_device(dev)) { + /* Wait until async scanning is finished */ + scsi_complete_async_scans(); + } + return 0; +} + static int scsi_bus_suspend(struct device *dev) { return scsi_bus_suspend_common(dev, PMSG_SUSPEND); @@ -87,6 +124,7 @@ static int scsi_bus_poweroff(struct device *dev) #else /* CONFIG_PM_SLEEP */ #define scsi_bus_resume_common NULL +#define scsi_bus_prepare NULL #define scsi_bus_suspend NULL #define scsi_bus_freeze NULL #define scsi_bus_poweroff NULL @@ -195,6 +233,7 @@ void scsi_autopm_put_host(struct Scsi_Host *shost) #endif /* CONFIG_PM_RUNTIME */ const struct dev_pm_ops scsi_bus_pm_ops = { + .prepare = scsi_bus_prepare, .suspend = scsi_bus_suspend, .resume = scsi_bus_resume_common, .freeze = scsi_bus_freeze, |