From: Frederic Weisbecker <frederic@kernel.org>
To: Christian Loehle <christian.loehle@arm.com>
Cc: LKML <linux-kernel@vger.kernel.org>,
Frederic Weisbecker <frederic@kernel.org>,
Anna-Maria Behnsen <anna-maria@linutronix.de>,
Sehee Jeong <sehee1.jeong@samsung.com>,
Thomas Gleixner <tglx@linutronix.de>,
Peter Zijlstra <peterz@infradead.org>
Subject: [PATCH 2/6] timers/migration: Defer initialization after capacity topology is setup
Date: Thu, 25 Jun 2026 18:41:10 +0200 [thread overview]
Message-ID: <20260625164114.51454-3-frederic@kernel.org> (raw)
In-Reply-To: <20260625164114.51454-1-frederic@kernel.org>
In order for the timer migration code to get informations from the CPU
capacity list, the tree must be prepared after the scheduler topology is
initialized.
Defer this part and the hotplug callbacks to a late initcall. Two new
cases are taken care of now that the registration of the callbacks
is made after CPUs have booted:
* The prepare callback may now run on the target.
* A subsequent root level can be created even if no CPU is available in
the hierarchy because prepare callbacks are called on all CPUs before
online callbacks.
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
---
kernel/time/timer_migration.c | 92 +++++++++++++++++++++++++----------
1 file changed, 67 insertions(+), 25 deletions(-)
diff --git a/kernel/time/timer_migration.c b/kernel/time/timer_migration.c
index 52c15affdbff..4209e695ec7b 100644
--- a/kernel/time/timer_migration.c
+++ b/kernel/time/timer_migration.c
@@ -1631,7 +1631,6 @@ static int __init tmigr_init_isolation(void)
/* Protect against RCU torture hotplug testing */
return tmigr_isolated_exclude_cpumask(cpumask);
}
-late_initcall(tmigr_init_isolation);
static void tmigr_init_group(struct tmigr_group *group, unsigned int lvl,
int node)
@@ -1703,7 +1702,7 @@ static struct tmigr_group *tmigr_get_group(int node, unsigned int lvl)
return group;
}
-static bool tmigr_init_root(struct tmigr_group *group, bool activate)
+static bool tmigr_init_root(struct tmigr_group *group, bool root_up)
{
if (!group->parent && group != tmigr_root) {
/*
@@ -1712,7 +1711,7 @@ static bool tmigr_init_root(struct tmigr_group *group, bool activate)
* created in the future and made visible before this groupmask.
*/
group->groupmask = BIT(0);
- WARN_ON_ONCE(activate);
+ WARN_ON_ONCE(root_up);
return true;
}
@@ -1723,9 +1722,9 @@ static bool tmigr_init_root(struct tmigr_group *group, bool activate)
static void tmigr_connect_child_parent(struct tmigr_group *child,
struct tmigr_group *parent,
- bool activate)
+ bool root_up)
{
- if (tmigr_init_root(parent, activate)) {
+ if (tmigr_init_root(parent, root_up)) {
/*
* The previous top level had prepared its groupmask already,
* simply account it in advance as the first child. If some groups
@@ -1736,7 +1735,7 @@ static void tmigr_connect_child_parent(struct tmigr_group *child,
}
/* Connecting old root to new root ? */
- if (!parent->parent && activate) {
+ if (!parent->parent && root_up) {
/*
* @child is the old top, or in case of node mismatch, some
* intermediate group between the old top and the new one in
@@ -1831,7 +1830,7 @@ static int tmigr_setup_groups(unsigned int cpu, unsigned int node,
tmc->tmgroup = group;
tmc->groupmask = BIT(group->num_children++);
- tmigr_init_root(group, activate);
+ tmigr_init_root(group, start);
trace_tmigr_connect_cpu_parent(tmc);
@@ -1839,7 +1838,7 @@ static int tmigr_setup_groups(unsigned int cpu, unsigned int node,
continue;
} else {
child = stack[i - 1];
- tmigr_connect_child_parent(child, group, activate);
+ tmigr_connect_child_parent(child, group, start);
}
}
@@ -1894,8 +1893,14 @@ static int tmigr_setup_groups(unsigned int cpu, unsigned int node,
if (state.active) {
data.childmask = start->groupmask;
__walk_groups_from(tmigr_active_up, &data, start, start->parent);
+ } else if (start) {
+ union tmigr_state state;
+
+ /* No available CPU so the old root should be inactive */
+ state.state = atomic_read(&start->migr_state);
+ WARN_ON_ONCE(state.active);
}
- }
+ }
/* Root update */
if (list_is_singular(&tmigr_level_list[top])) {
@@ -1914,18 +1919,9 @@ static int tmigr_setup_groups(unsigned int cpu, unsigned int node,
return err;
}
-static int tmigr_add_cpu(unsigned int cpu)
+static int tmigr_connect_old_root(int cpu, struct tmigr_group *old_root, bool activate)
{
- struct tmigr_group *old_root = tmigr_root;
- int node = cpu_to_node(cpu);
- int ret;
-
- guard(mutex)(&tmigr_mutex);
-
- ret = tmigr_setup_groups(cpu, node, NULL, false);
-
- /* Root has changed? Connect the old one to the new */
- if (ret >= 0 && old_root && old_root != tmigr_root) {
+ if (activate) {
/*
* The target CPU must never do the prepare work, except
* on early boot when the boot CPU is the target. Otherwise
@@ -1933,13 +1929,55 @@ static int tmigr_add_cpu(unsigned int cpu)
* the new one (nevertheless whether old top level group is
* active or not) and/or release an uninitialized childmask.
*/
- WARN_ON_ONCE(cpu == raw_smp_processor_id());
+ WARN_ON_ONCE(cpu == smp_processor_id());
/*
- * The (likely) current CPU is expected to be online in the hierarchy,
+ * The current CPU is expected to be online in the hierarchy,
* otherwise the old root may not be active as expected.
*/
- WARN_ON_ONCE(!per_cpu_ptr(&tmigr_cpu, raw_smp_processor_id())->available);
- ret = tmigr_setup_groups(-1, old_root->numa_node, old_root, true);
+ WARN_ON_ONCE(!__this_cpu_read(tmigr_cpu.available));
+ }
+
+ return tmigr_setup_groups(-1, old_root->numa_node, old_root, activate);
+}
+
+static long connect_old_root_work(void *arg)
+{
+ struct tmigr_group *old_root = arg;
+
+ return tmigr_connect_old_root(smp_processor_id(), old_root, true);
+}
+
+static int tmigr_add_cpu(unsigned int cpu)
+{
+ struct tmigr_group *old_root = tmigr_root;
+ int node = cpu_to_node(cpu);
+ int ret;
+
+ guard(mutex)(&tmigr_mutex);
+
+ ret = tmigr_setup_groups(cpu, node, NULL, false);
+
+ if (ret < 0 || !old_root || old_root == tmigr_root)
+ return ret;
+
+ /* Root has changed. Connect the old one to the new */
+ guard(migrate)();
+ if (cpumask_test_cpu(smp_processor_id(), tmigr_available_cpumask)) {
+ /*
+ * If the CPU is available in the hierarchy, the old root is expected
+ * to be active. Link and propagate to the new root.
+ */
+ ret = tmigr_connect_old_root(cpu, old_root, true);
+ } else {
+ int target = cpumask_first(tmigr_available_cpumask);
+
+ if (target < nr_cpu_ids) {
+ /* Defer the connection to an available CPU to propagate activation */
+ ret = work_on_cpu(target, connect_old_root_work, old_root);
+ } else {
+ /* No CPU available yet, connect but don't activate */
+ ret = tmigr_connect_old_root(cpu, old_root, false);
+ }
}
return ret;
@@ -2044,10 +2082,14 @@ static int __init tmigr_init(void)
if (ret)
goto err;
+ ret = tmigr_init_isolation();
+ if (ret)
+ goto err;
+
return 0;
err:
pr_err("Timer migration setup failed\n");
return ret;
}
-early_initcall(tmigr_init);
+late_initcall(tmigr_init);
--
2.53.0
next prev parent reply other threads:[~2026-06-25 16:41 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-25 16:41 [RFT][DONOTMERGE][PATCH 0/6] timers/migration: Prioritize lower capacity CPUs as migrators Frederic Weisbecker
2026-06-25 16:41 ` [PATCH 1/6] timers/migration: Revert per CPU capacity hierarchy Frederic Weisbecker
2026-06-25 16:41 ` Frederic Weisbecker [this message]
2026-06-25 16:41 ` [PATCH 3/6] sched/topology: Account asym capacities number Frederic Weisbecker
2026-06-25 16:41 ` [PATCH 4/6] timers/migration: Group CPUs per capacity Frederic Weisbecker
2026-06-25 16:41 ` [PATCH 5/6] timers/migration: Prefer lower capacity groups as migrators Frederic Weisbecker
2026-06-25 16:41 ` [PATCH 6/6] scripts/timer_migration_tree.py: Dump mask of each group Frederic Weisbecker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260625164114.51454-3-frederic@kernel.org \
--to=frederic@kernel.org \
--cc=anna-maria@linutronix.de \
--cc=christian.loehle@arm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=peterz@infradead.org \
--cc=sehee1.jeong@samsung.com \
--cc=tglx@linutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox