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 : : #ifdef CONFIG_PM_SLEEP
20 : :
21 : 0 : static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *))
22 : : {
23 : : int err;
24 : :
25 : 0 : err = scsi_device_quiesce(to_scsi_device(dev));
26 [ # # ]: 0 : if (err == 0) {
27 [ # # ]: 0 : if (cb) {
28 : 0 : err = cb(dev);
29 [ # # ]: 0 : if (err)
30 : 0 : scsi_device_resume(to_scsi_device(dev));
31 : : }
32 : : }
33 : : dev_dbg(dev, "scsi suspend: %d\n", err);
34 : 0 : return err;
35 : : }
36 : :
37 : 0 : static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *))
38 : : {
39 : : int err = 0;
40 : :
41 [ # # ]: 0 : if (cb)
42 : 0 : err = cb(dev);
43 : 0 : scsi_device_resume(to_scsi_device(dev));
44 : : dev_dbg(dev, "scsi resume: %d\n", err);
45 : 0 : return err;
46 : : }
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_runtime_suspend(struct device *dev)
149 : : {
150 : : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
151 : : struct scsi_device *sdev = to_scsi_device(dev);
152 : : int err;
153 : :
154 : : err = blk_pre_runtime_suspend(sdev->request_queue);
155 : : if (err)
156 : : return err;
157 : : if (pm && pm->runtime_suspend)
158 : : err = pm->runtime_suspend(dev);
159 : : blk_post_runtime_suspend(sdev->request_queue, err);
160 : :
161 : : return err;
162 : : }
163 : :
164 : : static int scsi_runtime_suspend(struct device *dev)
165 : : {
166 : : int err = 0;
167 : :
168 : : dev_dbg(dev, "scsi_runtime_suspend\n");
169 : : if (scsi_is_sdev_device(dev))
170 : : err = sdev_runtime_suspend(dev);
171 : :
172 : : /* Insert hooks here for targets, hosts, and transport classes */
173 : :
174 : : return err;
175 : : }
176 : :
177 : : static int sdev_runtime_resume(struct device *dev)
178 : : {
179 : : struct scsi_device *sdev = to_scsi_device(dev);
180 : : const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
181 : : int err = 0;
182 : :
183 : : blk_pre_runtime_resume(sdev->request_queue);
184 : : if (pm && pm->runtime_resume)
185 : : err = pm->runtime_resume(dev);
186 : : blk_post_runtime_resume(sdev->request_queue, err);
187 : :
188 : : return err;
189 : : }
190 : :
191 : : static int scsi_runtime_resume(struct device *dev)
192 : : {
193 : : int err = 0;
194 : :
195 : : dev_dbg(dev, "scsi_runtime_resume\n");
196 : : if (scsi_is_sdev_device(dev))
197 : : err = sdev_runtime_resume(dev);
198 : :
199 : : /* Insert hooks here for targets, hosts, and transport classes */
200 : :
201 : : return err;
202 : : }
203 : :
204 : : static int scsi_runtime_idle(struct device *dev)
205 : : {
206 : : dev_dbg(dev, "scsi_runtime_idle\n");
207 : :
208 : : /* Insert hooks here for targets, hosts, and transport classes */
209 : :
210 : : if (scsi_is_sdev_device(dev)) {
211 : : pm_runtime_mark_last_busy(dev);
212 : : pm_runtime_autosuspend(dev);
213 : : return -EBUSY;
214 : : }
215 : :
216 : : return 0;
217 : : }
218 : :
219 : : int scsi_autopm_get_device(struct scsi_device *sdev)
220 : : {
221 : : int err;
222 : :
223 : : err = pm_runtime_get_sync(&sdev->sdev_gendev);
224 : : if (err < 0 && err !=-EACCES)
225 : : pm_runtime_put_sync(&sdev->sdev_gendev);
226 : : else
227 : : err = 0;
228 : : return err;
229 : : }
230 : : EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
231 : :
232 : : void scsi_autopm_put_device(struct scsi_device *sdev)
233 : : {
234 : : pm_runtime_put_sync(&sdev->sdev_gendev);
235 : : }
236 : : EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
237 : :
238 : : void scsi_autopm_get_target(struct scsi_target *starget)
239 : : {
240 : : pm_runtime_get_sync(&starget->dev);
241 : : }
242 : :
243 : : void scsi_autopm_put_target(struct scsi_target *starget)
244 : : {
245 : : pm_runtime_put_sync(&starget->dev);
246 : : }
247 : :
248 : : int scsi_autopm_get_host(struct Scsi_Host *shost)
249 : : {
250 : : int err;
251 : :
252 : : err = pm_runtime_get_sync(&shost->shost_gendev);
253 : : if (err < 0 && err !=-EACCES)
254 : : pm_runtime_put_sync(&shost->shost_gendev);
255 : : else
256 : : err = 0;
257 : : return err;
258 : : }
259 : :
260 : : void scsi_autopm_put_host(struct Scsi_Host *shost)
261 : : {
262 : : pm_runtime_put_sync(&shost->shost_gendev);
263 : : }
264 : :
265 : : #else
266 : :
267 : : #define scsi_runtime_suspend NULL
268 : : #define scsi_runtime_resume NULL
269 : : #define scsi_runtime_idle NULL
270 : :
271 : : #endif /* CONFIG_PM_RUNTIME */
272 : :
273 : : const struct dev_pm_ops scsi_bus_pm_ops = {
274 : : .prepare = scsi_bus_prepare,
275 : : .suspend = scsi_bus_suspend,
276 : : .resume = scsi_bus_resume,
277 : : .freeze = scsi_bus_freeze,
278 : : .thaw = scsi_bus_thaw,
279 : : .poweroff = scsi_bus_poweroff,
280 : : .restore = scsi_bus_restore,
281 : : .runtime_suspend = scsi_runtime_suspend,
282 : : .runtime_resume = scsi_runtime_resume,
283 : : .runtime_idle = scsi_runtime_idle,
284 : : };
|