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



Reply via email to