Skip to content

Commit 101c669

Browse files
ChangSeokBaesuryasaimadhu
authored andcommitted
selftests/x86/amx: Add context switch test
XSAVE state is thread-local. The kernel switches between thread state at context switch time. Generally, running a selftest for a while will naturally expose it to some context switching and and will test the XSAVE code. Instead of just hoping that the tests get context-switched at random times, force context-switches on purpose. Spawn off a few userspace threads and force context-switches between them. Ensure that the kernel correctly context switches each thread's unique AMX state. [ dhansen: bunches of cleanups ] Signed-off-by: Chang S. Bae <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 6a3e065 commit 101c669

File tree

1 file changed

+157
-3
lines changed
  • tools/testing/selftests/x86

1 file changed

+157
-3
lines changed

tools/testing/selftests/x86/amx.c

Lines changed: 157 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
#define _GNU_SOURCE
44
#include <err.h>
55
#include <errno.h>
6+
#include <pthread.h>
67
#include <setjmp.h>
78
#include <stdio.h>
89
#include <string.h>
910
#include <stdbool.h>
1011
#include <unistd.h>
1112
#include <x86intrin.h>
1213

13-
#include <linux/futex.h>
14-
1514
#include <sys/auxv.h>
1615
#include <sys/mman.h>
1716
#include <sys/shm.h>
@@ -259,7 +258,6 @@ void sig_print(char *msg)
259258

260259
static volatile bool noperm_signaled;
261260
static int noperm_errs;
262-
263261
/*
264262
* Signal handler for when AMX is used but
265263
* permission has not been obtained.
@@ -674,6 +672,158 @@ static void test_fork(void)
674672
_exit(0);
675673
}
676674

675+
/* Context switching test */
676+
677+
static struct _ctxtswtest_cfg {
678+
unsigned int iterations;
679+
unsigned int num_threads;
680+
} ctxtswtest_config;
681+
682+
struct futex_info {
683+
pthread_t thread;
684+
int nr;
685+
pthread_mutex_t mutex;
686+
struct futex_info *next;
687+
};
688+
689+
static void *check_tiledata(void *info)
690+
{
691+
struct futex_info *finfo = (struct futex_info *)info;
692+
struct xsave_buffer *xbuf;
693+
int i;
694+
695+
xbuf = alloc_xbuf();
696+
if (!xbuf)
697+
fatal_error("unable to allocate XSAVE buffer");
698+
699+
/*
700+
* Load random data into 'xbuf' and then restore
701+
* it to the tile registers themselves.
702+
*/
703+
load_rand_tiledata(xbuf);
704+
for (i = 0; i < ctxtswtest_config.iterations; i++) {
705+
pthread_mutex_lock(&finfo->mutex);
706+
707+
/*
708+
* Ensure the register values have not
709+
* diverged from those recorded in 'xbuf'.
710+
*/
711+
validate_tiledata_regs_same(xbuf);
712+
713+
/* Load new, random values into xbuf and registers */
714+
load_rand_tiledata(xbuf);
715+
716+
/*
717+
* The last thread's last unlock will be for
718+
* thread 0's mutex. However, thread 0 will
719+
* have already exited the loop and the mutex
720+
* will already be unlocked.
721+
*
722+
* Because this is not an ERRORCHECK mutex,
723+
* that inconsistency will be silently ignored.
724+
*/
725+
pthread_mutex_unlock(&finfo->next->mutex);
726+
}
727+
728+
free(xbuf);
729+
/*
730+
* Return this thread's finfo, which is
731+
* a unique value for this thread.
732+
*/
733+
return finfo;
734+
}
735+
736+
static int create_threads(int num, struct futex_info *finfo)
737+
{
738+
int i;
739+
740+
for (i = 0; i < num; i++) {
741+
int next_nr;
742+
743+
finfo[i].nr = i;
744+
/*
745+
* Thread 'i' will wait on this mutex to
746+
* be unlocked. Lock it immediately after
747+
* initialization:
748+
*/
749+
pthread_mutex_init(&finfo[i].mutex, NULL);
750+
pthread_mutex_lock(&finfo[i].mutex);
751+
752+
next_nr = (i + 1) % num;
753+
finfo[i].next = &finfo[next_nr];
754+
755+
if (pthread_create(&finfo[i].thread, NULL, check_tiledata, &finfo[i]))
756+
fatal_error("pthread_create()");
757+
}
758+
return 0;
759+
}
760+
761+
static void affinitize_cpu0(void)
762+
{
763+
cpu_set_t cpuset;
764+
765+
CPU_ZERO(&cpuset);
766+
CPU_SET(0, &cpuset);
767+
768+
if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
769+
fatal_error("sched_setaffinity to CPU 0");
770+
}
771+
772+
static void test_context_switch(void)
773+
{
774+
struct futex_info *finfo;
775+
int i;
776+
777+
/* Affinitize to one CPU to force context switches */
778+
affinitize_cpu0();
779+
780+
req_xtiledata_perm();
781+
782+
printf("[RUN]\tCheck tiledata context switches, %d iterations, %d threads.\n",
783+
ctxtswtest_config.iterations,
784+
ctxtswtest_config.num_threads);
785+
786+
787+
finfo = malloc(sizeof(*finfo) * ctxtswtest_config.num_threads);
788+
if (!finfo)
789+
fatal_error("malloc()");
790+
791+
create_threads(ctxtswtest_config.num_threads, finfo);
792+
793+
/*
794+
* This thread wakes up thread 0
795+
* Thread 0 will wake up 1
796+
* Thread 1 will wake up 2
797+
* ...
798+
* the last thread will wake up 0
799+
*
800+
* ... this will repeat for the configured
801+
* number of iterations.
802+
*/
803+
pthread_mutex_unlock(&finfo[0].mutex);
804+
805+
/* Wait for all the threads to finish: */
806+
for (i = 0; i < ctxtswtest_config.num_threads; i++) {
807+
void *thread_retval;
808+
int rc;
809+
810+
rc = pthread_join(finfo[i].thread, &thread_retval);
811+
812+
if (rc)
813+
fatal_error("pthread_join() failed for thread %d err: %d\n",
814+
i, rc);
815+
816+
if (thread_retval != &finfo[i])
817+
fatal_error("unexpected thread retval for thread %d: %p\n",
818+
i, thread_retval);
819+
820+
}
821+
822+
printf("[OK]\tNo incorrect case was found.\n");
823+
824+
free(finfo);
825+
}
826+
677827
int main(void)
678828
{
679829
/* Check hardware availability at first */
@@ -690,6 +840,10 @@ int main(void)
690840

691841
test_fork();
692842

843+
ctxtswtest_config.iterations = 10;
844+
ctxtswtest_config.num_threads = 5;
845+
test_context_switch();
846+
693847
clearhandler(SIGILL);
694848
free_stashed_xsave();
695849

0 commit comments

Comments
 (0)