[PATCH 3/3] selftests: cgroup: Add vmtest-dmem runner based on hid vmtest

Albert Esteve posted 3 patches 6 days, 8 hours ago
[PATCH 3/3] selftests: cgroup: Add vmtest-dmem runner based on hid vmtest
Posted by Albert Esteve 6 days, 8 hours ago
Currently, test_dmem relies on the dmem_selftest helper module
and a VM setup that may not have the helper preinstalled.
This makes automated coverage of dmem charge paths harder in
virtme-based runs.

Add tools/testing/selftests/cgroup/vmtest-dmem.sh, modeled
after the existing selftests vmtest runners
(notably tools/testing/selftests/hid/vmtest.sh),
to provide a repeatable VM workflow for dmem tests.

The script boots a virtme-ng guest, validates dmem
controller availability, ensures the dmem helper path is
present, and runs tools/testing/selftests/cgroup/test_dmem.
If the helper is not available as a loaded module, it
attempts module build/load for the running guest kernel
before executing the test binary.

The runner also supports interactive shell mode and
reuses the same verbosity and exit-code conventions
used by other vmtest scripts, so it integrates with existing
kselftest workflows.

Signed-off-by: Albert Esteve <aesteve@redhat.com>
---
 tools/testing/selftests/cgroup/vmtest-dmem.sh | 189 ++++++++++++++++++++++++++
 1 file changed, 189 insertions(+)

diff --git a/tools/testing/selftests/cgroup/vmtest-dmem.sh b/tools/testing/selftests/cgroup/vmtest-dmem.sh
new file mode 100755
index 0000000000000..e481d3b2cdf8f
--- /dev/null
+++ b/tools/testing/selftests/cgroup/vmtest-dmem.sh
@@ -0,0 +1,189 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2026 Red Hat, Inc.
+#
+# Run cgroup test_dmem 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_dmem_vmtest_XXXX.pid)"
+readonly QEMU_OPTS=" --pidfile ${QEMU_PIDFILE} "
+
+QEMU="qemu-system-$(uname -m)"
+VERBOSE=0
+SHELL_MODE=0
+GUEST_TREE="${GUEST_TREE:-$KERNEL_CHECKOUT}"
+
+usage() {
+	echo
+	echo "$0 [OPTIONS]"
+	echo "  -q <qemu> QEMU binary/path (default: ${QEMU})"
+	echo "  -s        Start interactive shell in VM"
+	echo "  -v        Verbose output (use -vv for vng boot logs)"
+	echo
+}
+
+die() {
+	echo "$*" >&2
+	exit "${KSFT_FAIL}"
+}
+
+cleanup() {
+	if [[ -s "${QEMU_PIDFILE}" ]]; then
+		pkill -SIGTERM -F "${QEMU_PIDFILE}" >/dev/null 2>&1 || true
+	fi
+	rm -f "${QEMU_PIDFILE}"
+}
+
+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
+}
+
+vm_start() {
+	local logfile=/dev/null
+	local verbose_opt=""
+
+	if [[ "${VERBOSE}" -eq 2 ]]; then
+		verbose_opt="--verbose"
+		logfile=/dev/stdout
+	fi
+
+	vng \
+		--run \
+		${verbose_opt} \
+		--qemu-opts="${QEMU_OPTS}" \
+		--qemu="$(command -v "${QEMU}")" \
+		--user root \
+		--ssh "${SSH_GUEST_PORT}" \
+		--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() {
+	local cfg_ok
+	cfg_ok="$(vm_ssh -- "cfg=/boot/config-\$(uname -r); \
+		if [[ -r \"\$cfg\" ]]; then grep -Eq '^CONFIG_CGROUP_DMEM=(y|m)$' \"\$cfg\"; \
+		elif [[ -r /proc/config.gz ]]; then \
+			zgrep -Eq '^CONFIG_CGROUP_DMEM=(y|m)$' /proc/config.gz; \
+		else false; fi; echo \$?")"
+	[[ "${cfg_ok}" == "0" ]] || die "guest kernel missing CONFIG_CGROUP_DMEM"
+}
+
+setup_guest_dmem_helper() {
+	local kdir
+
+	vm_ssh -- "mountpoint -q /sys/kernel/debug || \
+		   mount -t debugfs none /sys/kernel/debug" || true
+
+	# Already available (built-in or loaded).
+	if vm_ssh -- "[[ -e /sys/kernel/debug/dmem_selftest/charge ]]"; then
+		echo "dmem_selftest ready"
+		return 0
+	fi
+
+	# Fast path: try installed module.
+	vm_ssh -- "modprobe -q dmem_selftest 2>/dev/null || true"
+	if vm_ssh -- "[[ -e /sys/kernel/debug/dmem_selftest/charge ]]"; then
+		echo "dmem_selftest ready"
+		return 0
+	fi
+
+	# Fallback: build only this module against running guest kernel,
+	# then insert it.
+	kdir="$(vm_ssh -- "echo /lib/modules/\$(uname -r)/build")"
+	if vm_ssh -- "[[ -d '${kdir}' ]]"; then
+		echo "Building dmem_selftest.ko against running guest kernel..."
+		vm_ssh -- "make -C '${kdir}' \
+			M='${GUEST_TREE}/kernel/cgroup' \
+			CONFIG_DMEM_SELFTEST=m modules"
+		vm_ssh -- "insmod '${GUEST_TREE}/kernel/cgroup/dmem_selftest.ko' \
+			2>/dev/null || modprobe -q dmem_selftest 2>/dev/null || true"
+	fi
+
+	if vm_ssh -- "[[ -e /sys/kernel/debug/dmem_selftest/charge ]]"; then
+		echo "dmem_selftest ready"
+		return 0
+	fi
+
+	die "dmem_selftest unavailable (modprobe/build+insmod failed)"
+}
+
+run_test() {
+	vm_ssh -- "cd '${GUEST_TREE}' && make -C tools/testing/selftests TARGETS=cgroup"
+	vm_ssh -- "cd '${GUEST_TREE}' && ./tools/testing/selftests/cgroup/test_dmem"
+}
+
+while getopts ":hvq:s" o; do
+	case "${o}" in
+	v) VERBOSE=$((VERBOSE + 1)) ;;
+	q) QEMU="${OPTARG}" ;;
+	s) SHELL_MODE=1 ;;
+	h|*) usage ;;
+	esac
+done
+
+trap cleanup EXIT
+
+check_deps
+echo "Booting virtme-ng VM..."
+vm_start
+vm_wait_for_ssh
+echo "VM is reachable via SSH."
+check_guest_requirements
+setup_guest_dmem_helper
+
+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
+
+echo "Running cgroup/test_dmem in VM..."
+run_test
+echo "PASS: test_dmem completed"
+exit "${KSFT_PASS}"

-- 
2.52.0