18fdc9ce9SDmitry Chagin /* $Id: avx_sig.c,v 1.12 2021/12/11 22:47:09 kostik Exp $ */ 28fdc9ce9SDmitry Chagin /* 38fdc9ce9SDmitry Chagin * Naive test to check that context switches and signal delivery do 48fdc9ce9SDmitry Chagin * not corrupt AVX registers file (%xmm). Run until some 58fdc9ce9SDmitry Chagin * inconsistency detected, then aborts. 68fdc9ce9SDmitry Chagin * 78fdc9ce9SDmitry Chagin * FreeBSD: 88fdc9ce9SDmitry Chagin * ${CC} -Wall -Wextra -O -g -o avx_sig avx_sig.c -lpthread 98fdc9ce9SDmitry Chagin * Linux 108fdc9ce9SDmitry Chagin * ${CC} -D_GNU_SOURCE -Wall -Wextra -O -g -o avx_sig avx_sig.c -lbsd -lpthread 118fdc9ce9SDmitry Chagin */ 128fdc9ce9SDmitry Chagin 138fdc9ce9SDmitry Chagin #include <sys/param.h> 148fdc9ce9SDmitry Chagin #include <sys/time.h> 158fdc9ce9SDmitry Chagin #include <sys/resource.h> 168fdc9ce9SDmitry Chagin #include <sys/syscall.h> 178fdc9ce9SDmitry Chagin #include <errno.h> 188fdc9ce9SDmitry Chagin #include <pthread.h> 198fdc9ce9SDmitry Chagin #ifdef __FreeBSD__ 208fdc9ce9SDmitry Chagin #include <pthread_np.h> 218fdc9ce9SDmitry Chagin #endif 228fdc9ce9SDmitry Chagin #ifdef __linux__ 238fdc9ce9SDmitry Chagin #ifdef __GLIBC__ 248fdc9ce9SDmitry Chagin #include <gnu/libc-version.h> 258fdc9ce9SDmitry Chagin #endif 268fdc9ce9SDmitry Chagin #if !defined(__GLIBC__) || (__GLIBC__ * 100 + __GLIBC_MINOR__) < 236 278fdc9ce9SDmitry Chagin #include <bsd/stdlib.h> 288fdc9ce9SDmitry Chagin #endif 298fdc9ce9SDmitry Chagin #endif 308fdc9ce9SDmitry Chagin #include <signal.h> 318fdc9ce9SDmitry Chagin #include <stdatomic.h> 328fdc9ce9SDmitry Chagin #include <stdbool.h> 338fdc9ce9SDmitry Chagin #include <stdint.h> 348fdc9ce9SDmitry Chagin #include <stdio.h> 358fdc9ce9SDmitry Chagin #include <stdlib.h> 368fdc9ce9SDmitry Chagin #include <string.h> 378fdc9ce9SDmitry Chagin #include <unistd.h> 388fdc9ce9SDmitry Chagin 398fdc9ce9SDmitry Chagin /* SIGALRM interval in seconds. */ 408fdc9ce9SDmitry Chagin #ifndef TIMO 418fdc9ce9SDmitry Chagin #define TIMO 5 428fdc9ce9SDmitry Chagin #endif 438fdc9ce9SDmitry Chagin 448fdc9ce9SDmitry Chagin #ifndef __unused 458fdc9ce9SDmitry Chagin #define __unused __attribute__((__unused__)) 468fdc9ce9SDmitry Chagin #endif 47*0be13a45SDmitry Chagin 48*0be13a45SDmitry Chagin struct xregs_bank { 49*0be13a45SDmitry Chagin const char *b_name; 50*0be13a45SDmitry Chagin const char *r_name; 51*0be13a45SDmitry Chagin uint32_t regs; 52*0be13a45SDmitry Chagin uint32_t bytes; 53*0be13a45SDmitry Chagin void (*x2c)(uint8_t *); 54*0be13a45SDmitry Chagin void (*c2x)(uint8_t *); 55*0be13a45SDmitry Chagin }; 568fdc9ce9SDmitry Chagin 578fdc9ce9SDmitry Chagin #if defined(__amd64__) 58*0be13a45SDmitry Chagin void cpu_to_xmm(uint8_t *); 59*0be13a45SDmitry Chagin void xmm_to_cpu(uint8_t *); 60*0be13a45SDmitry Chagin 61*0be13a45SDmitry Chagin static const struct xregs_bank xregs_banks[] = { 62*0be13a45SDmitry Chagin { 63*0be13a45SDmitry Chagin .b_name = "SSE", 64*0be13a45SDmitry Chagin .r_name = "xmm", 65*0be13a45SDmitry Chagin .regs = 16, 66*0be13a45SDmitry Chagin .bytes = 16, 67*0be13a45SDmitry Chagin .x2c = xmm_to_cpu, 68*0be13a45SDmitry Chagin .c2x = cpu_to_xmm, 69*0be13a45SDmitry Chagin }, 70*0be13a45SDmitry Chagin }; 718fdc9ce9SDmitry Chagin #elif defined(__aarch64__) 72*0be13a45SDmitry Chagin void cpu_to_vfp(uint8_t *); 73*0be13a45SDmitry Chagin void vfp_to_cpu(uint8_t *); 74*0be13a45SDmitry Chagin 75*0be13a45SDmitry Chagin static const struct xregs_bank xregs_banks[] = { 76*0be13a45SDmitry Chagin { 77*0be13a45SDmitry Chagin .b_name = "VFP", 78*0be13a45SDmitry Chagin .r_name = "q", 79*0be13a45SDmitry Chagin .regs = 32, 80*0be13a45SDmitry Chagin .bytes = 16, 81*0be13a45SDmitry Chagin .x2c = vfp_to_cpu, 82*0be13a45SDmitry Chagin .c2x = cpu_to_vfp, 83*0be13a45SDmitry Chagin }, 84*0be13a45SDmitry Chagin }; 858fdc9ce9SDmitry Chagin #endif 868fdc9ce9SDmitry Chagin 878fdc9ce9SDmitry Chagin static atomic_uint sigs; 88*0be13a45SDmitry Chagin static int max_bank_idx; 89*0be13a45SDmitry Chagin 908fdc9ce9SDmitry Chagin 918fdc9ce9SDmitry Chagin static void 928fdc9ce9SDmitry Chagin sigusr1_handler(int sig __unused, siginfo_t *si __unused, void *m __unused) 938fdc9ce9SDmitry Chagin { 948fdc9ce9SDmitry Chagin atomic_fetch_add_explicit(&sigs, 1, memory_order_relaxed); 958fdc9ce9SDmitry Chagin } 968fdc9ce9SDmitry Chagin 978fdc9ce9SDmitry Chagin static void 988fdc9ce9SDmitry Chagin sigalrm_handler(int sig __unused) 998fdc9ce9SDmitry Chagin { 1008fdc9ce9SDmitry Chagin struct rusage r; 1018fdc9ce9SDmitry Chagin 1028fdc9ce9SDmitry Chagin if (getrusage(RUSAGE_SELF, &r) == 0) { 1038fdc9ce9SDmitry Chagin printf("%lu vctx %lu nvctx %lu nsigs %u SIGUSR1\n", 1048fdc9ce9SDmitry Chagin r.ru_nvcsw, r.ru_nivcsw, r.ru_nsignals, sigs); 1058fdc9ce9SDmitry Chagin } 1068fdc9ce9SDmitry Chagin alarm(TIMO); 1078fdc9ce9SDmitry Chagin } 1088fdc9ce9SDmitry Chagin 1098fdc9ce9SDmitry Chagin 1108fdc9ce9SDmitry Chagin static void 111*0be13a45SDmitry Chagin fill_xregs(uint8_t *xregs, int bank) 1128fdc9ce9SDmitry Chagin { 113*0be13a45SDmitry Chagin arc4random_buf(xregs, xregs_banks[bank].regs * xregs_banks[bank].bytes); 1148fdc9ce9SDmitry Chagin } 1158fdc9ce9SDmitry Chagin 1168fdc9ce9SDmitry Chagin static void 117*0be13a45SDmitry Chagin dump_xregs(const uint8_t *r, int bank) 1188fdc9ce9SDmitry Chagin { 1198fdc9ce9SDmitry Chagin unsigned k; 1208fdc9ce9SDmitry Chagin 121*0be13a45SDmitry Chagin for (k = 0; k < xregs_banks[bank].bytes; k++) { 1228fdc9ce9SDmitry Chagin if (k != 0) 1238fdc9ce9SDmitry Chagin printf(" "); 124*0be13a45SDmitry Chagin printf("%02x", r[k]); 1258fdc9ce9SDmitry Chagin } 1268fdc9ce9SDmitry Chagin printf("\n"); 1278fdc9ce9SDmitry Chagin } 1288fdc9ce9SDmitry Chagin 1298fdc9ce9SDmitry Chagin static pthread_mutex_t show_lock; 1308fdc9ce9SDmitry Chagin 1318fdc9ce9SDmitry Chagin static void 132*0be13a45SDmitry Chagin show_diff(const uint8_t *xregs1, const uint8_t *xregs2, int bank) 1338fdc9ce9SDmitry Chagin { 134*0be13a45SDmitry Chagin const uint8_t *r1, *r2; 1358fdc9ce9SDmitry Chagin unsigned i, j; 1368fdc9ce9SDmitry Chagin 1378fdc9ce9SDmitry Chagin #if defined(__FreeBSD__) 1388fdc9ce9SDmitry Chagin printf("thr %d\n", pthread_getthreadid_np()); 1398fdc9ce9SDmitry Chagin #elif defined(__linux__) 1408fdc9ce9SDmitry Chagin printf("thr %ld\n", syscall(SYS_gettid)); 1418fdc9ce9SDmitry Chagin #endif 142*0be13a45SDmitry Chagin for (i = 0; i < xregs_banks[bank].regs; i++) { 143*0be13a45SDmitry Chagin r1 = xregs1 + i * xregs_banks[bank].bytes; 144*0be13a45SDmitry Chagin r2 = xregs2 + i * xregs_banks[bank].bytes; 145*0be13a45SDmitry Chagin for (j = 0; j < xregs_banks[bank].bytes; j++) { 146*0be13a45SDmitry Chagin if (r1[j] != r2[j]) { 147*0be13a45SDmitry Chagin printf("%%%s%u\n", xregs_banks[bank].r_name, i); 148*0be13a45SDmitry Chagin dump_xregs(r1, bank); 149*0be13a45SDmitry Chagin dump_xregs(r2, bank); 1508fdc9ce9SDmitry Chagin break; 1518fdc9ce9SDmitry Chagin } 1528fdc9ce9SDmitry Chagin } 1538fdc9ce9SDmitry Chagin } 1548fdc9ce9SDmitry Chagin } 1558fdc9ce9SDmitry Chagin 1568fdc9ce9SDmitry Chagin static void 1578fdc9ce9SDmitry Chagin my_pause(void) 1588fdc9ce9SDmitry Chagin { 1598fdc9ce9SDmitry Chagin usleep(0); 1608fdc9ce9SDmitry Chagin } 1618fdc9ce9SDmitry Chagin 1628fdc9ce9SDmitry Chagin static void * 163*0be13a45SDmitry Chagin worker_thread(void *arg) 1648fdc9ce9SDmitry Chagin { 165*0be13a45SDmitry Chagin int bank = (uintptr_t)arg; 166*0be13a45SDmitry Chagin int sz = xregs_banks[bank].regs * xregs_banks[bank].bytes; 167*0be13a45SDmitry Chagin uint8_t xregs[sz], xregs_cpu[sz], zero_xregs[sz]; 1688fdc9ce9SDmitry Chagin 169*0be13a45SDmitry Chagin memset(zero_xregs, 0, sz); 170*0be13a45SDmitry Chagin 171*0be13a45SDmitry Chagin fill_xregs(xregs, bank); 1728fdc9ce9SDmitry Chagin for (;;) { 173*0be13a45SDmitry Chagin xregs_banks[bank].x2c(xregs); 1748fdc9ce9SDmitry Chagin my_pause(); 175*0be13a45SDmitry Chagin xregs_banks[bank].c2x(xregs_cpu); 176*0be13a45SDmitry Chagin if (memcmp(xregs, xregs_cpu, sz) != 0) { 1778fdc9ce9SDmitry Chagin pthread_mutex_lock(&show_lock); 178*0be13a45SDmitry Chagin show_diff(xregs, xregs_cpu, bank); 1798fdc9ce9SDmitry Chagin abort(); 1808fdc9ce9SDmitry Chagin pthread_mutex_unlock(&show_lock); 1818fdc9ce9SDmitry Chagin } 1828fdc9ce9SDmitry Chagin 183*0be13a45SDmitry Chagin xregs_banks[bank].x2c(zero_xregs); 1848fdc9ce9SDmitry Chagin my_pause(); 185*0be13a45SDmitry Chagin xregs_banks[bank].c2x(xregs_cpu); 186*0be13a45SDmitry Chagin if (memcmp(zero_xregs, xregs_cpu, sz) != 0) { 1878fdc9ce9SDmitry Chagin pthread_mutex_lock(&show_lock); 188*0be13a45SDmitry Chagin show_diff(zero_xregs, xregs_cpu, bank); 1898fdc9ce9SDmitry Chagin abort(); 1908fdc9ce9SDmitry Chagin pthread_mutex_unlock(&show_lock); 1918fdc9ce9SDmitry Chagin } 1928fdc9ce9SDmitry Chagin } 1938fdc9ce9SDmitry Chagin return (NULL); 1948fdc9ce9SDmitry Chagin } 1958fdc9ce9SDmitry Chagin 1968fdc9ce9SDmitry Chagin int 1978fdc9ce9SDmitry Chagin main(void) 1988fdc9ce9SDmitry Chagin { 1998fdc9ce9SDmitry Chagin struct sigaction sa; 200*0be13a45SDmitry Chagin int error, i, ncpu, bank; 201*0be13a45SDmitry Chagin 202*0be13a45SDmitry Chagin max_bank_idx = 0; 2038fdc9ce9SDmitry Chagin 2048fdc9ce9SDmitry Chagin bzero(&sa, sizeof(sa)); 2058fdc9ce9SDmitry Chagin sa.sa_handler = sigalrm_handler; 2068fdc9ce9SDmitry Chagin if (sigaction(SIGALRM, &sa, NULL) == -1) { 2078fdc9ce9SDmitry Chagin fprintf(stderr, "sigaction SIGALRM %s\n", strerror(errno)); 2088fdc9ce9SDmitry Chagin exit(1); 2098fdc9ce9SDmitry Chagin } 2108fdc9ce9SDmitry Chagin 2118fdc9ce9SDmitry Chagin bzero(&sa, sizeof(sa)); 2128fdc9ce9SDmitry Chagin sa.sa_sigaction = sigusr1_handler; 2138fdc9ce9SDmitry Chagin sa.sa_flags = SA_SIGINFO; 2148fdc9ce9SDmitry Chagin if (sigaction(SIGUSR1, &sa, NULL) == -1) { 2158fdc9ce9SDmitry Chagin fprintf(stderr, "sigaction SIGUSR1 %s\n", strerror(errno)); 2168fdc9ce9SDmitry Chagin exit(1); 2178fdc9ce9SDmitry Chagin } 2188fdc9ce9SDmitry Chagin 2198fdc9ce9SDmitry Chagin error = pthread_mutex_init(&show_lock, NULL); 2208fdc9ce9SDmitry Chagin if (error != 0) { 2218fdc9ce9SDmitry Chagin fprintf(stderr, "pthread_mutex_init %s\n", strerror(error)); 2228fdc9ce9SDmitry Chagin exit(1); 2238fdc9ce9SDmitry Chagin } 2248fdc9ce9SDmitry Chagin 2258fdc9ce9SDmitry Chagin ncpu = sysconf(_SC_NPROCESSORS_ONLN); 226*0be13a45SDmitry Chagin if (max_bank_idx == 0) 2278fdc9ce9SDmitry Chagin ncpu *= 2; 228*0be13a45SDmitry Chagin bank = 0; 2298fdc9ce9SDmitry Chagin pthread_t wt[ncpu]; 230*0be13a45SDmitry Chagin nextbank: 231*0be13a45SDmitry Chagin printf("Starting %d threads for registers bank %s sized [%d][%d]\n", ncpu, 232*0be13a45SDmitry Chagin xregs_banks[bank].b_name, xregs_banks[bank].regs, xregs_banks[bank].bytes); 2338fdc9ce9SDmitry Chagin for (i = 0; i < ncpu; i++) { 234*0be13a45SDmitry Chagin error = pthread_create(&wt[i], NULL, worker_thread, 235*0be13a45SDmitry Chagin (void *)(uintptr_t)bank); 2368fdc9ce9SDmitry Chagin if (error != 0) { 2378fdc9ce9SDmitry Chagin fprintf(stderr, "pthread_create %s\n", strerror(error)); 2388fdc9ce9SDmitry Chagin } 2398fdc9ce9SDmitry Chagin } 240*0be13a45SDmitry Chagin if (++bank <= max_bank_idx) 241*0be13a45SDmitry Chagin goto nextbank; 2428fdc9ce9SDmitry Chagin 2438fdc9ce9SDmitry Chagin alarm(TIMO); 2448fdc9ce9SDmitry Chagin for (;;) { 2458fdc9ce9SDmitry Chagin for (i = 0; i < ncpu; i++) { 2468fdc9ce9SDmitry Chagin my_pause(); 2478fdc9ce9SDmitry Chagin pthread_kill(wt[i], SIGUSR1); 2488fdc9ce9SDmitry Chagin } 2498fdc9ce9SDmitry Chagin } 2508fdc9ce9SDmitry Chagin } 251