]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
cgroup/cpuset: Move the v1 empty cpus/mems check to cpuset1_validate_change()
authorWaiman Long <longman@redhat.com>
Mon, 12 Jan 2026 16:00:21 +0000 (11:00 -0500)
committerTejun Heo <tj@kernel.org>
Mon, 12 Jan 2026 19:03:22 +0000 (09:03 -1000)
As stated in commit 1c09b195d37f ("cpuset: fix a regression in validating
config change"), it is not allowed to clear masks of a cpuset if
there're tasks in it. This is specific to v1 since empty "cpuset.cpus"
or "cpuset.mems" will cause the v2 cpuset to inherit the effective CPUs
or memory nodes from its parent. So it is OK to have empty cpus or mems
even if there are tasks in the cpuset.

Move this empty cpus/mems check in validate_change() to
cpuset1_validate_change() to allow more flexibility in setting
cpus or mems in v2. cpuset_is_populated() needs to be moved into
cpuset-internal.h as it is needed by the empty cpus/mems checking code.

Also add a test case to test_cpuset_prs.sh to verify that.

Reported-by: Chen Ridong <chenridong@huaweicloud.com>
Closes: https://lore.kernel.org/lkml/7a3ec392-2e86-4693-aa9f-1e668a668b9c@huaweicloud.com/
Signed-off-by: Waiman Long <longman@redhat.com>
Reviewed-by: Chen Ridong <chenridong@huawei.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
kernel/cgroup/cpuset-internal.h
kernel/cgroup/cpuset-v1.c
kernel/cgroup/cpuset.c
tools/testing/selftests/cgroup/test_cpuset_prs.sh

index e8e2683cb067c00b9029b8f288590068d3704633..fd7d19842ded7d95bd29aa5dc55e76f963acc805 100644 (file)
@@ -260,6 +260,15 @@ static inline int nr_cpusets(void)
        return static_key_count(&cpusets_enabled_key.key) + 1;
 }
 
+static inline bool cpuset_is_populated(struct cpuset *cs)
+{
+       lockdep_assert_cpuset_lock_held();
+
+       /* Cpusets in the process of attaching should be considered as populated */
+       return cgroup_is_populated(cs->css.cgroup) ||
+               cs->attach_in_progress;
+}
+
 /**
  * cpuset_for_each_child - traverse online children of a cpuset
  * @child_cs: loop cursor pointing to the current child
index 04124c38a774da6ecb3b813c262299c87407c475..7a23b9e8778f126e1e154d09f978c7704071c990 100644 (file)
@@ -368,6 +368,20 @@ int cpuset1_validate_change(struct cpuset *cur, struct cpuset *trial)
        if (par && !is_cpuset_subset(trial, par))
                goto out;
 
+       /*
+        * Cpusets with tasks - existing or newly being attached - can't
+        * be changed to have empty cpus_allowed or mems_allowed.
+        */
+       ret = -ENOSPC;
+       if (cpuset_is_populated(cur)) {
+               if (!cpumask_empty(cur->cpus_allowed) &&
+                   cpumask_empty(trial->cpus_allowed))
+                       goto out;
+               if (!nodes_empty(cur->mems_allowed) &&
+                   nodes_empty(trial->mems_allowed))
+                       goto out;
+       }
+
        ret = 0;
 out:
        return ret;
index 83fb83a86b4ba4cf708642de050365eeac341f04..a3dbca125588e116b9770356d0ff453f391bbe2e 100644 (file)
@@ -370,15 +370,6 @@ static inline bool is_in_v2_mode(void)
              (cpuset_cgrp_subsys.root->flags & CGRP_ROOT_CPUSET_V2_MODE);
 }
 
-static inline bool cpuset_is_populated(struct cpuset *cs)
-{
-       lockdep_assert_held(&cpuset_mutex);
-
-       /* Cpusets in the process of attaching should be considered as populated */
-       return cgroup_is_populated(cs->css.cgroup) ||
-               cs->attach_in_progress;
-}
-
 /**
  * partition_is_populated - check if partition has tasks
  * @cs: partition root to be checked
@@ -695,20 +686,6 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial)
 
        par = parent_cs(cur);
 
-       /*
-        * Cpusets with tasks - existing or newly being attached - can't
-        * be changed to have empty cpus_allowed or mems_allowed.
-        */
-       ret = -ENOSPC;
-       if (cpuset_is_populated(cur)) {
-               if (!cpumask_empty(cur->cpus_allowed) &&
-                   cpumask_empty(trial->cpus_allowed))
-                       goto out;
-               if (!nodes_empty(cur->mems_allowed) &&
-                   nodes_empty(trial->mems_allowed))
-                       goto out;
-       }
-
        /*
         * We can't shrink if we won't have enough room for SCHED_DEADLINE
         * tasks. This check is not done when scheduling is disabled as the
index ff4540b0490ea3826d65400706a7538da8083eb1..5dff3ad53867c56db2f0e4905315783c35e1c35f 100755 (executable)
@@ -425,6 +425,9 @@ TEST_MATRIX=(
        # cpuset.cpus can be set to a subset of sibling's cpuset.cpus.exclusive
        " C1-3:X1-3  .      .    C4-5      .     .      .     C1-2   0 A1:1-3|B1:1-2"
 
+       # cpuset.cpus can become empty with task in it as it inherits parent's effective CPUs
+       " C1-3:S+   C2      .      .       .    T:C     .      .     0 A1:1-3|A2:1-3"
+
        #  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
        #  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
        # Failure cases: