* [PATCH 0/2] timers/migration: Fixes on top of per capacity hierarchies
@ 2026-05-19 22:09 Frederic Weisbecker
2026-05-19 22:09 ` [PATCH 1/2] timers/migration: Fix hotplug migrator selection target on asymetric capacity machines Frederic Weisbecker
2026-05-19 22:09 ` [PATCH 2/2] timers/migration: Deactivate per-capacity hierarchies under nohz_full Frederic Weisbecker
0 siblings, 2 replies; 8+ messages in thread
From: Frederic Weisbecker @ 2026-05-19 22:09 UTC (permalink / raw)
To: Thomas Gleixner; +Cc: LKML, Frederic Weisbecker, Anna-Maria Behnsen
Hi,
Looks like I overlooked a few things when I worked on making timer
migration capacity aware. Those patches apply on top of tip:timers/core
git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks.git
timers/fixes
HEAD: 2b343fb3e17b72d6c26c9cff29fe8e53493b4cb9
Thanks,
Frederic
---
Frederic Weisbecker (2):
timers/migration: Fix hotplug migrator selection target on asymetric capacity machines
timers/migration: Deactivate per-capacity hierarchies under nohz_full
kernel/time/timer_migration.c | 64 ++++++++++++++++++++++++++++++++++---------
1 file changed, 51 insertions(+), 13 deletions(-)
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/2] timers/migration: Fix hotplug migrator selection target on asymetric capacity machines
2026-05-19 22:09 [PATCH 0/2] timers/migration: Fixes on top of per capacity hierarchies Frederic Weisbecker
@ 2026-05-19 22:09 ` Frederic Weisbecker
2026-06-02 19:35 ` [tip: timers/core] " tip-bot2 for Frederic Weisbecker
[not found] ` <CGME20260608094538eucas1p1b87572e8b8af819ccb1537c38cd51957@eucas1p1.samsung.com>
2026-05-19 22:09 ` [PATCH 2/2] timers/migration: Deactivate per-capacity hierarchies under nohz_full Frederic Weisbecker
1 sibling, 2 replies; 8+ messages in thread
From: Frederic Weisbecker @ 2026-05-19 22:09 UTC (permalink / raw)
To: Thomas Gleixner; +Cc: LKML, Frederic Weisbecker, Anna-Maria Behnsen
When a top-level migrator is deactivated, either at CPU down hotplug
time or when a CPU is domain isolated, a new migrator is elected among
the available CPUs and woken up to take over the migration duty.
However that election must happen at the scope of a given hierarchy and
not globally, which the introduction of per-capacity hierarchies failed
to handle.
As a result a given hierarchy may end up without migrator to handle
global timers.
Fix it with making sure that the new migrator belongs to the same
hierarchy as the outgoing CPU.
Fixes: 098cbaad8e57 ("timers/migration: Split per-capacity hierarchies")
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
---
kernel/time/timer_migration.c | 42 ++++++++++++++++++++++++++---------
1 file changed, 32 insertions(+), 10 deletions(-)
diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c
index 25e3c563eb74..8032b0044f44 100644
--- a/kernel/time/timer_migration.c
+++ b/kernel/time/timer_migration.c
@@ -1464,6 +1464,18 @@ static long tmigr_trigger_active(void *unused)
return 0;
}
+static struct tmigr_hierarchy *__tmigr_get_hierarchy(unsigned int capacity)
+{
+ struct tmigr_hierarchy *iter;
+
+ list_for_each_entry(iter, &tmigr_hierarchy_list, node) {
+ if (iter->capacity == capacity)
+ return iter;
+ }
+
+ return NULL;
+}
+
static int tmigr_clear_cpu_available(unsigned int cpu)
{
struct tmigr_cpu *tmc = this_cpu_ptr(&tmigr_cpu);
@@ -1488,8 +1500,21 @@ static int tmigr_clear_cpu_available(unsigned int cpu)
}
if (firstexp != KTIME_MAX) {
- migrator = cpumask_any(tmigr_available_cpumask);
- work_on_cpu(migrator, tmigr_trigger_active, NULL);
+ struct tmigr_hierarchy *hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+
+ if (WARN_ON_ONCE(!hier))
+ return -EINVAL;
+
+ migrator = cpumask_any_and(tmigr_available_cpumask, hier->cpumask);
+ if (migrator < nr_cpu_ids) {
+ work_on_cpu(migrator, tmigr_trigger_active, NULL);
+ } else {
+ /*
+ * If deactivation returned an expiration, it belongs to an available
+ * nohz CPU in the hierarchy.
+ */
+ WARN_ONCE(1, "Expected available CPU in the hierarchy\n");
+ }
}
return 0;
@@ -1915,12 +1940,9 @@ static int tmigr_setup_groups(struct tmigr_hierarchy *hier, unsigned int cpu,
static struct tmigr_hierarchy *tmigr_get_hierarchy(unsigned int capacity)
{
- struct tmigr_hierarchy *hier = NULL, *iter;
+ struct tmigr_hierarchy *hier;
- list_for_each_entry(iter, &tmigr_hierarchy_list, node) {
- if (iter->capacity == capacity)
- hier = iter;
- }
+ hier = __tmigr_get_hierarchy(capacity);
if (hier)
return hier;
@@ -1978,9 +2000,9 @@ static long connect_old_root_work(void *arg)
struct tmigr_hierarchy *hier;
int cpu = smp_processor_id();
- hier = tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
- if (IS_ERR(hier))
- return PTR_ERR(hier);
+ hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+ if (WARN_ON_ONCE(!hier))
+ return -EINVAL;
return tmigr_connect_old_root(hier, cpu, old_root, true);
}
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/2] timers/migration: Deactivate per-capacity hierarchies under nohz_full
2026-05-19 22:09 [PATCH 0/2] timers/migration: Fixes on top of per capacity hierarchies Frederic Weisbecker
2026-05-19 22:09 ` [PATCH 1/2] timers/migration: Fix hotplug migrator selection target on asymetric capacity machines Frederic Weisbecker
@ 2026-05-19 22:09 ` Frederic Weisbecker
2026-06-02 19:35 ` [tip: timers/core] " tip-bot2 for Frederic Weisbecker
1 sibling, 1 reply; 8+ messages in thread
From: Frederic Weisbecker @ 2026-05-19 22:09 UTC (permalink / raw)
To: Thomas Gleixner; +Cc: LKML, Frederic Weisbecker, Anna-Maria Behnsen
Nohz_full CPUs global timers are guaranteed to be handled by the
timekeeper: that CPU never stops its tick and therefore remains active
in the hierarchy.
But since the introduction of per-capacity hierarchies, this guarantee
is broken because the timekeeper may not belong to the same hierarchy
as all the nohz_full CPUs.
Fix it with simply turning off capacity awareness when nohz_full is
running and force a single hierarchy. Nohz_full is not exactly optimized
powerwise anyway.
Fixes: 098cbaad8e57 ("timers/migration: Split per-capacity hierarchies")
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
---
kernel/time/timer_migration.c | 30 +++++++++++++++++++++++-------
1 file changed, 23 insertions(+), 7 deletions(-)
diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c
index 8032b0044f44..8ba53ad49173 100644
--- a/kernel/time/timer_migration.c
+++ b/kernel/time/timer_migration.c
@@ -1464,8 +1464,24 @@ static long tmigr_trigger_active(void *unused)
return 0;
}
-static struct tmigr_hierarchy *__tmigr_get_hierarchy(unsigned int capacity)
+static unsigned int tmigr_get_capacity(int cpu)
{
+ /*
+ * nohz_full CPUs need to make sure there is always an available (online)
+ * and never idle migrator to handle all their global timers. That duty
+ * is served by the timekeeper which then never stops its tick. But the
+ * timekeeper must then belong to the same hierarchy as all the nohz_full
+ * CPUs. Simply turn off capacity awareness when nohz_full is running.
+ */
+ if (tick_nohz_full_enabled())
+ return SCHED_CAPACITY_SCALE;
+ else
+ return arch_scale_cpu_capacity(cpu);
+}
+
+static struct tmigr_hierarchy *__tmigr_get_hierarchy(int cpu)
+{
+ unsigned int capacity = tmigr_get_capacity(cpu);
struct tmigr_hierarchy *iter;
list_for_each_entry(iter, &tmigr_hierarchy_list, node) {
@@ -1500,7 +1516,7 @@ static int tmigr_clear_cpu_available(unsigned int cpu)
}
if (firstexp != KTIME_MAX) {
- struct tmigr_hierarchy *hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+ struct tmigr_hierarchy *hier = __tmigr_get_hierarchy(cpu);
if (WARN_ON_ONCE(!hier))
return -EINVAL;
@@ -1938,11 +1954,11 @@ static int tmigr_setup_groups(struct tmigr_hierarchy *hier, unsigned int cpu,
return err;
}
-static struct tmigr_hierarchy *tmigr_get_hierarchy(unsigned int capacity)
+static struct tmigr_hierarchy *tmigr_get_hierarchy(int cpu)
{
struct tmigr_hierarchy *hier;
- hier = __tmigr_get_hierarchy(capacity);
+ hier = __tmigr_get_hierarchy(cpu);
if (hier)
return hier;
@@ -1962,7 +1978,7 @@ static struct tmigr_hierarchy *tmigr_get_hierarchy(unsigned int capacity)
for (int i = 0; i < tmigr_hierarchy_levels; i++)
INIT_LIST_HEAD(&hier->level_list[i]);
- hier->capacity = capacity;
+ hier->capacity = tmigr_get_capacity(cpu);
list_add_tail(&hier->node, &tmigr_hierarchy_list);
return hier;
@@ -2000,7 +2016,7 @@ static long connect_old_root_work(void *arg)
struct tmigr_hierarchy *hier;
int cpu = smp_processor_id();
- hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+ hier = __tmigr_get_hierarchy(cpu);
if (WARN_ON_ONCE(!hier))
return -EINVAL;
@@ -2016,7 +2032,7 @@ static int tmigr_add_cpu(unsigned int cpu)
guard(mutex)(&tmigr_mutex);
- hier = tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+ hier = tmigr_get_hierarchy(cpu);
if (IS_ERR(hier))
return PTR_ERR(hier);
--
2.53.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [tip: timers/core] timers/migration: Deactivate per-capacity hierarchies under nohz_full
2026-05-19 22:09 ` [PATCH 2/2] timers/migration: Deactivate per-capacity hierarchies under nohz_full Frederic Weisbecker
@ 2026-06-02 19:35 ` tip-bot2 for Frederic Weisbecker
0 siblings, 0 replies; 8+ messages in thread
From: tip-bot2 for Frederic Weisbecker @ 2026-06-02 19:35 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Frederic Weisbecker, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: d4f198c13611257f7f29d3c614721d0ac5d362f5
Gitweb: https://git.kernel.org/tip/d4f198c13611257f7f29d3c614721d0ac5d362f5
Author: Frederic Weisbecker <frederic@kernel.org>
AuthorDate: Wed, 20 May 2026 00:09:26 +02:00
Committer: Thomas Gleixner <tglx@kernel.org>
CommitterDate: Tue, 02 Jun 2026 21:34:03 +02:00
timers/migration: Deactivate per-capacity hierarchies under nohz_full
NOHZ_FULL CPUs global timers are guaranteed to be handled by the timekeeper
CPU, which never stops its tick and therefore remains active in the
hierarchy.
But since the introduction of per-capacity hierarchies, this guarantee is
broken because the timekeeper may not belong to the same hierarchy as all
the NOHZ_FULL CPUs.
Fix it with simply turning off capacity awareness when NOHZ_FULL is
running and force a single hierarchy. NOHZ_FULL is not exactly optimized
powerwise anyway.
Fixes: 098cbaad8e57 ("timers/migration: Split per-capacity hierarchies")
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260519220926.63437-3-frederic@kernel.org
---
kernel/time/timer_migration.c | 30 +++++++++++++++++++++++-------
1 file changed, 23 insertions(+), 7 deletions(-)
diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c
index 8032b00..8ba53ad 100644
--- a/kernel/time/timer_migration.c
+++ b/kernel/time/timer_migration.c
@@ -1464,8 +1464,24 @@ static long tmigr_trigger_active(void *unused)
return 0;
}
-static struct tmigr_hierarchy *__tmigr_get_hierarchy(unsigned int capacity)
+static unsigned int tmigr_get_capacity(int cpu)
{
+ /*
+ * nohz_full CPUs need to make sure there is always an available (online)
+ * and never idle migrator to handle all their global timers. That duty
+ * is served by the timekeeper which then never stops its tick. But the
+ * timekeeper must then belong to the same hierarchy as all the nohz_full
+ * CPUs. Simply turn off capacity awareness when nohz_full is running.
+ */
+ if (tick_nohz_full_enabled())
+ return SCHED_CAPACITY_SCALE;
+ else
+ return arch_scale_cpu_capacity(cpu);
+}
+
+static struct tmigr_hierarchy *__tmigr_get_hierarchy(int cpu)
+{
+ unsigned int capacity = tmigr_get_capacity(cpu);
struct tmigr_hierarchy *iter;
list_for_each_entry(iter, &tmigr_hierarchy_list, node) {
@@ -1500,7 +1516,7 @@ static int tmigr_clear_cpu_available(unsigned int cpu)
}
if (firstexp != KTIME_MAX) {
- struct tmigr_hierarchy *hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+ struct tmigr_hierarchy *hier = __tmigr_get_hierarchy(cpu);
if (WARN_ON_ONCE(!hier))
return -EINVAL;
@@ -1938,11 +1954,11 @@ out:
return err;
}
-static struct tmigr_hierarchy *tmigr_get_hierarchy(unsigned int capacity)
+static struct tmigr_hierarchy *tmigr_get_hierarchy(int cpu)
{
struct tmigr_hierarchy *hier;
- hier = __tmigr_get_hierarchy(capacity);
+ hier = __tmigr_get_hierarchy(cpu);
if (hier)
return hier;
@@ -1962,7 +1978,7 @@ static struct tmigr_hierarchy *tmigr_get_hierarchy(unsigned int capacity)
for (int i = 0; i < tmigr_hierarchy_levels; i++)
INIT_LIST_HEAD(&hier->level_list[i]);
- hier->capacity = capacity;
+ hier->capacity = tmigr_get_capacity(cpu);
list_add_tail(&hier->node, &tmigr_hierarchy_list);
return hier;
@@ -2000,7 +2016,7 @@ static long connect_old_root_work(void *arg)
struct tmigr_hierarchy *hier;
int cpu = smp_processor_id();
- hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+ hier = __tmigr_get_hierarchy(cpu);
if (WARN_ON_ONCE(!hier))
return -EINVAL;
@@ -2016,7 +2032,7 @@ static int tmigr_add_cpu(unsigned int cpu)
guard(mutex)(&tmigr_mutex);
- hier = tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+ hier = tmigr_get_hierarchy(cpu);
if (IS_ERR(hier))
return PTR_ERR(hier);
^ permalink raw reply [flat|nested] 8+ messages in thread
* [tip: timers/core] timers/migration: Fix hotplug migrator selection target on asymetric capacity machines
2026-05-19 22:09 ` [PATCH 1/2] timers/migration: Fix hotplug migrator selection target on asymetric capacity machines Frederic Weisbecker
@ 2026-06-02 19:35 ` tip-bot2 for Frederic Weisbecker
[not found] ` <CGME20260608094538eucas1p1b87572e8b8af819ccb1537c38cd51957@eucas1p1.samsung.com>
1 sibling, 0 replies; 8+ messages in thread
From: tip-bot2 for Frederic Weisbecker @ 2026-06-02 19:35 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Frederic Weisbecker, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the timers/core branch of tip:
Commit-ID: e4a70f5fbd43f55b474028a2cee3d78e4b443dd7
Gitweb: https://git.kernel.org/tip/e4a70f5fbd43f55b474028a2cee3d78e4b443dd7
Author: Frederic Weisbecker <frederic@kernel.org>
AuthorDate: Wed, 20 May 2026 00:09:25 +02:00
Committer: Thomas Gleixner <tglx@kernel.org>
CommitterDate: Tue, 02 Jun 2026 21:34:03 +02:00
timers/migration: Fix hotplug migrator selection target on asymetric capacity machines
When a top-level migrator is deactivated, either at CPU down hotplug time
or when a CPU is domain isolated, a new migrator is elected among the
available CPUs and woken up to take over the migration duty.
However that election must happen at the scope of a given hierarchy and not
globally, which the introduction of per-capacity hierarchies failed to
handle.
As a result a given hierarchy may end up without migrator to handle global
timers.
Fix it by making sure that the new migrator belongs to the same hierarchy
as the outgoing CPU.
Fixes: 098cbaad8e57 ("timers/migration: Split per-capacity hierarchies")
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260519220926.63437-2-frederic@kernel.org
---
kernel/time/timer_migration.c | 42 +++++++++++++++++++++++++---------
1 file changed, 32 insertions(+), 10 deletions(-)
diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c
index 25e3c56..8032b00 100644
--- a/kernel/time/timer_migration.c
+++ b/kernel/time/timer_migration.c
@@ -1464,6 +1464,18 @@ static long tmigr_trigger_active(void *unused)
return 0;
}
+static struct tmigr_hierarchy *__tmigr_get_hierarchy(unsigned int capacity)
+{
+ struct tmigr_hierarchy *iter;
+
+ list_for_each_entry(iter, &tmigr_hierarchy_list, node) {
+ if (iter->capacity == capacity)
+ return iter;
+ }
+
+ return NULL;
+}
+
static int tmigr_clear_cpu_available(unsigned int cpu)
{
struct tmigr_cpu *tmc = this_cpu_ptr(&tmigr_cpu);
@@ -1488,8 +1500,21 @@ static int tmigr_clear_cpu_available(unsigned int cpu)
}
if (firstexp != KTIME_MAX) {
- migrator = cpumask_any(tmigr_available_cpumask);
- work_on_cpu(migrator, tmigr_trigger_active, NULL);
+ struct tmigr_hierarchy *hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+
+ if (WARN_ON_ONCE(!hier))
+ return -EINVAL;
+
+ migrator = cpumask_any_and(tmigr_available_cpumask, hier->cpumask);
+ if (migrator < nr_cpu_ids) {
+ work_on_cpu(migrator, tmigr_trigger_active, NULL);
+ } else {
+ /*
+ * If deactivation returned an expiration, it belongs to an available
+ * nohz CPU in the hierarchy.
+ */
+ WARN_ONCE(1, "Expected available CPU in the hierarchy\n");
+ }
}
return 0;
@@ -1915,12 +1940,9 @@ out:
static struct tmigr_hierarchy *tmigr_get_hierarchy(unsigned int capacity)
{
- struct tmigr_hierarchy *hier = NULL, *iter;
+ struct tmigr_hierarchy *hier;
- list_for_each_entry(iter, &tmigr_hierarchy_list, node) {
- if (iter->capacity == capacity)
- hier = iter;
- }
+ hier = __tmigr_get_hierarchy(capacity);
if (hier)
return hier;
@@ -1978,9 +2000,9 @@ static long connect_old_root_work(void *arg)
struct tmigr_hierarchy *hier;
int cpu = smp_processor_id();
- hier = tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
- if (IS_ERR(hier))
- return PTR_ERR(hier);
+ hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
+ if (WARN_ON_ONCE(!hier))
+ return -EINVAL;
return tmigr_connect_old_root(hier, cpu, old_root, true);
}
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] timers/migration: Fix hotplug migrator selection target on asymetric capacity machines
[not found] ` <CGME20260608094538eucas1p1b87572e8b8af819ccb1537c38cd51957@eucas1p1.samsung.com>
@ 2026-06-08 9:45 ` Marek Szyprowski
2026-06-08 14:04 ` Frederic Weisbecker
0 siblings, 1 reply; 8+ messages in thread
From: Marek Szyprowski @ 2026-06-08 9:45 UTC (permalink / raw)
To: Frederic Weisbecker, Thomas Gleixner; +Cc: LKML, Anna-Maria Behnsen
Dear All,
On 20.05.2026 00:09, Frederic Weisbecker wrote:
> When a top-level migrator is deactivated, either at CPU down hotplug
> time or when a CPU is domain isolated, a new migrator is elected among
> the available CPUs and woken up to take over the migration duty.
>
> However that election must happen at the scope of a given hierarchy and
> not globally, which the introduction of per-capacity hierarchies failed
> to handle.
>
> As a result a given hierarchy may end up without migrator to handle
> global timers.
>
> Fix it with making sure that the new migrator belongs to the same
> hierarchy as the outgoing CPU.
>
> Fixes: 098cbaad8e57 ("timers/migration: Split per-capacity hierarchies")
> Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
This patch landed recently in linux-next as commit e4a70f5fbd43 ("timers/migration:
Fix hotplug migrator selection target on asymetric capacity machines"). In my tests
I found that it breaks system suspend/resume on some legacy big.LITTLE ARM machines.
Reverting $subject, together with dependent commit d4f198c13611 ("timers/migration:
Deactivate per-capacity hierarchies under nohz_full") on top of linux-next fixes
this issue. Here is the log from the system suspend/resume failure introduced by
the $subject patch:
root@target:~# time rtcwake -s10 -mmem
rtcwake: wakeup from "mem" using /dev/rtc0 at Mon Jun 8 11:17:23 2026
PM: suspend entry (deep)
Filesystems sync: 0.000 seconds
Freezing user space processes
Freezing user space processes completed (elapsed 0.002 seconds)
OOM killer disabled.
Freezing remaining freezable tasks
Freezing remaining freezable tasks completed (elapsed 0.042 seconds)
printk: Suspending console(s) (use no_console_suspend to debug)
...
Disabling non-boot CPUs ...
------------[ cut here ]------------
WARNING: kernel/time/timer_migration.c:1505 at tmigr_clear_cpu_available+0x3b8/0x3c8, CPU#5: cpuhp/5/40
Modules linked in:
CPU: 5 UID: 0 PID: 40 Comm: cpuhp/5 Not tainted 7.1.0-rc1-00028-ge4a70f5fbd43-dirty #16750 PREEMPT
Hardware name: Samsung Exynos (Flattened Device Tree)
Call trace:
unwind_backtrace from show_stack+0x10/0x14
show_stack from dump_stack_lvl+0x68/0x88
dump_stack_lvl from __warn+0x94/0x204
__warn from warn_slowpath_fmt+0x1b0/0x1bc
warn_slowpath_fmt from tmigr_clear_cpu_available+0x3b8/0x3c8
tmigr_clear_cpu_available from cpuhp_invoke_callback+0x190/0x380
cpuhp_invoke_callback from cpuhp_thread_fun+0x1a8/0x2e8
cpuhp_thread_fun from smpboot_thread_fn+0x174/0x32c
smpboot_thread_fn from kthread+0x128/0x168
kthread from ret_from_fork+0x14/0x28
Exception stack(0xf092dfb0 to 0xf092dff8)
dfa0: 00000000 00000000 00000000 00000000
dfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
dfe0: 00000000 00000000 00000000 00000000 00000013 00000000
irq event stamp: 1295
hardirqs last enabled at (1301): [<c01c4798>] __up_console_sem+0x50/0x60
hardirqs last disabled at (1306): [<c01c4784>] __up_console_sem+0x3c/0x60
softirqs last enabled at (0): [<c01326c8>] copy_process+0xa0c/0x1c9c
softirqs last disabled at (0): [<00000000>] 0x0
---[ end trace 0000000000000000 ]---
Error taking CPU5 down: -22
Non-boot CPUs are not disabled
Enabling non-boot CPUs ...
CPU6 is up
CPU7 is up
...
OOM killer enabled.
Restarting tasks: Starting
Restarting tasks: Done
random: crng reseeded on system resumption
PM: suspend exit
rtcwake: write error
> ---
> kernel/time/timer_migration.c | 42 ++++++++++++++++++++++++++---------
> 1 file changed, 32 insertions(+), 10 deletions(-)
>
> diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c
> index 25e3c563eb74..8032b0044f44 100644
> --- a/kernel/time/timer_migration.c
> +++ b/kernel/time/timer_migration.c
> @@ -1464,6 +1464,18 @@ static long tmigr_trigger_active(void *unused)
> return 0;
> }
>
> +static struct tmigr_hierarchy *__tmigr_get_hierarchy(unsigned int capacity)
> +{
> + struct tmigr_hierarchy *iter;
> +
> + list_for_each_entry(iter, &tmigr_hierarchy_list, node) {
> + if (iter->capacity == capacity)
> + return iter;
> + }
> +
> + return NULL;
> +}
> +
> static int tmigr_clear_cpu_available(unsigned int cpu)
> {
> struct tmigr_cpu *tmc = this_cpu_ptr(&tmigr_cpu);
> @@ -1488,8 +1500,21 @@ static int tmigr_clear_cpu_available(unsigned int cpu)
> }
>
> if (firstexp != KTIME_MAX) {
> - migrator = cpumask_any(tmigr_available_cpumask);
> - work_on_cpu(migrator, tmigr_trigger_active, NULL);
> + struct tmigr_hierarchy *hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
> +
> + if (WARN_ON_ONCE(!hier))
> + return -EINVAL;
> +
> + migrator = cpumask_any_and(tmigr_available_cpumask, hier->cpumask);
> + if (migrator < nr_cpu_ids) {
> + work_on_cpu(migrator, tmigr_trigger_active, NULL);
> + } else {
> + /*
> + * If deactivation returned an expiration, it belongs to an available
> + * nohz CPU in the hierarchy.
> + */
> + WARN_ONCE(1, "Expected available CPU in the hierarchy\n");
> + }
> }
>
> return 0;
> @@ -1915,12 +1940,9 @@ static int tmigr_setup_groups(struct tmigr_hierarchy *hier, unsigned int cpu,
>
> static struct tmigr_hierarchy *tmigr_get_hierarchy(unsigned int capacity)
> {
> - struct tmigr_hierarchy *hier = NULL, *iter;
> + struct tmigr_hierarchy *hier;
>
> - list_for_each_entry(iter, &tmigr_hierarchy_list, node) {
> - if (iter->capacity == capacity)
> - hier = iter;
> - }
> + hier = __tmigr_get_hierarchy(capacity);
>
> if (hier)
> return hier;
> @@ -1978,9 +2000,9 @@ static long connect_old_root_work(void *arg)
> struct tmigr_hierarchy *hier;
> int cpu = smp_processor_id();
>
> - hier = tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
> - if (IS_ERR(hier))
> - return PTR_ERR(hier);
> + hier = __tmigr_get_hierarchy(arch_scale_cpu_capacity(cpu));
> + if (WARN_ON_ONCE(!hier))
> + return -EINVAL;
>
> return tmigr_connect_old_root(hier, cpu, old_root, true);
> }
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] timers/migration: Fix hotplug migrator selection target on asymetric capacity machines
2026-06-08 9:45 ` [PATCH 1/2] " Marek Szyprowski
@ 2026-06-08 14:04 ` Frederic Weisbecker
2026-06-08 14:15 ` Marek Szyprowski
0 siblings, 1 reply; 8+ messages in thread
From: Frederic Weisbecker @ 2026-06-08 14:04 UTC (permalink / raw)
To: Marek Szyprowski; +Cc: Thomas Gleixner, LKML, Anna-Maria Behnsen
Le Mon, Jun 08, 2026 at 11:45:37AM +0200, Marek Szyprowski a écrit :
> Dear All,
>
> On 20.05.2026 00:09, Frederic Weisbecker wrote:
> > When a top-level migrator is deactivated, either at CPU down hotplug
> > time or when a CPU is domain isolated, a new migrator is elected among
> > the available CPUs and woken up to take over the migration duty.
> >
> > However that election must happen at the scope of a given hierarchy and
> > not globally, which the introduction of per-capacity hierarchies failed
> > to handle.
> >
> > As a result a given hierarchy may end up without migrator to handle
> > global timers.
> >
> > Fix it with making sure that the new migrator belongs to the same
> > hierarchy as the outgoing CPU.
> >
> > Fixes: 098cbaad8e57 ("timers/migration: Split per-capacity hierarchies")
> > Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
>
> This patch landed recently in linux-next as commit e4a70f5fbd43 ("timers/migration:
> Fix hotplug migrator selection target on asymetric capacity machines"). In my tests
> I found that it breaks system suspend/resume on some legacy big.LITTLE ARM machines.
>
>
> Reverting $subject, together with dependent commit d4f198c13611 ("timers/migration:
> Deactivate per-capacity hierarchies under nohz_full") on top of linux-next fixes
> this issue. Here is the log from the system suspend/resume failure introduced by
> the $subject patch:
>
>
> root@target:~# time rtcwake -s10 -mmem
> rtcwake: wakeup from "mem" using /dev/rtc0 at Mon Jun 8 11:17:23 2026
> PM: suspend entry (deep)
> Filesystems sync: 0.000 seconds
> Freezing user space processes
> Freezing user space processes completed (elapsed 0.002 seconds)
> OOM killer disabled.
> Freezing remaining freezable tasks
> Freezing remaining freezable tasks completed (elapsed 0.042 seconds)
> printk: Suspending console(s) (use no_console_suspend to debug)
> ...
> Disabling non-boot CPUs ...
> ------------[ cut here ]------------
> WARNING: kernel/time/timer_migration.c:1505 at
> tmigr_clear_cpu_available+0x3b8/0x3c8, CPU#5: cpuhp/5/40
Thanks but which tree is this? The only warning I see there is on line 1521
1532 (tip:timers/core).
It's probably line 1521 somehow. Is it possible that arch_scale_cpu_capacity()
returns a different result between CPU boot up and CPU down?
Thanks.
--
Frederic Weisbecker
SUSE Labs
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/2] timers/migration: Fix hotplug migrator selection target on asymetric capacity machines
2026-06-08 14:04 ` Frederic Weisbecker
@ 2026-06-08 14:15 ` Marek Szyprowski
0 siblings, 0 replies; 8+ messages in thread
From: Marek Szyprowski @ 2026-06-08 14:15 UTC (permalink / raw)
To: Frederic Weisbecker; +Cc: Thomas Gleixner, LKML, Anna-Maria Behnsen
On 08.06.2026 16:04, Frederic Weisbecker wrote:
> Le Mon, Jun 08, 2026 at 11:45:37AM +0200, Marek Szyprowski a écrit :
>> On 20.05.2026 00:09, Frederic Weisbecker wrote:
>>> When a top-level migrator is deactivated, either at CPU down hotplug
>>> time or when a CPU is domain isolated, a new migrator is elected among
>>> the available CPUs and woken up to take over the migration duty.
>>>
>>> However that election must happen at the scope of a given hierarchy and
>>> not globally, which the introduction of per-capacity hierarchies failed
>>> to handle.
>>>
>>> As a result a given hierarchy may end up without migrator to handle
>>> global timers.
>>>
>>> Fix it with making sure that the new migrator belongs to the same
>>> hierarchy as the outgoing CPU.
>>>
>>> Fixes: 098cbaad8e57 ("timers/migration: Split per-capacity hierarchies")
>>> Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
>> This patch landed recently in linux-next as commit e4a70f5fbd43 ("timers/migration:
>> Fix hotplug migrator selection target on asymetric capacity machines"). In my tests
>> I found that it breaks system suspend/resume on some legacy big.LITTLE ARM machines.
>>
>>
>> Reverting $subject, together with dependent commit d4f198c13611 ("timers/migration:
>> Deactivate per-capacity hierarchies under nohz_full") on top of linux-next fixes
>> this issue. Here is the log from the system suspend/resume failure introduced by
>> the $subject patch:
>>
>>
>> root@target:~# time rtcwake -s10 -mmem
>> rtcwake: wakeup from "mem" using /dev/rtc0 at Mon Jun 8 11:17:23 2026
>> PM: suspend entry (deep)
>> Filesystems sync: 0.000 seconds
>> Freezing user space processes
>> Freezing user space processes completed (elapsed 0.002 seconds)
>> OOM killer disabled.
>> Freezing remaining freezable tasks
>> Freezing remaining freezable tasks completed (elapsed 0.042 seconds)
>> printk: Suspending console(s) (use no_console_suspend to debug)
>> ...
>> Disabling non-boot CPUs ...
>> ------------[ cut here ]------------
>> WARNING: kernel/time/timer_migration.c:1505 at
>> tmigr_clear_cpu_available+0x3b8/0x3c8, CPU#5: cpuhp/5/40
> Thanks but which tree is this? The only warning I see there is on line 1521
> 1532 (tip:timers/core).
>
> It's probably line 1521 somehow. Is it possible that arch_scale_cpu_capacity()
> returns a different result between CPU boot up and CPU down?
The log has been captured on the kernel compiled from the e4a70f5fbd43 commit.
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-08 14:15 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-19 22:09 [PATCH 0/2] timers/migration: Fixes on top of per capacity hierarchies Frederic Weisbecker
2026-05-19 22:09 ` [PATCH 1/2] timers/migration: Fix hotplug migrator selection target on asymetric capacity machines Frederic Weisbecker
2026-06-02 19:35 ` [tip: timers/core] " tip-bot2 for Frederic Weisbecker
[not found] ` <CGME20260608094538eucas1p1b87572e8b8af819ccb1537c38cd51957@eucas1p1.samsung.com>
2026-06-08 9:45 ` [PATCH 1/2] " Marek Szyprowski
2026-06-08 14:04 ` Frederic Weisbecker
2026-06-08 14:15 ` Marek Szyprowski
2026-05-19 22:09 ` [PATCH 2/2] timers/migration: Deactivate per-capacity hierarchies under nohz_full Frederic Weisbecker
2026-06-02 19:35 ` [tip: timers/core] " tip-bot2 for Frederic Weisbecker
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome