mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
[1.3] libct: reset CPU affinity by default
In certain deployments, it's possible for runc to be spawned by a
process with a restrictive cpumask (such as from a systemd unit with
CPUAffinity=... configured) which will be inherited by runc and thus the
container process by default.
The cpuset cgroup used to reconfigure the cpumask automatically for
joining processes, but kcommit da019032819a ("sched: Enforce user
requested affinity") changed this behaviour in Linux 6.2.
The solution is to try to emulate the expected behaviour by resetting
our cpumask to correspond with the configured cpuset (in the case of
"runc exec", if the user did not configure an alternative one). Normally
we would have to parse /proc/stat and /sys/fs/cgroup, but luckily
sched_setaffinity(2) will transparently convert an all-set cpumask (even
if it has more entries than the number of CPUs on the system) to the
correct value for our usecase.
For some reason, in our CI it seems that rootless --systemd-cgroup
results in the cpuset (presumably temporarily?) being configured such
that sched_setaffinity(2) will allow the full set of CPUs. For this
particular case, all we care about is that it is different to the
original set, so include some special-casing (but we should probably
investigate this further...).
Reported-by: ningmingxiao <ning.mingxiao@zte.com.cn>
Reported-by: Martin Sivak <msivak@redhat.com>
Reported-by: Peter Hunt <pehunt@redhat.com>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
(Cherry-pick of commit 121192ade6c55f949d32ba486219e2b1d86898b2.)
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
@@ -4,9 +4,14 @@
|
||||
|
||||
load helpers
|
||||
|
||||
INITIAL_CPU_MASK="$(grep -F Cpus_allowed_list: /proc/self/status | awk '{ print $2 }')"
|
||||
|
||||
function setup() {
|
||||
requires smp cgroups_cpuset
|
||||
setup_busybox
|
||||
|
||||
echo "Initial CPU mask: $INITIAL_CPU_MASK" >&2
|
||||
echo "---" >&2
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
@@ -99,3 +104,107 @@ function cpus_to_mask() {
|
||||
[[ "$output" == *"nsexec"*": affinity: $mask"* ]]
|
||||
[[ "$output" == *"Cpus_allowed_list: $final"* ]] # Mind the literal tab.
|
||||
}
|
||||
|
||||
@test "runc run [CPU affinity should reset]" {
|
||||
# We need to use RUNC_CMDLINE since taskset requires a proper binary, not a
|
||||
# bash function (which is what runc and __runc are).
|
||||
setup_runc_cmdline
|
||||
|
||||
first="$(first_cpu)"
|
||||
|
||||
# Running without cpuset should result in an affinity for all CPUs.
|
||||
update_config '.process.args = [ "/bin/grep", "-F", "Cpus_allowed_list:", "/proc/self/status" ]'
|
||||
update_config 'del(.linux.resources.cpu)'
|
||||
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" run ctr
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != $'Cpus_allowed_list:\t'"$first" ]]
|
||||
[[ "$output" == $'Cpus_allowed_list:\t'"$INITIAL_CPU_MASK" ]]
|
||||
}
|
||||
|
||||
@test "runc run [CPU affinity should reset to cgroup cpuset]" {
|
||||
[ $EUID -ne 0 ] && requires rootless_cgroup
|
||||
set_cgroups_path
|
||||
|
||||
# We need to use RUNC_CMDLINE since taskset requires a proper binary, not a
|
||||
# bash function (which is what runc and __runc are).
|
||||
setup_runc_cmdline
|
||||
|
||||
first="$(first_cpu)"
|
||||
second="$((first + 1))" # Hacky; might not work in all environments.
|
||||
|
||||
# Running with a cpuset should result in an affinity that matches.
|
||||
update_config '.process.args = [ "/bin/grep", "-F", "Cpus_allowed_list:", "/proc/self/status" ]'
|
||||
update_config '.linux.resources.cpu = {"mems": "0", "cpus": "'"$first-$second"'"}'
|
||||
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" run ctr
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != $'Cpus_allowed_list:\t'"$first" ]]
|
||||
# XXX: For some reason, systemd-cgroup leads to us using the all-set
|
||||
# cpumask rather than the cpuset we configured?
|
||||
[ -v RUNC_USE_SYSTEMD ] || [[ "$output" == $'Cpus_allowed_list:\t'"$first-$second" ]]
|
||||
|
||||
# Ditto for a cpuset that has no overlap with the original cpumask.
|
||||
update_config '.linux.resources.cpu = {"mems": "0", "cpus": "'"$second"'"}'
|
||||
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" run ctr
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != $'Cpus_allowed_list:\t'"$first" ]]
|
||||
# XXX: For some reason, systemd-cgroup leads to us using the all-set
|
||||
# cpumask rather than the cpuset we configured?
|
||||
[ -v RUNC_USE_SYSTEMD ] || [[ "$output" == $'Cpus_allowed_list:\t'"$second" ]]
|
||||
}
|
||||
|
||||
@test "runc exec [default CPU affinity should reset]" {
|
||||
# We need to use RUNC_CMDLINE since taskset requires a proper binary, not a
|
||||
# bash function (which is what runc and __runc are).
|
||||
setup_runc_cmdline
|
||||
|
||||
first="$(first_cpu)"
|
||||
|
||||
# Running without cpuset should result in an affinity for all CPUs.
|
||||
update_config '.process.args = [ "/bin/sleep", "infinity" ]'
|
||||
update_config 'del(.linux.resources.cpu)'
|
||||
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" run -d --console-socket "$CONSOLE_SOCKET" ctr3
|
||||
[ "$status" -eq 0 ]
|
||||
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" exec ctr3 grep -F Cpus_allowed_list: /proc/self/status
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != $'Cpus_allowed_list:\t'"$first" ]]
|
||||
[[ "$output" == $'Cpus_allowed_list:\t'"$INITIAL_CPU_MASK" ]]
|
||||
}
|
||||
|
||||
@test "runc exec [default CPU affinity should reset to cgroup cpuset]" {
|
||||
[ $EUID -ne 0 ] && requires rootless_cgroup
|
||||
set_cgroups_path
|
||||
|
||||
# We need to use RUNC_CMDLINE since taskset requires a proper binary, not a
|
||||
# bash function (which is what runc and __runc are).
|
||||
setup_runc_cmdline
|
||||
|
||||
first="$(first_cpu)"
|
||||
second="$((first + 1))" # Hacky; might not work in all environments.
|
||||
|
||||
# Running with a cpuset should result in an affinity that matches.
|
||||
update_config '.process.args = [ "/bin/sleep", "infinity" ]'
|
||||
update_config '.linux.resources.cpu = {"mems": "0", "cpus": "'"$first-$second"'"}'
|
||||
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" run -d --console-socket "$CONSOLE_SOCKET" ctr
|
||||
[ "$status" -eq 0 ]
|
||||
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" exec ctr grep -F Cpus_allowed_list: /proc/self/status
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != $'Cpus_allowed_list:\t'"$first" ]]
|
||||
# XXX: For some reason, systemd-cgroup leads to us using the all-set
|
||||
# cpumask rather than the cpuset we configured?
|
||||
[ -v RUNC_USE_SYSTEMD ] || [[ "$output" == $'Cpus_allowed_list:\t'"$first-$second" ]]
|
||||
|
||||
# Stop the container so we can reconfigure it.
|
||||
runc delete -f ctr
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# Ditto for a cpuset that has no overlap with the original cpumask.
|
||||
update_config '.linux.resources.cpu = {"mems": "0", "cpus": "'"$second"'"}'
|
||||
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" run -d --console-socket "$CONSOLE_SOCKET" ctr
|
||||
[ "$status" -eq 0 ]
|
||||
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" exec ctr grep -F Cpus_allowed_list: /proc/self/status
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != $'Cpus_allowed_list:\t'"$first" ]]
|
||||
# XXX: For some reason, systemd-cgroup leads to us using the all-set
|
||||
# cpumask rather than the cpuset we configured?
|
||||
[ -v RUNC_USE_SYSTEMD ] || [[ "$output" == $'Cpus_allowed_list:\t'"$second" ]]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user