From: florian on
This allows for atomic notifications.

This is unecessary at the moment, but I had it flying around and it may be
necessary to have in some future scenarios.

The atomic listeners get called for _every_ change
of the target value, whereas the blocking notifiers, when called from
interrupt context, may be "folded" to notify just once.

Signed-off-by: Florian Mickler <florian(a)mickler.org>
---
include/linux/pm_qos_params.h | 2 +
kernel/pm_qos_params.c | 61 ++++++++++++++++++++++++++++++++++-------
2 files changed, 53 insertions(+), 10 deletions(-)

diff --git a/include/linux/pm_qos_params.h b/include/linux/pm_qos_params.h
index fdd8a78..4c0d209 100644
--- a/include/linux/pm_qos_params.h
+++ b/include/linux/pm_qos_params.h
@@ -30,6 +30,8 @@ void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req);

int pm_qos_request(int pm_qos_class);
int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
+int pm_qos_add_atomic_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_list *req);

diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c
index 640c367..fbd0a6d 100644
--- a/kernel/pm_qos_params.c
+++ b/kernel/pm_qos_params.c
@@ -57,6 +57,7 @@ enum pm_qos_type {
struct pm_qos_object {
struct plist_head requests;
struct blocking_notifier_head notifiers;
+ struct atomic_notifier_head atomic_notifiers;
struct miscdevice pm_qos_power_miscdev;
struct work_struct notify;
char *name;
@@ -73,9 +74,14 @@ static struct pm_qos_object pm_qos_objects[] = {
{
.requests = PLIST_HEAD_INIT(
pm_qos_objects[PM_QOS_CPU_DMA_LATENCY].requests,
- pm_qos_lock),
+ pm_qos_lock
+ ),
.notifiers = BLOCKING_NOTIFIER_INIT(
- pm_qos_objects[PM_QOS_CPU_DMA_LATENCY].notifiers),
+ pm_qos_objects[PM_QOS_CPU_DMA_LATENCY].notifiers
+ ),
+ .atomic_notifiers = ATOMIC_NOTIFIER_INIT(
+ pm_qos_objects[PM_QOS_CPU_DMA_LATENCY].atomic_notifiers
+ ),
.notify = __WORK_INITIALIZER(
pm_qos_objects[PM_QOS_CPU_DMA_LATENCY].notify,
update_notify),
@@ -91,6 +97,9 @@ static struct pm_qos_object pm_qos_objects[] = {
.notifiers = BLOCKING_NOTIFIER_INIT(
pm_qos_objects[PM_QOS_NETWORK_LATENCY].notifiers
),
+ .atomic_notifiers = ATOMIC_NOTIFIER_INIT(
+ pm_qos_objects[PM_QOS_NETWORK_LATENCY].atomic_notifiers
+ ),
.notify = __WORK_INITIALIZER(
pm_qos_objects[PM_QOS_NETWORK_LATENCY].notify,
update_notify
@@ -107,6 +116,9 @@ static struct pm_qos_object pm_qos_objects[] = {
.notifiers = BLOCKING_NOTIFIER_INIT(
pm_qos_objects[PM_QOS_NETWORK_THROUGHPUT].notifiers
),
+ .atomic_notifiers = ATOMIC_NOTIFIER_INIT(
+ pm_qos_objects[PM_QOS_NETWORK_THROUGHPUT].atomic_notifiers
+ ),
.notify = __WORK_INITIALIZER(
pm_qos_objects[PM_QOS_NETWORK_THROUGHPUT].notify,
update_notify
@@ -147,14 +159,15 @@ static inline int pm_qos_get_value(struct pm_qos_object *o)
}
}

-static void call_notifiers(struct pm_qos_object *o, unsigned long val)
+static void call_notifiers(struct pm_qos_object *o, unsigned long curr_value)
{

+ atomic_notifier_call_chain(&o->atomic_notifiers, curr_value, NULL);
+
if (in_interrupt())
schedule_work(&o->notify);
else
- blocking_notifier_call_chain(&o->notifiers, val,
- NULL);
+ blocking_notifier_call_chain(&o->notifiers, curr_value, NULL);


}
@@ -361,13 +374,14 @@ EXPORT_SYMBOL_GPL(pm_qos_remove_request);

/**
* pm_qos_add_notifier - sets notification entry for changes to target value
- * @pm_qos_class: identifies which qos target changes should be notified.
+ * @pm_qos_class: identifies which qos target changes should trigger
+ * notifications.
* @notifier: notifier block managed by caller.
*
* Will register the notifier into a notification chain that gets called
* upon changes to the pm_qos_class target value.
*/
-int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
+int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *nb)
{
int retval;
if (!pm_qos_valid_class(pm_qos_class)) {
@@ -375,13 +389,37 @@ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
return -EINVAL;
}
retval = blocking_notifier_chain_register(
- &pm_qos_objects[pm_qos_class].notifiers, notifier);
+ &pm_qos_objects[pm_qos_class].notifiers, nb);

return retval;
}
EXPORT_SYMBOL_GPL(pm_qos_add_notifier);

/**
+ * pm_qos_add_atomic_notifier - sets notification entry for changes to target value
+ * @pm_qos_class: identifies which qos target changes should trigger
+ * notifications.
+ * @notifier: notifier block managed by caller.
+ *
+ * Will register the notifier into a notification chain that gets
+ * called upon changes to the pm_qos_class target value. The notifier
+ * may be called from atomic context. use @pm_qos_remove_notifier to
+ * unregister.
+ */
+int pm_qos_add_atomic_notifier(int pm_qos_class, struct notifier_block *nb)
+{
+ int ret = 0;
+ if (!pm_qos_valid_class(pm_qos_class)) {
+ WARN(1, KERN_ERR "pm_qos_add_atmoic_notifier called for unknown qos class\n");
+ ret = -EINVAL;
+ } else
+ ret = atomic_notifier_chain_register(
+ &pm_qos_objects[pm_qos_class].atomic_notifiers, nb);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pm_qos_add_atomic_notifier);
+
+/**
* pm_qos_remove_notifier - deletes notification entry from chain.
* @pm_qos_class: identifies which qos target changes are notified.
* @notifier: notifier block to be removed.
@@ -389,7 +427,7 @@ EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
* Will remove the notifier from the notification chain that gets called
* upon changes to the pm_qos_class target value.
*/
-int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
+int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *nb)
{
int retval;
if (!pm_qos_valid_class(pm_qos_class)) {
@@ -398,7 +436,10 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
}

retval = blocking_notifier_chain_unregister(
- &pm_qos_objects[pm_qos_class].notifiers, notifier);
+ &pm_qos_objects[pm_qos_class].notifiers, nb);
+ if (retval)
+ retval = atomic_notifier_chain_unregister(
+ &pm_qos_objects[pm_qos_class].atomic_notifiers, nb);

return retval;
}
--
1.7.1.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/