Blame view

drivers/scsi/scsi_pm.c 7.75 KB
db5bd1e0b   Alan Stern   [SCSI] convert to...
1
2
3
4
5
6
7
8
  /*
   *	scsi_pm.c	Copyright (C) 2010 Alan Stern
   *
   *	SCSI dynamic Power Management
   *		Initial version: Alan Stern <stern@rowland.harvard.edu>
   */
  
  #include <linux/pm_runtime.h>
09703660e   Paul Gortmaker   scsi: Add export....
9
  #include <linux/export.h>
fea6d607e   Alan Stern   [SCSI] scsi_pm: F...
10
  #include <linux/async.h>
db5bd1e0b   Alan Stern   [SCSI] convert to...
11
12
13
14
15
16
17
  
  #include <scsi/scsi.h>
  #include <scsi/scsi_device.h>
  #include <scsi/scsi_driver.h>
  #include <scsi/scsi_host.h>
  
  #include "scsi_priv.h"
6627b38fd   Aaron Lu   [SCSI] sr: use bl...
18
  #ifdef CONFIG_PM_SLEEP
3c31b52f9   Dan Williams   scsi: async sd re...
19
  static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)
db5bd1e0b   Alan Stern   [SCSI] convert to...
20
  {
3c31b52f9   Dan Williams   scsi: async sd re...
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  	return pm && pm->suspend ? pm->suspend(dev) : 0;
  }
  
  static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm)
  {
  	return pm && pm->freeze ? pm->freeze(dev) : 0;
  }
  
  static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm)
  {
  	return pm && pm->poweroff ? pm->poweroff(dev) : 0;
  }
  
  static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm)
  {
  	return pm && pm->resume ? pm->resume(dev) : 0;
  }
  
  static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm)
  {
  	return pm && pm->thaw ? pm->thaw(dev) : 0;
  }
  
  static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm)
  {
  	return pm && pm->restore ? pm->restore(dev) : 0;
  }
  
  static int scsi_dev_type_suspend(struct device *dev,
  		int (*cb)(struct device *, const struct dev_pm_ops *))
  {
  	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
db5bd1e0b   Alan Stern   [SCSI] convert to...
53
  	int err;
3c31b52f9   Dan Williams   scsi: async sd re...
54
55
  	/* flush pending in-flight resume operations, suspend is synchronous */
  	async_synchronize_full_domain(&scsi_sd_pm_domain);
db5bd1e0b   Alan Stern   [SCSI] convert to...
56
57
  	err = scsi_device_quiesce(to_scsi_device(dev));
  	if (err == 0) {
3c31b52f9   Dan Williams   scsi: async sd re...
58
59
60
  		err = cb(dev, pm);
  		if (err)
  			scsi_device_resume(to_scsi_device(dev));
db5bd1e0b   Alan Stern   [SCSI] convert to...
61
62
63
64
65
  	}
  	dev_dbg(dev, "scsi suspend: %d
  ", err);
  	return err;
  }
3c31b52f9   Dan Williams   scsi: async sd re...
66
67
  static int scsi_dev_type_resume(struct device *dev,
  		int (*cb)(struct device *, const struct dev_pm_ops *))
db5bd1e0b   Alan Stern   [SCSI] convert to...
68
  {
3c31b52f9   Dan Williams   scsi: async sd re...
69
  	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
db5bd1e0b   Alan Stern   [SCSI] convert to...
70
  	int err = 0;
3c31b52f9   Dan Williams   scsi: async sd re...
71
  	err = cb(dev, pm);
db5bd1e0b   Alan Stern   [SCSI] convert to...
72
73
74
  	scsi_device_resume(to_scsi_device(dev));
  	dev_dbg(dev, "scsi resume: %d
  ", err);
3c31b52f9   Dan Williams   scsi: async sd re...
75
76
77
78
79
80
  
  	if (err == 0) {
  		pm_runtime_disable(dev);
  		pm_runtime_set_active(dev);
  		pm_runtime_enable(dev);
  	}
db5bd1e0b   Alan Stern   [SCSI] convert to...
81
82
  	return err;
  }
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
83
  static int
3c31b52f9   Dan Williams   scsi: async sd re...
84
85
  scsi_bus_suspend_common(struct device *dev,
  		int (*cb)(struct device *, const struct dev_pm_ops *))
db5bd1e0b   Alan Stern   [SCSI] convert to...
86
87
  {
  	int err = 0;
286405167   Lin Ming   [SCSI] check runt...
88
89
  	if (scsi_is_sdev_device(dev)) {
  		/*
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
90
91
  		 * All the high-level SCSI drivers that implement runtime
  		 * PM treat runtime suspend, system suspend, and system
95897910a   Oliver Neukum   [SCSI] sd: Add er...
92
93
  		 * hibernate nearly identically. In all cases the requirements
  		 * for runtime suspension are stricter.
286405167   Lin Ming   [SCSI] check runt...
94
  		 */
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
95
96
  		if (pm_runtime_suspended(dev))
  			return 0;
286405167   Lin Ming   [SCSI] check runt...
97

80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
98
  		err = scsi_dev_type_suspend(dev, cb);
286405167   Lin Ming   [SCSI] check runt...
99
  	}
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
100

db5bd1e0b   Alan Stern   [SCSI] convert to...
101
102
  	return err;
  }
3c31b52f9   Dan Williams   scsi: async sd re...
103
  static void async_sdev_resume(void *dev, async_cookie_t cookie)
db5bd1e0b   Alan Stern   [SCSI] convert to...
104
  {
3c31b52f9   Dan Williams   scsi: async sd re...
105
106
  	scsi_dev_type_resume(dev, do_scsi_resume);
  }
db5bd1e0b   Alan Stern   [SCSI] convert to...
107

3c31b52f9   Dan Williams   scsi: async sd re...
108
109
110
111
  static void async_sdev_thaw(void *dev, async_cookie_t cookie)
  {
  	scsi_dev_type_resume(dev, do_scsi_thaw);
  }
63347905d   Aaron Lu   [SCSI] Revert "[S...
112

3c31b52f9   Dan Williams   scsi: async sd re...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  static void async_sdev_restore(void *dev, async_cookie_t cookie)
  {
  	scsi_dev_type_resume(dev, do_scsi_restore);
  }
  
  static int scsi_bus_resume_common(struct device *dev,
  		int (*cb)(struct device *, const struct dev_pm_ops *))
  {
  	async_func_t fn;
  
  	if (!scsi_is_sdev_device(dev))
  		fn = NULL;
  	else if (cb == do_scsi_resume)
  		fn = async_sdev_resume;
  	else if (cb == do_scsi_thaw)
  		fn = async_sdev_thaw;
  	else if (cb == do_scsi_restore)
  		fn = async_sdev_restore;
  	else
  		fn = NULL;
  
  	if (fn) {
  		async_schedule_domain(fn, dev, &scsi_sd_pm_domain);
  
  		/*
  		 * If a user has disabled async probing a likely reason
  		 * is due to a storage enclosure that does not inject
  		 * staggered spin-ups.  For safety, make resume
  		 * synchronous as well in that case.
  		 */
  		if (strncmp(scsi_scan_type, "async", 5) != 0)
  			async_synchronize_full_domain(&scsi_sd_pm_domain);
  	} else {
bc4f24014   Alan Stern   [SCSI] implement ...
146
147
148
149
  		pm_runtime_disable(dev);
  		pm_runtime_set_active(dev);
  		pm_runtime_enable(dev);
  	}
3c31b52f9   Dan Williams   scsi: async sd re...
150
  	return 0;
db5bd1e0b   Alan Stern   [SCSI] convert to...
151
  }
fea6d607e   Alan Stern   [SCSI] scsi_pm: F...
152
153
154
155
  static int scsi_bus_prepare(struct device *dev)
  {
  	if (scsi_is_sdev_device(dev)) {
  		/* sd probing uses async_schedule.  Wait until it finishes. */
a7a20d103   Dan Williams   [SCSI] sd: limit ...
156
  		async_synchronize_full_domain(&scsi_sd_probe_domain);
fea6d607e   Alan Stern   [SCSI] scsi_pm: F...
157
158
159
160
161
162
163
  
  	} else if (scsi_is_host_device(dev)) {
  		/* Wait until async scanning is finished */
  		scsi_complete_async_scans();
  	}
  	return 0;
  }
db5bd1e0b   Alan Stern   [SCSI] convert to...
164
165
  static int scsi_bus_suspend(struct device *dev)
  {
3c31b52f9   Dan Williams   scsi: async sd re...
166
  	return scsi_bus_suspend_common(dev, do_scsi_suspend);
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
167
168
169
170
  }
  
  static int scsi_bus_resume(struct device *dev)
  {
3c31b52f9   Dan Williams   scsi: async sd re...
171
  	return scsi_bus_resume_common(dev, do_scsi_resume);
db5bd1e0b   Alan Stern   [SCSI] convert to...
172
173
174
175
  }
  
  static int scsi_bus_freeze(struct device *dev)
  {
3c31b52f9   Dan Williams   scsi: async sd re...
176
  	return scsi_bus_suspend_common(dev, do_scsi_freeze);
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
177
178
179
180
  }
  
  static int scsi_bus_thaw(struct device *dev)
  {
3c31b52f9   Dan Williams   scsi: async sd re...
181
  	return scsi_bus_resume_common(dev, do_scsi_thaw);
db5bd1e0b   Alan Stern   [SCSI] convert to...
182
183
184
185
  }
  
  static int scsi_bus_poweroff(struct device *dev)
  {
3c31b52f9   Dan Williams   scsi: async sd re...
186
  	return scsi_bus_suspend_common(dev, do_scsi_poweroff);
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
187
188
189
190
  }
  
  static int scsi_bus_restore(struct device *dev)
  {
3c31b52f9   Dan Williams   scsi: async sd re...
191
  	return scsi_bus_resume_common(dev, do_scsi_restore);
db5bd1e0b   Alan Stern   [SCSI] convert to...
192
193
194
  }
  
  #else /* CONFIG_PM_SLEEP */
fea6d607e   Alan Stern   [SCSI] scsi_pm: F...
195
  #define scsi_bus_prepare		NULL
db5bd1e0b   Alan Stern   [SCSI] convert to...
196
  #define scsi_bus_suspend		NULL
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
197
  #define scsi_bus_resume			NULL
db5bd1e0b   Alan Stern   [SCSI] convert to...
198
  #define scsi_bus_freeze			NULL
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
199
  #define scsi_bus_thaw			NULL
db5bd1e0b   Alan Stern   [SCSI] convert to...
200
  #define scsi_bus_poweroff		NULL
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
201
  #define scsi_bus_restore		NULL
db5bd1e0b   Alan Stern   [SCSI] convert to...
202
203
  
  #endif /* CONFIG_PM_SLEEP */
6627b38fd   Aaron Lu   [SCSI] sr: use bl...
204
  static int sdev_runtime_suspend(struct device *dev)
6df339a51   Lin Ming   [SCSI] sd: change...
205
  {
6627b38fd   Aaron Lu   [SCSI] sr: use bl...
206
207
  	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
  	struct scsi_device *sdev = to_scsi_device(dev);
49718f0fb   Alan Stern   SCSI: Fix NULL po...
208
  	int err = 0;
6df339a51   Lin Ming   [SCSI] sd: change...
209

1c69d3b6e   Ken Xue   Revert "SCSI: Fix...
210
211
212
213
  	err = blk_pre_runtime_suspend(sdev->request_queue);
  	if (err)
  		return err;
  	if (pm && pm->runtime_suspend)
6627b38fd   Aaron Lu   [SCSI] sr: use bl...
214
  		err = pm->runtime_suspend(dev);
1c69d3b6e   Ken Xue   Revert "SCSI: Fix...
215
  	blk_post_runtime_suspend(sdev->request_queue, err);
6df339a51   Lin Ming   [SCSI] sd: change...
216
217
  	return err;
  }
bc4f24014   Alan Stern   [SCSI] implement ...
218
219
220
221
222
223
  static int scsi_runtime_suspend(struct device *dev)
  {
  	int err = 0;
  
  	dev_dbg(dev, "scsi_runtime_suspend
  ");
6df339a51   Lin Ming   [SCSI] sd: change...
224
225
  	if (scsi_is_sdev_device(dev))
  		err = sdev_runtime_suspend(dev);
bc4f24014   Alan Stern   [SCSI] implement ...
226
227
228
229
230
  
  	/* Insert hooks here for targets, hosts, and transport classes */
  
  	return err;
  }
6627b38fd   Aaron Lu   [SCSI] sr: use bl...
231
  static int sdev_runtime_resume(struct device *dev)
bc4f24014   Alan Stern   [SCSI] implement ...
232
  {
6627b38fd   Aaron Lu   [SCSI] sr: use bl...
233
234
  	struct scsi_device *sdev = to_scsi_device(dev);
  	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
bc4f24014   Alan Stern   [SCSI] implement ...
235
  	int err = 0;
6df339a51   Lin Ming   [SCSI] sd: change...
236

1c69d3b6e   Ken Xue   Revert "SCSI: Fix...
237
238
  	blk_pre_runtime_resume(sdev->request_queue);
  	if (pm && pm->runtime_resume)
6627b38fd   Aaron Lu   [SCSI] sr: use bl...
239
  		err = pm->runtime_resume(dev);
1c69d3b6e   Ken Xue   Revert "SCSI: Fix...
240
  	blk_post_runtime_resume(sdev->request_queue, err);
6df339a51   Lin Ming   [SCSI] sd: change...
241
242
  	return err;
  }
6df339a51   Lin Ming   [SCSI] sd: change...
243
244
245
  static int scsi_runtime_resume(struct device *dev)
  {
  	int err = 0;
bc4f24014   Alan Stern   [SCSI] implement ...
246
247
248
249
  
  	dev_dbg(dev, "scsi_runtime_resume
  ");
  	if (scsi_is_sdev_device(dev))
6df339a51   Lin Ming   [SCSI] sd: change...
250
  		err = sdev_runtime_resume(dev);
bc4f24014   Alan Stern   [SCSI] implement ...
251
252
253
254
255
256
257
258
  
  	/* Insert hooks here for targets, hosts, and transport classes */
  
  	return err;
  }
  
  static int scsi_runtime_idle(struct device *dev)
  {
bc4f24014   Alan Stern   [SCSI] implement ...
259
260
261
262
  	dev_dbg(dev, "scsi_runtime_idle
  ");
  
  	/* Insert hooks here for targets, hosts, and transport classes */
6df339a51   Lin Ming   [SCSI] sd: change...
263
  	if (scsi_is_sdev_device(dev)) {
6627b38fd   Aaron Lu   [SCSI] sr: use bl...
264
265
266
  		pm_runtime_mark_last_busy(dev);
  		pm_runtime_autosuspend(dev);
  		return -EBUSY;
6df339a51   Lin Ming   [SCSI] sd: change...
267
  	}
6627b38fd   Aaron Lu   [SCSI] sr: use bl...
268

45f0a85c8   Rafael J. Wysocki   PM / Runtime: Rew...
269
  	return 0;
bc4f24014   Alan Stern   [SCSI] implement ...
270
271
272
273
274
275
276
  }
  
  int scsi_autopm_get_device(struct scsi_device *sdev)
  {
  	int	err;
  
  	err = pm_runtime_get_sync(&sdev->sdev_gendev);
632e270e0   Rafael J. Wysocki   PM / Runtime: Ret...
277
  	if (err < 0 && err !=-EACCES)
bc4f24014   Alan Stern   [SCSI] implement ...
278
  		pm_runtime_put_sync(&sdev->sdev_gendev);
632e270e0   Rafael J. Wysocki   PM / Runtime: Ret...
279
  	else
bc4f24014   Alan Stern   [SCSI] implement ...
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  		err = 0;
  	return err;
  }
  EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
  
  void scsi_autopm_put_device(struct scsi_device *sdev)
  {
  	pm_runtime_put_sync(&sdev->sdev_gendev);
  }
  EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
  
  void scsi_autopm_get_target(struct scsi_target *starget)
  {
  	pm_runtime_get_sync(&starget->dev);
  }
  
  void scsi_autopm_put_target(struct scsi_target *starget)
  {
  	pm_runtime_put_sync(&starget->dev);
  }
  
  int scsi_autopm_get_host(struct Scsi_Host *shost)
  {
  	int	err;
  
  	err = pm_runtime_get_sync(&shost->shost_gendev);
632e270e0   Rafael J. Wysocki   PM / Runtime: Ret...
306
  	if (err < 0 && err !=-EACCES)
bc4f24014   Alan Stern   [SCSI] implement ...
307
  		pm_runtime_put_sync(&shost->shost_gendev);
632e270e0   Rafael J. Wysocki   PM / Runtime: Ret...
308
  	else
bc4f24014   Alan Stern   [SCSI] implement ...
309
310
311
312
313
314
315
316
  		err = 0;
  	return err;
  }
  
  void scsi_autopm_put_host(struct Scsi_Host *shost)
  {
  	pm_runtime_put_sync(&shost->shost_gendev);
  }
db5bd1e0b   Alan Stern   [SCSI] convert to...
317
  const struct dev_pm_ops scsi_bus_pm_ops = {
fea6d607e   Alan Stern   [SCSI] scsi_pm: F...
318
  	.prepare =		scsi_bus_prepare,
db5bd1e0b   Alan Stern   [SCSI] convert to...
319
  	.suspend =		scsi_bus_suspend,
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
320
  	.resume =		scsi_bus_resume,
db5bd1e0b   Alan Stern   [SCSI] convert to...
321
  	.freeze =		scsi_bus_freeze,
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
322
  	.thaw =			scsi_bus_thaw,
db5bd1e0b   Alan Stern   [SCSI] convert to...
323
  	.poweroff =		scsi_bus_poweroff,
80d2fd48c   Aaron Lu   [SCSI] scsi_pm: u...
324
  	.restore =		scsi_bus_restore,
bc4f24014   Alan Stern   [SCSI] implement ...
325
326
327
  	.runtime_suspend =	scsi_runtime_suspend,
  	.runtime_resume =	scsi_runtime_resume,
  	.runtime_idle =		scsi_runtime_idle,
db5bd1e0b   Alan Stern   [SCSI] convert to...
328
  };