From: John Groves <[email protected]> Add test/daxctl-famfs-nfit.sh, which builds its own dax device from the emulated ACPI.NFIT bus (nfit_test) so it runs in the ndctl unit-test model rather than scanning for a pre-existing dax device.
nfit_test ranges have real DRAM backing, so kmem onlining works and the full transition matrix runs end-to-end: - devdax <-> famfs switches, including same-mode re-enable - system-ram <-> devdax <-> famfs, with real memory online/offline - system-ram -> famfs is rejected (the conversion must go via devdax) - JSON output reports the correct mode - invalid modes are rejected The test follows the existing ndctl test style: 'set -x' command logging, err/cleanup traps, check_dmesg at completion, and fixture teardown rather than restore-to-original cleanup. Suggested-by: Alison Schofield <[email protected]> Signed-off-by: John Groves <[email protected]> --- test/daxctl-famfs-nfit.sh | 215 ++++++++++++++++++++++++++++++++++++++ test/meson.build | 2 + 2 files changed, 217 insertions(+) create mode 100755 test/daxctl-famfs-nfit.sh diff --git a/test/daxctl-famfs-nfit.sh b/test/daxctl-famfs-nfit.sh new file mode 100755 index 0000000..5730279 --- /dev/null +++ b/test/daxctl-famfs-nfit.sh @@ -0,0 +1,215 @@ +#!/bin/bash -Ex +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 Micron Technology, Inc. All rights reserved. +# +# Test daxctl famfs mode transitions and mode detection, targeting a +# nfit_test-backed dax device. +# +# nfit_test-backed dax devices have real DRAM backing, so kmem onlining +# works normally. This test exercises the full matrix of transitions +# between devdax, famfs, and system-ram. + +rc=77 +. $(dirname $0)/common + +trap 'cleanup $LINENO' ERR + +testbus="" +testdev="" +daxdev="" + +cleanup() +{ + # Best-effort return to devdax so destroy-namespace can succeed. + if [[ -n $daxdev ]]; then + "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" 2>/dev/null || true + fi + [[ -n $testdev ]] && reset_dev + err "$1" +} + +check_fsdev_dax() +{ + modinfo fsdev_dax &>/dev/null && return 0 + grep -qF "fsdev_dax" "/lib/modules/$(uname -r)/modules.builtin" 2>/dev/null && return 0 + do_skip "fsdev_dax module not available" +} + +check_kmem() +{ + modinfo kmem &>/dev/null && return 0 + grep -qF "kmem" "/lib/modules/$(uname -r)/modules.builtin" 2>/dev/null && return 0 + do_skip "kmem module not available" +} + +find_testdev() +{ + testbus="$ACPI_BUS" + + # Ensure the bus has labels, like align.sh / daxctl-devices.sh rely on. + "$NDCTL" disable-region -b "$testbus" all + "$NDCTL" init-labels -f -b "$testbus" all + "$NDCTL" enable-region -b "$testbus" all + + testdev=$("$NDCTL" list -b "$testbus" -Ni | jq -er '.[0].dev | .//""') + [[ $testdev ]] || do_skip "no victim device on $testbus" +} + +setup_dev() +{ + test -n "$testbus" + test -n "$testdev" + + "$NDCTL" destroy-namespace -f -b "$testbus" "$testdev" + # x86_64 memory hotplug can require up to a 2GiB-aligned chunk of + # memory. Create a 4GiB namespace, so enough space is left after + # alignment for kmem + online. + testdev=$("$NDCTL" create-namespace -b "$testbus" -m devdax -fe "$testdev" -s 4G | \ + jq -er '.dev') + test -n "$testdev" + + daxdev=$("$NDCTL" list -n "$testdev" -X | jq -er '.[].daxregion.devices[0].chardev') + test -n "$daxdev" +} + +reset_dev() +{ + "$NDCTL" destroy-namespace -f -b "$testbus" "$testdev" +} + +daxctl_get_mode() +{ + "$DAXCTL" list -d "$1" | jq -er '.[].mode' +} + +save_online_policy() +{ + saved_policy="$(cat /sys/devices/system/memory/auto_online_blocks)" +} + +restore_online_policy() +{ + echo "$saved_policy" > /sys/devices/system/memory/auto_online_blocks +} + +unset_online_policy() +{ + echo "offline" > /sys/devices/system/memory/auto_online_blocks +} + +ensure_devdax_mode() +{ + local mode + mode=$(daxctl_get_mode "$daxdev") + + case "$mode" in + devdax) return 0 ;; + famfs) "$DAXCTL" reconfigure-device -m devdax "$daxdev" >/dev/null ;; + system-ram) "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" >/dev/null ;; + *) + echo "unexpected starting mode: $mode" + return 1 + ;; + esac + + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] +} + +test_famfs_mode_transitions() +{ + ensure_devdax_mode + + # devdax -> famfs + "$DAXCTL" reconfigure-device -m famfs "$daxdev" >/dev/null + [[ $(daxctl_get_mode "$daxdev") == "famfs" ]] + + # famfs -> famfs (re-enable in same mode) + "$DAXCTL" reconfigure-device -m famfs "$daxdev" >/dev/null + [[ $(daxctl_get_mode "$daxdev") == "famfs" ]] + + # famfs -> devdax + "$DAXCTL" reconfigure-device -m devdax "$daxdev" >/dev/null + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] + + # devdax -> devdax (re-enable in same mode) + "$DAXCTL" reconfigure-device -m devdax "$daxdev" >/dev/null + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] +} + +test_json_output() +{ + ensure_devdax_mode + [[ $("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') == "devdax" ]] + + "$DAXCTL" reconfigure-device -m famfs "$daxdev" >/dev/null + [[ $("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') == "famfs" ]] + + "$DAXCTL" reconfigure-device -m devdax "$daxdev" >/dev/null +} + +test_error_handling() +{ + "$DAXCTL" reconfigure-device -m famfs "$daxdev" >/dev/null + + # Invalid mode must be rejected + if "$DAXCTL" reconfigure-device -m invalidmode "$daxdev" &>/dev/null; then + echo "FAIL: invalid mode should be rejected" + return 1 + fi + + "$DAXCTL" reconfigure-device -m devdax "$daxdev" >/dev/null +} + +# Full system-ram transitions (real backing, so online_pages() works). +# Turns auto-online off so daxctl drives onlining explicitly. +test_system_ram_transitions() +{ + save_online_policy + unset_online_policy + + ensure_devdax_mode + + # devdax -> system-ram (no-online) + "$DAXCTL" reconfigure-device -N -m system-ram "$daxdev" >/dev/null + [[ $(daxctl_get_mode "$daxdev") == "system-ram" ]] + + # system-ram -> famfs must be rejected + if "$DAXCTL" reconfigure-device -m famfs "$daxdev" &>/dev/null; then + echo "FAIL: system-ram -> famfs should be rejected" + restore_online_policy + return 1 + fi + + # system-ram -> devdax -> famfs + "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" >/dev/null + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] + "$DAXCTL" reconfigure-device -m famfs "$daxdev" >/dev/null + [[ $(daxctl_get_mode "$daxdev") == "famfs" ]] + + # Full online cycle: devdax -> system-ram (with online) -> devdax. + "$DAXCTL" reconfigure-device -m devdax "$daxdev" >/dev/null + "$DAXCTL" reconfigure-device -m system-ram "$daxdev" >/dev/null + [[ $(daxctl_get_mode "$daxdev") == "system-ram" ]] + "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" >/dev/null + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] + + restore_online_policy +} + +check_fsdev_dax +check_kmem + +rc=1 + +find_testdev +setup_dev + +test_famfs_mode_transitions +test_json_output +test_error_handling +test_system_ram_transitions + +ensure_devdax_mode +reset_dev + +check_dmesg "$LINENO" diff --git a/test/meson.build b/test/meson.build index 8a3718d..cee8741 100644 --- a/test/meson.build +++ b/test/meson.build @@ -213,6 +213,7 @@ if get_option('destructive').enabled() device_dax_fio = find_program('device-dax-fio.sh') daxctl_devices = find_program('daxctl-devices.sh') daxctl_create = find_program('daxctl-create.sh') + daxctl_famfs_nfit = find_program('daxctl-famfs-nfit.sh') dm = find_program('dm.sh') mmap_test = find_program('mmap.sh') @@ -230,6 +231,7 @@ if get_option('destructive').enabled() [ 'device-dax-fio.sh', device_dax_fio, 'dax' ], [ 'daxctl-devices.sh', daxctl_devices, 'dax' ], [ 'daxctl-create.sh', daxctl_create, 'dax' ], + [ 'daxctl-famfs-nfit.sh', daxctl_famfs_nfit, 'dax' ], [ 'dm.sh', dm, 'dax' ], [ 'mmap.sh', mmap_test, 'dax' ], ] -- 2.53.0

