diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2010-11-30 18:14:42 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2010-12-24 09:02:41 -0500 |
commit | c7b61de5b7b17f0df34dc7d2f8b9576f8bd36fce (patch) | |
tree | d8cf6be1aab175839973d2c76060bd41d65b2ed5 | |
parent | 5262a47502adcfc3a64403120768f528418a3b79 (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.txt | 31 | ||||
-rw-r--r-- | drivers/base/power/runtime.c | 47 | ||||
-rw-r--r-- | include/linux/pm.h | 1 | ||||
-rw-r--r-- | include/linux/pm_runtime.h | 7 |
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 | |||
50 | and device class callbacks are referred to as subsystem-level callbacks in what | 50 | and device class callbacks are referred to as subsystem-level callbacks in what |
51 | follows. | 51 | follows. |
52 | 52 | ||
53 | By default, the callbacks are always invoked in process context with interrupts | ||
54 | enabled. However, subsystems can use the pm_runtime_irq_safe() helper function | ||
55 | to tell the PM core that a device's ->runtime_suspend() and ->runtime_resume() | ||
56 | callbacks should be invoked in atomic context with interrupts disabled | ||
57 | (->runtime_idle() is still invoked the default way). This implies that these | ||
58 | callback routines must not block or sleep, but it also means that the | ||
59 | synchronous helper functions listed at the end of Section 4 can be used within | ||
60 | an interrupt handler or in an atomic context. | ||
61 | |||
53 | The subsystem-level suspend callback is _entirely_ _responsible_ for handling | 62 | The subsystem-level suspend callback is _entirely_ _responsible_ for handling |
54 | the suspend of the device as appropriate, which may, but need not include | 63 | the suspend of the device as appropriate, which may, but need not include |
55 | executing the device driver's own ->runtime_suspend() callback (from the | 64 | executing 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() | |||
438 | pm_runtime_mark_last_busy() | 460 | pm_runtime_mark_last_busy() |
439 | pm_runtime_autosuspend_expiration() | 461 | pm_runtime_autosuspend_expiration() |
440 | 462 | ||
463 | If pm_runtime_irq_safe() has been called for a device then the following helper | ||
464 | functions may also be used in interrupt context: | ||
465 | |||
466 | pm_runtime_suspend() | ||
467 | pm_runtime_autosuspend() | ||
468 | pm_runtime_resume() | ||
469 | pm_runtime_get_sync() | ||
470 | pm_runtime_put_sync_suspend() | ||
471 | |||
441 | 5. Run-time PM Initialization, Device Probing and Removal | 472 | 5. Run-time PM Initialization, Device Probing and Removal |
442 | 473 | ||
443 | Initially, the run-time PM is disabled for all devices, which means that the | 474 | Initially, 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 | */ |
1070 | void pm_runtime_no_callbacks(struct device *dev) | 1076 | void pm_runtime_no_callbacks(struct device *dev) |
1071 | { | 1077 | { |
@@ -1078,6 +1084,27 @@ void pm_runtime_no_callbacks(struct device *dev) | |||
1078 | EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); | 1084 | EXPORT_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 | */ | ||
1097 | void 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 | } | ||
1105 | EXPORT_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); | |||
40 | extern int pm_generic_runtime_suspend(struct device *dev); | 40 | extern int pm_generic_runtime_suspend(struct device *dev); |
41 | extern int pm_generic_runtime_resume(struct device *dev); | 41 | extern int pm_generic_runtime_resume(struct device *dev); |
42 | extern void pm_runtime_no_callbacks(struct device *dev); | 42 | extern void pm_runtime_no_callbacks(struct device *dev); |
43 | extern void pm_runtime_irq_safe(struct device *dev); | ||
43 | extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); | 44 | extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); |
44 | extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); | 45 | extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); |
45 | extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); | 46 | extern 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; } | |||
124 | static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } | 125 | static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } |
125 | static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } | 126 | static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } |
126 | static inline void pm_runtime_no_callbacks(struct device *dev) {} | 127 | static inline void pm_runtime_no_callbacks(struct device *dev) {} |
128 | static inline void pm_runtime_irq_safe(struct device *dev) {} | ||
127 | 129 | ||
128 | static inline void pm_runtime_mark_last_busy(struct device *dev) {} | 130 | static inline void pm_runtime_mark_last_busy(struct device *dev) {} |
129 | static inline void __pm_runtime_use_autosuspend(struct device *dev, | 131 | static 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 | ||
201 | static inline int pm_runtime_put_sync_suspend(struct device *dev) | ||
202 | { | ||
203 | return __pm_runtime_suspend(dev, RPM_GET_PUT); | ||
204 | } | ||
205 | |||
199 | static inline int pm_runtime_put_sync_autosuspend(struct device *dev) | 206 | static 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); |