Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/powerpc: Test preservation of FPU and VMX regs across preemption

Loop in assembly checking the registers with many threads.

Signed-off-by: Cyril Bur <cyrilbur@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Cyril Bur and committed by
Michael Ellerman
e5ab8be6 01127f1e

+310 -3
+2
tools/testing/selftests/powerpc/math/.gitignore
··· 1 1 fpu_syscall 2 2 vmx_syscall 3 + fpu_preempt 4 + vmx_preempt
+4 -1
tools/testing/selftests/powerpc/math/Makefile
··· 1 - TEST_PROGS := fpu_syscall vmx_syscall 1 + TEST_PROGS := fpu_syscall fpu_preempt vmx_syscall vmx_preempt 2 2 3 3 all: $(TEST_PROGS) 4 4 ··· 6 6 $(TEST_PROGS): CFLAGS += -O2 -g -pthread -m64 -maltivec 7 7 8 8 fpu_syscall: fpu_asm.S 9 + fpu_preempt: fpu_asm.S 10 + 9 11 vmx_syscall: vmx_asm.S 12 + vmx_preempt: vmx_asm.S 10 13 11 14 include ../../lib.mk 12 15
+37
tools/testing/selftests/powerpc/math/fpu_asm.S
··· 159 159 POP_BASIC_STACK(256) 160 160 blr 161 161 FUNC_END(test_fpu) 162 + 163 + # int preempt_fpu(double *darray, int *threads_running, int *running) 164 + # On starting will (atomically) decrement not_ready as a signal that the FPU 165 + # has been loaded with darray. Will proceed to check the validity of the FPU 166 + # registers while running is not zero. 167 + FUNC_START(preempt_fpu) 168 + PUSH_BASIC_STACK(256) 169 + std r3,STACK_FRAME_PARAM(0)(sp) # double *darray 170 + std r4,STACK_FRAME_PARAM(1)(sp) # int *threads_starting 171 + std r5,STACK_FRAME_PARAM(2)(sp) # int *running 172 + PUSH_FPU(STACK_FRAME_LOCAL(3,0)) 173 + 174 + bl load_fpu 175 + nop 176 + 177 + sync 178 + # Atomic DEC 179 + ld r3,STACK_FRAME_PARAM(1)(sp) 180 + 1: lwarx r4,0,r3 181 + addi r4,r4,-1 182 + stwcx. r4,0,r3 183 + bne- 1b 184 + 185 + 2: ld r3,STACK_FRAME_PARAM(0)(sp) 186 + bl check_fpu 187 + nop 188 + cmpdi r3,0 189 + bne 3f 190 + ld r4,STACK_FRAME_PARAM(2)(sp) 191 + ld r5,0(r4) 192 + cmpwi r5,0 193 + bne 2b 194 + 195 + 3: POP_FPU(STACK_FRAME_LOCAL(3,0)) 196 + POP_BASIC_STACK(256) 197 + blr 198 + FUNC_END(preempt_fpu)
+113
tools/testing/selftests/powerpc/math/fpu_preempt.c
··· 1 + /* 2 + * Copyright 2015, Cyril Bur, IBM Corp. 3 + * 4 + * This program is free software; you can redistribute it and/or 5 + * modify it under the terms of the GNU General Public License 6 + * as published by the Free Software Foundation; either version 7 + * 2 of the License, or (at your option) any later version. 8 + * 9 + * This test attempts to see if the FPU registers change across preemption. 10 + * Two things should be noted here a) The check_fpu function in asm only checks 11 + * the non volatile registers as it is reused from the syscall test b) There is 12 + * no way to be sure preemption happened so this test just uses many threads 13 + * and a long wait. As such, a successful test doesn't mean much but a failure 14 + * is bad. 15 + */ 16 + 17 + #include <stdio.h> 18 + #include <unistd.h> 19 + #include <sys/syscall.h> 20 + #include <sys/time.h> 21 + #include <sys/types.h> 22 + #include <sys/wait.h> 23 + #include <stdlib.h> 24 + #include <pthread.h> 25 + 26 + #include "utils.h" 27 + 28 + /* Time to wait for workers to get preempted (seconds) */ 29 + #define PREEMPT_TIME 20 30 + /* 31 + * Factor by which to multiply number of online CPUs for total number of 32 + * worker threads 33 + */ 34 + #define THREAD_FACTOR 8 35 + 36 + 37 + __thread double darray[] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 38 + 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 39 + 2.1}; 40 + 41 + int threads_starting; 42 + int running; 43 + 44 + extern void preempt_fpu(double *darray, int *threads_starting, int *running); 45 + 46 + void *preempt_fpu_c(void *p) 47 + { 48 + int i; 49 + srand(pthread_self()); 50 + for (i = 0; i < 21; i++) 51 + darray[i] = rand(); 52 + 53 + /* Test failed if it ever returns */ 54 + preempt_fpu(darray, &threads_starting, &running); 55 + 56 + return p; 57 + } 58 + 59 + int test_preempt_fpu(void) 60 + { 61 + int i, rc, threads; 62 + pthread_t *tids; 63 + 64 + threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR; 65 + tids = malloc((threads) * sizeof(pthread_t)); 66 + FAIL_IF(!tids); 67 + 68 + running = true; 69 + threads_starting = threads; 70 + for (i = 0; i < threads; i++) { 71 + rc = pthread_create(&tids[i], NULL, preempt_fpu_c, NULL); 72 + FAIL_IF(rc); 73 + } 74 + 75 + setbuf(stdout, NULL); 76 + /* Not really necessary but nice to wait for every thread to start */ 77 + printf("\tWaiting for all workers to start..."); 78 + while(threads_starting) 79 + asm volatile("": : :"memory"); 80 + printf("done\n"); 81 + 82 + printf("\tWaiting for %d seconds to let some workers get preempted...", PREEMPT_TIME); 83 + sleep(PREEMPT_TIME); 84 + printf("done\n"); 85 + 86 + printf("\tStopping workers..."); 87 + /* 88 + * Working are checking this value every loop. In preempt_fpu 'cmpwi r5,0; bne 2b'. 89 + * r5 will have loaded the value of running. 90 + */ 91 + running = 0; 92 + for (i = 0; i < threads; i++) { 93 + void *rc_p; 94 + pthread_join(tids[i], &rc_p); 95 + 96 + /* 97 + * Harness will say the fail was here, look at why preempt_fpu 98 + * returned 99 + */ 100 + if ((long) rc_p) 101 + printf("oops\n"); 102 + FAIL_IF((long) rc_p); 103 + } 104 + printf("done\n"); 105 + 106 + free(tids); 107 + return 0; 108 + } 109 + 110 + int main(int argc, char *argv[]) 111 + { 112 + return test_harness(test_preempt_fpu, "fpu_preempt"); 113 + }
+42 -2
tools/testing/selftests/powerpc/math/vmx_asm.S
··· 9 9 10 10 #include "../basic_asm.h" 11 11 12 + # POS MUST BE 16 ALIGNED! 12 13 #define PUSH_VMX(pos,reg) \ 13 14 li reg,pos; \ 14 15 stvx v20,reg,sp; \ ··· 36 35 addi reg,reg,16; \ 37 36 stvx v31,reg,sp; 38 37 38 + # POS MUST BE 16 ALIGNED! 39 39 #define POP_VMX(pos,reg) \ 40 40 li reg,pos; \ 41 41 lvx v20,reg,sp; \ ··· 95 93 96 94 # Should be safe from C, only touches r4, r5 and v0,v1,v2 97 95 FUNC_START(check_vmx) 98 - PUSH_BASIC_STACK(16) 96 + PUSH_BASIC_STACK(32) 99 97 mr r4,r3 100 98 li r3,1 # assume a bad result 101 99 li r5,0 ··· 164 162 cmpdi r0,0xffffffffffffffff 165 163 bne 1f 166 164 li r3,0 167 - 1: POP_BASIC_STACK(16) 165 + 1: POP_BASIC_STACK(32) 168 166 blr 169 167 FUNC_END(check_vmx) 170 168 ··· 195 193 POP_BASIC_STACK(512) 196 194 blr 197 195 FUNC_END(test_vmx) 196 + 197 + # int preempt_vmx(vector int *varray, int *threads_starting, int *running) 198 + # On starting will (atomically) decrement threads_starting as a signal that 199 + # the VMX have been loaded with varray. Will proceed to check the validity of 200 + # the VMX registers while running is not zero. 201 + FUNC_START(preempt_vmx) 202 + PUSH_BASIC_STACK(512) 203 + std r3,STACK_FRAME_PARAM(0)(sp) # vector int *varray 204 + std r4,STACK_FRAME_PARAM(1)(sp) # int *threads_starting 205 + std r5,STACK_FRAME_PARAM(2)(sp) # int *running 206 + # VMX need to write to 16 byte aligned addresses, skip STACK_FRAME_LOCAL(3,0) 207 + PUSH_VMX(STACK_FRAME_LOCAL(4,0),r4) 208 + 209 + bl load_vmx 210 + nop 211 + 212 + sync 213 + # Atomic DEC 214 + ld r3,STACK_FRAME_PARAM(1)(sp) 215 + 1: lwarx r4,0,r3 216 + addi r4,r4,-1 217 + stwcx. r4,0,r3 218 + bne- 1b 219 + 220 + 2: ld r3,STACK_FRAME_PARAM(0)(sp) 221 + bl check_vmx 222 + nop 223 + cmpdi r3,0 224 + bne 3f 225 + ld r4,STACK_FRAME_PARAM(2)(sp) 226 + ld r5,0(r4) 227 + cmpwi r5,0 228 + bne 2b 229 + 230 + 3: POP_VMX(STACK_FRAME_LOCAL(4,0),r4) 231 + POP_BASIC_STACK(512) 232 + blr 233 + FUNC_END(preempt_vmx)
+112
tools/testing/selftests/powerpc/math/vmx_preempt.c
··· 1 + /* 2 + * Copyright 2015, Cyril Bur, IBM Corp. 3 + * 4 + * This program is free software; you can redistribute it and/or 5 + * modify it under the terms of the GNU General Public License 6 + * as published by the Free Software Foundation; either version 7 + * 2 of the License, or (at your option) any later version. 8 + * 9 + * This test attempts to see if the VMX registers change across preemption. 10 + * Two things should be noted here a) The check_vmx function in asm only checks 11 + * the non volatile registers as it is reused from the syscall test b) There is 12 + * no way to be sure preemption happened so this test just uses many threads 13 + * and a long wait. As such, a successful test doesn't mean much but a failure 14 + * is bad. 15 + */ 16 + 17 + #include <stdio.h> 18 + #include <unistd.h> 19 + #include <sys/syscall.h> 20 + #include <sys/time.h> 21 + #include <sys/types.h> 22 + #include <sys/wait.h> 23 + #include <stdlib.h> 24 + #include <pthread.h> 25 + 26 + #include "utils.h" 27 + 28 + /* Time to wait for workers to get preempted (seconds) */ 29 + #define PREEMPT_TIME 20 30 + /* 31 + * Factor by which to multiply number of online CPUs for total number of 32 + * worker threads 33 + */ 34 + #define THREAD_FACTOR 8 35 + 36 + __thread vector int varray[] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10,11,12}, 37 + {13,14,15,16},{17,18,19,20},{21,22,23,24}, 38 + {25,26,27,28},{29,30,31,32},{33,34,35,36}, 39 + {37,38,39,40},{41,42,43,44},{45,46,47,48}}; 40 + 41 + int threads_starting; 42 + int running; 43 + 44 + extern void preempt_vmx(vector int *varray, int *threads_starting, int *running); 45 + 46 + void *preempt_vmx_c(void *p) 47 + { 48 + int i, j; 49 + srand(pthread_self()); 50 + for (i = 0; i < 12; i++) 51 + for (j = 0; j < 4; j++) 52 + varray[i][j] = rand(); 53 + 54 + /* Test fails if it ever returns */ 55 + preempt_vmx(varray, &threads_starting, &running); 56 + return p; 57 + } 58 + 59 + int test_preempt_vmx(void) 60 + { 61 + int i, rc, threads; 62 + pthread_t *tids; 63 + 64 + threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR; 65 + tids = malloc(threads * sizeof(pthread_t)); 66 + FAIL_IF(!tids); 67 + 68 + running = true; 69 + threads_starting = threads; 70 + for (i = 0; i < threads; i++) { 71 + rc = pthread_create(&tids[i], NULL, preempt_vmx_c, NULL); 72 + FAIL_IF(rc); 73 + } 74 + 75 + setbuf(stdout, NULL); 76 + /* Not really nessesary but nice to wait for every thread to start */ 77 + printf("\tWaiting for all workers to start..."); 78 + while(threads_starting) 79 + asm volatile("": : :"memory"); 80 + printf("done\n"); 81 + 82 + printf("\tWaiting for %d seconds to let some workers get preempted...", PREEMPT_TIME); 83 + sleep(PREEMPT_TIME); 84 + printf("done\n"); 85 + 86 + printf("\tStopping workers..."); 87 + /* 88 + * Working are checking this value every loop. In preempt_vmx 'cmpwi r5,0; bne 2b'. 89 + * r5 will have loaded the value of running. 90 + */ 91 + running = 0; 92 + for (i = 0; i < threads; i++) { 93 + void *rc_p; 94 + pthread_join(tids[i], &rc_p); 95 + 96 + /* 97 + * Harness will say the fail was here, look at why preempt_vmx 98 + * returned 99 + */ 100 + if ((long) rc_p) 101 + printf("oops\n"); 102 + FAIL_IF((long) rc_p); 103 + } 104 + printf("done\n"); 105 + 106 + return 0; 107 + } 108 + 109 + int main(int argc, char *argv[]) 110 + { 111 + return test_harness(test_preempt_vmx, "vmx_preempt"); 112 + }