Re: [PATCH ver. 3] PM: add synchronous runtime interface for interrupt handlers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <stern@xxxxxxxxxxxxxxxxxxx>

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
> 
> 
> 

_______________________________________________
linux-pm mailing list
linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx
https://lists.linux-foundation.org/mailman/listinfo/linux-pm


[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux