Re: [PATCH] PM QoS: Use spinlock in the per-device PM QoS constraints code

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

 



On Wednesday, September 19, 2012, Jean Pihet wrote:
> The per-device PM QoS locking requires a spinlock to be used. The reasons
> are:
> - an alignement with the PM QoS core code, which is used by the per-device
>   PM QoS code for the constraints lists management. The PM QoS core code
>   uses spinlocks to protect the constraints lists,
> - some drivers need to use the per-device PM QoS functionality from
>   interrupt context or spinlock protected context.
>   An example of such a driver is the OMAP HSI (high-speed synchronous serial
>   interface) driver which needs to control the IP block idle state
>   depending on the FIFO empty state, from interrupt context.
> 
> Reported-by: Djamil Elaidi <d-elaidi@xxxxxx>
> Signed-off-by: Jean Pihet <j-pihet@xxxxxx>

Applied to the linux-next branch of the linux-pm.git tree as v3.7 material.

Next time please send CCs to linux-pm@xxxxxxxxxxxxxxx instead of the
Linux Foundation list (of the same name), which has been abandoned.

Thanks,
Rafael


> ---
>  drivers/base/power/qos.c |   67 ++++++++++++++++++++++++++++------------------
>  1 files changed, 41 insertions(+), 26 deletions(-)
> 
> diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
> index 74a67e0..968a771 100644
> --- a/drivers/base/power/qos.c
> +++ b/drivers/base/power/qos.c
> @@ -24,26 +24,32 @@
>   *  . a system-wide notification callback using the dev_pm_qos_*_global_notifier
>   *    API. The notification chain data is stored in a static variable.
>   *
> - * Note about the per-device constraint data struct allocation:
> - * . The per-device constraints data struct ptr is tored into the device
> + * Notes about the per-device constraint data struct allocation:
> + * . The per-device constraints data struct ptr is stored into the device
>   *    dev_pm_info.
>   * . To minimize the data usage by the per-device constraints, the data struct
> - *   is only allocated at the first call to dev_pm_qos_add_request.
> + *    is only allocated at the first call to dev_pm_qos_add_request.
>   * . The data is later free'd when the device is removed from the system.
> - *  . A global mutex protects the constraints users from the data being
> - *     allocated and free'd.
> + *
> + * Notes about locking:
> + * . The dev->power.lock lock protects the constraints list
> + *    (dev->power.constraints) allocation and free, as triggered by the
> + *   driver core code at device insertion and removal,
> + * . A global lock dev_pm_qos_lock protects the constraints list entries
> + *    from any modification and the notifiers registration and unregistration.
> + * . For both locks a spinlock is needed since this code can be called from
> + *    interrupt context or spinlock protected context.
>   */
>  
>  #include <linux/pm_qos.h>
>  #include <linux/spinlock.h>
>  #include <linux/slab.h>
>  #include <linux/device.h>
> -#include <linux/mutex.h>
>  #include <linux/export.h>
>  
>  #include "power.h"
>  
> -static DEFINE_MUTEX(dev_pm_qos_mtx);
> +static DEFINE_SPINLOCK(dev_pm_qos_lock);
>  
>  static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
>  
> @@ -110,18 +116,19 @@ static int apply_constraint(struct dev_pm_qos_request *req,
>   * @dev: device to allocate data for
>   *
>   * Called at the first call to add_request, for constraint data allocation
> - * Must be called with the dev_pm_qos_mtx mutex held
> + * Must be called with the dev_pm_qos_lock lock held
>   */
>  static int dev_pm_qos_constraints_allocate(struct device *dev)
>  {
>  	struct pm_qos_constraints *c;
>  	struct blocking_notifier_head *n;
> +	unsigned long flags;
>  
> -	c = kzalloc(sizeof(*c), GFP_KERNEL);
> +	c = kzalloc(sizeof(*c), GFP_ATOMIC);
>  	if (!c)
>  		return -ENOMEM;
>  
> -	n = kzalloc(sizeof(*n), GFP_KERNEL);
> +	n = kzalloc(sizeof(*n), GFP_ATOMIC);
>  	if (!n) {
>  		kfree(c);
>  		return -ENOMEM;
> @@ -134,9 +141,9 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
>  	c->type = PM_QOS_MIN;
>  	c->notifiers = n;
>  
> -	spin_lock_irq(&dev->power.lock);
> +	spin_lock_irqsave(&dev->power.lock, flags);
>  	dev->power.constraints = c;
> -	spin_unlock_irq(&dev->power.lock);
> +	spin_unlock_irqrestore(&dev->power.lock, flags);
>  
>  	return 0;
>  }
> @@ -150,10 +157,12 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
>   */
>  void dev_pm_qos_constraints_init(struct device *dev)
>  {
> -	mutex_lock(&dev_pm_qos_mtx);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev_pm_qos_lock, flags);
>  	dev->power.constraints = NULL;
>  	dev->power.power_state = PMSG_ON;
> -	mutex_unlock(&dev_pm_qos_mtx);
> +	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
>  }
>  
>  /**
> @@ -166,6 +175,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
>  {
>  	struct dev_pm_qos_request *req, *tmp;
>  	struct pm_qos_constraints *c;
> +	unsigned long flags;
>  
>  	/*
>  	 * If the device's PM QoS resume latency limit has been exposed to user
> @@ -173,7 +183,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
>  	 */
>  	dev_pm_qos_hide_latency_limit(dev);
>  
> -	mutex_lock(&dev_pm_qos_mtx);
> +	spin_lock_irqsave(&dev_pm_qos_lock, flags);
>  
>  	dev->power.power_state = PMSG_INVALID;
>  	c = dev->power.constraints;
> @@ -198,7 +208,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
>  	kfree(c);
>  
>   out:
> -	mutex_unlock(&dev_pm_qos_mtx);
> +	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
>  }
>  
>  /**
> @@ -223,6 +233,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>  			   s32 value)
>  {
>  	int ret = 0;
> +	unsigned long flags;
>  
>  	if (!dev || !req) /*guard against callers passing in null */
>  		return -EINVAL;
> @@ -233,7 +244,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>  
>  	req->dev = dev;
>  
> -	mutex_lock(&dev_pm_qos_mtx);
> +	spin_lock_irqsave(&dev_pm_qos_lock, flags);
>  
>  	if (!dev->power.constraints) {
>  		if (dev->power.power_state.event == PM_EVENT_INVALID) {
> @@ -255,7 +266,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>  		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
>  
>   out:
> -	mutex_unlock(&dev_pm_qos_mtx);
> +	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
>  
>  	return ret;
>  }
> @@ -280,6 +291,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
>  			      s32 new_value)
>  {
>  	int ret = 0;
> +	unsigned long flags;
>  
>  	if (!req) /*guard against callers passing in null */
>  		return -EINVAL;
> @@ -288,7 +300,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
>  		 "%s() called for unknown object\n", __func__))
>  		return -EINVAL;
>  
> -	mutex_lock(&dev_pm_qos_mtx);
> +	spin_lock_irqsave(&dev_pm_qos_lock, flags);
>  
>  	if (req->dev->power.constraints) {
>  		if (new_value != req->node.prio)
> @@ -299,7 +311,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
>  		ret = -ENODEV;
>  	}
>  
> -	mutex_unlock(&dev_pm_qos_mtx);
> +	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
> @@ -319,6 +331,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
>  int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
>  {
>  	int ret = 0;
> +	unsigned long flags;
>  
>  	if (!req) /*guard against callers passing in null */
>  		return -EINVAL;
> @@ -327,7 +340,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
>  		 "%s() called for unknown object\n", __func__))
>  		return -EINVAL;
>  
> -	mutex_lock(&dev_pm_qos_mtx);
> +	spin_lock_irqsave(&dev_pm_qos_lock, flags);
>  
>  	if (req->dev->power.constraints) {
>  		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
> @@ -338,7 +351,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
>  		ret = -ENODEV;
>  	}
>  
> -	mutex_unlock(&dev_pm_qos_mtx);
> +	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
> @@ -359,8 +372,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
>  int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
>  {
>  	int ret = 0;
> +	unsigned long flags;
>  
> -	mutex_lock(&dev_pm_qos_mtx);
> +	spin_lock_irqsave(&dev_pm_qos_lock, flags);
>  
>  	if (!dev->power.constraints)
>  		ret = dev->power.power_state.event != PM_EVENT_INVALID ?
> @@ -370,7 +384,7 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
>  		ret = blocking_notifier_chain_register(
>  				dev->power.constraints->notifiers, notifier);
>  
> -	mutex_unlock(&dev_pm_qos_mtx);
> +	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
> @@ -389,8 +403,9 @@ int dev_pm_qos_remove_notifier(struct device *dev,
>  			       struct notifier_block *notifier)
>  {
>  	int retval = 0;
> +	unsigned long flags;
>  
> -	mutex_lock(&dev_pm_qos_mtx);
> +	spin_lock_irqsave(&dev_pm_qos_lock, flags);
>  
>  	/* Silently return if the constraints object is not present. */
>  	if (dev->power.constraints)
> @@ -398,7 +413,7 @@ int dev_pm_qos_remove_notifier(struct device *dev,
>  				dev->power.constraints->notifiers,
>  				notifier);
>  
> -	mutex_unlock(&dev_pm_qos_mtx);
> +	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
>  	return retval;
>  }
>  EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
> 



[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