]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
timers/migration: Track CPUs in a hierarchy
authorFrederic Weisbecker <frederic@kernel.org>
Thu, 23 Apr 2026 16:53:51 +0000 (18:53 +0200)
committerThomas Gleixner <tglx@kernel.org>
Wed, 6 May 2026 06:33:06 +0000 (08:33 +0200)
When a new root is created, the old root is connected to it and
propagates up its own assumed to be active state, since the hotplug
control CPU is itself active and part of the old root.

However with per-capacity hierarchies, this assumption won't be true
anymore because the hotplug control CPU calling the timer migration
prepare callback may not belong to the same hierarchy as the booting
CPU.

To solve this, track the available CPUs per hierarchies so that the
root connection can be offlined to safe CPUs.

Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260423165354.95152-4-frederic@kernel.org
kernel/time/timer_migration.c
kernel/time/timer_migration.h

index a8ec85f2f5c79755cf27e97b4514167007343757..a68b9c746e12b76e08e7c7bba2a6f55c93b45db7 100644 (file)
@@ -1916,17 +1916,23 @@ static struct tmigr_hierarchy *tmigr_get_hierarchy(void)
        if (!hierarchy)
                return ERR_PTR(-ENOMEM);
 
+       hierarchy->cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
+       if (!hierarchy->cpumask)
+               goto err;
+
        hierarchy->level_list = kzalloc_objs(struct list_head, tmigr_hierarchy_levels);
-       if (!hierarchy->level_list) {
-               kfree(hierarchy);
-               hierarchy = NULL;
-               return ERR_PTR(-ENOMEM);
-       }
+       if (!hierarchy->level_list)
+               goto err;
 
        for (int i = 0; i < tmigr_hierarchy_levels; i++)
                INIT_LIST_HEAD(&hierarchy->level_list[i]);
 
        return hierarchy;
+err:
+       kfree(hierarchy->cpumask);
+       kfree(hierarchy);
+       hierarchy = NULL;
+       return ERR_PTR(-ENOMEM);
 }
 
 static int tmigr_add_cpu(unsigned int cpu)
@@ -1946,8 +1952,11 @@ static int tmigr_add_cpu(unsigned int cpu)
 
        ret = tmigr_setup_groups(hier, cpu, node, NULL, false);
 
+       if (ret < 0)
+               return ret;
+
        /* Root has changed? Connect the old one to the new */
-       if (ret >= 0 && old_root && old_root != hier->root) {
+       if (old_root && old_root != hier->root) {
                /*
                 * The target CPU must never do the prepare work, except
                 * on early boot when the boot CPU is the target. Otherwise
@@ -1964,6 +1973,9 @@ static int tmigr_add_cpu(unsigned int cpu)
                ret = tmigr_setup_groups(hier, -1, old_root->numa_node, old_root, true);
        }
 
+       if (ret >= 0)
+               cpumask_set_cpu(cpu, hier->cpumask);
+
        return ret;
 }
 
index 77df422e5f9a343a98b4186ddd79439451bbb57f..0cfbb8d799a6fbb00920d9d28b698b2c15cc218a 100644 (file)
@@ -8,10 +8,12 @@
 /**
  * struct tmigr_hierarchy - a hierarchy associated to a given CPU capacity.
  * @level_list:        Per level lists of tmigr groups
+ * @cpumask:   CPUs belonging to this hierarchy
  * @root:      The current root of the hierarchy
  */
 struct tmigr_hierarchy {
        struct list_head        *level_list;
+       struct cpumask          *cpumask;
        struct tmigr_group      *root;
 };