[PATCH 09/11] selftests: cgroup: add a vmtest script for memcg
Eric Chanudet <[email protected]>
| Newsgroups | org.kernel.vger.linux-kselftest,org.kernel.vger.cgroups,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kvack.linux-mm |
|---|---|
| Message-ID | <[email protected]> |
Following dmem's example[1], add a vmtest-memcg.sh to provide a repeatable VM workflow using virtme-ng for memcg tests. Boot a VM using virtme-ng with cma=64M in its kargs and enable memory_cma_accounting on cgroupfs, then run the cgroup tests inside the guest. Provide -b to build the kernel first, -q to use a different qemu binary, -s to start a shell in the VM, and -v for verbose boot. [1] https://lore.kernel.org/all/[email protected]/ Signed-off-by: Eric Chanudet <[email protected]> --- tools/testing/selftests/cgroup/vmtest-memcg.sh | 196 +++++++++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/tools/testing/selftests/cgroup/vmtest-memcg.sh b/tools/testing/selftests/cgroup/vmtest-memcg.sh new file mode 100755 index 000000000000..b19b96731784 --- /dev/null +++ b/tools/testing/selftests/cgroup/vmtest-memcg.sh @@ -0,0 +1,196 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2026 Red Hat, Inc. +# +# Run cgroup memory controller selftests inside a virtme-ng VM +# Dependencies: +# * virtme-ng +# * busybox-static (used by virtme-ng) +# * qemu (used by virtme-ng) + +set -euo pipefail + +readonly SCRIPT_DIR="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" +readonly KERNEL_CHECKOUT="$(realpath "${SCRIPT_DIR}"/../../../../)" + +source "${SCRIPT_DIR}"/../kselftest/ktap_helpers.sh + +readonly SSH_GUEST_PORT="${SSH_GUEST_PORT:-22}" +readonly WAIT_PERIOD=3 +readonly WAIT_PERIOD_MAX=80 +readonly WAIT_TOTAL=$((WAIT_PERIOD * WAIT_PERIOD_MAX)) +readonly QEMU_PIDFILE="$(mktemp /tmp/qemu_memcg_vmtest_XXXX.pid)" +readonly QEMU_OPTS=" --pidfile ${QEMU_PIDFILE} " + +BUILD=0 +QEMU="qemu-system-$(uname -m)" +VERBOSE=0 +SHELL_MODE=0 +GUEST_TREE="${GUEST_TREE:-$KERNEL_CHECKOUT}" + +usage() { + cat <<EOF +$0 [OPTIONS] + -b Build kernel from source tree before booting + -q <qemu> QEMU binary/path (default: $QEMU) + -s Start interactive shell in VM + -v Verbose output (vng boot logs on stdout) +EOF + exit 1 +} + +die() { + echo "$*" >&2 + exit "${KSFT_FAIL}" +} + +cleanup() { + if [[ -s "$QEMU_PIDFILE" ]]; then + pkill -SIGTERM -F "$QEMU_PIDFILE" >/dev/null 2>&1 || true + fi + + if [[ -e "$QEMU_PIDFILE" ]]; then + rm -f "$QEMU_PIDFILE" + fi +} + +vm_ssh() { + stdbuf -oL ssh -q \ + -F "$HOME/.cache/virtme-ng/.ssh/virtme-ng-ssh.conf" \ + -l root "virtme-ng%$SSH_GUEST_PORT" \ + "$@" +} + +check_deps() { + for dep in vng "$QEMU" busybox pkill ssh; do + if ! command -v "$dep" >/dev/null 2>&1; then + echo "skip: dependency $dep not found" + exit "$KSFT_SKIP" + fi + done +} + +handle_build() { + if [[ ! "$BUILD" -eq 1 ]]; then + return + fi + + if [[ ! -d "$KERNEL_CHECKOUT" ]]; then + echo "-b requires $0 to be called from the kernel source tree" >&2 + exit 1 + fi + + pushd "$KERNEL_CHECKOUT" &>/dev/null + + if ! vng --kconfig --config "$SCRIPT_DIR"/config; then + die "failed to generate .config for kernel source tree ($KERNEL_CHECKOUT)" + fi + + if ! make -j"$(nproc)"; then + die "failed to build kernel from source tree ($KERNEL_CHECKOUT)" + fi + + popd &>/dev/null +} + +vm_start() { + local logfile=/dev/null + local verbose_opt="" + local kernel_opt="" + + if [[ "$VERBOSE" -eq 1 ]]; then + verbose_opt="--verbose" + logfile=/dev/stdout + fi + + if [[ "$BUILD" -eq 1 ]]; then + kernel_opt="${KERNEL_CHECKOUT}" + fi + + vng \ + --run \ + ${kernel_opt} \ + ${verbose_opt} \ + --qemu-opts="$QEMU_OPTS" \ + --qemu="$(command -v "$QEMU")" \ + --user root \ + --ssh "$SSH_GUEST_PORT" \ + --append "cma=64M" \ + --rw &>"$logfile" & + + local vng_pid=$! + local elapsed=0 + + while [[ ! -s "$QEMU_PIDFILE" ]]; do + kill -0 "$vng_pid" 2>/dev/null || \ + die "vng exited early; failed to boot VM" + [[ "$elapsed" -ge "$WAIT_TOTAL" ]] && \ + die "timed out waiting for VM boot" + sleep 1 + elapsed=$((elapsed + 1)) + done +} + +vm_wait_for_ssh() { + local i=0 + while true; do + vm_ssh -- true && break + i=$((i + 1)) + [[ "${i}" -gt "$WAIT_PERIOD_MAX" ]] && \ + die "timed out waiting for guest ssh" + sleep "$WAIT_PERIOD" + done +} + +check_guest_requirements() { + vm_ssh -- "grep -q memory /sys/fs/cgroup/cgroup.controllers" \ + || die "memory controller not available (CONFIG_MEMCG?)" + vm_ssh -- "[[ -e /dev/dma_heap/default_cma_region ]]" \ + || die "CMA heap not available (CONFIG_DMABUF_HEAPS_CMA? cma= cmdline?)" +} + +setup_guest_cma_accounting() { + vm_ssh -- "mount -o remount,memory_cma_accounting /sys/fs/cgroup" \ + || die "failed to enable memory_cma_accounting" + vm_ssh -- "echo +memory > /sys/fs/cgroup/cgroup.subtree_control" \ + || die "failed to enable memory controller" +} + +run_test() { + vm_ssh -- "cd '$GUEST_TREE' && make -C tools/testing/selftests TARGETS=cgroup" + vm_ssh -- "cd '$GUEST_TREE' && make -C tools/testing/selftests TARGETS=cgroup run_tests" +} + +while getopts ":hvq:sb" opt; do + case "$opt" in + v) VERBOSE=1 ;; + q) QEMU="${OPTARG}" ;; + b) BUILD=1 ;; + s) SHELL_MODE=1 ;; + h|*) usage ;; + esac +done + +trap cleanup EXIT + +check_deps +handle_build +echo "Booting virtme-ng VM..." +vm_start +vm_wait_for_ssh +echo "VM is reachable via SSH." + +if [[ "$SHELL_MODE" -eq 1 ]]; then + echo "Starting interactive shell in VM. Exit to stop VM." + vm_ssh -t -- "cd '$GUEST_TREE' && exec bash --noprofile --norc" + exit "$KSFT_PASS" +fi + +check_guest_requirements +setup_guest_cma_accounting + +echo "Running cgroup selftests in VM..." +run_test +echo "PASS: cgroup selftests completed" +exit "$KSFT_PASS" -- 2.53.0