1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2015, Cyril Bur, IBM Corp. 4 * 5 * This test attempts to see if the FPU registers are correctly reported in a 6 * signal context. Each worker just spins checking its FPU registers, at some 7 * point a signal will interrupt it and C code will check the signal context 8 * ensuring it is also the same. 9 */ 10 11 #include <stdio.h> 12 #include <unistd.h> 13 #include <sys/syscall.h> 14 #include <sys/time.h> 15 #include <sys/types.h> 16 #include <sys/wait.h> 17 #include <stdlib.h> 18 #include <pthread.h> 19 20 #include "utils.h" 21 22 /* Number of times each thread should receive the signal */ 23 #define ITERATIONS 10 24 /* 25 * Factor by which to multiply number of online CPUs for total number of 26 * worker threads 27 */ 28 #define THREAD_FACTOR 8 29 30 __thread double darray[] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 31 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 32 2.1}; 33 34 bool bad_context; 35 int threads_starting; 36 int running; 37 38 extern long preempt_fpu(double *darray, int *threads_starting, int *running); 39 40 void signal_fpu_sig(int sig, siginfo_t *info, void *context) 41 { 42 int i; 43 ucontext_t *uc = context; 44 mcontext_t *mc = &uc->uc_mcontext; 45 46 /* Only the non volatiles were loaded up */ 47 for (i = 14; i < 32; i++) { 48 if (mc->fp_regs[i] != darray[i - 14]) { 49 bad_context = true; 50 break; 51 } 52 } 53 } 54 55 void *signal_fpu_c(void *p) 56 { 57 int i; 58 long rc; 59 struct sigaction act; 60 act.sa_sigaction = signal_fpu_sig; 61 act.sa_flags = SA_SIGINFO; 62 rc = sigaction(SIGUSR1, &act, NULL); 63 if (rc) 64 return p; 65 66 srand(pthread_self()); 67 for (i = 0; i < 21; i++) 68 darray[i] = rand(); 69 70 rc = preempt_fpu(darray, &threads_starting, &running); 71 72 return (void *) rc; 73 } 74 75 int test_signal_fpu(void) 76 { 77 int i, j, rc, threads; 78 void *rc_p; 79 pthread_t *tids; 80 81 threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR; 82 tids = malloc(threads * sizeof(pthread_t)); 83 FAIL_IF(!tids); 84 85 running = true; 86 threads_starting = threads; 87 for (i = 0; i < threads; i++) { 88 rc = pthread_create(&tids[i], NULL, signal_fpu_c, NULL); 89 FAIL_IF(rc); 90 } 91 92 setbuf(stdout, NULL); 93 printf("\tWaiting for all workers to start..."); 94 while (threads_starting) 95 asm volatile("": : :"memory"); 96 printf("done\n"); 97 98 printf("\tSending signals to all threads %d times...", ITERATIONS); 99 for (i = 0; i < ITERATIONS; i++) { 100 for (j = 0; j < threads; j++) { 101 pthread_kill(tids[j], SIGUSR1); 102 } 103 sleep(1); 104 } 105 printf("done\n"); 106 107 printf("\tStopping workers..."); 108 running = 0; 109 for (i = 0; i < threads; i++) { 110 pthread_join(tids[i], &rc_p); 111 112 /* 113 * Harness will say the fail was here, look at why signal_fpu 114 * returned 115 */ 116 if ((long) rc_p || bad_context) 117 printf("oops\n"); 118 if (bad_context) 119 fprintf(stderr, "\t!! bad_context is true\n"); 120 FAIL_IF((long) rc_p || bad_context); 121 } 122 printf("done\n"); 123 124 free(tids); 125 return 0; 126 } 127 128 int main(int argc, char *argv[]) 129 { 130 return test_harness(test_signal_fpu, "fpu_signal"); 131 } 132