mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
cgroup: Fixes for v7.4
- cgroup.kill did not kill a process whose thread group leader had exited
while other threads still ran, nor those threads once moved to another
cgroup, letting a process survive its container being killed.
- A cgroup.procs write under favordynmods could migrate a newly forked
process before fork linked it to its cgroup, corrupting cgroup task lists.
- cpuset partition fixes for nested hierarchies: invalidation, revival,
shrinking and switches between root and isolated could leak isolated CPUs,
skip or miscompute the housekeeping check, or leave wrong effective CPUs.
- A root partition can no longer be created under an isolated parent or
stay valid under one, as disabling it could leave no housekeeping CPUs.
- Enabling a partition failed when a grandchild held tasks, although the
parent had none of its own.
- Separately, an allocation racing with a cpuset.mems update could drop
its mempolicy nodemask and land on the wrong node, and a memoryless node
in cpuset.mems triggered a spurious "movable nodes only" warning.
- Two no-functional-change cleanups of the effective CPU computation.
-----BEGIN PGP SIGNATURE-----
iIQEABYKACwWIQTfIjM1kS57o3GsC/uxYfJx3gVYGQUCauD/Ow4cdGpAa2VybmVs
Lm9yZwAKCRCxYfJx3gVYGUHZAQDs9j6gMDplJmgdPYo9OE+lxV+CbeyQcAiKmYgU
UKkYWQD/dQqxs+SocRMl6uPMInFCd9tplbZgAhbAKsmRWUfyBwI=
=flEV
-----END PGP SIGNATURE-----
Merge tag 'cgroup-for-7.4-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup
Pull cgroup fixes from Tejun Heo:
- cgroup.kill did not kill a process whose thread group leader had
exited while other threads still ran, nor those threads once moved to
another cgroup, letting a process survive its container being killed
- A cgroup.procs write under favordynmods could migrate a newly forked
process before fork linked it to its cgroup, corrupting cgroup task
lists
- cpuset partition fixes for nested hierarchies: invalidation, revival,
shrinking and switches between root and isolated could leak isolated
CPUs, skip or miscompute the housekeeping check, or leave wrong
effective CPUs:
- A root partition can no longer be created under an isolated
parent or stay valid under one, as disabling it could leave no
housekeeping CPUs
- Enabling a partition failed when a grandchild held tasks,
although the parent had none of its own
- Separately, an allocation racing with a cpuset.mems update could
drop its mempolicy nodemask and land on the wrong node, and a
memoryless node in cpuset.mems triggered a spurious "movable
nodes only" warning
- Two no-functional-change cleanups of the effective CPU
computation
* tag 'cgroup-for-7.4-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup: (27 commits)
cgroup/cpuset: Integrate compute_partition_effective_cpumask() into compute_effective_cpumask()
cgroup/cpuset: Relocate compute_*effective_cpumask() and compute_effective_nodemask()
cgroup/cpuset: Release invalid isolated CPUs with a shrunk root partition parent
cgroup/cpuset: Release invalid isolated CPUs in compute_partition_effective_cpumask()
cgroup/cpuset: Make sure compute_partition_effective_cpumask() is only called for valid partition
cgroup/cpuset: Prevent recursive call of remote_cpus_update() via update_cpumasks_hier()
selftests/cgroup: Check for a single range nohz_full CPUs before running test matrix
cgroup/cpuset: Minor fixes to prstate_housekeeping_conflict()
cgroup/cpuset: Fix incorrect effective_cpus bug in update_cpumasks_hier()
cgroup/cpuset: Properly compute effective xcpus in update_parent_effective_cpumask()
cgroup/cpuset: Find furthest isolated ancestor for invalidated isolated partition
cgroup: don't kill migrated processes through their exiting threads
cgroup/cpuset: check effective mems for a movable-only configuration
cgroup/cpuset: Fix task checking when enabling a partition
cgroup: serialize migrations of newly forked processes
selftests/cgroup: test cgroup.kill with an exited thread group leader
cgroup: fix cgroup.kill for exited thread group leader
selftests/cgroup: Skip test_cpuset_prs.sh test if some required CPUs are offline
selftests/cgroup: Add tests for nohz_full housekeeping check
selftests/cgroup: Add tests for isolcpus housekeeping check
...
This commit is contained in:
commit
2152c755f7
|
|
@ -2778,7 +2778,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.
|
||||
|
|
|
|||
|
|
@ -4303,7 +4303,7 @@ static void __cgroup_kill(struct cgroup *cgrp)
|
|||
cgrp->kill_seq++;
|
||||
spin_unlock_irq(&css_set_lock);
|
||||
|
||||
css_task_iter_start(&cgrp->self, CSS_TASK_ITER_PROCS | CSS_TASK_ITER_THREADED, &it);
|
||||
css_task_iter_start(&cgrp->self, CSS_TASK_ITER_THREADED, &it);
|
||||
while ((task = css_task_iter_next(&it))) {
|
||||
/* Ignore kernel threads here. */
|
||||
if (task->flags & PF_KTHREAD)
|
||||
|
|
@ -6880,6 +6880,31 @@ static struct cgroup *cgroup_get_from_file(struct file *f)
|
|||
return cgrp;
|
||||
}
|
||||
|
||||
/*
|
||||
* A new threadgroup gets its own cgroup_threadgroup_rwsem. Once its PID is
|
||||
* visible, a cgroup.procs writer can find the child and lock that rwsem even
|
||||
* though cgroup_post_fork() has not linked the child to its css_set yet.
|
||||
*
|
||||
* cgroup_can_fork() already holds the global read lock, which keeps
|
||||
* cgroup_enable_per_threadgroup_rwsem stable. Lock the child's rwsem as a
|
||||
* nested instance until cgroup_post_fork() finishes. Threads share their
|
||||
* parent's signal_struct and are covered by the existing read lock.
|
||||
*/
|
||||
static void cgroup_fork_child_lock(struct task_struct *child)
|
||||
{
|
||||
if (cgroup_enable_per_threadgroup_rwsem &&
|
||||
child->signal != current->signal)
|
||||
down_read_nested(&child->signal->cgroup_threadgroup_rwsem,
|
||||
SINGLE_DEPTH_NESTING);
|
||||
}
|
||||
|
||||
static void cgroup_fork_child_unlock(struct task_struct *child)
|
||||
{
|
||||
if (cgroup_enable_per_threadgroup_rwsem &&
|
||||
child->signal != current->signal)
|
||||
up_read(&child->signal->cgroup_threadgroup_rwsem);
|
||||
}
|
||||
|
||||
/**
|
||||
* cgroup_css_set_fork - find or create a css_set for a child process
|
||||
* @kargs: the arguments passed to create the child process
|
||||
|
|
@ -7045,6 +7070,7 @@ int cgroup_can_fork(struct task_struct *child, struct kernel_clone_args *kargs)
|
|||
goto out_revert;
|
||||
} while_each_subsys_mask();
|
||||
|
||||
cgroup_fork_child_lock(child);
|
||||
return 0;
|
||||
|
||||
out_revert:
|
||||
|
|
@ -7079,6 +7105,7 @@ void cgroup_cancel_fork(struct task_struct *child,
|
|||
if (ss->cancel_fork)
|
||||
ss->cancel_fork(child, kargs->cset);
|
||||
|
||||
cgroup_fork_child_unlock(child);
|
||||
cgroup_css_set_put_fork(kargs);
|
||||
}
|
||||
|
||||
|
|
@ -7176,6 +7203,7 @@ void cgroup_post_fork(struct task_struct *child,
|
|||
if (unlikely(kill))
|
||||
do_send_sig_info(SIGKILL, SEND_SIG_NOINFO, child, PIDTYPE_TGID);
|
||||
|
||||
cgroup_fork_child_unlock(child);
|
||||
cgroup_css_set_put_fork(kargs);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ enum prs_errcode {
|
|||
PERR_HKEEPING,
|
||||
PERR_ACCESS,
|
||||
PERR_REMOTE,
|
||||
PERR_ISOPARENT,
|
||||
};
|
||||
|
||||
/* bits in struct cpuset flags field */
|
||||
|
|
@ -160,6 +161,11 @@ struct cpuset {
|
|||
*/
|
||||
bool remote_partition;
|
||||
|
||||
/*
|
||||
* Set to true when a remote partition is undergoing update.
|
||||
*/
|
||||
bool remote_partition_updating;
|
||||
|
||||
/*
|
||||
* number of SCHED_DEADLINE tasks attached to this cpuset, so that we
|
||||
* know when to rebuild associated root domain bandwidth information.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -67,6 +67,30 @@ then
|
|||
echo Y > /sys/kernel/debug/sched/verbose
|
||||
fi
|
||||
|
||||
cleanup()
|
||||
{
|
||||
online_cpus
|
||||
cd $CGROUP2
|
||||
rmdir A1/A2/A3 A1/A2 A1 B1 test/A1 test/B1 test > /dev/null 2>&1
|
||||
rmdir rtest/p1/c11 rtest/p1/c12 rtest/p2/c21 \
|
||||
rtest/p2/c22 rtest/p1 rtest/p2 rtest > /dev/null 2>&1
|
||||
[[ -n "$SCHED_DEBUG" ]] &&
|
||||
echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose
|
||||
}
|
||||
|
||||
online_cpus()
|
||||
{
|
||||
[[ -n "$OFFLINE_CPUS" ]] && {
|
||||
for C in $OFFLINE_CPUS
|
||||
do
|
||||
write_cpu_online ${C}=1
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
# Clean up the cgroupfs directory if the test is aborted.
|
||||
trap cleanup 0 2 3 6
|
||||
|
||||
cd $CGROUP2
|
||||
echo +cpuset > cgroup.subtree_control
|
||||
|
||||
|
|
@ -85,39 +109,84 @@ echo "" > test/cpuset.cpus
|
|||
[[ $RESULT -eq 0 ]] && skip_test "Child cgroups are using cpuset!"
|
||||
|
||||
#
|
||||
# If isolated CPUs have been reserved at boot time (as shown in
|
||||
# cpuset.cpus.isolated), these isolated CPUs should be outside of CPUs 0-8
|
||||
# that will be used by this script for testing purpose. If not, some of
|
||||
# the tests may fail incorrectly. Wait a bit and retry again just in case
|
||||
# these isolated CPUs are leftover from previous run and have just been
|
||||
# cleaned up earlier in this script.
|
||||
# If nohz_full CPUs have been reserved at boot time, they should not include
|
||||
# CPUs 0-7 as that may fail some of the test cases. On the other hand if the
|
||||
# set of nohz_full CPUs includes all the CPUs except 0-7 in a single range
|
||||
# <first>-<last>, we can run a special set of housekeeping CPUs exhaustion
|
||||
# tests to see if the cpuset code will reject them.
|
||||
#
|
||||
# These pre-isolated CPUs should stay in an isolated state throughout the
|
||||
FIRST_NOHZ_FULL_CPU=
|
||||
LAST_NOHZ_FULL_CPU=
|
||||
RUN_NOHZ_FULL_TESTS=
|
||||
NOHZ_FULL_CPUS=
|
||||
[[ -f /sys/devices/system/cpu/nohz_full ]] &&
|
||||
NOHZ_FULL_CPUS=$(cat /sys/devices/system/cpu/nohz_full)
|
||||
LAST_PRESENT_CPU=$(cat /sys/devices/system/cpu/present | sed -e "s/.*[,-]//")
|
||||
[[ -n "$NOHZ_FULL_CPUS" ]] && {
|
||||
FIRST_NOHZ_FULL_CPU=$(echo $NOHZ_FULL_CPUS | sed -e "s/[,-].*//")
|
||||
LAST_NOHZ_FULL_CPU=$(echo $NOHZ_FULL_CPUS | sed -e "s/,.*//;s/.*[-]//")
|
||||
echo "Boot nohz_full CPUs: $NOHZ_FULL_CPUS"
|
||||
}
|
||||
[[ -n "$FIRST_NOHZ_FULL_CPU" && "$FIRST_NOHZ_FULL_CPU" -le 7 ]] &&
|
||||
skip_test "Boot NOHZ_FULL CPUs overlap CPUs to be tested!"
|
||||
|
||||
[[ -n "$FIRST_NOHZ_FULL_CPU" && "$FIRST_NOHZ_FULL_CPU" = 8 &&
|
||||
"$LAST_NOHZ_FULL_CPU" = $LAST_PRESENT_CPU ]] && RUN_NOHZ_FULL_TESTS=1
|
||||
|
||||
#
|
||||
# If isolated CPUs have been reserved at boot time (as shown in
|
||||
# /sys/devices/system/cpu/isolated), these isolated CPUs should be outside of
|
||||
# CPUs 0-8 that will be used by this script for testing purpose. If not, some
|
||||
# of the tests may fail incorrectly.
|
||||
#
|
||||
# The current set of isolated CPUs (as shown in cpuset.cpus.isolated) should
|
||||
# be the same as the boot value. If not, wait a bit and retry again just in
|
||||
# case these isolated CPUs are leftover from previous run and have just been
|
||||
# cleaned up earlier in this script. If they still don't match, we report an
|
||||
# warning and skip the test.
|
||||
#
|
||||
# These boot isolated CPUs should stay in an isolated state throughout the
|
||||
# testing process for now.
|
||||
#
|
||||
BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
|
||||
[[ -n "$BOOT_ISOLCPUS" ]] && {
|
||||
sleep 0.5
|
||||
BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
|
||||
}
|
||||
if [[ -n "$BOOT_ISOLCPUS" ]]
|
||||
then
|
||||
[[ $(echo $BOOT_ISOLCPUS | sed -e "s/[,-].*//") -le 8 ]] &&
|
||||
skip_test "Pre-isolated CPUs ($BOOT_ISOLCPUS) overlap CPUs to be tested"
|
||||
echo "Pre-isolated CPUs: $BOOT_ISOLCPUS"
|
||||
fi
|
||||
BOOT_ISOLCPUS=$(cat /sys/devices/system/cpu/isolated)
|
||||
CURRENT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
|
||||
FIRST_ISOLCPUS=
|
||||
TEST_ISOLCPUS=
|
||||
|
||||
cleanup()
|
||||
{
|
||||
online_cpus
|
||||
cd $CGROUP2
|
||||
rmdir A1/A2/A3 A1/A2 A1 B1 test/A1 test/B1 test > /dev/null 2>&1
|
||||
rmdir rtest/p1/c11 rtest/p1/c12 rtest/p2/c21 \
|
||||
rtest/p2/c22 rtest/p1 rtest/p2 rtest > /dev/null 2>&1
|
||||
[[ -n "$SCHED_DEBUG" ]] &&
|
||||
echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose
|
||||
[[ -n "$BOOT_ISOLCPUS" ]] && {
|
||||
FIRST_ISOLCPUS=$(echo $BOOT_ISOLCPUS | sed -e "s/[,-].*//")
|
||||
echo "Boot isolated CPUs: $BOOT_ISOLCPUS"
|
||||
[[ $FIRST_ISOLCPUS -le 8 ]] &&
|
||||
skip_test "Boot isolated CPUs overlap CPUs to be tested"
|
||||
}
|
||||
|
||||
[[ "$BOOT_ISOLCPUS" != "$CURRENT_ISOLCPUS" ]] && {
|
||||
sleep 0.5
|
||||
CURRENT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated)
|
||||
[[ "$BOOT_ISOLCPUS" != "$CURRENT_ISOLCPUS" ]] &&
|
||||
skip_test "Current isolated CPUs ($CURRENT_ISOLCPUS) don't match boot isolated CPUs!"
|
||||
}
|
||||
|
||||
#
|
||||
# This test assumes that all the relevant CPUs being tested are online.
|
||||
# If some of them are offline, some of the tests may fail. So we are going to
|
||||
# skip running this test script if some of the required CPUs may be offline.
|
||||
# The required CPUs include CPUs 0-7 and the first boot time isolated CPU if
|
||||
# present. However the check below is not exhaustive and can be a false
|
||||
# positive, but users should not run this test if some CPUs are offline.
|
||||
#
|
||||
ONLINE_CPUS=$(cat /sys/devices/system/cpu/online | sed -e "s/,.*//")
|
||||
ONLINE_FIRST=$(echo $ONLINE_CPUS | sed -e "s/-.*//")
|
||||
ONLINE_LAST=$(echo $ONLINE_CPUS | sed -e "s/.*-//")
|
||||
if [[ -n "$FIRST_ISOLCPUS" ]]
|
||||
then
|
||||
LAST_REQUIRED_CPU=$FIRST_ISOLCPUS
|
||||
else
|
||||
LAST_REQUIRED_CPU=7
|
||||
fi
|
||||
[[ $ONLINE_FIRST != 0 || $ONLINE_LAST -lt $LAST_REQUIRED_CPU ]] &&
|
||||
skip_test "Some of the required CPUs are offline or not present!"
|
||||
|
||||
# Pause in ms
|
||||
pause()
|
||||
{
|
||||
|
|
@ -202,7 +271,7 @@ test_add_proc()
|
|||
#
|
||||
# ECPUs - effective CPUs of cpusets
|
||||
# Pstate - partition root state
|
||||
# ISOLCPUS - isolated CPUs (<icpus>[,<icpus2>])
|
||||
# ISOLCPUS - isolated CPUs (<icpus>[,<icpus2>] or .)
|
||||
#
|
||||
# Note that if there are 2 fields in ISOLCPUS, the first one is for
|
||||
# sched-debug matching which includes offline CPUs and single-CPU partitions
|
||||
|
|
@ -278,6 +347,7 @@ TEST_MATRIX=(
|
|||
" C0-3 C1-3 C2-3 C4 X2-3 X2-3 X2-3:P2 P2:C4-5 0 A3:2-3|B1:4-5 A3:P2|B1:P2 2-5"
|
||||
" C4:X0-3 X1-3 X2-3 . . P2 . . 0 A1:4|A2:1-3|A3:1-3 A2:P2 1-3"
|
||||
" C4:X0-3 X1-3 X2-3 . . . P2 . 0 A1:4|A2:4|A3:2-3 A3:P2 2-3"
|
||||
" CX1-6 C1-4:P1 . C4-5 . C1-3 . . 0 A1:4-6|A2:1-3|B1:4-5 A2:P1"
|
||||
|
||||
# Nested remote/local partition tests
|
||||
" C0-3 C1-3 C2-3 C4-5 X2-3 X2-3:P1 P2 P1 0 A1:0-1|A2:|A3:2-3|B1:4-5 \
|
||||
|
|
@ -288,16 +358,19 @@ 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"
|
||||
" C0-4:X2-4 C1-4:X2-4:P2 C2-4:X4:P1 \
|
||||
. . X5 . . 0 A1:0-4|A2:1-4|A3:2-4 \
|
||||
A1:P0|A2:P-2|A3:P-1 ."
|
||||
" 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-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-6:P2 C3-6:P2 C3-4:P2 . . C5-6 . . 0 A1:1-4|A2:5-6|A3:5-6 \
|
||||
A1:P2|A2:P2|A3:P-2 1-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 ."
|
||||
" C1-5:X3-5:P2 C3-5:X4-5:P2 C4-5:X5:P2 \
|
||||
. X1-4 . . . 0 A1:1-3|A2:4|A3:4 \
|
||||
A1:P2|A2:P2|A3:P-2 1-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 +404,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 \
|
||||
|
|
@ -360,6 +433,10 @@ TEST_MATRIX=(
|
|||
# cpuset.cpus is allowed, but those extra CPUs are ignored.
|
||||
" C2-3:P1 C3:P1 . . . C2-4 . . 0 A1:|A2:2-3 A1:P1|A2:P1"
|
||||
|
||||
# A child cannot recover until its exclusive CPUs are available again.
|
||||
" CX1-4:P1 CX3-4:P1 . . X1-2 . . . 0 A1:1-2|A2:1-2|XA2: A1:P1|A2:P-1"
|
||||
" CX1-4:P1 CX3-4:P1 . . X1-2:X1-4 . . . 0 A1:1-2|A2:3-4|XA2:3-4 A1:P1|A2:P1"
|
||||
|
||||
# Taking away all CPUs from parent or itself if there are tasks
|
||||
# will make the partition invalid.
|
||||
" C2-3:P1 C3:P1 . . T C2-3 . . 0 A1:2-3|A2:2-3 A1:P1|A2:P-1"
|
||||
|
|
@ -385,6 +462,9 @@ TEST_MATRIX=(
|
|||
# Enabling partition with child cpusets is allowed
|
||||
" C0-1 C1 . C2-3 P1 . . . 0 A1:0-1|A2:1 A1:P1"
|
||||
|
||||
# Tasks below a new partition belong to it, not to its parent.
|
||||
" C1-2:P1 C1-2 C1-2:T . . P1 . . 0 A1:|A2:1-2|A3:1-2 A1:P1|A2:P1"
|
||||
|
||||
# A partition root with non-partition root parent is invalid| but it
|
||||
# can be made valid if its parent becomes a partition root too.
|
||||
" C0-1 C1 . C2-3 . P2 . . 0 A1:0-1|A2:1 A1:P0|A2:P-2"
|
||||
|
|
@ -430,6 +510,28 @@ 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"
|
||||
|
||||
# A middle isolated parent shrunk below coverage: the released CPUs of
|
||||
# an invalidated child stay isolated as a higher ancestor still covers them.
|
||||
" CX0-4:P2 CX2-4:P2 CX3-4:P2 . . X2 . . 0 A1:0-1,3-4|A2:2|A3:2 \
|
||||
A1:P2|A2:P2|A3:P-2 0-4"
|
||||
|
||||
# When an isolated partition child with root partition parent is invalidated,
|
||||
# its effective xcpus will be removed from isolated_cpus.
|
||||
" C1-4:P1 C3-4:P2 . . T:C3-4 . . . 0 A1:3-4|A2:3-4 A1:P1|A2:P-2 ."
|
||||
|
||||
# When a root partition parent with an isolated partition child is shrunk
|
||||
# so that its effective xcpus is no longer a superset of its child effective
|
||||
# xcpus, the invalid isolated CPUs should be removed from cpuset.cpus.isolated.
|
||||
" C1-4:P1 C3-4:P2 . . T:C4-6 . . . 0 A1:5-6|A2:4 A1:P1|A2:P2 4"
|
||||
" C1-4:P1 C1-2:P2 . . T:C4-6 . . . 0 A1:4-6|A2:4-6 A1:P1|A2:P-2 ."
|
||||
|
||||
#
|
||||
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
|
||||
# ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
|
||||
# Failure cases:
|
||||
|
|
@ -444,6 +546,57 @@ TEST_MATRIX=(
|
|||
" C0-3 . . C4-5 X3-5 . . . 1 A1:0-3|B1:4-5"
|
||||
)
|
||||
|
||||
# Test matrix with boot time isolated CPUs present for housekeeping check testing
|
||||
ISOLCPUS_TEST_MATRIX=(
|
||||
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
|
||||
# ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
|
||||
# A local isolated partition containing boot isolated CPU cannot be
|
||||
# switched to root partition.
|
||||
" C1-2,I:P2 . . . P1 . . . 0 A1:1-2,I A1:P-1 ."
|
||||
|
||||
# A remote isolated partition containing boot isolated CPU cannot be
|
||||
# switched to root partition.
|
||||
" X1-2,I CX1-2,I:P2 . . . P1 . . 0 A2:1-2,I A2:P-1 ."
|
||||
|
||||
# A local isolated partition with a boot isolated CPU distributed to a
|
||||
# child isolated partition also cannot be switched to a root partition.
|
||||
" C1-3,I:P2 C1,I:P2 . . P1 . . . 0 A1:1-3,I|A2:1,I A1:P-1|A2:P-2 ."
|
||||
|
||||
# A remote isolated partition with a boot isolated CPU distributed to a
|
||||
# child isolated partition also cannot be switched to a root partition.
|
||||
" X1-3,I C1-3,I:P2 C1,I:P2 . . P1 . . 0 A2:1-3,I|A3:1,I A2:P-1|A3:P-2 ."
|
||||
|
||||
# The cpumask of a local root partition cannot be changed to include a
|
||||
# boot isolated CPU.
|
||||
" C1-2:P1 . . . C2,I . . . 0 A1:2,I A1:P-1 ."
|
||||
|
||||
# The cpumask of a remote root partition cannot be changed to include a
|
||||
# boot isolated CPU.
|
||||
" CX1-2,I CX1-2:P1 . . . CX2,I . . 0 A1:1-2,I|A2:2,I A2:P-1 ."
|
||||
|
||||
# Passing an boot isolated CPU down to a child root partition will invalidate it
|
||||
" C2-3,I:X2-3 CX2-3,I . . . P1 . . 0 A1:I|A2:2-3 A2:P1 ."
|
||||
" C2-3,I:X2-3 CX2-3,I:P1 . . X2-3,I . . . 0 A1:2-3,I|A2:2-3,I A2:P-1 ."
|
||||
|
||||
# An invalidated root partition must stay invalid when cpus grows but
|
||||
# effective xcpus still contains a boot isolated CPU.
|
||||
" CX1,I:P1 . . . C1,2,I . . . 0 A1:1-2,I|XA1:1,I A1:P-1 ."
|
||||
|
||||
# Growing cpus.exclusive must not revive it either
|
||||
" CX1-2,I:X1,I:P1 . . . X1,2,I . . . 0 A1:1-2,I|XA1:1-2,I A1:P-1 ."
|
||||
)
|
||||
|
||||
# Test matrix to be run when boot time nohz_full CPUs cover all CPUs except CPUs 0-7
|
||||
NOHZ_FULL_TEST_MATRIX=(
|
||||
# old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS
|
||||
# ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
|
||||
# Using all the nohz_full HK CPUs 0-7 in isolated partition is not allowed
|
||||
" C0-7 . . . P2 . . . 0 A1:0-7 A1:P-2 ."
|
||||
" C2-7:P2 . . . C0-4 . . . 0 A1:0-4 A1:P2 0-4"
|
||||
" C1-7:P2 C5-7:P2 . . . . . C0:P2 0 A1:1-4|A2:5-7|B1:0
|
||||
A1:P2|A2:P2|B1:P-2 1-7"
|
||||
)
|
||||
|
||||
#
|
||||
# Cpuset controller remote partition test matrix.
|
||||
#
|
||||
|
|
@ -496,8 +649,8 @@ REMOTE_TEST_MATRIX=(
|
|||
p1:P1|c11:P1|c12:P-1"
|
||||
# Narrowing cpuset.cpus to previously sibling-excluded CPUs should
|
||||
# not return CPUs that were never actually owned.
|
||||
" C1-4:P1 . C1-2:P1 C1-3:P2 . . \
|
||||
. . . C3 . . p1:4|c11:1-2|c12:3 \
|
||||
" C1-5:P1 . C1-2:P1 C1-3:P2 . . \
|
||||
. . . C3 . . p1:4-5|c11:1-2|c12:3 \
|
||||
p1:P1|c11:P1|c12:P2 3"
|
||||
# Expanding cpuset.cpus to include a previously sibling-excluded CPU
|
||||
# after the sibling has become a member should correctly request it.
|
||||
|
|
@ -541,6 +694,27 @@ write_cpu_online()
|
|||
pause 0.05
|
||||
}
|
||||
|
||||
#
|
||||
# Set the CPUS variable
|
||||
# $1 - the passed in cpu parameter
|
||||
#
|
||||
# The special "I" CPU if present is being replaced by the first boot CPU
|
||||
# and assigned to the "CPUS" variable when the TEST_ISOLCPUS flag is set.
|
||||
# If not, the program will exit with an error.
|
||||
#
|
||||
set_cpus()
|
||||
{
|
||||
local cpus=$1
|
||||
if [[ $(expr "$cpus" : ".*I") -gt 0 ]]
|
||||
then
|
||||
[[ -z "$TEST_ISOLCPUS" ]] &&
|
||||
skip_test "Special 'I' CPU added to non-isolcpus testing matrix!"
|
||||
CPUS=$(echo $cpus | sed -e "s/I/$FIRST_ISOLCPUS/")
|
||||
else
|
||||
CPUS=$cpus
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Set controller state
|
||||
# $1 - cgroup directory
|
||||
|
|
@ -575,16 +749,16 @@ set_ctrl_state()
|
|||
}
|
||||
case $CMD in
|
||||
X*)
|
||||
CPUS=${CMD#?}
|
||||
set_cpus ${CMD#?}
|
||||
COMM="echo $CPUS > $XFILE"
|
||||
eval $COMM $REDIRECT
|
||||
;;
|
||||
CX*)
|
||||
CPUS=${CMD#??}
|
||||
set_cpus ${CMD#??}
|
||||
COMM="echo $CPUS > $CFILE; echo $CPUS > $XFILE"
|
||||
eval $COMM $REDIRECT
|
||||
;;
|
||||
C*) CPUS=${CMD#?}
|
||||
C*) set_cpus ${CMD#?}
|
||||
COMM="echo $CPUS > $CFILE"
|
||||
eval $COMM $REDIRECT
|
||||
;;
|
||||
|
|
@ -641,16 +815,6 @@ set_ctrl_state_noerr()
|
|||
}
|
||||
}
|
||||
|
||||
online_cpus()
|
||||
{
|
||||
[[ -n "$OFFLINE_CPUS" ]] && {
|
||||
for C in $OFFLINE_CPUS
|
||||
do
|
||||
write_cpu_online ${C}=1
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Remove all the test cgroup directories
|
||||
#
|
||||
|
|
@ -978,8 +1142,11 @@ run_state_test()
|
|||
I=0
|
||||
eval CNT="\${#$TEST[@]}"
|
||||
|
||||
# Skip isolcpus specific test matrix if FIRST_ISOLCPUS not defined
|
||||
[[ -n "$TEST_ISOLCPUS" && -z "$FIRST_ISOLCPUS" ]] && return
|
||||
|
||||
reset_cgroup_states
|
||||
console_msg "Running state transition test ..."
|
||||
console_msg "Running state transition test for $TEST ..."
|
||||
|
||||
while [[ $I -lt $CNT ]]
|
||||
do
|
||||
|
|
@ -998,7 +1165,7 @@ run_state_test()
|
|||
NEW_A3=$7
|
||||
NEW_B1=$8
|
||||
RESULT=$9
|
||||
ECPUS=${10}
|
||||
ECPUS=$(echo ${10} | sed -e "s/I/${FIRST_ISOLCPUS}/g")
|
||||
STATES=${11}
|
||||
ICPUS=${12}
|
||||
|
||||
|
|
@ -1281,9 +1448,10 @@ test_inotify()
|
|||
echo "" > cpuset.cpus
|
||||
}
|
||||
|
||||
trap cleanup 0 2 3 6
|
||||
run_state_test TEST_MATRIX
|
||||
run_remote_state_test REMOTE_TEST_MATRIX
|
||||
TEST_ISOLCPUS=1 run_state_test ISOLCPUS_TEST_MATRIX
|
||||
[[ -n "$RUN_NOHZ_FULL_TESTS" ]] && run_state_test NOHZ_FULL_TEST_MATRIX
|
||||
test_isolated
|
||||
test_boot_isolated
|
||||
test_inotify
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <linux/limits.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -315,6 +318,159 @@ static int test_cgkill_clone_into_killed(const char *root)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int sleep_forever(void *arg)
|
||||
{
|
||||
for (;;)
|
||||
pause();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a thread and exit the main thread only, leaving behind a zombie
|
||||
* thread group leader with a live thread.
|
||||
*/
|
||||
static int exit_leader_fn(const char *cgroup, void *arg)
|
||||
{
|
||||
const size_t stack_size = 64 * 1024;
|
||||
char *stack = malloc(stack_size);
|
||||
|
||||
if (!stack)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (clone(sleep_forever, stack + stack_size,
|
||||
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
|
||||
CLONE_THREAD | CLONE_SYSVSEM, NULL) < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
syscall(SYS_exit, 0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int wait_for_zombie_leader(pid_t pid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
if (!proc_read_strstr(pid, false, "stat", ") Z "))
|
||||
return 0;
|
||||
usleep(10000);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that cgroup.kill kills a process whose thread group leader has
|
||||
* already exited while its other threads are still running.
|
||||
*/
|
||||
static int test_cgkill_exited_leader(const char *root)
|
||||
{
|
||||
int ret = KSFT_FAIL;
|
||||
char *cgroup = NULL;
|
||||
pid_t pid = -ESRCH;
|
||||
|
||||
cgroup = cg_name(root, "cg_test_exited_leader");
|
||||
if (!cgroup)
|
||||
goto cleanup;
|
||||
|
||||
if (cg_create(cgroup))
|
||||
goto cleanup;
|
||||
|
||||
pid = cg_run_nowait(cgroup, exit_leader_fn, NULL);
|
||||
if (pid < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (wait_for_zombie_leader(pid))
|
||||
goto cleanup;
|
||||
|
||||
if (cg_read_strcmp(cgroup, "cgroup.events", "populated 1\n"))
|
||||
goto cleanup;
|
||||
|
||||
if (cg_write(cgroup, "cgroup.kill", "1"))
|
||||
goto cleanup;
|
||||
|
||||
if (cg_read_strcmp_wait(cgroup, "cgroup.events", "populated 0\n"))
|
||||
goto cleanup;
|
||||
|
||||
ret = KSFT_PASS;
|
||||
|
||||
cleanup:
|
||||
if (pid > 0) {
|
||||
kill(pid, SIGKILL);
|
||||
wait_for_pid(pid);
|
||||
}
|
||||
if (cgroup)
|
||||
cg_destroy(cgroup);
|
||||
free(cgroup);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that cgroup.kill kills the threads of a process that were migrated
|
||||
* to the cgroup after their thread group leader had exited, and so are
|
||||
* not listed in its cgroup.procs.
|
||||
*/
|
||||
static int test_cgkill_exited_leader_migrated(const char *root)
|
||||
{
|
||||
char *parent = NULL, *src = NULL, *dst = NULL;
|
||||
int ret = KSFT_FAIL;
|
||||
pid_t pid = -ESRCH;
|
||||
|
||||
parent = cg_name(root, "cg_test_exited_leader_migrated");
|
||||
if (!parent)
|
||||
goto cleanup;
|
||||
|
||||
src = cg_name(parent, "src");
|
||||
if (!src)
|
||||
goto cleanup;
|
||||
|
||||
dst = cg_name(parent, "dst");
|
||||
if (!dst)
|
||||
goto cleanup;
|
||||
|
||||
if (cg_create(parent) || cg_create(src) || cg_create(dst))
|
||||
goto cleanup;
|
||||
|
||||
pid = cg_run_nowait(src, exit_leader_fn, NULL);
|
||||
if (pid < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (wait_for_zombie_leader(pid))
|
||||
goto cleanup;
|
||||
|
||||
/* The exiting leader stays behind, only the live thread is moved. */
|
||||
if (cg_enter(dst, pid))
|
||||
goto cleanup;
|
||||
|
||||
if (cg_read_strcmp(dst, "cgroup.events", "populated 1\n") ||
|
||||
cg_read_strcmp(dst, "cgroup.procs", ""))
|
||||
goto cleanup;
|
||||
|
||||
if (cg_write(dst, "cgroup.kill", "1"))
|
||||
goto cleanup;
|
||||
|
||||
if (cg_read_strcmp_wait(dst, "cgroup.events", "populated 0\n"))
|
||||
goto cleanup;
|
||||
|
||||
ret = KSFT_PASS;
|
||||
|
||||
cleanup:
|
||||
if (pid > 0) {
|
||||
kill(pid, SIGKILL);
|
||||
wait_for_pid(pid);
|
||||
}
|
||||
if (dst)
|
||||
cg_destroy(dst);
|
||||
if (src)
|
||||
cg_destroy(src);
|
||||
if (parent)
|
||||
cg_destroy(parent);
|
||||
free(dst);
|
||||
free(src);
|
||||
free(parent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define T(x) { x, #x }
|
||||
struct cgkill_test {
|
||||
int (*fn)(const char *root);
|
||||
|
|
@ -324,6 +480,8 @@ struct cgkill_test {
|
|||
T(test_cgkill_tree),
|
||||
T(test_cgkill_forkbomb),
|
||||
T(test_cgkill_clone_into_killed),
|
||||
T(test_cgkill_exited_leader),
|
||||
T(test_cgkill_exited_leader_migrated),
|
||||
};
|
||||
#undef T
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user