1

cgroup/cpuset: Delay setting of CS_CPU_EXCLUSIVE until valid partition

The CS_CPU_EXCLUSIVE flag is currently set whenever cpuset.cpus.exclusive
is set to make sure that the exclusivity test will be run to ensure its
exclusiveness. At the same time, this flag can be changed whenever the
partition root state is changed. For example, the CS_CPU_EXCLUSIVE flag
will be reset whenever a partition root becomes invalid. This makes
using CS_CPU_EXCLUSIVE to ensure exclusiveness a bit fragile.

The current scheme also makes setting up a cpuset.cpus.exclusive
hierarchy to enable remote partition harder as cpuset.cpus.exclusive
cannot overlap with any cpuset.cpus of sibling cpusets if their
cpuset.cpus.exclusive aren't set.

Solve these issues by deferring the setting of CS_CPU_EXCLUSIVE flag
until the cpuset become a valid partition root while adding new checks
in validate_change() to ensure that cpuset.cpus.exclusive of sibling
cpusets cannot overlap.

An additional check is also added to validate_change() to make sure that
cpuset.cpus of one cpuset cannot be a subset of cpuset.cpus.exclusive
of a sibling cpuset to avoid the problem that none of those CPUs will
be available when these exclusive CPUs are extracted out to a newly
enabled partition root. The Documentation/admin-guide/cgroup-v2.rst
file is updated to document the new constraints.

Signed-off-by: Waiman Long <longman@redhat.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
This commit is contained in:
Waiman Long 2024-06-17 10:39:43 -04:00 committed by Tejun Heo
parent 43ee401453
commit fe8cd2736e
2 changed files with 35 additions and 9 deletions

View File

@ -2359,8 +2359,12 @@ Cpuset Interface Files
is always a subset of it. is always a subset of it.
Users can manually set it to a value that is different from Users can manually set it to a value that is different from
"cpuset.cpus". The only constraint in setting it is that the "cpuset.cpus". One constraint in setting it is that the list of
list of CPUs must be exclusive with respect to its sibling. CPUs must be exclusive with respect to "cpuset.cpus.exclusive"
of its sibling. If "cpuset.cpus.exclusive" of a sibling cgroup
isn't set, its "cpuset.cpus" value, if set, cannot be a subset
of it to leave at least one CPU available when the exclusive
CPUs are taken away.
For a parent cgroup, any one of its exclusive CPUs can only For a parent cgroup, any one of its exclusive CPUs can only
be distributed to at most one of its child cgroups. Having an be distributed to at most one of its child cgroups. Having an

View File

@ -825,17 +825,41 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial)
/* /*
* If either I or some sibling (!= me) is exclusive, we can't * If either I or some sibling (!= me) is exclusive, we can't
* overlap * overlap. exclusive_cpus cannot overlap with each other if set.
*/ */
ret = -EINVAL; ret = -EINVAL;
cpuset_for_each_child(c, css, par) { cpuset_for_each_child(c, css, par) {
if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) && bool txset, cxset; /* Are exclusive_cpus set? */
c != cur) {
if (c == cur)
continue;
txset = !cpumask_empty(trial->exclusive_cpus);
cxset = !cpumask_empty(c->exclusive_cpus);
if (is_cpu_exclusive(trial) || is_cpu_exclusive(c) ||
(txset && cxset)) {
if (!cpusets_are_exclusive(trial, c)) if (!cpusets_are_exclusive(trial, c))
goto out; goto out;
} else if (txset || cxset) {
struct cpumask *xcpus, *acpus;
/*
* When just one of the exclusive_cpus's is set,
* cpus_allowed of the other cpuset, if set, cannot be
* a subset of it or none of those CPUs will be
* available if these exclusive CPUs are activated.
*/
if (txset) {
xcpus = trial->exclusive_cpus;
acpus = c->cpus_allowed;
} else {
xcpus = c->exclusive_cpus;
acpus = trial->cpus_allowed;
}
if (!cpumask_empty(acpus) && cpumask_subset(acpus, xcpus))
goto out;
} }
if ((is_mem_exclusive(trial) || is_mem_exclusive(c)) && if ((is_mem_exclusive(trial) || is_mem_exclusive(c)) &&
c != cur &&
nodes_intersects(trial->mems_allowed, c->mems_allowed)) nodes_intersects(trial->mems_allowed, c->mems_allowed))
goto out; goto out;
} }
@ -1375,7 +1399,7 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs,
*/ */
static int update_partition_exclusive(struct cpuset *cs, int new_prs) static int update_partition_exclusive(struct cpuset *cs, int new_prs)
{ {
bool exclusive = (new_prs > 0); bool exclusive = (new_prs > PRS_MEMBER);
if (exclusive && !is_cpu_exclusive(cs)) { if (exclusive && !is_cpu_exclusive(cs)) {
if (update_flag(CS_CPU_EXCLUSIVE, cs, 1)) if (update_flag(CS_CPU_EXCLUSIVE, cs, 1))
@ -2620,8 +2644,6 @@ static int update_exclusive_cpumask(struct cpuset *cs, struct cpuset *trialcs,
retval = cpulist_parse(buf, trialcs->exclusive_cpus); retval = cpulist_parse(buf, trialcs->exclusive_cpus);
if (retval < 0) if (retval < 0)
return retval; return retval;
if (!is_cpu_exclusive(cs))
set_bit(CS_CPU_EXCLUSIVE, &trialcs->flags);
} }
/* Nothing to do if the CPUs didn't change */ /* Nothing to do if the CPUs didn't change */