Skip to content

Commit e5ab8be

Browse files
cyrilbur-ibmmpe
authored andcommitted
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>
1 parent 01127f1 commit e5ab8be

File tree

6 files changed

+310
-3
lines changed

6 files changed

+310
-3
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
fpu_syscall
22
vmx_syscall
3+
fpu_preempt
4+
vmx_preempt

tools/testing/selftests/powerpc/math/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
TEST_PROGS := fpu_syscall vmx_syscall
1+
TEST_PROGS := fpu_syscall fpu_preempt vmx_syscall vmx_preempt
22

33
all: $(TEST_PROGS)
44

55
$(TEST_PROGS): ../harness.c
66
$(TEST_PROGS): CFLAGS += -O2 -g -pthread -m64 -maltivec
77

88
fpu_syscall: fpu_asm.S
9+
fpu_preempt: fpu_asm.S
10+
911
vmx_syscall: vmx_asm.S
12+
vmx_preempt: vmx_asm.S
1013

1114
include ../../lib.mk
1215

tools/testing/selftests/powerpc/math/fpu_asm.S

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,40 @@ FUNC_START(test_fpu)
159159
POP_BASIC_STACK(256)
160160
blr
161161
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)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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+
}

tools/testing/selftests/powerpc/math/vmx_asm.S

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "../basic_asm.h"
1111

12+
# POS MUST BE 16 ALIGNED!
1213
#define PUSH_VMX(pos,reg) \
1314
li reg,pos; \
1415
stvx v20,reg,sp; \
@@ -35,6 +36,7 @@
3536
addi reg,reg,16; \
3637
stvx v31,reg,sp;
3738

39+
# POS MUST BE 16 ALIGNED!
3840
#define POP_VMX(pos,reg) \
3941
li reg,pos; \
4042
lvx v20,reg,sp; \
@@ -93,7 +95,7 @@ FUNC_END(load_vmx)
9395

9496
# Should be safe from C, only touches r4, r5 and v0,v1,v2
9597
FUNC_START(check_vmx)
96-
PUSH_BASIC_STACK(16)
98+
PUSH_BASIC_STACK(32)
9799
mr r4,r3
98100
li r3,1 # assume a bad result
99101
li r5,0
@@ -162,7 +164,7 @@ FUNC_START(check_vmx)
162164
cmpdi r0,0xffffffffffffffff
163165
bne 1f
164166
li r3,0
165-
1: POP_BASIC_STACK(16)
167+
1: POP_BASIC_STACK(32)
166168
blr
167169
FUNC_END(check_vmx)
168170

@@ -193,3 +195,41 @@ FUNC_START(test_vmx)
193195
POP_BASIC_STACK(512)
194196
blr
195197
FUNC_END(test_vmx)
198+
199+
# int preempt_vmx(vector int *varray, int *threads_starting, int *running)
200+
# On starting will (atomically) decrement threads_starting as a signal that
201+
# the VMX have been loaded with varray. Will proceed to check the validity of
202+
# the VMX registers while running is not zero.
203+
FUNC_START(preempt_vmx)
204+
PUSH_BASIC_STACK(512)
205+
std r3,STACK_FRAME_PARAM(0)(sp) # vector int *varray
206+
std r4,STACK_FRAME_PARAM(1)(sp) # int *threads_starting
207+
std r5,STACK_FRAME_PARAM(2)(sp) # int *running
208+
# VMX need to write to 16 byte aligned addresses, skip STACK_FRAME_LOCAL(3,0)
209+
PUSH_VMX(STACK_FRAME_LOCAL(4,0),r4)
210+
211+
bl load_vmx
212+
nop
213+
214+
sync
215+
# Atomic DEC
216+
ld r3,STACK_FRAME_PARAM(1)(sp)
217+
1: lwarx r4,0,r3
218+
addi r4,r4,-1
219+
stwcx. r4,0,r3
220+
bne- 1b
221+
222+
2: ld r3,STACK_FRAME_PARAM(0)(sp)
223+
bl check_vmx
224+
nop
225+
cmpdi r3,0
226+
bne 3f
227+
ld r4,STACK_FRAME_PARAM(2)(sp)
228+
ld r5,0(r4)
229+
cmpwi r5,0
230+
bne 2b
231+
232+
3: POP_VMX(STACK_FRAME_LOCAL(4,0),r4)
233+
POP_BASIC_STACK(512)
234+
blr
235+
FUNC_END(preempt_vmx)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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+
}

0 commit comments

Comments
 (0)