aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-11-30 18:14:42 -0500
committerRafael J. Wysocki <rjw@sisk.pl>2010-12-24 09:02:41 -0500
commitc7b61de5b7b17f0df34dc7d2f8b9576f8bd36fce (patch)
treed8cf6be1aab175839973d2c76060bd41d65b2ed5
parent5262a47502adcfc3a64403120768f528418a3b79 (diff)
PM / Runtime: Add synchronous runtime interface for interrupt handlers (v3)
This patch (as1431c) makes the synchronous runtime-PM interface suitable for use in interrupt handlers. Subsystems can call the new pm_runtime_irq_safe() function to tell the PM core that a device's runtime_suspend and runtime_resume callbacks should be invoked with interrupts disabled and the spinlock held. This permits the pm_runtime_get_sync() and the new pm_runtime_put_sync_suspend() routines to be called from within interrupt handlers. When a device is declared irq-safe in this way, the PM core increments the parent's usage count, so the parent will never be runtime suspended. This prevents difficult situations in which an irq-safe device can't resume because it is forced to wait for its non-irq-safe parent. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
-rw-r--r--Documentation/power/runtime_pm.txt31
-rw-r--r--drivers/base/power/runtime.c47
-rw-r--r--include/linux/pm.h1
-rw-r--r--include/linux/pm_runtime.h7
4 files changed, 77 insertions, 9 deletions
diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt
index 41cc7b30d7dd..ffe55ffa540a 100644
--- a/Documentation/power/runtime_pm.txt
+++ b/Documentation/power/runtime_pm.txt
@@ -50,6 +50,15 @@ type's callbacks are not defined) of given device. The bus type, device type
50and device class callbacks are referred to as subsystem-level callbacks in what 50and device class callbacks are referred to as subsystem-level callbacks in what
51follows. 51follows.
52 52
53By default, the callbacks are always invoked in process context with interrupts
54enabled. However, subsystems can use the pm_runtime_irq_safe() helper function
55to tell the PM core that a device's ->runtime_suspend() and ->runtime_resume()
56callbacks should be invoked in atomic context with interrupts disabled
57(->runtime_idle() is still invoked the default way). This implies that these
58callback routines must not block or sleep, but it also means that the
59synchronous helper functions listed at the end of Section 4 can be used within
60an interrupt handler or in an atomic context.
61
53The subsystem-level suspend callback is _entirely_ _responsible_ for handling 62The subsystem-level suspend callback is _entirely_ _responsible_ for handling
54the suspend of the device as appropriate, which may, but need not include 63the suspend of the device as appropriate, which may, but need not include
55executing the device driver's own ->runtime_suspend() callback (from the 64executing the device driver's own ->runtime_suspend() callback (from the
@@ -237,6 +246,10 @@ defined in include/linux/pm.h:
237 Section 8); it may be modified only by the pm_runtime_no_callbacks() 246 Section 8); it may be modified only by the pm_runtime_no_callbacks()
238 helper function 247 helper function
239 248
249 unsigned int irq_safe;
250 - indicates that the ->runtime_suspend() and ->runtime_resume() callbacks
251 will be invoked with the spinlock held and interrupts disabled
252
240 unsigned int use_autosuspend; 253 unsigned int use_autosuspend;
241 - indicates that the device's driver supports delayed autosuspend (see 254 - indicates that the device's driver supports delayed autosuspend (see
242 Section 9); it may be modified only by the 255 Section 9); it may be modified only by the
@@ -344,6 +357,10 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
344 - decrement the device's usage counter; if the result is 0 then run 357 - decrement the device's usage counter; if the result is 0 then run
345 pm_runtime_idle(dev) and return its result 358 pm_runtime_idle(dev) and return its result
346 359
360 int pm_runtime_put_sync_suspend(struct device *dev);
361 - decrement the device's usage counter; if the result is 0 then run
362 pm_runtime_suspend(dev) and return its result
363
347 int pm_runtime_put_sync_autosuspend(struct device *dev); 364 int pm_runtime_put_sync_autosuspend(struct device *dev);
348 - decrement the device's usage counter; if the result is 0 then run 365 - decrement the device's usage counter; if the result is 0 then run
349 pm_runtime_autosuspend(dev) and return its result 366 pm_runtime_autosuspend(dev) and return its result
@@ -397,6 +414,11 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
397 PM attributes from /sys/devices/.../power (or prevent them from being 414 PM attributes from /sys/devices/.../power (or prevent them from being
398 added when the device is registered) 415 added when the device is registered)
399 416
417 void pm_runtime_irq_safe(struct device *dev);
418 - set the power.irq_safe flag for the device, causing the runtime-PM
419 suspend and resume callbacks (but not the idle callback) to be invoked
420 with interrupts disabled
421
400 void pm_runtime_mark_last_busy(struct device *dev); 422 void pm_runtime_mark_last_busy(struct device *dev);
401 - set the power.last_busy field to the current time 423 - set the power.last_busy field to the current time
402 424
@@ -438,6 +460,15 @@ pm_runtime_suspended()
438pm_runtime_mark_last_busy() 460pm_runtime_mark_last_busy()
439pm_runtime_autosuspend_expiration() 461pm_runtime_autosuspend_expiration()
440 462
463If pm_runtime_irq_safe() has been called for a device then the following helper
464functions may also be used in interrupt context:
465
466pm_runtime_suspend()
467pm_runtime_autosuspend()
468pm_runtime_resume()
469pm_runtime_get_sync()
470pm_runtime_put_sync_suspend()
471
4415. Run-time PM Initialization, Device Probing and Removal 4725. Run-time PM Initialization, Device Probing and Removal
442 473
443Initially, the run-time PM is disabled for all devices, which means that the 474Initially, the run-time PM is disabled for all devices, which means that the
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 02c652be83e7..656493a5e073 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -250,13 +250,16 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
250 if (!cb) 250 if (!cb)
251 return -ENOSYS; 251 return -ENOSYS;
252 252
253 spin_unlock_irq(&dev->power.lock); 253 if (dev->power.irq_safe) {
254 retval = cb(dev);
255 } else {
256 spin_unlock_irq(&dev->power.lock);
254 257
255 retval = cb(dev); 258 retval = cb(dev);
256 259
257 spin_lock_irq(&dev->power.lock); 260 spin_lock_irq(&dev->power.lock);
261 }
258 dev->power.runtime_error = retval; 262 dev->power.runtime_error = retval;
259
260 return retval; 263 return retval;
261} 264}
262 265
@@ -404,7 +407,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
404 goto out; 407 goto out;
405 } 408 }
406 409
407 if (parent && !parent->power.ignore_children) { 410 if (parent && !parent->power.ignore_children && !dev->power.irq_safe) {
408 spin_unlock_irq(&dev->power.lock); 411 spin_unlock_irq(&dev->power.lock);
409 412
410 pm_request_idle(parent); 413 pm_request_idle(parent);
@@ -527,10 +530,13 @@ static int rpm_resume(struct device *dev, int rpmflags)
527 530
528 if (!parent && dev->parent) { 531 if (!parent && dev->parent) {
529 /* 532 /*
530 * Increment the parent's resume counter and resume it if 533 * Increment the parent's usage counter and resume it if
531 * necessary. 534 * necessary. Not needed if dev is irq-safe; then the
535 * parent is permanently resumed.
532 */ 536 */
533 parent = dev->parent; 537 parent = dev->parent;
538 if (dev->power.irq_safe)
539 goto skip_parent;
534 spin_unlock(&dev->power.lock); 540 spin_unlock(&dev->power.lock);
535 541
536 pm_runtime_get_noresume(parent); 542 pm_runtime_get_noresume(parent);
@@ -553,6 +559,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
553 goto out; 559 goto out;
554 goto repeat; 560 goto repeat;
555 } 561 }
562 skip_parent:
556 563
557 if (dev->power.no_callbacks) 564 if (dev->power.no_callbacks)
558 goto no_callback; /* Assume success. */ 565 goto no_callback; /* Assume success. */
@@ -584,7 +591,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
584 rpm_idle(dev, RPM_ASYNC); 591 rpm_idle(dev, RPM_ASYNC);
585 592
586 out: 593 out:
587 if (parent) { 594 if (parent && !dev->power.irq_safe) {
588 spin_unlock_irq(&dev->power.lock); 595 spin_unlock_irq(&dev->power.lock);
589 596
590 pm_runtime_put(parent); 597 pm_runtime_put(parent);
@@ -1065,7 +1072,6 @@ EXPORT_SYMBOL_GPL(pm_runtime_allow);
1065 * Set the power.no_callbacks flag, which tells the PM core that this 1072 * Set the power.no_callbacks flag, which tells the PM core that this
1066 * device is power-managed through its parent and has no run-time PM 1073 * device is power-managed through its parent and has no run-time PM
1067 * callbacks of its own. The run-time sysfs attributes will be removed. 1074 * callbacks of its own. The run-time sysfs attributes will be removed.
1068 *
1069 */ 1075 */
1070void pm_runtime_no_callbacks(struct device *dev) 1076void pm_runtime_no_callbacks(struct device *dev)
1071{ 1077{
@@ -1078,6 +1084,27 @@ void pm_runtime_no_callbacks(struct device *dev)
1078EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); 1084EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks);
1079 1085
1080/** 1086/**
1087 * pm_runtime_irq_safe - Leave interrupts disabled during callbacks.
1088 * @dev: Device to handle
1089 *
1090 * Set the power.irq_safe flag, which tells the PM core that the
1091 * ->runtime_suspend() and ->runtime_resume() callbacks for this device should
1092 * always be invoked with the spinlock held and interrupts disabled. It also
1093 * causes the parent's usage counter to be permanently incremented, preventing
1094 * the parent from runtime suspending -- otherwise an irq-safe child might have
1095 * to wait for a non-irq-safe parent.
1096 */
1097void pm_runtime_irq_safe(struct device *dev)
1098{
1099 if (dev->parent)
1100 pm_runtime_get_sync(dev->parent);
1101 spin_lock_irq(&dev->power.lock);
1102 dev->power.irq_safe = 1;
1103 spin_unlock_irq(&dev->power.lock);
1104}
1105EXPORT_SYMBOL_GPL(pm_runtime_irq_safe);
1106
1107/**
1081 * update_autosuspend - Handle a change to a device's autosuspend settings. 1108 * update_autosuspend - Handle a change to a device's autosuspend settings.
1082 * @dev: Device to handle. 1109 * @dev: Device to handle.
1083 * @old_delay: The former autosuspend_delay value. 1110 * @old_delay: The former autosuspend_delay value.
@@ -1199,4 +1226,6 @@ void pm_runtime_remove(struct device *dev)
1199 /* Change the status back to 'suspended' to match the initial status. */ 1226 /* Change the status back to 'suspended' to match the initial status. */
1200 if (dev->power.runtime_status == RPM_ACTIVE) 1227 if (dev->power.runtime_status == RPM_ACTIVE)
1201 pm_runtime_set_suspended(dev); 1228 pm_runtime_set_suspended(dev);
1229 if (dev->power.irq_safe && dev->parent)
1230 pm_runtime_put_sync(dev->parent);
1202} 1231}
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 40f3f45702ba..61f2066e6852 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -486,6 +486,7 @@ struct dev_pm_info {
486 unsigned int run_wake:1; 486 unsigned int run_wake:1;
487 unsigned int runtime_auto:1; 487 unsigned int runtime_auto:1;
488 unsigned int no_callbacks:1; 488 unsigned int no_callbacks:1;
489 unsigned int irq_safe:1;
489 unsigned int use_autosuspend:1; 490 unsigned int use_autosuspend:1;
490 unsigned int timer_autosuspends:1; 491 unsigned int timer_autosuspends:1;
491 enum rpm_request request; 492 enum rpm_request request;
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h
index d19f1cca7f74..e9cc049ccb62 100644
--- a/include/linux/pm_runtime.h
+++ b/include/linux/pm_runtime.h
@@ -40,6 +40,7 @@ extern int pm_generic_runtime_idle(struct device *dev);
40extern int pm_generic_runtime_suspend(struct device *dev); 40extern int pm_generic_runtime_suspend(struct device *dev);
41extern int pm_generic_runtime_resume(struct device *dev); 41extern int pm_generic_runtime_resume(struct device *dev);
42extern void pm_runtime_no_callbacks(struct device *dev); 42extern void pm_runtime_no_callbacks(struct device *dev);
43extern void pm_runtime_irq_safe(struct device *dev);
43extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); 44extern void __pm_runtime_use_autosuspend(struct device *dev, bool use);
44extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); 45extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
45extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); 46extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev);
@@ -124,6 +125,7 @@ static inline int pm_generic_runtime_idle(struct device *dev) { return 0; }
124static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } 125static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; }
125static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } 126static inline int pm_generic_runtime_resume(struct device *dev) { return 0; }
126static inline void pm_runtime_no_callbacks(struct device *dev) {} 127static inline void pm_runtime_no_callbacks(struct device *dev) {}
128static inline void pm_runtime_irq_safe(struct device *dev) {}
127 129
128static inline void pm_runtime_mark_last_busy(struct device *dev) {} 130static inline void pm_runtime_mark_last_busy(struct device *dev) {}
129static inline void __pm_runtime_use_autosuspend(struct device *dev, 131static inline void __pm_runtime_use_autosuspend(struct device *dev,
@@ -196,6 +198,11 @@ static inline int pm_runtime_put_sync(struct device *dev)
196 return __pm_runtime_idle(dev, RPM_GET_PUT); 198 return __pm_runtime_idle(dev, RPM_GET_PUT);
197} 199}
198 200
201static inline int pm_runtime_put_sync_suspend(struct device *dev)
202{
203 return __pm_runtime_suspend(dev, RPM_GET_PUT);
204}
205
199static inline int pm_runtime_put_sync_autosuspend(struct device *dev) 206static inline int pm_runtime_put_sync_autosuspend(struct device *dev)
200{ 207{
201 return __pm_runtime_suspend(dev, RPM_GET_PUT | RPM_AUTO); 208 return __pm_runtime_suspend(dev, RPM_GET_PUT | RPM_AUTO);