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