blob: 243b2d13fa0d4c525b3df4112218c0692311cbdc [file] [log] [blame]
Alan Sterndb5bd1e2010-06-17 10:36:49 -04001/*
2 * scsi_pm.c Copyright (C) 2010 Alan Stern
3 *
4 * SCSI dynamic Power Management
5 * Initial version: Alan Stern <stern@rowland.harvard.edu>
6 */
7
8#include <linux/pm_runtime.h>
Paul Gortmaker09703662011-05-27 09:37:25 -04009#include <linux/export.h>
Alan Sternfea6d602012-02-17 16:25:08 -050010#include <linux/async.h>
Alan Sterndb5bd1e2010-06-17 10:36:49 -040011
12#include <scsi/scsi.h>
13#include <scsi/scsi_device.h>
14#include <scsi/scsi_driver.h>
15#include <scsi/scsi_host.h>
16
17#include "scsi_priv.h"
18
Subhash Jadavani57d6b142016-07-29 16:50:07 -070019static int do_scsi_runtime_resume(struct device *dev,
20 const struct dev_pm_ops *pm);
21
Aaron Lu6627b382013-10-28 15:27:49 +080022#ifdef CONFIG_PM_SLEEP
23
Dan Williams3c31b522014-04-10 15:30:35 -070024static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)
Alan Sterndb5bd1e2010-06-17 10:36:49 -040025{
Dan Williams3c31b522014-04-10 15:30:35 -070026 return pm && pm->suspend ? pm->suspend(dev) : 0;
27}
28
29static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm)
30{
31 return pm && pm->freeze ? pm->freeze(dev) : 0;
32}
33
34static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm)
35{
36 return pm && pm->poweroff ? pm->poweroff(dev) : 0;
37}
38
39static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm)
40{
41 return pm && pm->resume ? pm->resume(dev) : 0;
42}
43
44static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm)
45{
46 return pm && pm->thaw ? pm->thaw(dev) : 0;
47}
48
49static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm)
50{
51 return pm && pm->restore ? pm->restore(dev) : 0;
52}
53
54static int scsi_dev_type_suspend(struct device *dev,
55 int (*cb)(struct device *, const struct dev_pm_ops *))
56{
57 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
Alan Sterndb5bd1e2010-06-17 10:36:49 -040058 int err;
59
Dan Williams3c31b522014-04-10 15:30:35 -070060 /* flush pending in-flight resume operations, suspend is synchronous */
61 async_synchronize_full_domain(&scsi_sd_pm_domain);
62
Alan Sterndb5bd1e2010-06-17 10:36:49 -040063 err = scsi_device_quiesce(to_scsi_device(dev));
64 if (err == 0) {
Dan Williams3c31b522014-04-10 15:30:35 -070065 err = cb(dev, pm);
66 if (err)
67 scsi_device_resume(to_scsi_device(dev));
Alan Sterndb5bd1e2010-06-17 10:36:49 -040068 }
69 dev_dbg(dev, "scsi suspend: %d\n", err);
70 return err;
71}
72
Dan Williams3c31b522014-04-10 15:30:35 -070073static int scsi_dev_type_resume(struct device *dev,
74 int (*cb)(struct device *, const struct dev_pm_ops *))
Alan Sterndb5bd1e2010-06-17 10:36:49 -040075{
Dan Williams3c31b522014-04-10 15:30:35 -070076 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
Alan Sterndb5bd1e2010-06-17 10:36:49 -040077 int err = 0;
78
Dan Williams3c31b522014-04-10 15:30:35 -070079 err = cb(dev, pm);
Alan Sterndb5bd1e2010-06-17 10:36:49 -040080 scsi_device_resume(to_scsi_device(dev));
81 dev_dbg(dev, "scsi resume: %d\n", err);
Dan Williams3c31b522014-04-10 15:30:35 -070082
Subhash Jadavani57d6b142016-07-29 16:50:07 -070083 if (err == 0 && (cb != do_scsi_runtime_resume)) {
Dan Williams3c31b522014-04-10 15:30:35 -070084 pm_runtime_disable(dev);
Subhash Jadavani57d6b142016-07-29 16:50:07 -070085 err = pm_runtime_set_active(dev);
Dan Williams3c31b522014-04-10 15:30:35 -070086 pm_runtime_enable(dev);
Subhash Jadavani57d6b142016-07-29 16:50:07 -070087
88 if (!err && scsi_is_sdev_device(dev)) {
89 struct scsi_device *sdev = to_scsi_device(dev);
90
91 /*
92 * If scsi device runtime PM is managed by block layer
93 * then we should update request queue's runtime status
94 * as well.
95 */
96 if (sdev->request_queue->dev)
97 blk_post_runtime_resume(sdev->request_queue, 0);
98 }
Dan Williams3c31b522014-04-10 15:30:35 -070099 }
100
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400101 return err;
102}
103
Aaron Lu80d2fd42012-11-09 15:27:54 +0800104static int
Dan Williams3c31b522014-04-10 15:30:35 -0700105scsi_bus_suspend_common(struct device *dev,
106 int (*cb)(struct device *, const struct dev_pm_ops *))
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400107{
108 int err = 0;
109
Lin Ming28640512011-12-05 09:20:25 +0800110 if (scsi_is_sdev_device(dev)) {
111 /*
Aaron Lu80d2fd42012-11-09 15:27:54 +0800112 * All the high-level SCSI drivers that implement runtime
113 * PM treat runtime suspend, system suspend, and system
Oliver Neukum95897912013-09-16 13:28:15 +0200114 * hibernate nearly identically. In all cases the requirements
115 * for runtime suspension are stricter.
Lin Ming28640512011-12-05 09:20:25 +0800116 */
Aaron Lu80d2fd42012-11-09 15:27:54 +0800117 if (pm_runtime_suspended(dev))
118 return 0;
Lin Ming28640512011-12-05 09:20:25 +0800119
Aaron Lu80d2fd42012-11-09 15:27:54 +0800120 err = scsi_dev_type_suspend(dev, cb);
Lin Ming28640512011-12-05 09:20:25 +0800121 }
Aaron Lu80d2fd42012-11-09 15:27:54 +0800122
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400123 return err;
124}
125
Dan Williams3c31b522014-04-10 15:30:35 -0700126static void async_sdev_resume(void *dev, async_cookie_t cookie)
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400127{
Dan Williams3c31b522014-04-10 15:30:35 -0700128 scsi_dev_type_resume(dev, do_scsi_resume);
129}
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400130
Dan Williams3c31b522014-04-10 15:30:35 -0700131static void async_sdev_thaw(void *dev, async_cookie_t cookie)
132{
133 scsi_dev_type_resume(dev, do_scsi_thaw);
134}
Aaron Lu63347902012-11-09 15:27:52 +0800135
Dan Williams3c31b522014-04-10 15:30:35 -0700136static void async_sdev_restore(void *dev, async_cookie_t cookie)
137{
138 scsi_dev_type_resume(dev, do_scsi_restore);
139}
140
141static int scsi_bus_resume_common(struct device *dev,
142 int (*cb)(struct device *, const struct dev_pm_ops *))
143{
144 async_func_t fn;
145
146 if (!scsi_is_sdev_device(dev))
147 fn = NULL;
148 else if (cb == do_scsi_resume)
149 fn = async_sdev_resume;
150 else if (cb == do_scsi_thaw)
151 fn = async_sdev_thaw;
152 else if (cb == do_scsi_restore)
153 fn = async_sdev_restore;
154 else
155 fn = NULL;
156
157 if (fn) {
158 async_schedule_domain(fn, dev, &scsi_sd_pm_domain);
159
160 /*
161 * If a user has disabled async probing a likely reason
162 * is due to a storage enclosure that does not inject
163 * staggered spin-ups. For safety, make resume
164 * synchronous as well in that case.
165 */
166 if (strncmp(scsi_scan_type, "async", 5) != 0)
167 async_synchronize_full_domain(&scsi_sd_pm_domain);
168 } else {
Alan Sternbc4f2402010-06-17 10:41:42 -0400169 pm_runtime_disable(dev);
170 pm_runtime_set_active(dev);
171 pm_runtime_enable(dev);
172 }
Dan Williams3c31b522014-04-10 15:30:35 -0700173 return 0;
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400174}
175
Alan Sternfea6d602012-02-17 16:25:08 -0500176static int scsi_bus_prepare(struct device *dev)
177{
178 if (scsi_is_sdev_device(dev)) {
179 /* sd probing uses async_schedule. Wait until it finishes. */
Dan Williamsa7a20d12012-03-22 17:05:11 -0700180 async_synchronize_full_domain(&scsi_sd_probe_domain);
Alan Sternfea6d602012-02-17 16:25:08 -0500181
182 } else if (scsi_is_host_device(dev)) {
183 /* Wait until async scanning is finished */
184 scsi_complete_async_scans();
185 }
186 return 0;
187}
188
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400189static int scsi_bus_suspend(struct device *dev)
190{
Dan Williams3c31b522014-04-10 15:30:35 -0700191 return scsi_bus_suspend_common(dev, do_scsi_suspend);
Aaron Lu80d2fd42012-11-09 15:27:54 +0800192}
193
194static int scsi_bus_resume(struct device *dev)
195{
Dan Williams3c31b522014-04-10 15:30:35 -0700196 return scsi_bus_resume_common(dev, do_scsi_resume);
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400197}
198
199static int scsi_bus_freeze(struct device *dev)
200{
Dan Williams3c31b522014-04-10 15:30:35 -0700201 return scsi_bus_suspend_common(dev, do_scsi_freeze);
Aaron Lu80d2fd42012-11-09 15:27:54 +0800202}
203
204static int scsi_bus_thaw(struct device *dev)
205{
Dan Williams3c31b522014-04-10 15:30:35 -0700206 return scsi_bus_resume_common(dev, do_scsi_thaw);
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400207}
208
209static int scsi_bus_poweroff(struct device *dev)
210{
Dan Williams3c31b522014-04-10 15:30:35 -0700211 return scsi_bus_suspend_common(dev, do_scsi_poweroff);
Aaron Lu80d2fd42012-11-09 15:27:54 +0800212}
213
214static int scsi_bus_restore(struct device *dev)
215{
Dan Williams3c31b522014-04-10 15:30:35 -0700216 return scsi_bus_resume_common(dev, do_scsi_restore);
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400217}
218
219#else /* CONFIG_PM_SLEEP */
220
Alan Sternfea6d602012-02-17 16:25:08 -0500221#define scsi_bus_prepare NULL
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400222#define scsi_bus_suspend NULL
Aaron Lu80d2fd42012-11-09 15:27:54 +0800223#define scsi_bus_resume NULL
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400224#define scsi_bus_freeze NULL
Aaron Lu80d2fd42012-11-09 15:27:54 +0800225#define scsi_bus_thaw NULL
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400226#define scsi_bus_poweroff NULL
Aaron Lu80d2fd42012-11-09 15:27:54 +0800227#define scsi_bus_restore NULL
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400228
229#endif /* CONFIG_PM_SLEEP */
230
Subhash Jadavani57d6b142016-07-29 16:50:07 -0700231static int do_scsi_runtime_suspend(struct device *dev,
232 const struct dev_pm_ops *pm)
233{
234 return pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;
235}
236
237static int do_scsi_runtime_resume(struct device *dev,
238 const struct dev_pm_ops *pm)
239{
240 return pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
241}
242
Aaron Lu6627b382013-10-28 15:27:49 +0800243static int sdev_runtime_suspend(struct device *dev)
Lin Ming6df339a2013-03-23 11:42:28 +0800244{
Aaron Lu6627b382013-10-28 15:27:49 +0800245 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
246 struct scsi_device *sdev = to_scsi_device(dev);
Alan Stern49718f02015-08-17 11:02:42 -0400247 int err = 0;
Lin Ming6df339a2013-03-23 11:42:28 +0800248
Subhash Jadavani57d6b142016-07-29 16:50:07 -0700249 if (!sdev->request_queue->dev) {
250 err = scsi_dev_type_suspend(dev, do_scsi_runtime_suspend);
251 if (err == -EAGAIN)
252 pm_schedule_suspend(dev, jiffies_to_msecs(
253 round_jiffies_up_relative(HZ/10)));
254 return err;
255 }
256
Ken Xue1c69d3b2015-12-01 14:45:23 +0800257 err = blk_pre_runtime_suspend(sdev->request_queue);
258 if (err)
259 return err;
260 if (pm && pm->runtime_suspend)
Aaron Lu6627b382013-10-28 15:27:49 +0800261 err = pm->runtime_suspend(dev);
Ken Xue1c69d3b2015-12-01 14:45:23 +0800262 blk_post_runtime_suspend(sdev->request_queue, err);
263
Lin Ming6df339a2013-03-23 11:42:28 +0800264 return err;
265}
266
Alan Sternbc4f2402010-06-17 10:41:42 -0400267static int scsi_runtime_suspend(struct device *dev)
268{
269 int err = 0;
270
271 dev_dbg(dev, "scsi_runtime_suspend\n");
Lin Ming6df339a2013-03-23 11:42:28 +0800272 if (scsi_is_sdev_device(dev))
273 err = sdev_runtime_suspend(dev);
Alan Sternbc4f2402010-06-17 10:41:42 -0400274
275 /* Insert hooks here for targets, hosts, and transport classes */
276
277 return err;
278}
279
Lin Ming6df339a2013-03-23 11:42:28 +0800280static int sdev_runtime_resume(struct device *dev)
281{
282 struct scsi_device *sdev = to_scsi_device(dev);
283 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
Aaron Lu6627b382013-10-28 15:27:49 +0800284 int err = 0;
Lin Ming6df339a2013-03-23 11:42:28 +0800285
Subhash Jadavani57d6b142016-07-29 16:50:07 -0700286 if (!sdev->request_queue->dev)
287 return scsi_dev_type_resume(dev, do_scsi_runtime_resume);
288
Ken Xue1c69d3b2015-12-01 14:45:23 +0800289 blk_pre_runtime_resume(sdev->request_queue);
290 if (pm && pm->runtime_resume)
Aaron Lu6627b382013-10-28 15:27:49 +0800291 err = pm->runtime_resume(dev);
Ken Xue1c69d3b2015-12-01 14:45:23 +0800292 blk_post_runtime_resume(sdev->request_queue, err);
293
Aaron Lu6627b382013-10-28 15:27:49 +0800294 return err;
Lin Ming6df339a2013-03-23 11:42:28 +0800295}
296
Alan Sternbc4f2402010-06-17 10:41:42 -0400297static int scsi_runtime_resume(struct device *dev)
298{
299 int err = 0;
300
301 dev_dbg(dev, "scsi_runtime_resume\n");
302 if (scsi_is_sdev_device(dev))
Lin Ming6df339a2013-03-23 11:42:28 +0800303 err = sdev_runtime_resume(dev);
Alan Sternbc4f2402010-06-17 10:41:42 -0400304
305 /* Insert hooks here for targets, hosts, and transport classes */
306
307 return err;
308}
309
310static int scsi_runtime_idle(struct device *dev)
311{
Alan Sternbc4f2402010-06-17 10:41:42 -0400312 dev_dbg(dev, "scsi_runtime_idle\n");
313
314 /* Insert hooks here for targets, hosts, and transport classes */
315
Lin Ming6df339a2013-03-23 11:42:28 +0800316 if (scsi_is_sdev_device(dev)) {
Aaron Lu6627b382013-10-28 15:27:49 +0800317 pm_runtime_mark_last_busy(dev);
318 pm_runtime_autosuspend(dev);
319 return -EBUSY;
Lin Ming6df339a2013-03-23 11:42:28 +0800320 }
Aaron Lu6627b382013-10-28 15:27:49 +0800321
Rafael J. Wysocki45f0a852013-06-03 21:49:52 +0200322 return 0;
Alan Sternbc4f2402010-06-17 10:41:42 -0400323}
324
325int scsi_autopm_get_device(struct scsi_device *sdev)
326{
327 int err;
328
329 err = pm_runtime_get_sync(&sdev->sdev_gendev);
Rafael J. Wysocki632e2702011-07-01 22:29:15 +0200330 if (err < 0 && err !=-EACCES)
Alan Sternbc4f2402010-06-17 10:41:42 -0400331 pm_runtime_put_sync(&sdev->sdev_gendev);
Rafael J. Wysocki632e2702011-07-01 22:29:15 +0200332 else
Alan Sternbc4f2402010-06-17 10:41:42 -0400333 err = 0;
334 return err;
335}
336EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
337
338void scsi_autopm_put_device(struct scsi_device *sdev)
339{
340 pm_runtime_put_sync(&sdev->sdev_gendev);
341}
342EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
343
344void scsi_autopm_get_target(struct scsi_target *starget)
345{
346 pm_runtime_get_sync(&starget->dev);
347}
348
349void scsi_autopm_put_target(struct scsi_target *starget)
350{
351 pm_runtime_put_sync(&starget->dev);
352}
353
354int scsi_autopm_get_host(struct Scsi_Host *shost)
355{
356 int err;
357
358 err = pm_runtime_get_sync(&shost->shost_gendev);
Rafael J. Wysocki632e2702011-07-01 22:29:15 +0200359 if (err < 0 && err !=-EACCES)
Alan Sternbc4f2402010-06-17 10:41:42 -0400360 pm_runtime_put_sync(&shost->shost_gendev);
Rafael J. Wysocki632e2702011-07-01 22:29:15 +0200361 else
Alan Sternbc4f2402010-06-17 10:41:42 -0400362 err = 0;
363 return err;
364}
365
366void scsi_autopm_put_host(struct Scsi_Host *shost)
367{
368 pm_runtime_put_sync(&shost->shost_gendev);
369}
370
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400371const struct dev_pm_ops scsi_bus_pm_ops = {
Alan Sternfea6d602012-02-17 16:25:08 -0500372 .prepare = scsi_bus_prepare,
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400373 .suspend = scsi_bus_suspend,
Aaron Lu80d2fd42012-11-09 15:27:54 +0800374 .resume = scsi_bus_resume,
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400375 .freeze = scsi_bus_freeze,
Aaron Lu80d2fd42012-11-09 15:27:54 +0800376 .thaw = scsi_bus_thaw,
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400377 .poweroff = scsi_bus_poweroff,
Aaron Lu80d2fd42012-11-09 15:27:54 +0800378 .restore = scsi_bus_restore,
Alan Sternbc4f2402010-06-17 10:41:42 -0400379 .runtime_suspend = scsi_runtime_suspend,
380 .runtime_resume = scsi_runtime_resume,
381 .runtime_idle = scsi_runtime_idle,
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400382};