git: 4626aa7fda0a - main - Add a tool to test AVX context integrity under ctx switches and signals (amd64)
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 13 Dec 2021 02:33:15 UTC
The branch main has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=4626aa7fda0a677a72a791056fd2d6cad68e944f commit 4626aa7fda0a677a72a791056fd2d6cad68e944f Author: Konstantin Belousov <kib@FreeBSD.org> AuthorDate: 2021-12-13 02:31:40 +0000 Commit: Konstantin Belousov <kib@FreeBSD.org> CommitDate: 2021-12-13 02:31:40 +0000 Add a tool to test AVX context integrity under ctx switches and signals (amd64) Sponsored by: The FreeBSD Foundation MFC after: 1 week --- tools/test/avx_sig/avx_sig.c | 220 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) diff --git a/tools/test/avx_sig/avx_sig.c b/tools/test/avx_sig/avx_sig.c new file mode 100644 index 000000000000..711d40f2c231 --- /dev/null +++ b/tools/test/avx_sig/avx_sig.c @@ -0,0 +1,220 @@ +/* $Id: avx_sig.c,v 1.12 2021/12/11 22:47:09 kostik Exp $ */ +/* + * Naive test to check that context switches and signal delivery do + * not corrupt AVX registers file (%xmm). Run until some + * inconsistency detected, then aborts. + * + * FreeBSD: + * ${CC} -Wall -Wextra -O -g -o avx_sig avx_sig.c -lpthread + * Linux + * ${CC} -D_GNU_SOURCE -Wall -Wextra -O -g -o avx_sig avx_sig.c -lbsd -lpthread + */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/syscall.h> +#include <errno.h> +#include <pthread.h> +#ifdef __FreeBSD__ +#include <pthread_np.h> +#endif +#ifdef __linux__ +#include <bsd/stdlib.h> +#endif +#include <signal.h> +#include <stdatomic.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif +#ifndef nitems +#define nitems(x) (sizeof((x)) / sizeof((x)[0])) +#endif + +struct xmmreg { + uint8_t xmm_bytes[16]; +}; + +struct xmm { + struct xmmreg xmmreg[16]; +}; + +#define X2C(r) asm("movdqu %0, %%xmm" #r : "=m" (xmm->xmmreg[r])) +#define C2X(r) asm("movdqu %%xmm" #r ", %0" : : "m" (xmm->xmmreg[r]) : "xmm" #r) + +static void +cpu_to_xmm(struct xmm *xmm) +{ + C2X(0); C2X(1); C2X(2); C2X(3); C2X(4); C2X(5); C2X(6); C2X(7); + C2X(8); C2X(9); C2X(10); C2X(11); C2X(12); C2X(13); C2X(14); C2X(15); +} + +static void +xmm_to_cpu(struct xmm *xmm) +{ + X2C(0); X2C(1); X2C(2); X2C(3); X2C(4); X2C(5); X2C(6); X2C(7); + X2C(8); X2C(9); X2C(10); X2C(11); X2C(12); X2C(13); X2C(14); X2C(15); +} + +#undef C2X +#undef X2C + +static atomic_uint sigs; + +static void +sigusr1_handler(int sig __unused, siginfo_t *si __unused, void *m __unused) +{ + atomic_fetch_add_explicit(&sigs, 1, memory_order_relaxed); +} + +#ifdef SIGINFO +static void +siginfo_handler(int sig __unused) +{ + struct rusage r; + + if (getrusage(RUSAGE_SELF, &r) == 0) { + printf("%lu vctx %lu nvctx %lu nsigs ", + r.ru_nvcsw, r.ru_nivcsw, r.ru_nsignals); + } + printf("%u SIGUSR1\n", sigs); +} +#endif + +static struct xmm zero_xmm = {}; + +static void +fill_xmm(struct xmm *xmm) +{ + arc4random_buf(xmm, sizeof(*xmm)); +} + +static void +dump_xmm(const struct xmmreg *r) +{ + unsigned k; + + for (k = 0; k < nitems(r->xmm_bytes); k++) { + if (k != 0) + printf(" "); + printf("%02x", r->xmm_bytes[k]); + } + printf("\n"); +} + +static pthread_mutex_t show_lock; + +static void +show_diff(const struct xmm *xmm1, const struct xmm *xmm2) +{ + const struct xmmreg *r1, *r2; + unsigned i, j; + +#if defined(__FreeBSD__) + printf("thr %d\n", pthread_getthreadid_np()); +#elif defined(__linux__) + printf("thr %ld\n", syscall(SYS_gettid)); +#endif + for (i = 0; i < nitems(xmm1->xmmreg); i++) { + r1 = &xmm1->xmmreg[i]; + r2 = &xmm2->xmmreg[i]; + for (j = 0; j < nitems(r1->xmm_bytes); j++) { + if (r1->xmm_bytes[j] != r2->xmm_bytes[j]) { + printf("xmm%u\n", i); + dump_xmm(r1); + dump_xmm(r2); + break; + } + } + } +} + +static void +my_pause(void) +{ + usleep(0); +} + +static void * +worker_thread(void *arg __unused) +{ + struct xmm xmm, xmm_cpu; + + fill_xmm(&xmm); + for (;;) { + xmm_to_cpu(&xmm); + my_pause(); + cpu_to_xmm(&xmm_cpu); + if (memcmp(&xmm, &xmm_cpu, sizeof(struct xmm)) != 0) { + pthread_mutex_lock(&show_lock); + show_diff(&xmm, &xmm_cpu); + abort(); + pthread_mutex_unlock(&show_lock); + } + + xmm_to_cpu(&zero_xmm); + my_pause(); + cpu_to_xmm(&xmm_cpu); + if (memcmp(&zero_xmm, &xmm_cpu, sizeof(struct xmm)) != 0) { + pthread_mutex_lock(&show_lock); + show_diff(&zero_xmm, &xmm_cpu); + abort(); + pthread_mutex_unlock(&show_lock); + } + } + return (NULL); +} + +int +main(void) +{ + struct sigaction sa; + int error, i, ncpu; + +#ifdef SIGINFO + bzero(&sa, sizeof(sa)); + sa.sa_handler = siginfo_handler; + if (sigaction(SIGINFO, &sa, NULL) == -1) { + fprintf(stderr, "sigaction SIGINFO %s\n", strerror(errno)); + exit(1); + } +#endif + + bzero(&sa, sizeof(sa)); + sa.sa_sigaction = sigusr1_handler; + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGUSR1, &sa, NULL) == -1) { + fprintf(stderr, "sigaction SIGUSR1 %s\n", strerror(errno)); + exit(1); + } + + error = pthread_mutex_init(&show_lock, NULL); + if (error != 0) { + fprintf(stderr, "pthread_mutex_init %s\n", strerror(error)); + exit(1); + } + + ncpu = sysconf(_SC_NPROCESSORS_ONLN); + ncpu *= 2; + pthread_t wt[ncpu]; + for (i = 0; i < ncpu; i++) { + error = pthread_create(&wt[i], NULL, worker_thread, NULL); + if (error != 0) { + fprintf(stderr, "pthread_create %s\n", strerror(error)); + } + } + + for (;;) { + for (i = 0; i < ncpu; i++) { + my_pause(); + pthread_kill(wt[i], SIGUSR1); + } + } +}