From: "Chang S. Bae" <chang.seok.bae@intel.com>
To: linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org
Cc: x86@kernel.org, tglx@linutronix.de, mingo@redhat.com,
bp@alien8.de, dave.hansen@linux.intel.com, shuah@kernel.org,
chang.seok.bae@intel.com
Subject: [PATCH 1/6] selftests/x86/xstate: Track testing status by kselftest API
Date: Tue, 10 Nov 2026 03:15:58 +0000 [thread overview]
Message-ID: <20261110031603.698635-2-chang.seok.bae@intel.com> (raw)
In-Reply-To: <20261110031603.698635-1-chang.seok.bae@intel.com>
The XSTATE facility is shared across different component testing.
Currently, it relies on plain printf() for reporting results, which
provides less convenient way to track failures or determine the overall
test status.
Convert the test code to use the kselftest framework for result tracking
and and a return value in the end.
While at it, also shorten and refine the test messages a bit.
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
tools/testing/selftests/x86/amx.c | 5 +-
tools/testing/selftests/x86/apx.c | 2 +-
tools/testing/selftests/x86/avx.c | 33 +++++-
tools/testing/selftests/x86/xstate.c | 155 +++++++++++++++++++--------
tools/testing/selftests/x86/xstate.h | 2 +-
5 files changed, 142 insertions(+), 55 deletions(-)
diff --git a/tools/testing/selftests/x86/amx.c b/tools/testing/selftests/x86/amx.c
index 40769c16de1b..1e4206c8a03a 100644
--- a/tools/testing/selftests/x86/amx.c
+++ b/tools/testing/selftests/x86/amx.c
@@ -482,6 +482,7 @@ int main(void)
{
unsigned long features;
long rc;
+ int ret;
rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features);
if (rc || (features & XFEATURE_MASK_XTILE) != XFEATURE_MASK_XTILE) {
@@ -509,10 +510,10 @@ int main(void)
* Perform generic xstate tests for context switching, ptrace,
* and signal.
*/
- test_xstate(XFEATURE_XTILEDATA);
+ ret = test_xstate(XFEATURE_XTILEDATA);
clearhandler(SIGILL);
free_stashed_xsave();
- return 0;
+ return ret;
}
diff --git a/tools/testing/selftests/x86/apx.c b/tools/testing/selftests/x86/apx.c
index d9c8d41b8c5a..8d353b75bca1 100644
--- a/tools/testing/selftests/x86/apx.c
+++ b/tools/testing/selftests/x86/apx.c
@@ -6,5 +6,5 @@
int main(void)
{
- test_xstate(XFEATURE_APX);
+ return test_xstate(XFEATURE_APX);
}
diff --git a/tools/testing/selftests/x86/avx.c b/tools/testing/selftests/x86/avx.c
index 11d5367c235f..2b79da16ee2b 100644
--- a/tools/testing/selftests/x86/avx.c
+++ b/tools/testing/selftests/x86/avx.c
@@ -3,10 +3,35 @@
#define _GNU_SOURCE /* Required for inline xstate helpers */
#include "xstate.h"
+static uint32_t avx_features[] = {
+ XFEATURE_YMM,
+ XFEATURE_OPMASK,
+ XFEATURE_ZMM_Hi256,
+ XFEATURE_Hi16_ZMM,
+};
+
int main(void)
{
- test_xstate(XFEATURE_YMM);
- test_xstate(XFEATURE_OPMASK);
- test_xstate(XFEATURE_ZMM_Hi256);
- test_xstate(XFEATURE_Hi16_ZMM);
+ unsigned int failed = 0, skipped = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(avx_features); i++) {
+ switch (test_xstate(avx_features[i])) {
+ case KSFT_FAIL:
+ failed++;
+ break;
+ case KSFT_SKIP:
+ skipped++;
+ break;
+ case KSFT_PASS:
+ default:
+ }
+ }
+
+ if (skipped == ARRAY_SIZE(avx_features))
+ return KSFT_SKIP;
+ else if (failed)
+ return KSFT_FAIL;
+ else
+ return KSFT_PASS;
}
diff --git a/tools/testing/selftests/x86/xstate.c b/tools/testing/selftests/x86/xstate.c
index 0ab577157cd7..c26dbe33622f 100644
--- a/tools/testing/selftests/x86/xstate.c
+++ b/tools/testing/selftests/x86/xstate.c
@@ -198,15 +198,16 @@ static void affinitize_cpu0(void)
ksft_exit_fail_msg("sched_setaffinity to CPU 0 failed\n");
}
-static void test_context_switch(uint32_t num_threads, uint32_t iterations)
+static void test_context_switch(void)
{
+ const unsigned int num_threads = 5, iterations = 10;
struct futex_info *finfo;
/* Affinitize to one CPU to force context switches */
affinitize_cpu0();
- printf("[RUN]\t%s: check context switches, %d iterations, %d threads.\n",
- xstate.name, iterations, num_threads);
+ ksft_print_msg("[RUN]\t%s: check context switches, %d iterations, %d threads.\n",
+ xstate.name, iterations, num_threads);
finfo = malloc(sizeof(*finfo) * num_threads);
if (!finfo)
@@ -228,9 +229,9 @@ static void test_context_switch(uint32_t num_threads, uint32_t iterations)
/* Wait for all the threads to finish: */
if (checkout_threads(num_threads, finfo))
- printf("[OK]\tNo incorrect case was found.\n");
+ ksft_test_result_pass("%s for %s\n", __func__, xstate.name);
else
- printf("[FAIL]\tFailed with context switching test.\n");
+ ksft_test_result_fail("%s for %s\n", __func__, xstate.name);
free(finfo);
}
@@ -256,6 +257,40 @@ static inline void ptracee_touch_xstate(void)
free(xbuf);
}
+static bool validate_ptracer_xstate(pid_t target, struct iovec iov,
+ struct xsave_buffer *xbuf,
+ struct xsave_buffer *expected)
+{
+ unsigned int failed = 0;
+
+ if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
+ ksft_exit_fail_msg("PTRACE_SETREGSET failed\n");
+
+ if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
+ ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
+
+ /*
+ * To track this whole result only, use printf() for skipping sub-test
+ * counting.
+ */
+
+ if (*(uint64_t *)get_fpx_sw_bytes(xbuf) == xgetbv(0)) {
+ printf("#\tok\tSW reserved area\n");
+ } else {
+ printf("#\tnot ok\tSW reserved area\n");
+ failed++;
+ }
+
+ if (validate_xstate_same(xbuf, expected)) {
+ printf("#\tok\txstate area\n");
+ } else {
+ printf("#\tnot ok\txstate area\n");
+ failed++;
+ }
+
+ return failed == 0;
+}
+
/*
* Ptracer injects the randomized xstate data. It also reads before and
* after that, which will execute the kernel's state copy functions.
@@ -281,26 +316,15 @@ static void ptracer_inject_xstate(pid_t target)
if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
- printf("[RUN]\t%s: inject xstate via ptrace().\n", xstate.name);
+ ksft_print_msg("[RUN]\t%s: inject xstate via ptrace().\n", xstate.name);
load_rand_xstate(&xstate, xbuf1);
copy_xstate(xbuf2, xbuf1);
- if (ptrace(PTRACE_SETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
- ksft_exit_fail_msg("PTRACE_SETREGSET failed\n");
-
- if (ptrace(PTRACE_GETREGSET, target, (uint32_t)NT_X86_XSTATE, &iov))
- ksft_exit_fail_msg("PTRACE_GETREGSET failed\n");
-
- if (*(uint64_t *)get_fpx_sw_bytes(xbuf1) == xgetbv(0))
- printf("[OK]\t'xfeatures' in SW reserved area was correctly written\n");
+ if (validate_ptracer_xstate(target, iov, xbuf1, xbuf2))
+ ksft_test_result_pass("%s for %s\n", __func__, xstate.name);
else
- printf("[FAIL]\t'xfeatures' in SW reserved area was not correctly written\n");
-
- if (validate_xstate_same(xbuf2, xbuf1))
- printf("[OK]\txstate was correctly updated.\n");
- else
- printf("[FAIL]\txstate was not correctly updated.\n");
+ ksft_test_result_fail("%s for %s\n", __func__, xstate.name);
free(xbuf1);
free(xbuf2);
@@ -347,13 +371,26 @@ static void test_ptrace(void)
*/
#define SIGNAL_BUF_LEN 1000
static char signal_message_buffer[SIGNAL_BUF_LEN];
-static void sig_print(char *msg)
+static inline void sig_print(char *msg)
{
int left = SIGNAL_BUF_LEN - strlen(signal_message_buffer) - 1;
strncat(signal_message_buffer, msg, left);
}
+static unsigned int sig_failed;
+
+static inline void sig_print_pass(char *msg)
+{
+ sig_print(msg);
+}
+
+static inline void sig_print_fail(char *msg)
+{
+ sig_failed++;
+ sig_print(msg);
+}
+
static struct xsave_buffer *stashed_xbuf;
static void validate_sigfpstate(int sig, siginfo_t *si, void *ctx_void)
@@ -368,30 +405,30 @@ static void validate_sigfpstate(int sig, siginfo_t *si, void *ctx_void)
sw_bytes = get_fpx_sw_bytes(xbuf);
if (sw_bytes->magic1 == FP_XSTATE_MAGIC1)
- sig_print("[OK]\t'magic1' is valid\n");
+ sig_print_pass("#\tok\tmagic1\n");
else
- sig_print("[FAIL]\t'magic1' is not valid\n");
+ sig_print_fail("#\tnot ok\tmagic1\n");
if (get_fpx_sw_bytes_features(xbuf) & xstate.mask)
- sig_print("[OK]\t'xfeatures' in SW reserved area is valid\n");
+ sig_print_pass("#\tok\tSW reserved area\n");
else
- sig_print("[FAIL]\t'xfeatures' in SW reserved area is not valid\n");
+ sig_print_fail("#\tnot ok\tSW reserved area\n");
if (get_xstatebv(xbuf) & xstate.mask)
- sig_print("[OK]\t'xfeatures' in XSAVE header is valid\n");
+ sig_print_pass("#\tok\tXSTATE_BV\n");
else
- sig_print("[FAIL]\t'xfeatures' in XSAVE header is not valid\n");
+ sig_print_fail("#\tnot ok\tXSTATE_BV\n");
if (validate_xstate_same(stashed_xbuf, xbuf))
- sig_print("[OK]\txstate delivery was successful\n");
+ sig_print_pass("#\tok\txstate area\n");
else
- sig_print("[FAIL]\txstate delivery was not successful\n");
+ sig_print_fail("#\tnot ok\txstate area\n");
magic2 = *(uint32_t *)(xbuf + sw_bytes->xstate_size);
if (magic2 == FP_XSTATE_MAGIC2)
- sig_print("[OK]\t'magic2' is valid\n");
+ sig_print_pass("#\tok\tmagic2\n");
else
- sig_print("[FAIL]\t'magic2' is not valid\n");
+ sig_print_fail("#\tnot ok\tmagic2\n");
set_rand_data(&xstate, xbuf);
copy_xstate(stashed_xbuf, xbuf);
@@ -399,7 +436,7 @@ static void validate_sigfpstate(int sig, siginfo_t *si, void *ctx_void)
static void test_signal(void)
{
- bool valid_xstate;
+ bool sigreturn_failed;
/*
* The signal handler will access this to verify xstate context
@@ -409,49 +446,62 @@ static void test_signal(void)
if (!stashed_xbuf)
ksft_exit_fail_msg("unable to allocate XSAVE buffer\n");
- printf("[RUN]\t%s: load xstate and raise SIGUSR1\n", xstate.name);
+ ksft_print_msg("[RUN]\t%s: validate signal delivery\n", xstate.name);
sethandler(SIGUSR1, validate_sigfpstate, 0);
load_rand_xstate(&xstate, stashed_xbuf);
+ sig_failed = 0;
raise(SIGUSR1);
/*
* Immediately record the test result, deferring printf() to
* prevent unintended state contamination by that.
*/
- valid_xstate = validate_xregs_same(stashed_xbuf);
+ sigreturn_failed = !validate_xregs_same(stashed_xbuf);
printf("%s", signal_message_buffer);
+ if (!sig_failed)
+ ksft_test_result_pass("%s (delivery) for %s\n", __func__, xstate.name);
+ else
+ ksft_test_result_fail("%s (delivery) for %s\n", __func__, xstate.name);
- printf("[RUN]\t%s: load new xstate from sighandler and check it after sigreturn\n",
- xstate.name);
+ ksft_print_msg("[RUN]\t%s: update xstate on sigframe, then validate xstate on sigreturn\n",
+ xstate.name);
- if (valid_xstate)
- printf("[OK]\txstate was restored correctly\n");
+ if (!sigreturn_failed)
+ ksft_test_result_pass("%s (return) for %s\n", __func__, xstate.name);
else
- printf("[FAIL]\txstate restoration failed\n");
+ ksft_test_result_fail("%s (return) for %s\n", __func__, xstate.name);
clearhandler(SIGUSR1);
free(stashed_xbuf);
}
-void test_xstate(uint32_t feature_num)
+static struct test_info {
+ void (*fn)(void);
+ unsigned int nr;
+} tests[] = {
+ { .nr = 1, .fn = test_context_switch, },
+ { .nr = 1, .fn = test_ptrace, },
+ { .nr = 2, .fn = test_signal, },
+};
+
+int test_xstate(uint32_t feature_num)
{
- const unsigned int ctxtsw_num_threads = 5, ctxtsw_iterations = 10;
unsigned long features;
- long rc;
+ long rc, i, nr = 0;
if (!(XFEATURE_MASK_TEST_SUPPORTED & (1 << feature_num))) {
ksft_print_msg("The xstate test does not fully support the component %u, yet.\n",
feature_num);
- return;
+ return KSFT_SKIP;
}
rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features);
if (rc || !(features & (1 << feature_num))) {
ksft_print_msg("The kernel does not support feature number: %u\n", feature_num);
- return;
+ return KSFT_SKIP;
}
xstate = get_xstate_info(feature_num);
@@ -460,7 +510,18 @@ void test_xstate(uint32_t feature_num)
xstate.size, xstate.xbuf_offset);
}
- test_context_switch(ctxtsw_num_threads, ctxtsw_iterations);
- test_ptrace();
- test_signal();
+ ksft_reset_state();
+ ksft_print_header();
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++)
+ nr += tests[i].nr;
+
+ ksft_set_plan(nr);
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++)
+ tests[i].fn();
+
+ ksft_print_cnts();
+
+ return (ksft_cnt.ksft_fail == 0) ? KSFT_PASS : KSFT_FAIL;
}
diff --git a/tools/testing/selftests/x86/xstate.h b/tools/testing/selftests/x86/xstate.h
index eedf0cab7ccb..5e712c979dc1 100644
--- a/tools/testing/selftests/x86/xstate.h
+++ b/tools/testing/selftests/x86/xstate.h
@@ -212,6 +212,6 @@ static inline void set_rand_data(struct xstate_info *xstate, struct xsave_buffer
}
/* Testing kernel's context switching and ABI support for the xstate. */
-void test_xstate(uint32_t feature_num);
+int test_xstate(uint32_t feature_num);
#endif /* __SELFTESTS_X86_XSTATE_H */
--
2.53.0
next prev parent reply other threads:[~2026-11-10 3:43 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 3:15 [PATCH 0/6] selftests/x86/xstate: more test cases for coredump and ptrace Chang S. Bae
2026-11-10 3:15 ` Chang S. Bae [this message]
2026-11-10 3:15 ` [PATCH 2/6] selftests/x86/xstate: Pre-allocate ptracee XSAVE buffer Chang S. Bae
2026-11-10 3:16 ` [PATCH 3/6] selftests/x86/xstate: Validate XSTATE_BV in ptrace test Chang S. Bae
2026-11-10 3:16 ` [PATCH 4/6] selftests/x86/xstate: Validate state reset " Chang S. Bae
2026-11-10 3:16 ` [PATCH 5/6] selftests/x86/xstate: Add coredump test Chang S. Bae
2026-11-10 3:16 ` [PATCH 6/6] selftests/mm: Add coredump test for x86 Chang S. Bae
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=20261110031603.698635-2-chang.seok.bae@intel.com \
--to=chang.seok.bae@intel.com \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=shuah@kernel.org \
--cc=tglx@linutronix.de \
--cc=x86@kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®