The patch below does not apply to the 6.12-stable tree. If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to stable@vger.kernel.org.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y git checkout FETCH_HEAD git cherry-pick -x 1ebe8f7e782523e62cd1fa8237f7afba5d1dae83 # <resolve conflicts, build, test, etc.> git commit -s git send-email --to 'stable@vger.kernel.org' --in-reply-to '2025101616-gigahertz-profane-b22c@gregkh' --subject-prefix 'PATCH 6.12.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 1ebe8f7e782523e62cd1fa8237f7afba5d1dae83 Mon Sep 17 00:00:00 2001 From: Christian Loehle christian.loehle@arm.com Date: Sun, 31 Aug 2025 22:43:57 +0100 Subject: [PATCH] PM: EM: Fix late boot with holes in CPU topology
Commit e3f1164fc9ee ("PM: EM: Support late CPUs booting and capacity adjustment") added a mechanism to handle CPUs that come up late by retrying when any of the `cpufreq_cpu_get()` call fails.
However, if there are holes in the CPU topology (offline CPUs, e.g. nosmt), the first missing CPU causes the loop to break, preventing subsequent online CPUs from being updated.
Instead of aborting on the first missing CPU policy, loop through all and retry if any were missing.
Fixes: e3f1164fc9ee ("PM: EM: Support late CPUs booting and capacity adjustment") Suggested-by: Kenneth Crudup kenneth.crudup@gmail.com Reported-by: Kenneth Crudup kenneth.crudup@gmail.com Link: https://lore.kernel.org/linux-pm/40212796-734c-4140-8a85-854f72b8144d@panix.... Cc: 6.9+ stable@vger.kernel.org # 6.9+ Signed-off-by: Christian Loehle christian.loehle@arm.com Link: https://patch.msgid.link/20250831214357.2020076-1-christian.loehle@arm.com [ rjw: Drop the new pr_debug() message which is not very useful ] Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com
diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index 8df55397414a..5f17d2e8e954 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -799,7 +799,7 @@ void em_adjust_cpu_capacity(unsigned int cpu) static void em_check_capacity_update(void) { cpumask_var_t cpu_done_mask; - int cpu; + int cpu, failed_cpus = 0;
if (!zalloc_cpumask_var(&cpu_done_mask, GFP_KERNEL)) { pr_warn("no free memory\n"); @@ -817,10 +817,8 @@ static void em_check_capacity_update(void)
policy = cpufreq_cpu_get(cpu); if (!policy) { - pr_debug("Accessing cpu%d policy failed\n", cpu); - schedule_delayed_work(&em_update_work, - msecs_to_jiffies(1000)); - break; + failed_cpus++; + continue; } cpufreq_cpu_put(policy);
@@ -835,6 +833,9 @@ static void em_check_capacity_update(void) em_adjust_new_capacity(cpu, dev, pd); }
+ if (failed_cpus) + schedule_delayed_work(&em_update_work, msecs_to_jiffies(1000)); + free_cpumask_var(cpu_done_mask); }
From: "Rafael J. Wysocki" rafael.j.wysocki@intel.com
[ Upstream commit 5fad775d432c6c9158ea12e7e00d8922ef8d3dfc ]
The max_cap parameter is never used in em_adjust_new_capacity(), so drop it.
No functional impact.
Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Reviewed-by: Lukasz Luba lukasz.luba@arm.com Link: https://patch.msgid.link/2369979.ElGaqSPkdT@rjwysocki.net Stable-dep-of: 1ebe8f7e7825 ("PM: EM: Fix late boot with holes in CPU topology") Signed-off-by: Sasha Levin sashal@kernel.org --- kernel/power/energy_model.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index 1c9fe741fe6d5..8ee72c6c1daf3 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -723,8 +723,7 @@ static int em_recalc_and_update(struct device *dev, struct em_perf_domain *pd, * are correctly calculated. */ static void em_adjust_new_capacity(struct device *dev, - struct em_perf_domain *pd, - u64 max_cap) + struct em_perf_domain *pd) { struct em_perf_table *em_table;
@@ -795,7 +794,7 @@ static void em_check_capacity_update(void) cpu, cpu_capacity, em_max_perf);
dev = get_cpu_device(cpu); - em_adjust_new_capacity(dev, pd, cpu_capacity); + em_adjust_new_capacity(dev, pd); }
free_cpumask_var(cpu_done_mask);
From: "Rafael J. Wysocki" rafael.j.wysocki@intel.com
[ Upstream commit a8e62726ac0dd7b610c87ba1a938a5a9091c34df ]
Every iteration of the loop over all possible CPUs in em_check_capacity_update() causes get_cpu_device() to be called twice for the same CPU, once indirectly via em_cpu_get() and once directly.
Get rid of the indirect get_cpu_device() call by moving the direct invocation of it earlier and using em_pd_get() instead of em_cpu_get() to get a pd pointer for the dev one returned by it.
This also exposes the fact that dev is needed to get a pd, so the code becomes somewhat easier to follow after it.
No functional impact.
Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Reviewed-by: Lukasz Luba lukasz.luba@arm.com Link: https://patch.msgid.link/1925950.tdWV9SEqCh@rjwysocki.net Stable-dep-of: 1ebe8f7e7825 ("PM: EM: Fix late boot with holes in CPU topology") Signed-off-by: Sasha Levin sashal@kernel.org --- kernel/power/energy_model.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index 8ee72c6c1daf3..a035b030ff734 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -769,7 +769,8 @@ static void em_check_capacity_update(void) } cpufreq_cpu_put(policy);
- pd = em_cpu_get(cpu); + dev = get_cpu_device(cpu); + pd = em_pd_get(dev); if (!pd || em_is_artificial(pd)) continue;
@@ -793,7 +794,6 @@ static void em_check_capacity_update(void) pr_debug("updating cpu%d cpu_cap=%lu old capacity=%lu\n", cpu, cpu_capacity, em_max_perf);
- dev = get_cpu_device(cpu); em_adjust_new_capacity(dev, pd); }
From: "Rafael J. Wysocki" rafael.j.wysocki@intel.com
[ Upstream commit 3e3ba654d3097e0031f2add215b12ff81c23814e ]
Move the check of the CPU capacity currently stored in the energy model against the arch_scale_cpu_capacity() value to em_adjust_new_capacity() so it will be done regardless of where the latter is called from.
This will be useful when a new em_adjust_new_capacity() caller is added subsequently.
While at it, move the pd local variable declaration in em_check_capacity_update() into the loop in which it is used.
No intentional functional impact.
Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Reviewed-by: Lukasz Luba lukasz.luba@arm.com Tested-by: Christian Loehle christian.loehle@arm.com Reviewed-by: Dietmar Eggemann dietmar.eggemann@arm.com Link: https://patch.msgid.link/7810787.EvYhyI6sBW@rjwysocki.net Stable-dep-of: 1ebe8f7e7825 ("PM: EM: Fix late boot with holes in CPU topology") Signed-off-by: Sasha Levin sashal@kernel.org --- kernel/power/energy_model.c | 40 ++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 23 deletions(-)
diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index a035b030ff734..2ef0a7d9d8405 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -722,10 +722,24 @@ static int em_recalc_and_update(struct device *dev, struct em_perf_domain *pd, * Adjustment of CPU performance values after boot, when all CPUs capacites * are correctly calculated. */ -static void em_adjust_new_capacity(struct device *dev, +static void em_adjust_new_capacity(unsigned int cpu, struct device *dev, struct em_perf_domain *pd) { + unsigned long cpu_capacity = arch_scale_cpu_capacity(cpu); struct em_perf_table *em_table; + struct em_perf_state *table; + unsigned long em_max_perf; + + rcu_read_lock(); + table = em_perf_state_from_pd(pd); + em_max_perf = table[pd->nr_perf_states - 1].performance; + rcu_read_unlock(); + + if (em_max_perf == cpu_capacity) + return; + + pr_debug("updating cpu%d cpu_cap=%lu old capacity=%lu\n", cpu, + cpu_capacity, em_max_perf);
em_table = em_table_dup(pd); if (!em_table) { @@ -741,9 +755,6 @@ static void em_adjust_new_capacity(struct device *dev, static void em_check_capacity_update(void) { cpumask_var_t cpu_done_mask; - struct em_perf_state *table; - struct em_perf_domain *pd; - unsigned long cpu_capacity; int cpu;
if (!zalloc_cpumask_var(&cpu_done_mask, GFP_KERNEL)) { @@ -754,7 +765,7 @@ static void em_check_capacity_update(void) /* Check if CPUs capacity has changed than update EM */ for_each_possible_cpu(cpu) { struct cpufreq_policy *policy; - unsigned long em_max_perf; + struct em_perf_domain *pd; struct device *dev;
if (cpumask_test_cpu(cpu, cpu_done_mask)) @@ -777,24 +788,7 @@ static void em_check_capacity_update(void) cpumask_or(cpu_done_mask, cpu_done_mask, em_span_cpus(pd));
- cpu_capacity = arch_scale_cpu_capacity(cpu); - - rcu_read_lock(); - table = em_perf_state_from_pd(pd); - em_max_perf = table[pd->nr_perf_states - 1].performance; - rcu_read_unlock(); - - /* - * Check if the CPU capacity has been adjusted during boot - * and trigger the update for new performance values. - */ - if (em_max_perf == cpu_capacity) - continue; - - pr_debug("updating cpu%d cpu_cap=%lu old capacity=%lu\n", - cpu, cpu_capacity, em_max_perf); - - em_adjust_new_capacity(dev, pd); + em_adjust_new_capacity(cpu, dev, pd); }
free_cpumask_var(cpu_done_mask);
From: Christian Loehle christian.loehle@arm.com
[ Upstream commit 1ebe8f7e782523e62cd1fa8237f7afba5d1dae83 ]
Commit e3f1164fc9ee ("PM: EM: Support late CPUs booting and capacity adjustment") added a mechanism to handle CPUs that come up late by retrying when any of the `cpufreq_cpu_get()` call fails.
However, if there are holes in the CPU topology (offline CPUs, e.g. nosmt), the first missing CPU causes the loop to break, preventing subsequent online CPUs from being updated.
Instead of aborting on the first missing CPU policy, loop through all and retry if any were missing.
Fixes: e3f1164fc9ee ("PM: EM: Support late CPUs booting and capacity adjustment") Suggested-by: Kenneth Crudup kenneth.crudup@gmail.com Reported-by: Kenneth Crudup kenneth.crudup@gmail.com Link: https://lore.kernel.org/linux-pm/40212796-734c-4140-8a85-854f72b8144d@panix.... Cc: 6.9+ stable@vger.kernel.org # 6.9+ Signed-off-by: Christian Loehle christian.loehle@arm.com Link: https://patch.msgid.link/20250831214357.2020076-1-christian.loehle@arm.com [ rjw: Drop the new pr_debug() message which is not very useful ] Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Signed-off-by: Sasha Levin sashal@kernel.org --- kernel/power/energy_model.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index 2ef0a7d9d8405..d839b564522f6 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -755,7 +755,7 @@ static void em_adjust_new_capacity(unsigned int cpu, struct device *dev, static void em_check_capacity_update(void) { cpumask_var_t cpu_done_mask; - int cpu; + int cpu, failed_cpus = 0;
if (!zalloc_cpumask_var(&cpu_done_mask, GFP_KERNEL)) { pr_warn("no free memory\n"); @@ -773,10 +773,8 @@ static void em_check_capacity_update(void)
policy = cpufreq_cpu_get(cpu); if (!policy) { - pr_debug("Accessing cpu%d policy failed\n", cpu); - schedule_delayed_work(&em_update_work, - msecs_to_jiffies(1000)); - break; + failed_cpus++; + continue; } cpufreq_cpu_put(policy);
@@ -791,6 +789,9 @@ static void em_check_capacity_update(void) em_adjust_new_capacity(cpu, dev, pd); }
+ if (failed_cpus) + schedule_delayed_work(&em_update_work, msecs_to_jiffies(1000)); + free_cpumask_var(cpu_done_mask); }
linux-stable-mirror@lists.linaro.org