summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/scsi_pm.c')
-rw-r--r--drivers/scsi/scsi_pm.c43
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,