Register a DEV_PM_QOS_RESUME_LATENCY notifier for each CPU device and
bump only that CPU's latency_req generation when its resume latency
constraint changes.

Signed-off-by: Yaxiong Tian <[email protected]>
---
 drivers/cpuidle/cpuidle.c  | 15 +++++++++---
 drivers/cpuidle/cpuidle.h  |  2 ++
 drivers/cpuidle/governor.c | 48 +++++++++++++++++++++++++++++++++++++-
 3 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 2d2f40a2cb81..9495f904fe85 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -613,6 +613,8 @@ static void __cpuidle_unregister_device(struct 
cpuidle_device *dev)
 {
        struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
 
+       cpuidle_latency_req_notifier_unregister(dev->cpu);
+
        list_del(&dev->device_list);
        per_cpu(cpuidle_devices, dev->cpu) = NULL;
        module_put(drv->owner);
@@ -661,10 +663,17 @@ static int __cpuidle_register_device(struct 
cpuidle_device *dev)
 
        ret = cpuidle_coupled_register_device(dev);
        if (ret)
-               __cpuidle_unregister_device(dev);
-       else
-               dev->registered = 1;
+               goto unreg;
+
+       ret = cpuidle_latency_req_notifier_register(cpu);
+       if (ret)
+               goto unreg;
 
+       dev->registered = 1;
+       return 0;
+
+unreg:
+       __cpuidle_unregister_device(dev);
        return ret;
 }
 
diff --git a/drivers/cpuidle/cpuidle.h b/drivers/cpuidle/cpuidle.h
index 52701d9588f1..151fa9ebe483 100644
--- a/drivers/cpuidle/cpuidle.h
+++ b/drivers/cpuidle/cpuidle.h
@@ -25,6 +25,8 @@ extern void cpuidle_uninstall_idle_handler(void);
 /* governors */
 extern struct cpuidle_governor *cpuidle_find_governor(const char *str);
 extern int cpuidle_switch_governor(struct cpuidle_governor *gov);
+int cpuidle_latency_req_notifier_register(unsigned int cpu);
+void cpuidle_latency_req_notifier_unregister(unsigned int cpu);
 
 /* sysfs */
 
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index bc4a70d30c34..d286ccf19a69 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -27,10 +27,18 @@ struct cpuidle_governor *cpuidle_prev_governor;
 
 /*
  * Per-CPU generation bumped to invalidate that CPU's cached latency
- * constraint.  Consumers of the generation are added in later changes.
+ * constraint.  Global QoS changes invalidate every CPU; per-CPU resume
+ * latency changes invalidate only the affected CPU.
  */
 static DEFINE_PER_CPU(atomic_t, latency_req_gen);
 
+struct cpuidle_cpu_qos_nb {
+       struct notifier_block nb;
+       unsigned int cpu;
+};
+
+static DEFINE_PER_CPU(struct cpuidle_cpu_qos_nb, cpuidle_cpu_qos_nb);
+
 static void cpuidle_latency_req_invalidate_cpu(unsigned int cpu)
 {
        atomic_inc(per_cpu_ptr(&latency_req_gen, cpu));
@@ -61,6 +69,44 @@ static struct notifier_block cpuidle_wakeup_qos_nb = {
 };
 #endif
 
+static int cpuidle_cpu_qos_notify(struct notifier_block *nb,
+                                unsigned long action, void *data)
+{
+       struct cpuidle_cpu_qos_nb *qos_nb =
+               container_of(nb, struct cpuidle_cpu_qos_nb, nb);
+
+       cpuidle_latency_req_invalidate_cpu(qos_nb->cpu);
+       return NOTIFY_OK;
+}
+
+int cpuidle_latency_req_notifier_register(unsigned int cpu)
+{
+       struct device *device = get_cpu_device(cpu);
+       struct cpuidle_cpu_qos_nb *qos_nb =
+               per_cpu_ptr(&cpuidle_cpu_qos_nb, cpu);
+
+       if (!device)
+               return -ENODEV;
+
+       qos_nb->cpu = cpu;
+       qos_nb->nb.notifier_call = cpuidle_cpu_qos_notify;
+       return dev_pm_qos_add_notifier(device, &qos_nb->nb,
+                                      DEV_PM_QOS_RESUME_LATENCY);
+}
+
+void cpuidle_latency_req_notifier_unregister(unsigned int cpu)
+{
+       struct device *device = get_cpu_device(cpu);
+       struct cpuidle_cpu_qos_nb *qos_nb =
+               per_cpu_ptr(&cpuidle_cpu_qos_nb, cpu);
+
+       if (!device)
+               return;
+
+       dev_pm_qos_remove_notifier(device, &qos_nb->nb,
+                                  DEV_PM_QOS_RESUME_LATENCY);
+}
+
 static int __init cpuidle_latency_req_init(void)
 {
        int ret;
-- 
2.43.0


Reply via email to