Although it is not typical for real users to create a complicated nested
partition hierarchy, it is currently allowed to create nested root and
isolated partitions. In addition, disabling or invalidating a partition
should always be allowed especially when the cpuset is being deleted.

With a nested root and isolated partition hierarchy, it is possible
that all active housekeeping CPUs can be exhausted when disabling or
invalidating a root partition and returning its CPUs to the isolated
parent.

One way to avoid this problem is to disallow the creation of a root
partition under an isolated parent. It is indeed a new limitation imposed
on the existing cpuset partition functionality. However an isolated
partition is usually a left node where scheduling and some other kernel
noises are disabled. It doesn't make much sense to create a child root
partition under an isolated parent and remove some CPUs from the set
of isolated CPUs again. Creation of a child isolated partition under a
root or isolated parent is still allowed.

If a partition with child root partitions is converted to isolated,
the child root partitions will be invalidated.

The cgroup-v2 documentation is also updated to reflect this new
limitation. New test cases are added to test_cpuset_prs.sh to verify
this new behavior. Some existing test cases are also updated accordingly.

Signed-off-by: Waiman Long <[email protected]>
---
 Documentation/admin-guide/cgroup-v2.rst       |  4 ++-
 kernel/cgroup/cpuset-internal.h               |  1 +
 kernel/cgroup/cpuset.c                        | 32 ++++++++++++++++---
 .../selftests/cgroup/test_cpuset_prs.sh       | 23 +++++++------
 4 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/Documentation/admin-guide/cgroup-v2.rst 
b/Documentation/admin-guide/cgroup-v2.rst
index bd8c80f3bbc9..2c445829e24d 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -2767,7 +2767,9 @@ Cpuset Interface Files
        be retained, but behaves more like a "member".
 
        All possible state transitions among "member", "root" and
-       "isolated" are allowed.
+       "isolated" are allowed with the exception that a local child
+       "root" partition is not allowed to be created under an "isolated"
+       parent partition.
 
        On read, the "cpuset.cpus.partition" file can show the following
        values.
diff --git a/kernel/cgroup/cpuset-internal.h b/kernel/cgroup/cpuset-internal.h
index 5da9c91295b0..2e44dc8634dc 100644
--- a/kernel/cgroup/cpuset-internal.h
+++ b/kernel/cgroup/cpuset-internal.h
@@ -35,6 +35,7 @@ enum prs_errcode {
        PERR_HKEEPING,
        PERR_ACCESS,
        PERR_REMOTE,
+       PERR_ISOPARENT,
 };
 
 /* bits in struct cpuset flags field */
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 5a0a4269e7c0..0f047d6bbd9a 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -60,6 +60,7 @@ static const char * const perr_strings[] = {
        [PERR_HKEEPING]  = "partition config conflicts with housekeeping setup",
        [PERR_ACCESS]    = "Enable partition not permitted",
        [PERR_REMOTE]    = "Have remote partition underneath",
+       [PERR_ISOPARENT] = "Root partition is not allowed with isolated parent",
 };
 
 /*
@@ -1899,6 +1900,10 @@ static int update_parent_effective_cpumask(struct cpuset 
*cs, int cmd,
                        part_error = PERR_CPUSEMPTY;
                        goto write_error;
                }
+               if ((parent_prs == PRS_ISOLATED) && (old_prs == -PRS_ROOT)) {
+                       part_error = PERR_ISOPARENT;
+                       goto write_error;
+               }
 
                /* Check newmask again, whether cpus are available for 
parent/cs */
                nocpu |= tasks_nocpu_error(parent, cs, newmask);
@@ -1974,6 +1979,13 @@ static int update_parent_effective_cpumask(struct cpuset 
*cs, int cmd,
                                adding = cpumask_and(tmp->addmask,
                                                     cs->effective_xcpus,
                                                     parent->effective_xcpus);
+               } else if ((parent_prs == PRS_ISOLATED) &&
+                          ((old_prs == PRS_ROOT) || (old_prs == -PRS_ROOT))) {
+                       part_error = PERR_ISOPARENT;
+                       if (old_prs == PRS_ROOT) {
+                               cpumask_copy(tmp->addmask, cs->effective_xcpus);
+                               adding = true;
+                       }
                } else if (is_partition_invalid(cs) && !cpumask_empty(xcpus) &&
                           cpumask_subset(xcpus, parent->effective_xcpus)) {
                        struct cgroup_subsys_state *css;
@@ -2927,6 +2939,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
        int err = PERR_NONE, old_prs = cs->partition_root_state;
        struct cpuset *parent = parent_cs(cs);
        struct tmpmasks tmpmask;
+       int parent_prs = parent->partition_root_state;
        bool isolcpus_updated = false;
        bool disable_partition = false;
 
@@ -2972,6 +2985,14 @@ static int update_prstate(struct cpuset *cs, int new_prs)
                        enum partition_cmd cmd = (new_prs == PRS_ROOT)
                                               ? partcmd_enable : 
partcmd_enablei;
 
+                       /*
+                        * It is not allowed to create a root partition under
+                        * an isolated parent partition.
+                        */
+                       if ((new_prs == PRS_ROOT) && (parent_prs == 
PRS_ISOLATED)) {
+                               err = PERR_ISOPARENT;
+                               goto out;
+                       }
                        err = update_parent_effective_cpumask(cs, cmd, NULL, 
&tmpmask);
                } else {
                        err = remote_partition_enable(cs, new_prs, &tmpmask);
@@ -2985,12 +3006,15 @@ static int update_prstate(struct cpuset *cs, int 
new_prs)
                 * Need to update isolated_cpus.
                 */
                if (prstate_housekeeping_conflict(new_prs, parent_prs,
-                                                 cs->effective_xcpus, NULL)) {
-                       disable_partition = true;
+                                                 cs->effective_xcpus, NULL))
                        err = PERR_HKEEPING;
-               } else {
+               else if (parent_prs == PRS_ISOLATED)
+                       err = PERR_ISOPARENT;
+
+               if (!err)
                        isolcpus_updated = true;
-               }
+               else
+                       disable_partition = true;
        } else {
                /*
                 * Switching back to member is always allowed even if it
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh 
b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index f1dda27b1394..872a9988e4c4 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -288,16 +288,14 @@ TEST_MATRIX=(
                                                                       
A1:P0|A2:P1|A3:P0|B1:P1"
        "   C0-3    C1-3   C3     C4    X2-3  X2-3:P1   P2     P1    0 
A1:0-1|A2:2|A3:3|B1:4 \
                                                                       
A1:P0|A2:P1|A3:P2|B1:P1 2-4|3"
-       "   C0-4    C1-4  C2-4     .    X2-4  X2-4:P2  X4:P1    .    0 
A1:0-1|A2:2-3|A3:4 \
-                                                                      
A1:P0|A2:P2|A3:P1 2-4|2-3"
-       "   C0-4    C1-4  C2-4     .    X2-4  X2-4:P2 X3-4:P1   .    0 
A1:0-1|A2:2|A3:3-4 \
-                                                                      
A1:P0|A2:P2|A3:P1 2"
+       "  C1-6:P1 C3-6:P1 C5-6:P1 .      .      P2      .      .    0 
A1:1-2|A2:3-6|A3:5-6 \
+                                                                      
A1:P1|A2:P2|A3:P-1 3-6"
        " C1-4:X2-4 C2-4:X3-4:P1 C3-4:X4:P2 \
                                   .      .      X5      .      .    0 
A1:1-4|A2:2-4|A3:3-4 \
                                                                       
A1:P0|A2:P-1|A3:P-2 ."
-       " C0-4:X2-4 C1-4:X2-4:P2 C2-4:X4:P1 \
-                                  .      .      .      X1      .    0 
A1:0-1|A2:2-4|A3:2-4 \
-                                                                      
A1:P0|A2:P2|A3:P-1 2-4"
+       " C1-4:X2-4 C1-4:X3-4:P1 C3-4:X4:P2 \
+                                  .      .      .      X1      .    0 
A1:1-2|A2:3-4|A3:3-4 \
+                                                                      
A1:P0|A2:P1|A3:P-2 ."
        " CX1-3:P1 CX1-3  CX1-3    .      .      .      P1      .    0 
A1:1-3|A2:1-3|A3:1-3 \
                                                                       
A1:P1|A2:P0|A3:P-1"
 
@@ -331,8 +329,8 @@ TEST_MATRIX=(
        " C0-3:X1-3:P2 C1-3:X2-3:P2 C2-3:X3:P2 \
                                   .      .    C4:X     .      .     0 
A1:1-3|A2:1-3|A3:2-3|XA2:|XA3: A1:P2|A2:P-2|A3:P-2 1-3"
        # Local partition CPU change tests
-       " C0-5:P2  C4-5:P1  .      .      .    C3-5     .      .     0 
A1:0-2|A2:3-5 A1:P2|A2:P1 0-2"
-       " C0-5:P2  C4-5:P1  .      .    C1-5     .      .      .     0 
A1:1-3|A2:4-5 A1:P2|A2:P1 1-3"
+       " C0-5:P1  C4-5:P2  .      .      .    C3-5     .      .     0 
A1:0-2|A2:3-5 A1:P1|A2:P2 3-5"
+       " C0-5:P1  C4-5:P2  .      .    C1-5     .      .      .     0 
A1:1-3|A2:4-5 A1:P1|A2:P2 4-5"
 
        # cpus_allowed/exclusive_cpus update tests
        " C0-3:X2-3 C1-3:X2-3 C2-3:X2-3 \
@@ -430,6 +428,13 @@ TEST_MATRIX=(
        # cpuset.cpus can become empty with task in it as it inherits parent's 
effective CPUs
        "   C1-3    C2      .      .       .    T:C     .      .     0 
A1:1-3|A2:1-3"
 
+       # Creation of root partition under an isolated partition is not allowed
+       "  C1-3:P2  C2      .      .       .     P1     .      .     0 
A1:1-3|A2:2 A1:P2|A2:P-1 1-3"
+       "  C1-3:P2  C2      .      .       .     P2     .      .     0 
A1:1,3|A2:2 A1:P2|A2:P2  1-3"
+       "  C1-3:P2 C2:P2    .      .       .     P1     .      .     0 
A1:1-3|A2:2 A1:P2|A2:P-1 1-3"
+       "  C1-3:P2 C2:P1    .      .       .    C2-3    .      .     0 
A1:1-3|A2:2-3 A1:P2|A2:P-1 1-3"
+
+       #
        #  old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs 
Pstate ISOLCPUS
        #  ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- 
------ --------
        # Failure cases:
-- 
2.55.0


Reply via email to