From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CB0F43E8355 for ; Thu, 25 Jun 2026 16:41:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782405693; cv=none; b=HlIipT9zcva3n7CLz1GCpmFdh1rvVRQ9lAW0DiypknEkHbdsav2LDzBI4IfbyZhS/gEgyXsBZPggiE2n7V8A+qiCa1crXZFCcp9PttspZxhh6JFiLupZpNRaRpam4N4PHGfaJLa7Iymc4OIGi07ttYrZK7mmjvigMhSqG2wk0sY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782405693; c=relaxed/simple; bh=9a70nWcZXFuLTg78rssB12sK5EZIkJaEGFL2irS3POA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mKUfkFil6Z8FWxJ6jrBNLxB5lz1rhKyo80aUr0mqW0ZJuJQSCvFzsTQd1Ry9K4l2QVns12KKkXlPsKVS//OHLTAY1iGDcapyoY2bZOxVs6Y4Iume6T7mL/rEnwAmYs95V2PZN92qjFnHcl5tMuNs2ThZcsjxA/t5LOID/U5vDbU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=igfILH2l; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="igfILH2l" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 42DA51F000E9; Thu, 25 Jun 2026 16:41:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1782405691; bh=dX68oHKzsIOhswsgS/z78KpCy/nCO63eqpTegVozGwk=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=igfILH2lsWLqfIKDS0ZTZ347u1UoZDM56JUA4qT0hmqDAzSW4TQbEMKX/aAjWryNp xuNaa5slHcOjRnkcaqt2ptFRkx0urkN39pNeZwqhfXcqntgu249XRqdEFShWsI8Do0 OGSndvtLw+nw6beCagPF2NdOKxI11v+uMcPFGalgiSEFssbEzi3rhWcApWlOsQjznA rjEnmiv2mrn3Oj/+ZJnLuTDHFSuxbn5rSWbs/He/N4mfyq5bYBjwft8jSXWEHazEON dT5SFgxA6CtXCem5/cWvLJ3UR7IJ54i4sR6vgyApv3YGaWxHunQ/jj33SLiBGGkBAG HhDhlMXo8jEZg== From: Frederic Weisbecker To: Christian Loehle Cc: LKML , Frederic Weisbecker , Anna-Maria Behnsen , Sehee Jeong , Thomas Gleixner , Peter Zijlstra Subject: [PATCH 2/6] timers/migration: Defer initialization after capacity topology is setup Date: Thu, 25 Jun 2026 18:41:10 +0200 Message-ID: <20260625164114.51454-3-frederic@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260625164114.51454-1-frederic@kernel.org> References: <20260625164114.51454-1-frederic@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 --- 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