Branch data Line data Source code
1 : : /*
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>
9 : : #include <linux/export.h>
10 : : #include <linux/async.h>
11 : :
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 : :
19 : 0 : static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *))
20 : : {
21 : : int err;
22 : :
23 : 0 : err = scsi_device_quiesce(to_scsi_device(dev));
24 [ # # ]: 0 : if (err == 0) {
25 [ # # ]: 0 : if (cb) {
26 : 0 : err = cb(dev);
27 [ # # ]: 0 : if (err)
28 : 0 : scsi_device_resume(to_scsi_device(dev));
29 : : }
30 : : }
31 : : dev_dbg(dev, "scsi suspend: %d\n", err);
32 : 0 : return err;
33 : : }
34 : :
35 : 0 : static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *))
36 : : {
37 : : int err = 0;
38 : :
39 [ # # ]: 0 : if (cb)
40 : 0 : err = cb(dev);
41 : 0 : scsi_device_resume(to_scsi_device(dev));
42 : : dev_dbg(dev, "scsi resume: %d\n", err);
43 : 0 : return err;
44 : : }
45 : :
46 : : #ifdef CONFIG_PM_SLEEP
47 : :
48 : : static int
49 : 0 : scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *))
50 : : {
51 : : int err = 0;
52 : :
53 [ # # ]: 0 : if (scsi_is_sdev_device(dev)) {
54 : : /*
55 : : * All the high-level SCSI drivers that implement runtime
56 : : * PM treat runtime suspend, system suspend, and system
57 : : * hibernate nearly identically. In all cases the requirements
58 : : * for runtime suspension are stricter.
59 : : */
60 : : if (pm_runtime_suspended(dev))
61 : : return 0;
62 : :
63 : 0 : err = scsi_dev_type_suspend(dev, cb);
64 : : }
65 : :
66 : : return err;
67 : : }
68 : :
69 : : static int
70 : 0 : scsi_bus_resume_common(struct device *dev, int (*cb)(struct device *))
71 : : {
72 : : int err = 0;
73 : :
74 [ # # ]: 0 : if (scsi_is_sdev_device(dev))
75 : 0 : err = scsi_dev_type_resume(dev, cb);
76 : :
77 : : if (err == 0) {
78 : : pm_runtime_disable(dev);
79 : : pm_runtime_set_active(dev);
80 : : pm_runtime_enable(dev);
81 : : }
82 : 0 : return err;
83 : : }
84 : :
85 : 0 : static int scsi_bus_prepare(struct device *dev)
86 : : {
87 [ # # ]: 0 : if (scsi_is_sdev_device(dev)) {
88 : : /* sd probing uses async_schedule. Wait until it finishes. */
89 : 0 : async_synchronize_full_domain(&scsi_sd_probe_domain);
90 : :
91 [ # # ]: 0 : } else if (scsi_is_host_device(dev)) {
92 : : /* Wait until async scanning is finished */
93 : 0 : scsi_complete_async_scans();
94 : : }
95 : 0 : return 0;
96 : : }
97 : :
98 : 0 : static int scsi_bus_suspend(struct device *dev)
99 : : {
100 [ # # ]: 0 : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
101 [ # # ]: 0 : return scsi_bus_suspend_common(dev, pm ? pm->suspend : NULL);
102 : : }
103 : :
104 : 0 : static int scsi_bus_resume(struct device *dev)
105 : : {
106 [ # # ]: 0 : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
107 [ # # ]: 0 : return scsi_bus_resume_common(dev, pm ? pm->resume : NULL);
108 : : }
109 : :
110 : 0 : static int scsi_bus_freeze(struct device *dev)
111 : : {
112 [ # # ]: 0 : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
113 [ # # ]: 0 : return scsi_bus_suspend_common(dev, pm ? pm->freeze : NULL);
114 : : }
115 : :
116 : 0 : static int scsi_bus_thaw(struct device *dev)
117 : : {
118 [ # # ]: 0 : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
119 [ # # ]: 0 : return scsi_bus_resume_common(dev, pm ? pm->thaw : NULL);
120 : : }
121 : :
122 : 0 : static int scsi_bus_poweroff(struct device *dev)
123 : : {
124 [ # # ]: 0 : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
125 [ # # ]: 0 : return scsi_bus_suspend_common(dev, pm ? pm->poweroff : NULL);
126 : : }
127 : :
128 : 0 : static int scsi_bus_restore(struct device *dev)
129 : : {
130 [ # # ]: 0 : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
131 [ # # ]: 0 : return scsi_bus_resume_common(dev, pm ? pm->restore : NULL);
132 : : }
133 : :
134 : : #else /* CONFIG_PM_SLEEP */
135 : :
136 : : #define scsi_bus_prepare NULL
137 : : #define scsi_bus_suspend NULL
138 : : #define scsi_bus_resume NULL
139 : : #define scsi_bus_freeze NULL
140 : : #define scsi_bus_thaw NULL
141 : : #define scsi_bus_poweroff NULL
142 : : #define scsi_bus_restore NULL
143 : :
144 : : #endif /* CONFIG_PM_SLEEP */
145 : :
146 : : #ifdef CONFIG_PM_RUNTIME
147 : :
148 : : static int sdev_blk_runtime_suspend(struct scsi_device *sdev,
149 : : int (*cb)(struct device *))
150 : : {
151 : : int err;
152 : :
153 : : err = blk_pre_runtime_suspend(sdev->request_queue);
154 : : if (err)
155 : : return err;
156 : : if (cb)
157 : : err = cb(&sdev->sdev_gendev);
158 : : blk_post_runtime_suspend(sdev->request_queue, err);
159 : :
160 : : return err;
161 : : }
162 : :
163 : : static int sdev_runtime_suspend(struct device *dev)
164 : : {
165 : : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
166 : : int (*cb)(struct device *) = pm ? pm->runtime_suspend : NULL;
167 : : struct scsi_device *sdev = to_scsi_device(dev);
168 : : int err;
169 : :
170 : : if (sdev->request_queue->dev)
171 : : return sdev_blk_runtime_suspend(sdev, cb);
172 : :
173 : : err = scsi_dev_type_suspend(dev, cb);
174 : : if (err == -EAGAIN)
175 : : pm_schedule_suspend(dev, jiffies_to_msecs(
176 : : round_jiffies_up_relative(HZ/10)));
177 : : return err;
178 : : }
179 : :
180 : : static int scsi_runtime_suspend(struct device *dev)
181 : : {
182 : : int err = 0;
183 : :
184 : : dev_dbg(dev, "scsi_runtime_suspend\n");
185 : : if (scsi_is_sdev_device(dev))
186 : : err = sdev_runtime_suspend(dev);
187 : :
188 : : /* Insert hooks here for targets, hosts, and transport classes */
189 : :
190 : : return err;
191 : : }
192 : :
193 : : static int sdev_blk_runtime_resume(struct scsi_device *sdev,
194 : : int (*cb)(struct device *))
195 : : {
196 : : int err = 0;
197 : :
198 : : blk_pre_runtime_resume(sdev->request_queue);
199 : : if (cb)
200 : : err = cb(&sdev->sdev_gendev);
201 : : blk_post_runtime_resume(sdev->request_queue, err);
202 : :
203 : : return err;
204 : : }
205 : :
206 : : static int sdev_runtime_resume(struct device *dev)
207 : : {
208 : : struct scsi_device *sdev = to_scsi_device(dev);
209 : : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
210 : : int (*cb)(struct device *) = pm ? pm->runtime_resume : NULL;
211 : :
212 : : if (sdev->request_queue->dev)
213 : : return sdev_blk_runtime_resume(sdev, cb);
214 : : else
215 : : return scsi_dev_type_resume(dev, cb);
216 : : }
217 : :
218 : : static int scsi_runtime_resume(struct device *dev)
219 : : {
220 : : int err = 0;
221 : :
222 : : dev_dbg(dev, "scsi_runtime_resume\n");
223 : : if (scsi_is_sdev_device(dev))
224 : : err = sdev_runtime_resume(dev);
225 : :
226 : : /* Insert hooks here for targets, hosts, and transport classes */
227 : :
228 : : return err;
229 : : }
230 : :
231 : : static int scsi_runtime_idle(struct device *dev)
232 : : {
233 : : dev_dbg(dev, "scsi_runtime_idle\n");
234 : :
235 : : /* Insert hooks here for targets, hosts, and transport classes */
236 : :
237 : : if (scsi_is_sdev_device(dev)) {
238 : : struct scsi_device *sdev = to_scsi_device(dev);
239 : :
240 : : if (sdev->request_queue->dev) {
241 : : pm_runtime_mark_last_busy(dev);
242 : : pm_runtime_autosuspend(dev);
243 : : return -EBUSY;
244 : : }
245 : : }
246 : : return 0;
247 : : }
248 : :
249 : : int scsi_autopm_get_device(struct scsi_device *sdev)
250 : : {
251 : : int err;
252 : :
253 : : err = pm_runtime_get_sync(&sdev->sdev_gendev);
254 : : if (err < 0 && err !=-EACCES)
255 : : pm_runtime_put_sync(&sdev->sdev_gendev);
256 : : else
257 : : err = 0;
258 : : return err;
259 : : }
260 : : EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
261 : :
262 : : void scsi_autopm_put_device(struct scsi_device *sdev)
263 : : {
264 : : pm_runtime_put_sync(&sdev->sdev_gendev);
265 : : }
266 : : EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
267 : :
268 : : void scsi_autopm_get_target(struct scsi_target *starget)
269 : : {
270 : : pm_runtime_get_sync(&starget->dev);
271 : : }
272 : :
273 : : void scsi_autopm_put_target(struct scsi_target *starget)
274 : : {
275 : : pm_runtime_put_sync(&starget->dev);
276 : : }
277 : :
278 : : int scsi_autopm_get_host(struct Scsi_Host *shost)
279 : : {
280 : : int err;
281 : :
282 : : err = pm_runtime_get_sync(&shost->shost_gendev);
283 : : if (err < 0 && err !=-EACCES)
284 : : pm_runtime_put_sync(&shost->shost_gendev);
285 : : else
286 : : err = 0;
287 : : return err;
288 : : }
289 : :
290 : : void scsi_autopm_put_host(struct Scsi_Host *shost)
291 : : {
292 : : pm_runtime_put_sync(&shost->shost_gendev);
293 : : }
294 : :
295 : : #else
296 : :
297 : : #define scsi_runtime_suspend NULL
298 : : #define scsi_runtime_resume NULL
299 : : #define scsi_runtime_idle NULL
300 : :
301 : : #endif /* CONFIG_PM_RUNTIME */
302 : :
303 : : const struct dev_pm_ops scsi_bus_pm_ops = {
304 : : .prepare = scsi_bus_prepare,
305 : : .suspend = scsi_bus_suspend,
306 : : .resume = scsi_bus_resume,
307 : : .freeze = scsi_bus_freeze,
308 : : .thaw = scsi_bus_thaw,
309 : : .poweroff = scsi_bus_poweroff,
310 : : .restore = scsi_bus_restore,
311 : : .runtime_suspend = scsi_runtime_suspend,
312 : : .runtime_resume = scsi_runtime_resume,
313 : : .runtime_idle = scsi_runtime_idle,
314 : : };
|