CXL KUnit suites run when their modules are loaded, but the ndctl CXL
test suite does not load them. As a result, they are not exercised by
the existing ndctl tests.

Add cxl-kunit.sh to discover and load installed CXL KUnit modules, check
their KTAP results in debugfs, and unload the modules on exit. Test
failures are reported individually without preventing the remaining suites
from running.

Discover modules by the cxl_*_kunit naming convention so newly added suites
are included automatically.

Add CONFIG_KUNIT, CONFIG_KUNIT_DEBUGFS, and CONFIG_CXL_KUNIT_TEST=m to the
README kernel configuration.

Signed-off-by: Alison Schofield <[email protected]>
---
 README.md         |  3 ++
 test/cxl-kunit.sh | 86 +++++++++++++++++++++++++++++++++++++++++++++++
 test/meson.build  |  2 ++
 3 files changed, 91 insertions(+)
 create mode 100755 test/cxl-kunit.sh

diff --git a/README.md b/README.md
index a71db28f62fb..83e809a2b325 100644
--- a/README.md
+++ b/README.md
@@ -130,6 +130,9 @@ loaded.  To build and install nfit_test.ko:
    CONFIG_NVDIMM_SECURITY_TEST=y
    CONFIG_ENCRYPTED_KEYS=y
    CONFIG_NVDIMM_KEYS=y
+   CONFIG_KUNIT=y
+   CONFIG_KUNIT_DEBUGFS=y
+   CONFIG_CXL_KUNIT_TEST=m
    ```
 1. Install cxl_test and related mock modules.
    ```
diff --git a/test/cxl-kunit.sh b/test/cxl-kunit.sh
new file mode 100755
index 000000000000..12e94e417cac
--- /dev/null
+++ b/test/cxl-kunit.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Intel Corporation. All rights reserved.
+
+# Run CXL KUnit suites through the ndctl test harness.
+# Discover test modules by name and check their KTAP results in debugfs.
+# See Documentation/driver-api/cxl/testing.rst in the kernel.
+
+# shellcheck disable=SC1091 # common is sourced from the test directory
+. "$(dirname "$0")"/common
+
+kunit_debugfs=/sys/kernel/debug/kunit
+
+mapfile -t modules < <(find /lib/modules/"$(uname -r)" \
+       -name 'cxl_*_kunit.ko*' -printf '%f\n' | sed 's/\.ko.*$//' | sort)
+
+[ ${#modules[@]} -gt 0 ] ||
+       do_skip "test requires CONFIG_CXL_KUNIT_TEST=m"
+
+unload_modules() {
+       local mod
+
+       for mod in "${modules[@]}"; do
+               modprobe -r "$mod" 2>/dev/null || true
+       done
+}
+
+trap unload_modules EXIT
+trap 'err $LINENO' ERR
+set -ex
+# shellcheck disable=SC2034 # rc is the exit status used by err()
+rc=1
+
+# Force test suites to rerun on module load
+unload_modules
+
+loaded=()
+summary=()
+nr_failed=0
+
+for mod in "${modules[@]}"; do
+       if modprobe "$mod"; then
+               loaded+=("$mod")
+       else
+               summary+=("$mod: FAIL, module did not load")
+               nr_failed=$((nr_failed + 1))
+       fi
+done
+
+[ -d "$kunit_debugfs" ] ||
+       do_skip "test requires CONFIG_KUNIT_DEBUGFS=y"
+
+for mod in "${loaded[@]}"; do
+       nr_suites=0
+
+       for results in "$kunit_debugfs"/cxl_*/results; do
+               [ -f "$results" ] || continue
+               grep -qE "^[[:space:]]*# module: $mod\$" "$results" || continue
+
+               suite=$(basename "$(dirname "$results")")
+               nr_suites=$((nr_suites + 1))
+               cat "$results"
+
+               # Match the suite result, not individual test cases
+               if grep -qE "^ok [0-9]+ $suite\$" "$results"; then
+                       summary+=("$mod: suite $suite PASS")
+               else
+                       summary+=("$mod: suite $suite FAIL")
+                       nr_failed=$((nr_failed + 1))
+               fi
+       done
+
+       if [ "$nr_suites" -eq 0 ]; then
+               summary+=("$mod: FAIL, no suite results in $kunit_debugfs")
+               nr_failed=$((nr_failed + 1))
+       fi
+done
+
+set +x
+echo "CXL KUnit summary:"
+printf '  %s\n' "${summary[@]}"
+set -x
+
+[ "$nr_failed" -eq 0 ] || err "$LINENO"
+
+echo "CXL KUnit: all suites from ${#modules[@]} modules passed"
diff --git a/test/meson.build b/test/meson.build
index 07c8618cbc6e..3b3a634ef25d 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -173,6 +173,7 @@ cxl_elc = find_program('cxl-elc.sh')
 cxl_dax_hmem = find_program('cxl-dax-hmem.sh')
 cxl_region_replay = find_program('cxl-region-replay.sh')
 cxl_type2 = find_program('cxl-type2.sh')
+cxl_kunit = find_program('cxl-kunit.sh')
 
 tests = [
   [ 'libndctl',               libndctl,                  'ndctl' ],
@@ -211,6 +212,7 @@ tests = [
   [ 'cxl-dax-hmem.sh',        cxl_dax_hmem,       'cxl'   ],
   [ 'cxl-region-replay.sh',   cxl_region_replay,  'cxl'   ],
   [ 'cxl-type2.sh',           cxl_type2,          'cxl'   ],
+  [ 'cxl-kunit.sh',           cxl_kunit,          'cxl'   ],
 ]
 
 if get_option('destructive').enabled()
-- 
2.37.3


Reply via email to