Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c26d4114 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'pm-qos'

* pm-qos:
  PM QoS: Use spinlock in the per-device PM QoS constraints code
parents 721b50ee fc2fb3a0
Loading
Loading
Loading
Loading
+41 −26
Original line number Original line Diff line number Diff line
@@ -24,26 +24,32 @@
 *  . a system-wide notification callback using the dev_pm_qos_*_global_notifier
 *  . a system-wide notification callback using the dev_pm_qos_*_global_notifier
 *    API. The notification chain data is stored in a static variable.
 *    API. The notification chain data is stored in a static variable.
 *
 *
 * Note about the per-device constraint data struct allocation:
 * Notes about the per-device constraint data struct allocation:
 * . The per-device constraints data struct ptr is tored into the device
 * . The per-device constraints data struct ptr is stored into the device
 *    dev_pm_info.
 *    dev_pm_info.
 * . To minimize the data usage by the per-device constraints, the data struct
 * . 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.
 * . 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/pm_qos.h>
#include <linux/spinlock.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/export.h>
#include <linux/export.h>


#include "power.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);
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
 * @dev: device to allocate data for
 *
 *
 * Called at the first call to add_request, for constraint data allocation
 * 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)
static int dev_pm_qos_constraints_allocate(struct device *dev)
{
{
	struct pm_qos_constraints *c;
	struct pm_qos_constraints *c;
	struct blocking_notifier_head *n;
	struct blocking_notifier_head *n;
	unsigned long flags;


	c = kzalloc(sizeof(*c), GFP_KERNEL);
	c = kzalloc(sizeof(*c), GFP_ATOMIC);
	if (!c)
	if (!c)
		return -ENOMEM;
		return -ENOMEM;


	n = kzalloc(sizeof(*n), GFP_KERNEL);
	n = kzalloc(sizeof(*n), GFP_ATOMIC);
	if (!n) {
	if (!n) {
		kfree(c);
		kfree(c);
		return -ENOMEM;
		return -ENOMEM;
@@ -134,9 +141,9 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
	c->type = PM_QOS_MIN;
	c->type = PM_QOS_MIN;
	c->notifiers = n;
	c->notifiers = n;


	spin_lock_irq(&dev->power.lock);
	spin_lock_irqsave(&dev->power.lock, flags);
	dev->power.constraints = c;
	dev->power.constraints = c;
	spin_unlock_irq(&dev->power.lock);
	spin_unlock_irqrestore(&dev->power.lock, flags);


	return 0;
	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)
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.constraints = NULL;
	dev->power.power_state = PMSG_ON;
	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 dev_pm_qos_request *req, *tmp;
	struct pm_qos_constraints *c;
	struct pm_qos_constraints *c;
	unsigned long flags;


	/*
	/*
	 * If the device's PM QoS resume latency limit has been exposed to user
	 * 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);
	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;
	dev->power.power_state = PMSG_INVALID;
	c = dev->power.constraints;
	c = dev->power.constraints;
@@ -198,7 +208,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
	kfree(c);
	kfree(c);


 out:
 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)
			   s32 value)
{
{
	int ret = 0;
	int ret = 0;
	unsigned long flags;


	if (!dev || !req) /*guard against callers passing in null */
	if (!dev || !req) /*guard against callers passing in null */
		return -EINVAL;
		return -EINVAL;
@@ -233,7 +244,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,


	req->dev = dev;
	req->dev = dev;


	mutex_lock(&dev_pm_qos_mtx);
	spin_lock_irqsave(&dev_pm_qos_lock, flags);


	if (!dev->power.constraints) {
	if (!dev->power.constraints) {
		if (dev->power.power_state.event == PM_EVENT_INVALID) {
		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);
		ret = apply_constraint(req, PM_QOS_ADD_REQ, value);


 out:
 out:
	mutex_unlock(&dev_pm_qos_mtx);
	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);


	return ret;
	return ret;
}
}
@@ -280,6 +291,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
			      s32 new_value)
			      s32 new_value)
{
{
	int ret = 0;
	int ret = 0;
	unsigned long flags;


	if (!req) /*guard against callers passing in null */
	if (!req) /*guard against callers passing in null */
		return -EINVAL;
		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__))
		 "%s() called for unknown object\n", __func__))
		return -EINVAL;
		return -EINVAL;


	mutex_lock(&dev_pm_qos_mtx);
	spin_lock_irqsave(&dev_pm_qos_lock, flags);


	if (req->dev->power.constraints) {
	if (req->dev->power.constraints) {
		if (new_value != req->node.prio)
		if (new_value != req->node.prio)
@@ -299,7 +311,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
		ret = -ENODEV;
		ret = -ENODEV;
	}
	}


	mutex_unlock(&dev_pm_qos_mtx);
	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
	return ret;
	return ret;
}
}
EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
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 dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
{
{
	int ret = 0;
	int ret = 0;
	unsigned long flags;


	if (!req) /*guard against callers passing in null */
	if (!req) /*guard against callers passing in null */
		return -EINVAL;
		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__))
		 "%s() called for unknown object\n", __func__))
		return -EINVAL;
		return -EINVAL;


	mutex_lock(&dev_pm_qos_mtx);
	spin_lock_irqsave(&dev_pm_qos_lock, flags);


	if (req->dev->power.constraints) {
	if (req->dev->power.constraints) {
		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
		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;
		ret = -ENODEV;
	}
	}


	mutex_unlock(&dev_pm_qos_mtx);
	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
	return ret;
	return ret;
}
}
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
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 dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
{
{
	int ret = 0;
	int ret = 0;
	unsigned long flags;


	mutex_lock(&dev_pm_qos_mtx);
	spin_lock_irqsave(&dev_pm_qos_lock, flags);


	if (!dev->power.constraints)
	if (!dev->power.constraints)
		ret = dev->power.power_state.event != PM_EVENT_INVALID ?
		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(
		ret = blocking_notifier_chain_register(
				dev->power.constraints->notifiers, notifier);
				dev->power.constraints->notifiers, notifier);


	mutex_unlock(&dev_pm_qos_mtx);
	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
	return ret;
	return ret;
}
}
EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
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)
			       struct notifier_block *notifier)
{
{
	int retval = 0;
	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. */
	/* Silently return if the constraints object is not present. */
	if (dev->power.constraints)
	if (dev->power.constraints)
@@ -398,7 +413,7 @@ int dev_pm_qos_remove_notifier(struct device *dev,
				dev->power.constraints->notifiers,
				dev->power.constraints->notifiers,
				notifier);
				notifier);


	mutex_unlock(&dev_pm_qos_mtx);
	spin_unlock_irqrestore(&dev_pm_qos_lock, flags);
	return retval;
	return retval;
}
}
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);