@@ -8,6 +8,12 @@
*
* This QoS design is best effort based. Dependents register their QoS needs.
* Watchers register to keep track of the current QoS needs of the system.
+ * Watchers can register different types of notification callbacks:
+ * . a per-device notification callback using the dev_pm_qos_*_notifier API.
+ * The notification chain data is stored in the per-device constraint
+ * data struct.
+ * . 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
@@ -41,6 +47,7 @@
#include <linux/kernel.h>
+static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
static void dev_pm_qos_constraints_allocate(struct device *dev);
int dev_pm_qos_request_active(struct dev_pm_qos_request *req)
@@ -64,6 +71,8 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_request_active);
void dev_pm_qos_add_request(struct dev_pm_qos_request *req, struct device *dev,
s32 value)
{
+ int ret, curr_value;
+
if (!req) /*guard against callers passing in null */
return;
@@ -82,8 +91,19 @@ void dev_pm_qos_add_request(struct dev_pm_qos_request *req, struct device *dev,
if (req->dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
return;
- pm_qos_update_target(dev->power.constraints,
- &req->node, PM_QOS_ADD_REQ, value);
+ /*
+ * Update constraints list and call the per-device callbacks if needed
+ */
+ ret = pm_qos_update_target(dev->power.constraints,
+ &req->node, PM_QOS_ADD_REQ, value);
+
+ if (ret) {
+ /* Call the global callbacks if needed */
+ curr_value = pm_qos_read_value(req->dev->power.constraints);
+ blocking_notifier_call_chain(&dev_pm_notifiers,
+ (unsigned long)curr_value,
+ req);
+ }
}
EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
@@ -98,8 +118,10 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
* Attempts are made to make this code callable on hot code paths.
*/
void dev_pm_qos_update_request(struct dev_pm_qos_request *req,
- s32 new_value)
+ s32 new_value)
{
+ int ret, curr_value;
+
if (!req) /*guard against callers passing in null */
return;
@@ -113,10 +135,23 @@ void dev_pm_qos_update_request(struct dev_pm_qos_request *req,
if (req->dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
return;
- if (new_value != req->node.prio)
- pm_qos_update_target(
- req->dev->power.constraints,
- &req->node, PM_QOS_UPDATE_REQ, new_value);
+ if (new_value != req->node.prio) {
+ /*
+ * Update constraints list and call the per-device callbacks
+ * if needed
+ */
+ ret = pm_qos_update_target(req->dev->power.constraints,
+ &req->node, PM_QOS_UPDATE_REQ,
+ new_value);
+ if (ret) {
+ /* Call the global callbacks if needed */
+ curr_value = pm_qos_read_value(
+ req->dev->power.constraints);
+ blocking_notifier_call_chain(&dev_pm_notifiers,
+ (unsigned long)curr_value,
+ req);
+ }
+ }
}
EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
@@ -129,6 +164,8 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
*/
void dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
{
+ int ret, curr_value;
+
if (!req) /*guard against callers passing in null */
return;
@@ -142,9 +179,20 @@ void dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
if (req->dev->power.constraints_state != DEV_PM_QOS_ALLOCATED)
return;
- pm_qos_update_target(req->dev->power.constraints,
- &req->node, PM_QOS_REMOVE_REQ,
- PM_QOS_DEFAULT_VALUE);
+ /*
+ * Update constraints list and call the per-device callbacks if needed
+ */
+ ret = pm_qos_update_target(req->dev->power.constraints,
+ &req->node, PM_QOS_REMOVE_REQ,
+ PM_QOS_DEFAULT_VALUE);
+
+ if (ret) {
+ /* Call the global callbacks if needed */
+ curr_value = pm_qos_read_value(req->dev->power.constraints);
+ blocking_notifier_call_chain(&dev_pm_notifiers,
+ (unsigned long)curr_value,
+ req);
+ }
memset(req, 0, sizeof(*req));
}
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
@@ -202,6 +250,36 @@ int dev_pm_qos_remove_notifier(struct device *dev,
}
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
+/**
+ * dev_pm_qos_add_global_notifier - sets notification entry for changes to
+ * target value of the PM QoS constraints for any device
+ *
+ * @notifier: notifier block managed by caller.
+ *
+ * Will register the notifier into a notification chain that gets called
+ * upon changes to the target value for any device.
+ */
+int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
+{
+ return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
+
+/**
+ * dev_pm_qos_remove_global_notifier - deletes notification for changes to
+ * target value of PM QoS constraints for any device
+ *
+ * @notifier: notifier block to be removed.
+ *
+ * Will remove the notifier from the notification chain that gets called
+ * upon changes to the target value for any device.
+ */
+int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
+{
+ return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
+}
+EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
+
/* Called at the first call to add_request, for constraint data allocation */
static void dev_pm_qos_constraints_allocate(struct device *dev)
{
@@ -239,6 +317,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
{
struct dev_pm_qos_request *req, *tmp;
enum dev_pm_qos_state constraints_state = dev->power.constraints_state;
+ int ret = 0, curr_value;
dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE;
if (constraints_state == DEV_PM_QOS_ALLOCATED) {
@@ -250,9 +329,18 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
* Update constraints list and call the per-device
* callbacks if needed
*/
- pm_qos_update_target(req->dev->power.constraints,
- &req->node, PM_QOS_REMOVE_REQ,
- PM_QOS_DEFAULT_VALUE);
+ ret |= pm_qos_update_target(req->dev->power.constraints,
+ &req->node,
+ PM_QOS_REMOVE_REQ,
+ PM_QOS_DEFAULT_VALUE);
+
+ if (ret) {
+ /* Call the global callbacks if needed */
+ curr_value = dev->power.constraints->default_value;
+ blocking_notifier_call_chain(&dev_pm_notifiers,
+ (unsigned long)curr_value,
+ req);
+ }
kfree(dev->power.constraints->notifiers);
kfree(dev->power.constraints);
@@ -70,6 +70,7 @@ int pm_qos_request(int pm_qos_class);
int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_request_active(struct pm_qos_request *req);
+s32 pm_qos_read_value(struct pm_qos_constraints *c);
int dev_pm_qos_request_active(struct dev_pm_qos_request *req);
void dev_pm_qos_add_request(struct dev_pm_qos_request *req, struct device *dev,
@@ -80,6 +81,8 @@ int dev_pm_qos_add_notifier(struct device *dev,
struct notifier_block *notifier);
int dev_pm_qos_remove_notifier(struct device *dev,
struct notifier_block *notifier);
+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);
void dev_pm_qos_constraints_destroy(struct device *dev);
#else
@@ -107,6 +110,8 @@ static inline int pm_qos_remove_notifier(int pm_qos_class,
{ return 0; }
static inline int pm_qos_request_active(struct pm_qos_request *req)
{ return 0; }
+static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
+ { return 0; }
static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req)
{ return 0; }
@@ -124,6 +129,12 @@ static inline int dev_pm_qos_add_notifier(struct device *dev,
static inline int dev_pm_qos_remove_notifier(struct device *dev,
struct notifier_block *notifier)
{ return 0; }
+static inline int dev_pm_qos_add_global_notifier(
+ struct notifier_block *notifier)
+ { return 0; }
+static inline int dev_pm_qos_remove_global_notifier(
+ struct notifier_block *notifier)
+ { return 0; }
static inline void dev_pm_qos_constraints_init(struct device *dev)
{ return; }
static inline void dev_pm_qos_constraints_destroy(struct device *dev)
@@ -139,7 +139,7 @@ static inline int pm_qos_get_value(struct pm_qos_constraints *c)
}
}
-static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
+s32 pm_qos_read_value(struct pm_qos_constraints *c)
{
return c->target_value;
}