From: Shaikh Kamaluddin <shaikhkamal2012@gmail.com>
To: linux-cxl@vger.kernel.org, nvdimm@lists.linux.dev
Cc: dave.jiang@intel.com, dave@stgolabs.net,
jonathan.cameron@huawei.com, alison.schofield@intel.com,
vishal.l.verma@intel.com, ira.weiny@intel.com,
dan.j.williams@intel.com, benjamin.cheatham@amd.com,
Shaikh Kamaluddin <shaikhkamal2012@gmail.com>
Subject: [ndctl PATCH] cxl/test: Add ELC MCE alias retirement tests
Date: Sun, 6 Sep 2026 09:48:44 +0530 [thread overview]
Message-ID: <20260906041844.13891-1-shaikhkamal2012@gmail.com> (raw)
ELC alias retirement depends on the error severity and on whether
vendor-specific MCE rules consider the reported address usable. Add
regression coverage for these decisions to cxl-elc.sh.
Inject MCE records through the mce-inject debugfs interface and verify
the resulting bank status and alias-offlining messages in dmesg. Test
corrected and uncorrected errors on Intel, and exercise the poison,
deferred, and legacy bank-4 cases required by AMD address validation.
Derive the ELC SPA and its alias from cxl-list output, select offline
test addresses from lsmem, and discover available MCA banks dynamically.
Use a distinct SPA for each case and clean up modules loaded by the test.
Suggested-by: Ben Cheatham <benjamin.cheatham@amd.com>
Suggested-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Shaikh Kamaluddin <shaikhkamal2012@gmail.com>
---
Testing:
The test was run under virtme-ng with cxl_test configured for Extended
Linear Cache operation:
Intel, GenuineIntel:
--------------------
1. vng -v -r ./arch/x86/boot/bzImage --disable-kvm --qemu-opts='-cpu Skylake-Server-v4,+mce,+mca -m 4G -machine q35,cxl=on -object memory-backend-ram,id=cxl-mem0,size=512M -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.0 -device cxl-rp,port=0,bus=cxl.0,id=root_port0,chassis=0,slot=0 -device cxl-type3,bus=root_port0,volatile-memdev=cxl-mem0,id=cxl-mem-device0 -M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.size=512M'
2 . Go to ndctl-main directoy with this patches
env NDCTL="$PWD/build/ndctl/ndctl"
DAXCTL="$PWD/build/daxctl/daxctl"
CXL="$PWD/build/cxl/cxl"
./test/cxl-elc.sh
RESULT:
--------
PASS Intel corrected: alias no
PASS Intel uncorrected: alias yes
CXL ELC MCE validation passed for GenuineIntel: 2 cases
-----------------------------------------------------------------
AMD, AuthenticAMD legacy MCA:
-----------------------------
1. vng -v -r ./arch/x86/boot/bzImage --disable-kvm --qemu-opts='-cpu EPYC-Milan -m 4G -machine q35,cxl=on -object memory-backend-ram,id=cxl-mem0,size=512M -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.0 -device cxl-rp,port=0,bus=cxl.0,id=root_port0,chassis=0,slot=0 -device cxl-type3,bus=root_port0,volatile-memdev=cxl-mem0,id=cxl-mem-device0 -M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.size=512M'
2. Go to ndctl-main directoy with this patches
env NDCTL="$PWD/build/ndctl/ndctl"
DAXCTL="$PWD/build/daxctl/daxctl"
CXL="$PWD/build/cxl/cxl"
./test/cxl-elc.sh
RESULT:
---------
PASS AMD plain corrected: alias no
PASS AMD plain uncorrected: alias no
PASS AMD poison-only (invalid encoding): alias no
PASS AMD deferred without poison: alias no
PASS AMD deferred with poison: alias yes
PASS AMD uncorrected with poison: alias yes
PASS AMD bank-4 non-memory poison (invalid encoding): alias no
PASS AMD legacy bank-4 corrected DRAM ECC: alias no
CXL ELC MCE validation passed for AuthenticAMD: 8 cases
test/cxl-elc.sh | 313 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 312 insertions(+), 1 deletion(-)
diff --git a/test/cxl-elc.sh b/test/cxl-elc.sh
index cfa09cb..bd600b4 100755
--- a/test/cxl-elc.sh
+++ b/test/cxl-elc.sh
@@ -8,6 +8,30 @@ rc=77
set -ex
+mce_inject_loaded=0
+
+cleanup()
+{
+ local status=$?
+ local cleanup_status=0
+
+ trap - EXIT ERR
+ set +e
+
+ if ((mce_inject_loaded)); then
+ modprobe -r mce-inject || cleanup_status=1
+ fi
+
+ modprobe -r cxl_test || cleanup_status=1
+
+ if ((status == 0 && cleanup_status != 0)); then
+ status=1
+ fi
+
+ exit "$status"
+}
+
+trap cleanup EXIT
trap 'err $LINENO' ERR
check_prereq "jq"
@@ -85,10 +109,297 @@ compare_bases()
((cxlrd_hpa == ep1_hpa - ep1_size)) || err "$LINENO"
}
+mce_skip()
+{
+ echo "SKIP CXL ELC MCE subtest: $*"
+ return 0
+}
+
+dmesg_count()
+{
+ dmesg | grep -F -c -- "$1" || true
+}
+
+wait_for_dmesg()
+{
+ local pattern="$1"
+ local previous="$2"
+ local count
+
+ for _ in {1..50}; do
+ count="$(dmesg_count "$pattern")"
+ ((count > previous)) && return 0
+ sleep 0.1
+ done
+
+ return 1
+}
+
+address_is_offline()
+{
+ local address="$1"
+ local range start end
+
+ while read -r range; do
+ [[ "$range" == *-* ]] || continue
+ start=$(( ${range%%-*} ))
+ end=$(( ${range##*-} ))
+ ((address >= start && address <= end)) && return 0
+ done < <(echo "$lsmem_json" | jq -r \
+ '.memory[] | select((.state | ascii_downcase) == "offline") |
+ .range')
+
+ return 1
+}
+
+find_offline_spas()
+{
+ local nr_spas="$1"
+ local range start end low high candidate alias spa_string alias_string
+ local page_size region_end cache_end i
+ local stride=0x10000
+
+ lsmem_json="$(lsmem --json --bytes --output RANGE,STATE)"
+ page_size="$(getconf PAGESIZE)"
+ region_end=$((region_hpa + region_size))
+ cache_end=$((region_hpa + elc_size))
+ inject_spas=()
+ alias_spas=()
+
+ while read -r range; do
+ [[ "$range" == *-* ]] || continue
+ start=$(( ${range%%-*} ))
+ end=$(( ${range##*-} ))
+
+ low=$start
+ ((low < region_hpa)) && low=$region_hpa
+ high=$((end + 1))
+ ((high > cache_end)) && high=$cache_end
+ ((low < high)) || continue
+
+ # Stay away from the first pages of the hotplugged range.
+ candidate=$((low + stride))
+ candidate=$((
+ (candidate + page_size - 1) / page_size * page_size
+ ))
+ inject_spas=()
+ alias_spas=()
+ for ((i = 0; i < nr_spas; i++)); do
+ candidate=$((candidate + (i > 0 ? stride : 0)))
+ alias=$((candidate + elc_size))
+ ((candidate < high && alias < region_end)) || break
+ address_is_offline "$alias" || break
+ printf -v spa_string '0x%x' "$candidate"
+ printf -v alias_string '0x%x' "$alias"
+ inject_spas+=("$spa_string")
+ alias_spas+=("$alias_string")
+ done
+ ((${#inject_spas[@]} == nr_spas)) || continue
+ return 0
+ done < <(echo "$lsmem_json" | jq -r \
+ '.memory[] | select((.state | ascii_downcase) == "offline") |
+ .range')
+
+ return 1
+}
+
+discover_mca_banks()
+{
+ local path name
+
+ for path in \
+ /sys/devices/system/machinecheck/machinecheck0/bank[0-9]*; do
+ [[ -e "$path" ]] || continue
+ name="${path##*/}"
+ printf '%s\n' "${name#bank}"
+ done | sort -n -u
+}
+
+has_bank()
+{
+ local wanted="$1"
+ local bank
+
+ for bank in "${mca_banks[@]}"; do
+ [[ "$bank" == "$wanted" ]] && return 0
+ done
+
+ return 1
+}
+
+select_mce_parameters()
+{
+ local bank
+
+ vendor="$(awk '/^vendor_id/{ print $3; exit }' /proc/cpuinfo)"
+ readarray -t mca_banks < <(discover_mca_banks)
+ ((${#mca_banks[@]})) || return 1
+
+ case "$vendor" in
+ GenuineIntel)
+ test_bank="${mca_banks[-1]}"
+ test_case_count=2
+ ;;
+ AuthenticAMD)
+ # The usable-address corrected-error case is the legacy bank-4 DRAM
+ # ECC encoding (XEC 8). It is not valid for an SMCA bank.
+ grep -qw smca /proc/cpuinfo && return 1
+ has_bank 4 || return 1
+ non_b4_bank=
+ for bank in "${mca_banks[@]}"; do
+ [[ "$bank" == 4 ]] || non_b4_bank="$bank"
+ done
+ [[ -n "$non_b4_bank" ]] || return 1
+ test_case_count=8
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+}
+
+inject_mce()
+{
+ local status="$1"
+ local bank="$2"
+ local mce_inject=/sys/kernel/debug/mce-inject
+
+ echo sw > "$mce_inject/flags"
+ echo "$status" > "$mce_inject/status"
+ echo 0x80 > "$mce_inject/misc"
+ echo "$inject_spa" > "$mce_inject/addr"
+ echo "$bank" > "$mce_inject/bank"
+}
+
+run_mce_case()
+{
+ local name="$1"
+ local status="$2"
+ local bank="$3"
+ local expect_alias="$4"
+ local index="$5"
+ local alias_msg bank_msg status_tag
+ local alias_before bank_before
+
+ inject_spa="${inject_spas[$index]}"
+ alias_spa="${alias_spas[$index]}"
+ alias_msg="Offlining aliased SPA address0: $alias_spa"
+ status_tag="${status#0x}"
+ bank_msg="Bank $bank: $status_tag"
+ alias_before="$(dmesg_count "$alias_msg")"
+ bank_before="$(dmesg_count "$bank_msg")"
+
+ inject_mce "$status" "$bank"
+ wait_for_dmesg "$bank_msg" "$bank_before" || err "$LINENO"
+
+ if [[ "$expect_alias" == "yes" ]]; then
+ wait_for_dmesg "$alias_msg" "$alias_before" || err "$LINENO"
+ else
+ (($(dmesg_count "$alias_msg") == alias_before)) || \
+ err "$LINENO"
+ fi
+
+ echo "PASS $name: SPA $inject_spa, alias $alias_spa, bank $bank, alias $expect_alias"
+}
+
+run_intel_mce_cases()
+{
+ run_mce_case "Intel corrected" 0x9c00000000000080 \
+ "$test_bank" no 0
+ run_mce_case "Intel uncorrected" 0xbc00000000000080 \
+ "$test_bank" yes 1
+}
+
+run_amd_mce_cases()
+{
+ # Mirror the full AMD matrix documented with the kernel fix. The two
+ # poison-only records have UC=0 and Deferred=0, so they are not valid
+ # architectural records. Keep them as software-injection robustness
+ # coverage and expect the corrected-error filter to reject them.
+ run_mce_case "AMD plain corrected" 0x9c00000000000080 \
+ "$non_b4_bank" no 0
+ run_mce_case "AMD plain uncorrected" 0xbc00000000000080 \
+ "$non_b4_bank" no 1
+ run_mce_case "AMD poison-only (invalid encoding)" \
+ 0x9c00080000000080 "$non_b4_bank" no 2
+ run_mce_case "AMD deferred without poison" 0x9c00100000000080 \
+ "$non_b4_bank" no 3
+ run_mce_case "AMD deferred with poison" 0x9c00180000000080 \
+ "$non_b4_bank" yes 4
+ run_mce_case "AMD uncorrected with poison" 0xbc00080000000080 \
+ "$non_b4_bank" yes 5
+ run_mce_case "AMD bank-4 non-memory poison (invalid encoding)" \
+ 0x9c00080000000080 4 no 6
+ run_mce_case "AMD legacy bank-4 corrected DRAM ECC" \
+ 0x9c00000000080000 4 no 7
+}
+
+test_mce_alias()
+{
+ local region_json
+
+ [[ "$(uname -m)" == "x86_64" ]] || {
+ mce_skip "x86_64 is required"
+ return 0
+ }
+ command -v lsmem >/dev/null 2>&1 || {
+ mce_skip "lsmem is unavailable"
+ return 0
+ }
+
+ region_json="$(echo "$json" | jq -c --arg region "$region" \
+ '.[] | select(.region == $region)')"
+ region_hpa="$(echo "$region_json" | jq -r '.resource // empty')"
+ region_size="$(echo "$region_json" | jq -r '.size // empty')"
+ elc_size="$(echo "$region_json" | jq -r \
+ '.extended_linear_cache_size // empty')"
+
+ [[ "$region_hpa" =~ ^[0-9]+$ ]] || err "$LINENO"
+ [[ "$region_size" =~ ^[0-9]+$ ]] || err "$LINENO"
+ [[ "$elc_size" =~ ^[0-9]+$ ]] || err "$LINENO"
+ ((elc_size > 0x10000 && region_size >= elc_size * 2)) || \
+ err "$LINENO"
+
+ select_mce_parameters || {
+ mce_skip "no supported MCA bank configuration for $vendor"
+ return 0
+ }
+
+ if [[ ! -d /sys/module/mce_inject ]]; then
+ modprobe mce-inject || {
+ mce_skip "mce-inject module is unavailable"
+ return 0
+ }
+ mce_inject_loaded=1
+ fi
+ [[ -d /sys/kernel/debug/mce-inject ]] || {
+ mce_skip "mce-inject debugfs interface is unavailable"
+ return 0
+ }
+
+ find_offline_spas "$test_case_count" || {
+ mce_skip "offline lsmem range lacks enough ELC test addresses"
+ return 0
+ }
+
+ case "$vendor" in
+ GenuineIntel)
+ run_intel_mce_cases
+ ;;
+ AuthenticAMD)
+ run_amd_mce_cases
+ ;;
+ esac
+
+ echo "CXL ELC MCE validation passed for $vendor: $test_case_count cases"
+}
+
find_region
retrieve_info
compare_sizes
compare_bases
+# Check before injecting the expected machine-check messages.
+check_dmesg "$LINENO"
+test_mce_alias
check_dmesg "$LINENO"
-modprobe -r cxl_test
base-commit: 15e932c4e1318a9608ad9b799ad83a32a8b5970d
--
2.43.0
reply other threads:[~2026-09-06 4:19 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260906041844.13891-1-shaikhkamal2012@gmail.com \
--to=shaikhkamal2012@gmail.com \
--cc=alison.schofield@intel.com \
--cc=benjamin.cheatham@amd.com \
--cc=dan.j.williams@intel.com \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=ira.weiny@intel.com \
--cc=jonathan.cameron@huawei.com \
--cc=linux-cxl@vger.kernel.org \
--cc=nvdimm@lists.linux.dev \
--cc=vishal.l.verma@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.