On Thursday, November 25, 2010, Alan Stern wrote:
> 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 <[email protected]>
Applied to suspend-2.6/linux-next.
Thanks,
Rafael
> ---
>
> Index: usb-2.6/include/linux/pm.h
> ===================================================================
> --- usb-2.6.orig/include/linux/pm.h
> +++ usb-2.6/include/linux/pm.h
> @@ -486,6 +486,7 @@ struct dev_pm_info {
> unsigned int run_wake:1;
> unsigned int runtime_auto:1;
> unsigned int no_callbacks:1;
> + unsigned int irq_safe:1;
> unsigned int use_autosuspend:1;
> unsigned int timer_autosuspends:1;
> enum rpm_request request;
> Index: usb-2.6/include/linux/pm_runtime.h
> ===================================================================
> --- usb-2.6.orig/include/linux/pm_runtime.h
> +++ usb-2.6/include/linux/pm_runtime.h
> @@ -40,6 +40,7 @@ extern int pm_generic_runtime_idle(struc
> extern int pm_generic_runtime_suspend(struct device *dev);
> extern int pm_generic_runtime_resume(struct device *dev);
> extern void pm_runtime_no_callbacks(struct device *dev);
> +extern void pm_runtime_irq_safe(struct device *dev);
> extern void __pm_runtime_use_autosuspend(struct device *dev, bool use);
> extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
> extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev);
> @@ -123,6 +124,7 @@ static inline int pm_generic_runtime_idl
> static inline int pm_generic_runtime_suspend(struct device *dev) { return 0;
> }
> static inline int pm_generic_runtime_resume(struct device *dev) { return 0; }
> static inline void pm_runtime_no_callbacks(struct device *dev) {}
> +static inline void pm_runtime_irq_safe(struct device *dev) {}
>
> static inline void pm_runtime_mark_last_busy(struct device *dev) {}
> static inline void __pm_runtime_use_autosuspend(struct device *dev,
> @@ -195,6 +197,11 @@ static inline int pm_runtime_put_sync(st
> return __pm_runtime_idle(dev, RPM_GET_PUT);
> }
>
> +static inline int pm_runtime_put_sync_suspend(struct device *dev)
> +{
> + return __pm_runtime_suspend(dev, RPM_GET_PUT);
> +}
> +
> static inline int pm_runtime_put_sync_autosuspend(struct device *dev)
> {
> return __pm_runtime_suspend(dev, RPM_GET_PUT | RPM_AUTO);
> Index: usb-2.6/drivers/base/power/runtime.c
> ===================================================================
> --- usb-2.6.orig/drivers/base/power/runtime.c
> +++ usb-2.6/drivers/base/power/runtime.c
> @@ -250,13 +250,16 @@ static int rpm_callback(int (*cb)(struct
> if (!cb)
> return -ENOSYS;
>
> - spin_unlock_irq(&dev->power.lock);
> + if (dev->power.irq_safe) {
> + retval = cb(dev);
> + } else {
> + spin_unlock_irq(&dev->power.lock);
>
> - retval = cb(dev);
> + retval = cb(dev);
>
> - spin_lock_irq(&dev->power.lock);
> + spin_lock_irq(&dev->power.lock);
> + }
> dev->power.runtime_error = retval;
> -
> return retval;
> }
>
> @@ -404,7 +407,7 @@ static int rpm_suspend(struct device *de
> goto out;
> }
>
> - if (parent && !parent->power.ignore_children) {
> + if (parent && !parent->power.ignore_children && !dev->power.irq_safe) {
> spin_unlock_irq(&dev->power.lock);
>
> pm_request_idle(parent);
> @@ -527,10 +530,13 @@ static int rpm_resume(struct device *dev
>
> if (!parent && dev->parent) {
> /*
> - * Increment the parent's resume counter and resume it if
> - * necessary.
> + * Increment the parent's usage counter and resume it if
> + * necessary. Not needed if dev is irq-safe; then the
> + * parent is permanently resumed.
> */
> parent = dev->parent;
> + if (dev->power.irq_safe)
> + goto skip_parent;
> spin_unlock(&dev->power.lock);
>
> pm_runtime_get_noresume(parent);
> @@ -553,6 +559,7 @@ static int rpm_resume(struct device *dev
> goto out;
> goto repeat;
> }
> + skip_parent:
>
> if (dev->power.no_callbacks)
> goto no_callback; /* Assume success. */
> @@ -584,7 +591,7 @@ static int rpm_resume(struct device *dev
> rpm_idle(dev, RPM_ASYNC);
>
> out:
> - if (parent) {
> + if (parent && !dev->power.irq_safe) {
> spin_unlock_irq(&dev->power.lock);
>
> pm_runtime_put(parent);
> @@ -1065,7 +1072,6 @@ EXPORT_SYMBOL_GPL(pm_runtime_allow);
> * Set the power.no_callbacks flag, which tells the PM core that this
> * device is power-managed through its parent and has no run-time PM
> * callbacks of its own. The run-time sysfs attributes will be removed.
> - *
> */
> void pm_runtime_no_callbacks(struct device *dev)
> {
> @@ -1078,6 +1084,27 @@ void pm_runtime_no_callbacks(struct devi
> EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks);
>
> /**
> + * pm_runtime_irq_safe - Leave interrupts disabled during callbacks.
> + * @dev: Device to handle
> + *
> + * Set the power.irq_safe flag, which tells the PM core that the
> + * ->runtime_suspend() and ->runtime_resume() callbacks for this device
> should
> + * always be invoked with the spinlock held and interrupts disabled. It also
> + * causes the parent's usage counter to be permanently incremented,
> preventing
> + * the parent from runtime suspending -- otherwise an irq-safe child might
> have
> + * to wait for a non-irq-safe parent.
> + */
> +void pm_runtime_irq_safe(struct device *dev)
> +{
> + if (dev->parent)
> + pm_runtime_get_sync(dev->parent);
> + spin_lock_irq(&dev->power.lock);
> + dev->power.irq_safe = 1;
> + spin_unlock_irq(&dev->power.lock);
> +}
> +EXPORT_SYMBOL_GPL(pm_runtime_irq_safe);
> +
> +/**
> * update_autosuspend - Handle a change to a device's autosuspend settings.
> * @dev: Device to handle.
> * @old_delay: The former autosuspend_delay value.
> @@ -1199,4 +1226,6 @@ void pm_runtime_remove(struct device *de
> /* Change the status back to 'suspended' to match the initial status. */
> if (dev->power.runtime_status == RPM_ACTIVE)
> pm_runtime_set_suspended(dev);
> + if (dev->power.irq_safe && dev->parent)
> + pm_runtime_put_sync(dev->parent);
> }
> Index: usb-2.6/Documentation/power/runtime_pm.txt
> ===================================================================
> --- usb-2.6.orig/Documentation/power/runtime_pm.txt
> +++ usb-2.6/Documentation/power/runtime_pm.txt
> @@ -50,6 +50,15 @@ type's callbacks are not defined) of giv
> and device class callbacks are referred to as subsystem-level callbacks in
> what
> follows.
>
> +By default, the callbacks are always invoked in process context with
> interrupts
> +enabled. However, subsystems can use the pm_runtime_irq_safe() helper
> function
> +to tell the PM core that a device's ->runtime_suspend() and
> ->runtime_resume()
> +callbacks should be invoked in atomic context with interrupts disabled
> +(->runtime_idle() is still invoked the default way). This implies that these
> +callback routines must not block or sleep, but it also means that the
> +synchronous helper functions listed at the end of Section 4 can be used
> within
> +an interrupt handler or in an atomic context.
> +
> The subsystem-level suspend callback is _entirely_ _responsible_ for handling
> the suspend of the device as appropriate, which may, but need not include
> executing the device driver's own ->runtime_suspend() callback (from the
> @@ -237,6 +246,10 @@ defined in include/linux/pm.h:
> Section 8); it may be modified only by the pm_runtime_no_callbacks()
> helper function
>
> + unsigned int irq_safe;
> + - indicates that the ->runtime_suspend() and ->runtime_resume() callbacks
> + will be invoked with the spinlock held and interrupts disabled
> +
> unsigned int use_autosuspend;
> - indicates that the device's driver supports delayed autosuspend (see
> Section 9); it may be modified only by the
> @@ -344,6 +357,10 @@ drivers/base/power/runtime.c and include
> - decrement the device's usage counter; if the result is 0 then run
> pm_runtime_idle(dev) and return its result
>
> + int pm_runtime_put_sync_suspend(struct device *dev);
> + - decrement the device's usage counter; if the result is 0 then run
> + pm_runtime_suspend(dev) and return its result
> +
> int pm_runtime_put_sync_autosuspend(struct device *dev);
> - decrement the device's usage counter; if the result is 0 then run
> pm_runtime_autosuspend(dev) and return its result
> @@ -397,6 +414,11 @@ drivers/base/power/runtime.c and include
> PM attributes from /sys/devices/.../power (or prevent them from being
> added when the device is registered)
>
> + void pm_runtime_irq_safe(struct device *dev);
> + - set the power.irq_safe flag for the device, causing the runtime-PM
> + suspend and resume callbacks (but not the idle callback) to be invoked
> + with interrupts disabled
> +
> void pm_runtime_mark_last_busy(struct device *dev);
> - set the power.last_busy field to the current time
>
> @@ -438,6 +460,15 @@ pm_runtime_suspended()
> pm_runtime_mark_last_busy()
> pm_runtime_autosuspend_expiration()
>
> +If pm_runtime_irq_safe() has been called for a device then the following
> helper
> +functions may also be used in interrupt context:
> +
> +pm_runtime_suspend()
> +pm_runtime_autosuspend()
> +pm_runtime_resume()
> +pm_runtime_get_sync()
> +pm_runtime_put_sync_suspend()
> +
> 5. Run-time PM Initialization, Device Probing and Removal
>
> Initially, the run-time PM is disabled for all devices, which means that the
>
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html