diff options
Diffstat (limited to 'drivers/base/power/qos.c')
| -rw-r--r-- | drivers/base/power/qos.c | 160 | 
1 files changed, 90 insertions, 70 deletions
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 8d0b81151c1..91e06141738 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -30,15 +30,6 @@   * . 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.   * . The data is later free'd when the device is removed from the system. - * . The constraints_state variable from dev_pm_info tracks the data struct - *    allocation state: - *    DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data - *     allocated, - *    DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be - *     allocated at the first call to dev_pm_qos_add_request, - *    DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device - *     PM QoS constraints framework is operational and constraints can be - *     added, updated or removed using the dev_pm_qos_* API.   *  . A global mutex protects the constraints users from the data being   *     allocated and free'd.   */ @@ -51,8 +42,30 @@  static DEFINE_MUTEX(dev_pm_qos_mtx); +  static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); +/** + * dev_pm_qos_read_value - Get PM QoS constraint for a given device. + * @dev: Device to get the PM QoS constraint value for. + */ +s32 dev_pm_qos_read_value(struct device *dev) +{ +	struct pm_qos_constraints *c; +	unsigned long flags; +	s32 ret = 0; + +	spin_lock_irqsave(&dev->power.lock, flags); + +	c = dev->power.constraints; +	if (c) +		ret = pm_qos_read_value(c); + +	spin_unlock_irqrestore(&dev->power.lock, flags); + +	return ret; +} +  /*   * apply_constraint   * @req: constraint request to apply @@ -105,27 +118,31 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)  	}  	BLOCKING_INIT_NOTIFIER_HEAD(n); +	plist_head_init(&c->list); +	c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; +	c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; +	c->type = PM_QOS_MIN; +	c->notifiers = n; + +	spin_lock_irq(&dev->power.lock);  	dev->power.constraints = c; -	plist_head_init(&dev->power.constraints->list); -	dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; -	dev->power.constraints->default_value =	PM_QOS_DEV_LAT_DEFAULT_VALUE; -	dev->power.constraints->type = PM_QOS_MIN; -	dev->power.constraints->notifiers = n; -	dev->power.constraints_state = DEV_PM_QOS_ALLOCATED; +	spin_unlock_irq(&dev->power.lock);  	return 0;  }  /** - * dev_pm_qos_constraints_init + * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.   * @dev: target device   * - * Called from the device PM subsystem at device insertion + * Called from the device PM subsystem during device insertion under + * device_pm_lock().   */  void dev_pm_qos_constraints_init(struct device *dev)  {  	mutex_lock(&dev_pm_qos_mtx); -	dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT; +	dev->power.constraints = NULL; +	dev->power.power_state = PMSG_ON;  	mutex_unlock(&dev_pm_qos_mtx);  } @@ -133,34 +150,38 @@ void dev_pm_qos_constraints_init(struct device *dev)   * dev_pm_qos_constraints_destroy   * @dev: target device   * - * Called from the device PM subsystem at device removal + * Called from the device PM subsystem on device removal under device_pm_lock().   */  void dev_pm_qos_constraints_destroy(struct device *dev)  {  	struct dev_pm_qos_request *req, *tmp; +	struct pm_qos_constraints *c;  	mutex_lock(&dev_pm_qos_mtx); -	if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { -		/* Flush the constraints list for the device */ -		plist_for_each_entry_safe(req, tmp, -					  &dev->power.constraints->list, -					  node) { -			/* -			 * Update constraints list and call the notification -			 * callbacks if needed -			 */ -			apply_constraint(req, PM_QOS_REMOVE_REQ, -					 PM_QOS_DEFAULT_VALUE); -			memset(req, 0, sizeof(*req)); -		} +	dev->power.power_state = PMSG_INVALID; +	c = dev->power.constraints; +	if (!c) +		goto out; -		kfree(dev->power.constraints->notifiers); -		kfree(dev->power.constraints); -		dev->power.constraints = NULL; +	/* Flush the constraints list for the device */ +	plist_for_each_entry_safe(req, tmp, &c->list, node) { +		/* +		 * Update constraints list and call the notification +		 * callbacks if needed +		 */ +		apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); +		memset(req, 0, sizeof(*req));  	} -	dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE; +	spin_lock_irq(&dev->power.lock); +	dev->power.constraints = NULL; +	spin_unlock_irq(&dev->power.lock); + +	kfree(c->notifiers); +	kfree(c); + + out:  	mutex_unlock(&dev_pm_qos_mtx);  } @@ -178,8 +199,9 @@ void dev_pm_qos_constraints_destroy(struct device *dev)   *   * Returns 1 if the aggregated constraint value has changed,   * 0 if the aggregated constraint value has not changed, - * -EINVAL in case of wrong parameters, -ENODEV if the device has been - * removed from the system + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory + * to allocate for data structures, -ENODEV if the device has just been removed + * from the system.   */  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,  			   s32 value) @@ -195,28 +217,32 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,  		return -EINVAL;  	} -	mutex_lock(&dev_pm_qos_mtx);  	req->dev = dev; -	/* Return if the device has been removed */ -	if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) { -		ret = -ENODEV; -		goto out; -	} +	mutex_lock(&dev_pm_qos_mtx); -	/* -	 * Allocate the constraints data on the first call to add_request, -	 * i.e. only if the data is not already allocated and if the device has -	 * not been removed -	 */ -	if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT) -		ret = dev_pm_qos_constraints_allocate(dev); +	if (!dev->power.constraints) { +		if (dev->power.power_state.event == PM_EVENT_INVALID) { +			/* The device has been removed from the system. */ +			req->dev = NULL; +			ret = -ENODEV; +			goto out; +		} else { +			/* +			 * Allocate the constraints data on the first call to +			 * add_request, i.e. only if the data is not already +			 * allocated and if the device has not been removed. +			 */ +			ret = dev_pm_qos_constraints_allocate(dev); +		} +	}  	if (!ret)  		ret = apply_constraint(req, PM_QOS_ADD_REQ, value); -out: + out:  	mutex_unlock(&dev_pm_qos_mtx); +  	return ret;  }  EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); @@ -252,7 +278,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req,  	mutex_lock(&dev_pm_qos_mtx); -	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { +	if (req->dev->power.constraints) {  		if (new_value != req->node.prio)  			ret = apply_constraint(req, PM_QOS_UPDATE_REQ,  					       new_value); @@ -293,7 +319,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)  	mutex_lock(&dev_pm_qos_mtx); -	if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { +	if (req->dev->power.constraints) {  		ret = apply_constraint(req, PM_QOS_REMOVE_REQ,  				       PM_QOS_DEFAULT_VALUE);  		memset(req, 0, sizeof(*req)); @@ -323,15 +349,12 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)  	mutex_lock(&dev_pm_qos_mtx); -	/* Silently return if the device has been removed */ -	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED) -		goto out; - -	retval = blocking_notifier_chain_register( -			dev->power.constraints->notifiers, -			notifier); +	/* Silently return if the constraints object is not present. */ +	if (dev->power.constraints) +		retval = blocking_notifier_chain_register( +				dev->power.constraints->notifiers, +				notifier); -out:  	mutex_unlock(&dev_pm_qos_mtx);  	return retval;  } @@ -354,15 +377,12 @@ int dev_pm_qos_remove_notifier(struct device *dev,  	mutex_lock(&dev_pm_qos_mtx); -	/* Silently return if the device has been removed */ -	if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED) -		goto out; - -	retval = blocking_notifier_chain_unregister( -			dev->power.constraints->notifiers, -			notifier); +	/* Silently return if the constraints object is not present. */ +	if (dev->power.constraints) +		retval = blocking_notifier_chain_unregister( +				dev->power.constraints->notifiers, +				notifier); -out:  	mutex_unlock(&dev_pm_qos_mtx);  	return retval;  }  |