diff mbox

[2/2] PM / Qos: Add notifier for pm qos flags

Message ID 1351766846-24654-2-git-send-email-tianyu.lan@intel.com (mailing list archive)
State Rejected, archived
Headers show

Commit Message

lan,Tianyu Nov. 1, 2012, 10:47 a.m. UTC
If pm qos flags was changed, device driver would take different
action according flags. E.g NO_POWER_OFF flag, if it was set when
a device had been power off, device driver should power on the device,
resume it and suspend it again without not power off. So device driver
should be notified. This patch is to add notifier for pm qos flags and
notify device when flags is changed.

Signed-off-by: Lan Tianyu <tianyu.lan@intel.com>
---
 drivers/base/power/qos.c |   86 +++++++++++++++++++++++++++++++++++++---------
 include/linux/pm_qos.h   |    7 ++--
 2 files changed, 75 insertions(+), 18 deletions(-)

Comments

Rafael Wysocki Nov. 1, 2012, 2:26 p.m. UTC | #1
On Thursday, November 01, 2012 06:47:26 PM Lan Tianyu wrote:
> If pm qos flags was changed, device driver would take different
> action according flags. E.g NO_POWER_OFF flag, if it was set when
> a device had been power off, device driver should power on the device,
> resume it and suspend it again without not power off. So device driver
> should be notified. This patch is to add notifier for pm qos flags and
> notify device when flags is changed.
> 
> Signed-off-by: Lan Tianyu <tianyu.lan@intel.com>

I haven't added notifiers for PM QoS flags on purpose, so I'm not going
to apply this patch.

Instead, it is required that callers of dev_pm_qos_update_request() for
DEV_PM_QOS_FLAGS type of requests ensure that the device is not RPM_SUSPENDED
when calling that routine (please note that the user interface calls
pm_runtime_get_sync() on the device before changing its flags).  Well, I think
I should add that to the kerneldoc comment of dev_pm_qos_update_request().

Moreover, I'm planning to remove the notifiers from the device PM QoS code
entirely and change the locking so that dev_pm_qos_update_request() and
friends may be called from interrupt context (which is necessary for some
use cases).

Thanks,
Rafael


> ---
>  drivers/base/power/qos.c |   86 +++++++++++++++++++++++++++++++++++++---------
>  include/linux/pm_qos.h   |    7 ++--
>  2 files changed, 75 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
> index 31d3f48..c37f52f 100644
> --- a/drivers/base/power/qos.c
> +++ b/drivers/base/power/qos.c
> @@ -149,6 +149,10 @@ static int apply_constraint(struct dev_pm_qos_request *req,
>  	case DEV_PM_QOS_FLAGS:
>  		ret = pm_qos_update_flags(&qos->flags, &req->data.flr,
>  					  action, value);
> +		if (ret)
> +			blocking_notifier_call_chain(qos->flags.notifiers,
> +				(unsigned long)qos->flags.effective_flags,
> +				req);
>  		break;
>  	default:
>  		ret = -EINVAL;
> @@ -168,26 +172,38 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
>  {
>  	struct dev_pm_qos *qos;
>  	struct pm_qos_constraints *c;
> -	struct blocking_notifier_head *n;
> +	struct pm_qos_flags	*flags;
> +	struct blocking_notifier_head *constraints_notify;
> +	struct blocking_notifier_head *flags_notify;
> +	int ret = 0;
>  
>  	qos = kzalloc(sizeof(*qos), GFP_KERNEL);
>  	if (!qos)
>  		return -ENOMEM;
>  
> -	n = kzalloc(sizeof(*n), GFP_KERNEL);
> -	if (!n) {
> -		kfree(qos);
> -		return -ENOMEM;
> +	constraints_notify = kzalloc(sizeof(*constraints_notify), GFP_KERNEL);
> +	if (!constraints_notify) {
> +		ret = -ENOMEM;
> +		goto alloc_constraints_fail;
> +	}
> +	BLOCKING_INIT_NOTIFIER_HEAD(constraints_notify);
> +
> +	flags_notify = kzalloc(sizeof(*flags_notify), GFP_KERNEL);
> +	if (!flags_notify) {
> +		ret = -ENOMEM;
> +		goto alloc_flags_fail;
>  	}
> -	BLOCKING_INIT_NOTIFIER_HEAD(n);
> +	BLOCKING_INIT_NOTIFIER_HEAD(flags_notify);
>  
>  	c = &qos->latency;
>  	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;
> +	c->notifiers = constraints_notify;
>  
> +	flags = &qos->flags;
> +	flags->notifiers = flags_notify;
>  	INIT_LIST_HEAD(&qos->flags.list);
>  
>  	spin_lock_irq(&dev->power.lock);
> @@ -195,6 +211,12 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
>  	spin_unlock_irq(&dev->power.lock);
>  
>  	return 0;
> +
> +alloc_flags_fail:
> +	kfree(constraints_notify);
> +alloc_constraints_fail:
> +	kfree(qos);
> +	return ret;
>  }
>  
>  /**
> @@ -432,6 +454,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
>   *
>   * @dev: target device for the constraint
>   * @notifier: notifier block managed by caller.
> + * @type: type of notifier
>   *
>   * Will register the notifier into a notification chain that gets called
>   * upon changes to the target value for the device.
> @@ -439,9 +462,11 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
>   * If the device's constraints object doesn't exist when this routine is called,
>   * it will be created (or error code will be returned if that fails).
>   */
> -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,
> +		enum dev_pm_qos_req_type type)
>  {
>  	int ret = 0;
> +	struct blocking_notifier_head *notifiers_head = NULL;
>  
>  	mutex_lock(&dev_pm_qos_mtx);
>  
> @@ -449,9 +474,22 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
>  		ret = dev->power.power_state.event != PM_EVENT_INVALID ?
>  			dev_pm_qos_constraints_allocate(dev) : -ENODEV;
>  
> -	if (!ret)
> -		ret = blocking_notifier_chain_register(
> -				dev->power.qos->latency.notifiers, notifier);
> +	if (!ret) {
> +		switch (type) {
> +		case DEV_PM_QOS_LATENCY:
> +			notifiers_head = dev->power.qos->latency.notifiers;
> +			break;
> +		case DEV_PM_QOS_FLAGS:
> +			notifiers_head = dev->power.qos->flags.notifiers;
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		if (notifiers_head)
> +			ret = blocking_notifier_chain_register(
> +					notifiers_head, notifier);
> +	}
>  
>  	mutex_unlock(&dev_pm_qos_mtx);
>  	return ret;
> @@ -464,22 +502,38 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
>   *
>   * @dev: target device for the constraint
>   * @notifier: notifier block to be removed.
> + * @type: type of notifier
>   *
>   * Will remove the notifier from the notification chain that gets called
>   * upon changes to the target value.
>   */
>  int dev_pm_qos_remove_notifier(struct device *dev,
> -			       struct notifier_block *notifier)
> +		struct notifier_block *notifier, enum dev_pm_qos_req_type type)
>  {
>  	int retval = 0;
> +	struct blocking_notifier_head *notifiers_head = NULL;
> +
>  
>  	mutex_lock(&dev_pm_qos_mtx);
>  
>  	/* Silently return if the constraints object is not present. */
> -	if (dev->power.qos)
> -		retval = blocking_notifier_chain_unregister(
> -				dev->power.qos->latency.notifiers,
> -				notifier);
> +	if (dev->power.qos) {
> +		switch (type) {
> +		case DEV_PM_QOS_LATENCY:
> +			notifiers_head = dev->power.qos->latency.notifiers;
> +			break;
> +		case DEV_PM_QOS_FLAGS:
> +			notifiers_head = dev->power.qos->flags.notifiers;
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		if (notifiers_head)
> +			retval = blocking_notifier_chain_unregister(
> +					notifiers_head,
> +					notifier);
> +	}
>  
>  	mutex_unlock(&dev_pm_qos_mtx);
>  	return retval;
> diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
> index 5a95013..479da6c 100644
> --- a/include/linux/pm_qos.h
> +++ b/include/linux/pm_qos.h
> @@ -84,6 +84,7 @@ struct pm_qos_constraints {
>  struct pm_qos_flags {
>  	struct list_head list;
>  	s32 effective_flags;	/* Do not change to 64 bit */
> +	struct blocking_notifier_head *notifiers;
>  };
>  
>  struct dev_pm_qos {
> @@ -134,9 +135,11 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
>  int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
>  int dev_pm_qos_remove_request(struct dev_pm_qos_request *req);
>  int dev_pm_qos_add_notifier(struct device *dev,
> -			    struct notifier_block *notifier);
> +			    struct notifier_block *notifier,
> +			    enum dev_pm_qos_req_type type);
>  int dev_pm_qos_remove_notifier(struct device *dev,
> -			       struct notifier_block *notifier);
> +				struct notifier_block *notifier,
> +			    enum dev_pm_qos_req_type type);
>  int dev_pm_qos_add_global_notifier(struct notifier_block *notifier);
>  int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier);
>  void dev_pm_qos_constraints_init(struct device *dev);
>
diff mbox

Patch

diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 31d3f48..c37f52f 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -149,6 +149,10 @@  static int apply_constraint(struct dev_pm_qos_request *req,
 	case DEV_PM_QOS_FLAGS:
 		ret = pm_qos_update_flags(&qos->flags, &req->data.flr,
 					  action, value);
+		if (ret)
+			blocking_notifier_call_chain(qos->flags.notifiers,
+				(unsigned long)qos->flags.effective_flags,
+				req);
 		break;
 	default:
 		ret = -EINVAL;
@@ -168,26 +172,38 @@  static int dev_pm_qos_constraints_allocate(struct device *dev)
 {
 	struct dev_pm_qos *qos;
 	struct pm_qos_constraints *c;
-	struct blocking_notifier_head *n;
+	struct pm_qos_flags	*flags;
+	struct blocking_notifier_head *constraints_notify;
+	struct blocking_notifier_head *flags_notify;
+	int ret = 0;
 
 	qos = kzalloc(sizeof(*qos), GFP_KERNEL);
 	if (!qos)
 		return -ENOMEM;
 
-	n = kzalloc(sizeof(*n), GFP_KERNEL);
-	if (!n) {
-		kfree(qos);
-		return -ENOMEM;
+	constraints_notify = kzalloc(sizeof(*constraints_notify), GFP_KERNEL);
+	if (!constraints_notify) {
+		ret = -ENOMEM;
+		goto alloc_constraints_fail;
+	}
+	BLOCKING_INIT_NOTIFIER_HEAD(constraints_notify);
+
+	flags_notify = kzalloc(sizeof(*flags_notify), GFP_KERNEL);
+	if (!flags_notify) {
+		ret = -ENOMEM;
+		goto alloc_flags_fail;
 	}
-	BLOCKING_INIT_NOTIFIER_HEAD(n);
+	BLOCKING_INIT_NOTIFIER_HEAD(flags_notify);
 
 	c = &qos->latency;
 	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;
+	c->notifiers = constraints_notify;
 
+	flags = &qos->flags;
+	flags->notifiers = flags_notify;
 	INIT_LIST_HEAD(&qos->flags.list);
 
 	spin_lock_irq(&dev->power.lock);
@@ -195,6 +211,12 @@  static int dev_pm_qos_constraints_allocate(struct device *dev)
 	spin_unlock_irq(&dev->power.lock);
 
 	return 0;
+
+alloc_flags_fail:
+	kfree(constraints_notify);
+alloc_constraints_fail:
+	kfree(qos);
+	return ret;
 }
 
 /**
@@ -432,6 +454,7 @@  EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
  *
  * @dev: target device for the constraint
  * @notifier: notifier block managed by caller.
+ * @type: type of notifier
  *
  * Will register the notifier into a notification chain that gets called
  * upon changes to the target value for the device.
@@ -439,9 +462,11 @@  EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
  * If the device's constraints object doesn't exist when this routine is called,
  * it will be created (or error code will be returned if that fails).
  */
-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,
+		enum dev_pm_qos_req_type type)
 {
 	int ret = 0;
+	struct blocking_notifier_head *notifiers_head = NULL;
 
 	mutex_lock(&dev_pm_qos_mtx);
 
@@ -449,9 +474,22 @@  int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
 		ret = dev->power.power_state.event != PM_EVENT_INVALID ?
 			dev_pm_qos_constraints_allocate(dev) : -ENODEV;
 
-	if (!ret)
-		ret = blocking_notifier_chain_register(
-				dev->power.qos->latency.notifiers, notifier);
+	if (!ret) {
+		switch (type) {
+		case DEV_PM_QOS_LATENCY:
+			notifiers_head = dev->power.qos->latency.notifiers;
+			break;
+		case DEV_PM_QOS_FLAGS:
+			notifiers_head = dev->power.qos->flags.notifiers;
+			break;
+		default:
+			break;
+		}
+
+		if (notifiers_head)
+			ret = blocking_notifier_chain_register(
+					notifiers_head, notifier);
+	}
 
 	mutex_unlock(&dev_pm_qos_mtx);
 	return ret;
@@ -464,22 +502,38 @@  EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
  *
  * @dev: target device for the constraint
  * @notifier: notifier block to be removed.
+ * @type: type of notifier
  *
  * Will remove the notifier from the notification chain that gets called
  * upon changes to the target value.
  */
 int dev_pm_qos_remove_notifier(struct device *dev,
-			       struct notifier_block *notifier)
+		struct notifier_block *notifier, enum dev_pm_qos_req_type type)
 {
 	int retval = 0;
+	struct blocking_notifier_head *notifiers_head = NULL;
+
 
 	mutex_lock(&dev_pm_qos_mtx);
 
 	/* Silently return if the constraints object is not present. */
-	if (dev->power.qos)
-		retval = blocking_notifier_chain_unregister(
-				dev->power.qos->latency.notifiers,
-				notifier);
+	if (dev->power.qos) {
+		switch (type) {
+		case DEV_PM_QOS_LATENCY:
+			notifiers_head = dev->power.qos->latency.notifiers;
+			break;
+		case DEV_PM_QOS_FLAGS:
+			notifiers_head = dev->power.qos->flags.notifiers;
+			break;
+		default:
+			break;
+		}
+
+		if (notifiers_head)
+			retval = blocking_notifier_chain_unregister(
+					notifiers_head,
+					notifier);
+	}
 
 	mutex_unlock(&dev_pm_qos_mtx);
 	return retval;
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 5a95013..479da6c 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -84,6 +84,7 @@  struct pm_qos_constraints {
 struct pm_qos_flags {
 	struct list_head list;
 	s32 effective_flags;	/* Do not change to 64 bit */
+	struct blocking_notifier_head *notifiers;
 };
 
 struct dev_pm_qos {
@@ -134,9 +135,11 @@  int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
 int dev_pm_qos_remove_request(struct dev_pm_qos_request *req);
 int dev_pm_qos_add_notifier(struct device *dev,
-			    struct notifier_block *notifier);
+			    struct notifier_block *notifier,
+			    enum dev_pm_qos_req_type type);
 int dev_pm_qos_remove_notifier(struct device *dev,
-			       struct notifier_block *notifier);
+				struct notifier_block *notifier,
+			    enum dev_pm_qos_req_type type);
 int dev_pm_qos_add_global_notifier(struct notifier_block *notifier);
 int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier);
 void dev_pm_qos_constraints_init(struct device *dev);