scsi_pm.c 7.7 KB
Newer Older
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>
9
#include <linux/export.h>
10
#include <linux/async.h>
11 12 13 14 15 16 17 18

#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_driver.h>
#include <scsi/scsi_host.h>

#include "scsi_priv.h"

A
Aaron Lu 已提交
19 20
#ifdef CONFIG_PM_SLEEP

D
Dan Williams 已提交
21
static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)
22
{
D
Dan Williams 已提交
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 53 54
	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;
55 56
	int err;

D
Dan Williams 已提交
57 58 59
	/* flush pending in-flight resume operations, suspend is synchronous */
	async_synchronize_full_domain(&scsi_sd_pm_domain);

60 61
	err = scsi_device_quiesce(to_scsi_device(dev));
	if (err == 0) {
D
Dan Williams 已提交
62 63 64
		err = cb(dev, pm);
		if (err)
			scsi_device_resume(to_scsi_device(dev));
65 66 67 68 69
	}
	dev_dbg(dev, "scsi suspend: %d\n", err);
	return err;
}

D
Dan Williams 已提交
70 71
static int scsi_dev_type_resume(struct device *dev,
		int (*cb)(struct device *, const struct dev_pm_ops *))
72
{
D
Dan Williams 已提交
73
	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
74 75
	int err = 0;

D
Dan Williams 已提交
76
	err = cb(dev, pm);
77 78
	scsi_device_resume(to_scsi_device(dev));
	dev_dbg(dev, "scsi resume: %d\n", err);
D
Dan Williams 已提交
79 80 81 82 83 84 85

	if (err == 0) {
		pm_runtime_disable(dev);
		pm_runtime_set_active(dev);
		pm_runtime_enable(dev);
	}

86 87 88
	return err;
}

89
static int
D
Dan Williams 已提交
90 91
scsi_bus_suspend_common(struct device *dev,
		int (*cb)(struct device *, const struct dev_pm_ops *))
92 93 94
{
	int err = 0;

95 96
	if (scsi_is_sdev_device(dev)) {
		/*
97 98
		 * All the high-level SCSI drivers that implement runtime
		 * PM treat runtime suspend, system suspend, and system
99 100
		 * hibernate nearly identically. In all cases the requirements
		 * for runtime suspension are stricter.
101
		 */
102 103
		if (pm_runtime_suspended(dev))
			return 0;
104

105
		err = scsi_dev_type_suspend(dev, cb);
106
	}
107

108 109 110
	return err;
}

D
Dan Williams 已提交
111
static void async_sdev_resume(void *dev, async_cookie_t cookie)
112
{
D
Dan Williams 已提交
113 114
	scsi_dev_type_resume(dev, do_scsi_resume);
}
115

D
Dan Williams 已提交
116 117 118 119
static void async_sdev_thaw(void *dev, async_cookie_t cookie)
{
	scsi_dev_type_resume(dev, do_scsi_thaw);
}
120

D
Dan Williams 已提交
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 146 147 148 149 150 151 152 153
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 {
154 155 156 157
		pm_runtime_disable(dev);
		pm_runtime_set_active(dev);
		pm_runtime_enable(dev);
	}
D
Dan Williams 已提交
158
	return 0;
159 160
}

161 162 163 164
static int scsi_bus_prepare(struct device *dev)
{
	if (scsi_is_sdev_device(dev)) {
		/* sd probing uses async_schedule.  Wait until it finishes. */
165
		async_synchronize_full_domain(&scsi_sd_probe_domain);
166 167 168 169 170 171 172 173

	} else if (scsi_is_host_device(dev)) {
		/* Wait until async scanning is finished */
		scsi_complete_async_scans();
	}
	return 0;
}

174 175
static int scsi_bus_suspend(struct device *dev)
{
D
Dan Williams 已提交
176
	return scsi_bus_suspend_common(dev, do_scsi_suspend);
177 178 179 180
}

static int scsi_bus_resume(struct device *dev)
{
D
Dan Williams 已提交
181
	return scsi_bus_resume_common(dev, do_scsi_resume);
182 183 184 185
}

static int scsi_bus_freeze(struct device *dev)
{
D
Dan Williams 已提交
186
	return scsi_bus_suspend_common(dev, do_scsi_freeze);
187 188 189 190
}

static int scsi_bus_thaw(struct device *dev)
{
D
Dan Williams 已提交
191
	return scsi_bus_resume_common(dev, do_scsi_thaw);
192 193 194 195
}

static int scsi_bus_poweroff(struct device *dev)
{
D
Dan Williams 已提交
196
	return scsi_bus_suspend_common(dev, do_scsi_poweroff);
197 198 199 200
}

static int scsi_bus_restore(struct device *dev)
{
D
Dan Williams 已提交
201
	return scsi_bus_resume_common(dev, do_scsi_restore);
202 203 204 205
}

#else /* CONFIG_PM_SLEEP */

206
#define scsi_bus_prepare		NULL
207
#define scsi_bus_suspend		NULL
208
#define scsi_bus_resume			NULL
209
#define scsi_bus_freeze			NULL
210
#define scsi_bus_thaw			NULL
211
#define scsi_bus_poweroff		NULL
212
#define scsi_bus_restore		NULL
213 214 215

#endif /* CONFIG_PM_SLEEP */

A
Aaron Lu 已提交
216
static int sdev_runtime_suspend(struct device *dev)
217
{
A
Aaron Lu 已提交
218 219
	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
	struct scsi_device *sdev = to_scsi_device(dev);
220 221 222 223 224
	int err;

	err = blk_pre_runtime_suspend(sdev->request_queue);
	if (err)
		return err;
A
Aaron Lu 已提交
225 226
	if (pm && pm->runtime_suspend)
		err = pm->runtime_suspend(dev);
227 228 229 230 231
	blk_post_runtime_suspend(sdev->request_queue, err);

	return err;
}

232 233 234 235 236
static int scsi_runtime_suspend(struct device *dev)
{
	int err = 0;

	dev_dbg(dev, "scsi_runtime_suspend\n");
237 238
	if (scsi_is_sdev_device(dev))
		err = sdev_runtime_suspend(dev);
239 240 241 242 243 244

	/* Insert hooks here for targets, hosts, and transport classes */

	return err;
}

A
Aaron Lu 已提交
245
static int sdev_runtime_resume(struct device *dev)
246
{
A
Aaron Lu 已提交
247 248
	struct scsi_device *sdev = to_scsi_device(dev);
	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
249
	int err = 0;
250 251

	blk_pre_runtime_resume(sdev->request_queue);
A
Aaron Lu 已提交
252 253
	if (pm && pm->runtime_resume)
		err = pm->runtime_resume(dev);
254 255 256 257 258 259 260 261
	blk_post_runtime_resume(sdev->request_queue, err);

	return err;
}

static int scsi_runtime_resume(struct device *dev)
{
	int err = 0;
262 263 264

	dev_dbg(dev, "scsi_runtime_resume\n");
	if (scsi_is_sdev_device(dev))
265
		err = sdev_runtime_resume(dev);
266 267 268 269 270 271 272 273 274 275 276 277

	/* Insert hooks here for targets, hosts, and transport classes */

	return err;
}

static int scsi_runtime_idle(struct device *dev)
{
	dev_dbg(dev, "scsi_runtime_idle\n");

	/* Insert hooks here for targets, hosts, and transport classes */

278
	if (scsi_is_sdev_device(dev)) {
A
Aaron Lu 已提交
279 280 281
		pm_runtime_mark_last_busy(dev);
		pm_runtime_autosuspend(dev);
		return -EBUSY;
282
	}
A
Aaron Lu 已提交
283

284
	return 0;
285 286 287 288 289 290 291
}

int scsi_autopm_get_device(struct scsi_device *sdev)
{
	int	err;

	err = pm_runtime_get_sync(&sdev->sdev_gendev);
292
	if (err < 0 && err !=-EACCES)
293
		pm_runtime_put_sync(&sdev->sdev_gendev);
294
	else
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
		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);
321
	if (err < 0 && err !=-EACCES)
322
		pm_runtime_put_sync(&shost->shost_gendev);
323
	else
324 325 326 327 328 329 330 331 332
		err = 0;
	return err;
}

void scsi_autopm_put_host(struct Scsi_Host *shost)
{
	pm_runtime_put_sync(&shost->shost_gendev);
}

333
const struct dev_pm_ops scsi_bus_pm_ops = {
334
	.prepare =		scsi_bus_prepare,
335
	.suspend =		scsi_bus_suspend,
336
	.resume =		scsi_bus_resume,
337
	.freeze =		scsi_bus_freeze,
338
	.thaw =			scsi_bus_thaw,
339
	.poweroff =		scsi_bus_poweroff,
340
	.restore =		scsi_bus_restore,
341 342 343
	.runtime_suspend =	scsi_runtime_suspend,
	.runtime_resume =	scsi_runtime_resume,
	.runtime_idle =		scsi_runtime_idle,
344
};