diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-07 02:19:02 +0200 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-07 02:19:02 +0200 |
| commit | 0d32b3ec5f902bf40d03db08837f3eafbb6a4577 (patch) | |
| tree | 2aee0ffb92d604ebff45127a72935b282ab3f49f | |
| parent | 762122d75e50e4919ef77afc2dffdc4931c5be87 (diff) | |
| parent | b8eb5fd5bdb4c03758b056c70e5e1d962ca89757 (diff) | |
Merge tag 'cgroup-for-7.3-rc6-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
Pull cgroup fixes from Tejun Heo:
- During CPU offline, the active mask drops the CPU before cpuset
updates the effective CPUs, so a task placement in that window could
find no active CPU in the top cpuset and dereference NULL. Restore
the NULL check.
- The cpuset v2-mode test read the subsystem's root pointer, which is
stale during a cgroup filesystem rebind, and the hotplug handler
evaluated it before taking the cpuset mutex. Record the mode in a
flag and test it under the mutex.
* tag 'cgroup-for-7.3-rc6-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup:
cgroup/cpuset: Call is_in_v2_mode() after acquiring cpuset_mutex in cpuset_handle_hotplug()
cgroup/cpuset: Handle cpu hotplug race in guarantee_active_cpus()
cgroup/cpuset: Don't access cpuset_cgrp_subsys.root in is_in_v2_mode()
| -rw-r--r-- | kernel/cgroup/cpuset.c | 40 |
1 files changed, 32 insertions, 8 deletions
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 3f52717c1965..8b220e9df91e 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -153,6 +153,12 @@ static cpumask_var_t isolated_cpus; /* CSCB */ static bool update_housekeeping; /* RWCS */ /* + * Set if "cpuset_v2_mode" mount option is used + * Cached at bind time and not lock protected; accessed via {READ,WRITE}_ONCE + */ +static bool cpuset_v2_mode; + +/* * Copy of isolated_cpus to be passed to housekeeping_update() */ static cpumask_var_t isolated_hk_cpus; /* T */ @@ -439,8 +445,7 @@ static inline bool cpuset_v2(void) */ static inline bool is_in_v2_mode(void) { - return cpuset_v2() || - (cpuset_cgrp_subsys.root->flags & CGRP_ROOT_CPUSET_V2_MODE); + return cpuset_v2() || READ_ONCE(cpuset_v2_mode); } /** @@ -513,10 +518,26 @@ static void guarantee_active_cpus(struct task_struct *tsk, rcu_read_lock(); cs = task_cs(tsk); - while (!cpumask_intersects(cs->effective_cpus, pmask)) + while (!cpumask_intersects(cs->effective_cpus, pmask)) { cs = parent_cs(cs); - + if (unlikely(!cs)) { + /* + * The top cpuset doesn't have any active cpu as a + * consequence of a race between its caller and the cpu + * hotplug operation where cpu_active_mask is updated + * asynchronously before cpuset_handle_hotplug() is + * being called to adjust the effective_cpus of the + * affected cpusets. But we know the top cpuset's + * effective_cpus is on its way to be identical to + * cpu_active_mask minus the exclusive CPUs dedicated + * to other valid cpuset partitions. Just pass back + * the filtered cpu_active_mask in this case. + */ + goto out_unlock; + } + } cpumask_and(pmask, pmask, cs->effective_cpus); +out_unlock: rcu_read_unlock(); } @@ -3732,6 +3753,8 @@ static void cpuset_bind(struct cgroup_subsys_state *root_css) mutex_lock(&cpuset_mutex); spin_lock_irq(&callback_lock); + WRITE_ONCE(cpuset_v2_mode, + !!(cpuset_cgrp_subsys.root->flags & CGRP_ROOT_CPUSET_V2_MODE)); if (is_in_v2_mode()) { cpumask_copy(top_cpuset.cpus_allowed, cpu_possible_mask); cpumask_copy(top_cpuset.effective_xcpus, cpu_possible_mask); @@ -4044,15 +4067,16 @@ static void cpuset_handle_hotplug(void) static cpumask_t new_cpus; static nodemask_t new_mems; bool cpus_updated, mems_updated; - bool on_dfl = is_in_v2_mode(); + bool on_dfl; struct tmpmasks tmp, *ptmp = NULL; - if (on_dfl && !alloc_tmpmasks(&tmp)) - ptmp = &tmp; - lockdep_assert_cpus_held(); mutex_lock(&cpuset_mutex); + on_dfl = is_in_v2_mode(); + if (on_dfl && !alloc_tmpmasks(&tmp)) + ptmp = &tmp; + /* fetch the available cpus/mems and find out which changed how */ cpumask_copy(&new_cpus, cpu_active_mask); new_mems = node_states[N_MEMORY]; |
