Skip to content

Commit

Permalink
selftests/powerpc: Test preservation of FPU and VMX regs across preem…
Browse files Browse the repository at this point in the history
…ption

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>
  • Loading branch information
Cyril Bur authored and Michael Ellerman committed Mar 2, 2016
1 parent 01127f1 commit e5ab8be
Show file tree
Hide file tree
Showing 6 changed files with 310 additions and 3 deletions.
2 changes: 2 additions & 0 deletions tools/testing/selftests/powerpc/math/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
fpu_syscall
vmx_syscall
fpu_preempt
vmx_preempt
5 changes: 4 additions & 1 deletion tools/testing/selftests/powerpc/math/Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
TEST_PROGS := fpu_syscall vmx_syscall
TEST_PROGS := fpu_syscall fpu_preempt vmx_syscall vmx_preempt

all: $(TEST_PROGS)

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

fpu_syscall: fpu_asm.S
fpu_preempt: fpu_asm.S

vmx_syscall: vmx_asm.S
vmx_preempt: vmx_asm.S

include ../../lib.mk

Expand Down
37 changes: 37 additions & 0 deletions tools/testing/selftests/powerpc/math/fpu_asm.S
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,40 @@ FUNC_START(test_fpu)
POP_BASIC_STACK(256)
blr
FUNC_END(test_fpu)

# int preempt_fpu(double *darray, int *threads_running, int *running)
# On starting will (atomically) decrement not_ready as a signal that the FPU
# has been loaded with darray. Will proceed to check the validity of the FPU
# registers while running is not zero.
FUNC_START(preempt_fpu)
PUSH_BASIC_STACK(256)
std r3,STACK_FRAME_PARAM(0)(sp) # double *darray
std r4,STACK_FRAME_PARAM(1)(sp) # int *threads_starting
std r5,STACK_FRAME_PARAM(2)(sp) # int *running
PUSH_FPU(STACK_FRAME_LOCAL(3,0))

bl load_fpu
nop

sync
# Atomic DEC
ld r3,STACK_FRAME_PARAM(1)(sp)
1: lwarx r4,0,r3
addi r4,r4,-1
stwcx. r4,0,r3
bne- 1b

2: ld r3,STACK_FRAME_PARAM(0)(sp)
bl check_fpu
nop
cmpdi r3,0
bne 3f
ld r4,STACK_FRAME_PARAM(2)(sp)
ld r5,0(r4)
cmpwi r5,0
bne 2b

3: POP_FPU(STACK_FRAME_LOCAL(3,0))
POP_BASIC_STACK(256)
blr
FUNC_END(preempt_fpu)
113 changes: 113 additions & 0 deletions tools/testing/selftests/powerpc/math/fpu_preempt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* This test attempts to see if the FPU registers change across preemption.
* Two things should be noted here a) The check_fpu function in asm only checks
* the non volatile registers as it is reused from the syscall test b) There is
* no way to be sure preemption happened so this test just uses many threads
* and a long wait. As such, a successful test doesn't mean much but a failure
* is bad.
*/

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <pthread.h>

#include "utils.h"

/* Time to wait for workers to get preempted (seconds) */
#define PREEMPT_TIME 20
/*
* Factor by which to multiply number of online CPUs for total number of
* worker threads
*/
#define THREAD_FACTOR 8


__thread double darray[] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0,
2.1};

int threads_starting;
int running;

extern void preempt_fpu(double *darray, int *threads_starting, int *running);

void *preempt_fpu_c(void *p)
{
int i;
srand(pthread_self());
for (i = 0; i < 21; i++)
darray[i] = rand();

/* Test failed if it ever returns */
preempt_fpu(darray, &threads_starting, &running);

return p;
}

int test_preempt_fpu(void)
{
int i, rc, threads;
pthread_t *tids;

threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR;
tids = malloc((threads) * sizeof(pthread_t));
FAIL_IF(!tids);

running = true;
threads_starting = threads;
for (i = 0; i < threads; i++) {
rc = pthread_create(&tids[i], NULL, preempt_fpu_c, NULL);
FAIL_IF(rc);
}

setbuf(stdout, NULL);
/* Not really necessary but nice to wait for every thread to start */
printf("\tWaiting for all workers to start...");
while(threads_starting)
asm volatile("": : :"memory");
printf("done\n");

printf("\tWaiting for %d seconds to let some workers get preempted...", PREEMPT_TIME);
sleep(PREEMPT_TIME);
printf("done\n");

printf("\tStopping workers...");
/*
* Working are checking this value every loop. In preempt_fpu 'cmpwi r5,0; bne 2b'.
* r5 will have loaded the value of running.
*/
running = 0;
for (i = 0; i < threads; i++) {
void *rc_p;
pthread_join(tids[i], &rc_p);

/*
* Harness will say the fail was here, look at why preempt_fpu
* returned
*/
if ((long) rc_p)
printf("oops\n");
FAIL_IF((long) rc_p);
}
printf("done\n");

free(tids);
return 0;
}

int main(int argc, char *argv[])
{
return test_harness(test_preempt_fpu, "fpu_preempt");
}
44 changes: 42 additions & 2 deletions tools/testing/selftests/powerpc/math/vmx_asm.S
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "../basic_asm.h"

# POS MUST BE 16 ALIGNED!
#define PUSH_VMX(pos,reg) \
li reg,pos; \
stvx v20,reg,sp; \
Expand All @@ -35,6 +36,7 @@
addi reg,reg,16; \
stvx v31,reg,sp;

# POS MUST BE 16 ALIGNED!
#define POP_VMX(pos,reg) \
li reg,pos; \
lvx v20,reg,sp; \
Expand Down Expand Up @@ -93,7 +95,7 @@ FUNC_END(load_vmx)

# Should be safe from C, only touches r4, r5 and v0,v1,v2
FUNC_START(check_vmx)
PUSH_BASIC_STACK(16)
PUSH_BASIC_STACK(32)
mr r4,r3
li r3,1 # assume a bad result
li r5,0
Expand Down Expand Up @@ -162,7 +164,7 @@ FUNC_START(check_vmx)
cmpdi r0,0xffffffffffffffff
bne 1f
li r3,0
1: POP_BASIC_STACK(16)
1: POP_BASIC_STACK(32)
blr
FUNC_END(check_vmx)

Expand Down Expand Up @@ -193,3 +195,41 @@ FUNC_START(test_vmx)
POP_BASIC_STACK(512)
blr
FUNC_END(test_vmx)

# int preempt_vmx(vector int *varray, int *threads_starting, int *running)
# On starting will (atomically) decrement threads_starting as a signal that
# the VMX have been loaded with varray. Will proceed to check the validity of
# the VMX registers while running is not zero.
FUNC_START(preempt_vmx)
PUSH_BASIC_STACK(512)
std r3,STACK_FRAME_PARAM(0)(sp) # vector int *varray
std r4,STACK_FRAME_PARAM(1)(sp) # int *threads_starting
std r5,STACK_FRAME_PARAM(2)(sp) # int *running
# VMX need to write to 16 byte aligned addresses, skip STACK_FRAME_LOCAL(3,0)
PUSH_VMX(STACK_FRAME_LOCAL(4,0),r4)

bl load_vmx
nop

sync
# Atomic DEC
ld r3,STACK_FRAME_PARAM(1)(sp)
1: lwarx r4,0,r3
addi r4,r4,-1
stwcx. r4,0,r3
bne- 1b

2: ld r3,STACK_FRAME_PARAM(0)(sp)
bl check_vmx
nop
cmpdi r3,0
bne 3f
ld r4,STACK_FRAME_PARAM(2)(sp)
ld r5,0(r4)
cmpwi r5,0
bne 2b

3: POP_VMX(STACK_FRAME_LOCAL(4,0),r4)
POP_BASIC_STACK(512)
blr
FUNC_END(preempt_vmx)
112 changes: 112 additions & 0 deletions tools/testing/selftests/powerpc/math/vmx_preempt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2015, Cyril Bur, IBM Corp.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* This test attempts to see if the VMX registers change across preemption.
* Two things should be noted here a) The check_vmx function in asm only checks
* the non volatile registers as it is reused from the syscall test b) There is
* no way to be sure preemption happened so this test just uses many threads
* and a long wait. As such, a successful test doesn't mean much but a failure
* is bad.
*/

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <pthread.h>

#include "utils.h"

/* Time to wait for workers to get preempted (seconds) */
#define PREEMPT_TIME 20
/*
* Factor by which to multiply number of online CPUs for total number of
* worker threads
*/
#define THREAD_FACTOR 8

__thread vector int varray[] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10,11,12},
{13,14,15,16},{17,18,19,20},{21,22,23,24},
{25,26,27,28},{29,30,31,32},{33,34,35,36},
{37,38,39,40},{41,42,43,44},{45,46,47,48}};

int threads_starting;
int running;

extern void preempt_vmx(vector int *varray, int *threads_starting, int *running);

void *preempt_vmx_c(void *p)
{
int i, j;
srand(pthread_self());
for (i = 0; i < 12; i++)
for (j = 0; j < 4; j++)
varray[i][j] = rand();

/* Test fails if it ever returns */
preempt_vmx(varray, &threads_starting, &running);
return p;
}

int test_preempt_vmx(void)
{
int i, rc, threads;
pthread_t *tids;

threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR;
tids = malloc(threads * sizeof(pthread_t));
FAIL_IF(!tids);

running = true;
threads_starting = threads;
for (i = 0; i < threads; i++) {
rc = pthread_create(&tids[i], NULL, preempt_vmx_c, NULL);
FAIL_IF(rc);
}

setbuf(stdout, NULL);
/* Not really nessesary but nice to wait for every thread to start */
printf("\tWaiting for all workers to start...");
while(threads_starting)
asm volatile("": : :"memory");
printf("done\n");

printf("\tWaiting for %d seconds to let some workers get preempted...", PREEMPT_TIME);
sleep(PREEMPT_TIME);
printf("done\n");

printf("\tStopping workers...");
/*
* Working are checking this value every loop. In preempt_vmx 'cmpwi r5,0; bne 2b'.
* r5 will have loaded the value of running.
*/
running = 0;
for (i = 0; i < threads; i++) {
void *rc_p;
pthread_join(tids[i], &rc_p);

/*
* Harness will say the fail was here, look at why preempt_vmx
* returned
*/
if ((long) rc_p)
printf("oops\n");
FAIL_IF((long) rc_p);
}
printf("done\n");

return 0;
}

int main(int argc, char *argv[])
{
return test_harness(test_preempt_vmx, "vmx_preempt");
}

0 comments on commit e5ab8be

Please sign in to comment.