Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * scsi_pm.c Copyright (C) 2010 Alan Stern
4 : : *
5 : : * SCSI dynamic Power Management
6 : : * Initial version: Alan Stern <stern@rowland.harvard.edu>
7 : : */
8 : :
9 : : #include <linux/pm_runtime.h>
10 : : #include <linux/export.h>
11 : : #include <linux/async.h>
12 : : #include <linux/blk-pm.h>
13 : :
14 : : #include <scsi/scsi.h>
15 : : #include <scsi/scsi_device.h>
16 : : #include <scsi/scsi_driver.h>
17 : : #include <scsi/scsi_host.h>
18 : :
19 : : #include "scsi_priv.h"
20 : :
21 : : #ifdef CONFIG_PM_SLEEP
22 : :
23 : : static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)
24 : : {
25 : : return pm && pm->suspend ? pm->suspend(dev) : 0;
26 : : }
27 : :
28 : : static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm)
29 : : {
30 : : return pm && pm->freeze ? pm->freeze(dev) : 0;
31 : : }
32 : :
33 : : static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm)
34 : : {
35 : : return pm && pm->poweroff ? pm->poweroff(dev) : 0;
36 : : }
37 : :
38 : : static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm)
39 : : {
40 : : return pm && pm->resume ? pm->resume(dev) : 0;
41 : : }
42 : :
43 : : static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm)
44 : : {
45 : : return pm && pm->thaw ? pm->thaw(dev) : 0;
46 : : }
47 : :
48 : : static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm)
49 : : {
50 : : return pm && pm->restore ? pm->restore(dev) : 0;
51 : : }
52 : :
53 : : static int scsi_dev_type_suspend(struct device *dev,
54 : : int (*cb)(struct device *, const struct dev_pm_ops *))
55 : : {
56 : : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
57 : : int err;
58 : :
59 : : /* flush pending in-flight resume operations, suspend is synchronous */
60 : : async_synchronize_full_domain(&scsi_sd_pm_domain);
61 : :
62 : : err = scsi_device_quiesce(to_scsi_device(dev));
63 : : if (err == 0) {
64 : : err = cb(dev, pm);
65 : : if (err)
66 : : scsi_device_resume(to_scsi_device(dev));
67 : : }
68 : : dev_dbg(dev, "scsi suspend: %d\n", err);
69 : : return err;
70 : : }
71 : :
72 : : static int scsi_dev_type_resume(struct device *dev,
73 : : int (*cb)(struct device *, const struct dev_pm_ops *))
74 : : {
75 : : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
76 : : int err = 0;
77 : :
78 : : err = cb(dev, pm);
79 : : scsi_device_resume(to_scsi_device(dev));
80 : : dev_dbg(dev, "scsi resume: %d\n", err);
81 : :
82 : : if (err == 0) {
83 : : bool was_runtime_suspended;
84 : :
85 : : was_runtime_suspended = pm_runtime_suspended(dev);
86 : :
87 : : pm_runtime_disable(dev);
88 : : err = pm_runtime_set_active(dev);
89 : : pm_runtime_enable(dev);
90 : :
91 : : /*
92 : : * Forcibly set runtime PM status of request queue to "active"
93 : : * to make sure we can again get requests from the queue
94 : : * (see also blk_pm_peek_request()).
95 : : *
96 : : * The resume hook will correct runtime PM status of the disk.
97 : : */
98 : : if (!err && scsi_is_sdev_device(dev)) {
99 : : struct scsi_device *sdev = to_scsi_device(dev);
100 : : if (was_runtime_suspended)
101 : : blk_post_runtime_resume(sdev->request_queue, 0);
102 : : else
103 : : blk_set_runtime_active(sdev->request_queue);
104 : : }
105 : : }
106 : :
107 : : return err;
108 : : }
109 : :
110 : : static int
111 : : scsi_bus_suspend_common(struct device *dev,
112 : : int (*cb)(struct device *, const struct dev_pm_ops *))
113 : : {
114 : : int err = 0;
115 : :
116 : : if (scsi_is_sdev_device(dev)) {
117 : : /*
118 : : * All the high-level SCSI drivers that implement runtime
119 : : * PM treat runtime suspend, system suspend, and system
120 : : * hibernate nearly identically. In all cases the requirements
121 : : * for runtime suspension are stricter.
122 : : */
123 : : if (pm_runtime_suspended(dev))
124 : : return 0;
125 : :
126 : : err = scsi_dev_type_suspend(dev, cb);
127 : : }
128 : :
129 : : return err;
130 : : }
131 : :
132 : : static void async_sdev_resume(void *dev, async_cookie_t cookie)
133 : : {
134 : : scsi_dev_type_resume(dev, do_scsi_resume);
135 : : }
136 : :
137 : : static void async_sdev_thaw(void *dev, async_cookie_t cookie)
138 : : {
139 : : scsi_dev_type_resume(dev, do_scsi_thaw);
140 : : }
141 : :
142 : : static void async_sdev_restore(void *dev, async_cookie_t cookie)
143 : : {
144 : : scsi_dev_type_resume(dev, do_scsi_restore);
145 : : }
146 : :
147 : : static int scsi_bus_resume_common(struct device *dev,
148 : : int (*cb)(struct device *, const struct dev_pm_ops *))
149 : : {
150 : : async_func_t fn;
151 : :
152 : : if (!scsi_is_sdev_device(dev))
153 : : fn = NULL;
154 : : else if (cb == do_scsi_resume)
155 : : fn = async_sdev_resume;
156 : : else if (cb == do_scsi_thaw)
157 : : fn = async_sdev_thaw;
158 : : else if (cb == do_scsi_restore)
159 : : fn = async_sdev_restore;
160 : : else
161 : : fn = NULL;
162 : :
163 : : if (fn) {
164 : : async_schedule_domain(fn, dev, &scsi_sd_pm_domain);
165 : :
166 : : /*
167 : : * If a user has disabled async probing a likely reason
168 : : * is due to a storage enclosure that does not inject
169 : : * staggered spin-ups. For safety, make resume
170 : : * synchronous as well in that case.
171 : : */
172 : : if (strncmp(scsi_scan_type, "async", 5) != 0)
173 : : async_synchronize_full_domain(&scsi_sd_pm_domain);
174 : : } else {
175 : : pm_runtime_disable(dev);
176 : : pm_runtime_set_active(dev);
177 : : pm_runtime_enable(dev);
178 : : }
179 : : return 0;
180 : : }
181 : :
182 : : static int scsi_bus_prepare(struct device *dev)
183 : : {
184 : : if (scsi_is_host_device(dev)) {
185 : : /* Wait until async scanning is finished */
186 : : scsi_complete_async_scans();
187 : : }
188 : : return 0;
189 : : }
190 : :
191 : : static int scsi_bus_suspend(struct device *dev)
192 : : {
193 : : return scsi_bus_suspend_common(dev, do_scsi_suspend);
194 : : }
195 : :
196 : : static int scsi_bus_resume(struct device *dev)
197 : : {
198 : : return scsi_bus_resume_common(dev, do_scsi_resume);
199 : : }
200 : :
201 : : static int scsi_bus_freeze(struct device *dev)
202 : : {
203 : : return scsi_bus_suspend_common(dev, do_scsi_freeze);
204 : : }
205 : :
206 : : static int scsi_bus_thaw(struct device *dev)
207 : : {
208 : : return scsi_bus_resume_common(dev, do_scsi_thaw);
209 : : }
210 : :
211 : : static int scsi_bus_poweroff(struct device *dev)
212 : : {
213 : : return scsi_bus_suspend_common(dev, do_scsi_poweroff);
214 : : }
215 : :
216 : : static int scsi_bus_restore(struct device *dev)
217 : : {
218 : : return scsi_bus_resume_common(dev, do_scsi_restore);
219 : : }
220 : :
221 : : #else /* CONFIG_PM_SLEEP */
222 : :
223 : : #define scsi_bus_prepare NULL
224 : : #define scsi_bus_suspend NULL
225 : : #define scsi_bus_resume NULL
226 : : #define scsi_bus_freeze NULL
227 : : #define scsi_bus_thaw NULL
228 : : #define scsi_bus_poweroff NULL
229 : : #define scsi_bus_restore NULL
230 : :
231 : : #endif /* CONFIG_PM_SLEEP */
232 : :
233 : 0 : static int sdev_runtime_suspend(struct device *dev)
234 : : {
235 [ # # ]: 0 : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
236 : : struct scsi_device *sdev = to_scsi_device(dev);
237 : : int err = 0;
238 : :
239 : 0 : err = blk_pre_runtime_suspend(sdev->request_queue);
240 [ # # ]: 0 : if (err)
241 : : return err;
242 [ # # # # ]: 0 : if (pm && pm->runtime_suspend)
243 : 0 : err = pm->runtime_suspend(dev);
244 : 0 : blk_post_runtime_suspend(sdev->request_queue, err);
245 : :
246 : 0 : return err;
247 : : }
248 : :
249 : 0 : static int scsi_runtime_suspend(struct device *dev)
250 : : {
251 : : int err = 0;
252 : :
253 : : dev_dbg(dev, "scsi_runtime_suspend\n");
254 [ # # ]: 0 : if (scsi_is_sdev_device(dev))
255 : 0 : err = sdev_runtime_suspend(dev);
256 : :
257 : : /* Insert hooks here for targets, hosts, and transport classes */
258 : :
259 : 0 : return err;
260 : : }
261 : :
262 : 0 : static int sdev_runtime_resume(struct device *dev)
263 : : {
264 : : struct scsi_device *sdev = to_scsi_device(dev);
265 [ # # ]: 0 : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
266 : : int err = 0;
267 : :
268 : 0 : blk_pre_runtime_resume(sdev->request_queue);
269 [ # # # # ]: 0 : if (pm && pm->runtime_resume)
270 : 0 : err = pm->runtime_resume(dev);
271 : 0 : blk_post_runtime_resume(sdev->request_queue, err);
272 : :
273 : 0 : return err;
274 : : }
275 : :
276 : 0 : static int scsi_runtime_resume(struct device *dev)
277 : : {
278 : : int err = 0;
279 : :
280 : : dev_dbg(dev, "scsi_runtime_resume\n");
281 [ # # ]: 0 : if (scsi_is_sdev_device(dev))
282 : 0 : err = sdev_runtime_resume(dev);
283 : :
284 : : /* Insert hooks here for targets, hosts, and transport classes */
285 : :
286 : 0 : return err;
287 : : }
288 : :
289 : 0 : static int scsi_runtime_idle(struct device *dev)
290 : : {
291 : : dev_dbg(dev, "scsi_runtime_idle\n");
292 : :
293 : : /* Insert hooks here for targets, hosts, and transport classes */
294 : :
295 [ # # ]: 0 : if (scsi_is_sdev_device(dev)) {
296 : : pm_runtime_mark_last_busy(dev);
297 : : pm_runtime_autosuspend(dev);
298 : 0 : return -EBUSY;
299 : : }
300 : :
301 : : return 0;
302 : : }
303 : :
304 : 0 : int scsi_autopm_get_device(struct scsi_device *sdev)
305 : : {
306 : : int err;
307 : :
308 : 0 : err = pm_runtime_get_sync(&sdev->sdev_gendev);
309 [ # # ]: 0 : if (err < 0 && err !=-EACCES)
310 : : pm_runtime_put_sync(&sdev->sdev_gendev);
311 : : else
312 : : err = 0;
313 : 0 : return err;
314 : : }
315 : : EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
316 : :
317 : 0 : void scsi_autopm_put_device(struct scsi_device *sdev)
318 : : {
319 : 0 : pm_runtime_put_sync(&sdev->sdev_gendev);
320 : 0 : }
321 : : EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
322 : :
323 : 0 : void scsi_autopm_get_target(struct scsi_target *starget)
324 : : {
325 : 0 : pm_runtime_get_sync(&starget->dev);
326 : 0 : }
327 : :
328 : 0 : void scsi_autopm_put_target(struct scsi_target *starget)
329 : : {
330 : 0 : pm_runtime_put_sync(&starget->dev);
331 : 0 : }
332 : :
333 : 0 : int scsi_autopm_get_host(struct Scsi_Host *shost)
334 : : {
335 : : int err;
336 : :
337 : 0 : err = pm_runtime_get_sync(&shost->shost_gendev);
338 [ # # ]: 0 : if (err < 0 && err !=-EACCES)
339 : : pm_runtime_put_sync(&shost->shost_gendev);
340 : : else
341 : : err = 0;
342 : 0 : return err;
343 : : }
344 : :
345 : 0 : void scsi_autopm_put_host(struct Scsi_Host *shost)
346 : : {
347 : 0 : pm_runtime_put_sync(&shost->shost_gendev);
348 : 0 : }
349 : :
350 : : const struct dev_pm_ops scsi_bus_pm_ops = {
351 : : .prepare = scsi_bus_prepare,
352 : : .suspend = scsi_bus_suspend,
353 : : .resume = scsi_bus_resume,
354 : : .freeze = scsi_bus_freeze,
355 : : .thaw = scsi_bus_thaw,
356 : : .poweroff = scsi_bus_poweroff,
357 : : .restore = scsi_bus_restore,
358 : : .runtime_suspend = scsi_runtime_suspend,
359 : : .runtime_resume = scsi_runtime_resume,
360 : : .runtime_idle = scsi_runtime_idle,
361 : : };
|