1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * POWER Data Stream Control Register (DSCR) explicit test |
4 | * |
5 | * This test modifies the DSCR value using mtspr instruction and |
6 | * verifies the change with mfspr instruction. It uses both the |
7 | * privilege state SPR and the problem state SPR for this purpose. |
8 | * |
9 | * When using the privilege state SPR, the instructions such as |
10 | * mfspr or mtspr are privileged and the kernel emulates them |
11 | * for us. Instructions using problem state SPR can be executed |
12 | * directly without any emulation if the HW supports them. Else |
13 | * they also get emulated by the kernel. |
14 | * |
15 | * Copyright 2012, Anton Blanchard, IBM Corporation. |
16 | * Copyright 2015, Anshuman Khandual, IBM Corporation. |
17 | */ |
18 | |
19 | #define _GNU_SOURCE |
20 | |
21 | #include "dscr.h" |
22 | #include "utils.h" |
23 | |
24 | #include <pthread.h> |
25 | #include <sched.h> |
26 | #include <semaphore.h> |
27 | |
28 | void *dscr_explicit_lockstep_thread(void *args) |
29 | { |
30 | sem_t *prev = (sem_t *)args; |
31 | sem_t *next = (sem_t *)args + 1; |
32 | unsigned long expected_dscr = 0; |
33 | |
34 | set_dscr(expected_dscr); |
35 | srand(gettid()); |
36 | |
37 | for (int i = 0; i < COUNT; i++) { |
38 | FAIL_IF_EXIT(sem_wait(prev)); |
39 | |
40 | FAIL_IF_EXIT(expected_dscr != get_dscr()); |
41 | FAIL_IF_EXIT(expected_dscr != get_dscr_usr()); |
42 | |
43 | expected_dscr = (expected_dscr + 1) % DSCR_MAX; |
44 | set_dscr(expected_dscr); |
45 | |
46 | FAIL_IF_EXIT(sem_post(next)); |
47 | } |
48 | |
49 | return NULL; |
50 | } |
51 | |
52 | int dscr_explicit_lockstep_test(void) |
53 | { |
54 | pthread_t thread; |
55 | sem_t semaphores[2]; |
56 | sem_t *prev = &semaphores[1]; /* reversed prev/next than for the other thread */ |
57 | sem_t *next = &semaphores[0]; |
58 | unsigned long expected_dscr = 0; |
59 | |
60 | SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR)); |
61 | |
62 | srand(gettid()); |
63 | set_dscr(expected_dscr); |
64 | |
65 | FAIL_IF(sem_init(prev, 0, 0)); |
66 | FAIL_IF(sem_init(next, 0, 1)); /* other thread starts first */ |
67 | FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0); |
68 | FAIL_IF(pthread_create(&thread, NULL, dscr_explicit_lockstep_thread, (void *)semaphores)); |
69 | |
70 | for (int i = 0; i < COUNT; i++) { |
71 | FAIL_IF(sem_wait(prev)); |
72 | |
73 | FAIL_IF(expected_dscr != get_dscr()); |
74 | FAIL_IF(expected_dscr != get_dscr_usr()); |
75 | |
76 | expected_dscr = (expected_dscr - 1) % DSCR_MAX; |
77 | set_dscr(expected_dscr); |
78 | |
79 | FAIL_IF(sem_post(next)); |
80 | } |
81 | |
82 | FAIL_IF(pthread_join(thread, NULL)); |
83 | FAIL_IF(sem_destroy(prev)); |
84 | FAIL_IF(sem_destroy(next)); |
85 | |
86 | return 0; |
87 | } |
88 | |
89 | struct random_thread_args { |
90 | pthread_t thread_id; |
91 | bool do_yields; |
92 | pthread_barrier_t *barrier; |
93 | }; |
94 | |
95 | void *dscr_explicit_random_thread(void *in) |
96 | { |
97 | struct random_thread_args *args = (struct random_thread_args *)in; |
98 | unsigned long expected_dscr = 0; |
99 | int err; |
100 | |
101 | srand(gettid()); |
102 | |
103 | err = pthread_barrier_wait(args->barrier); |
104 | FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD); |
105 | |
106 | for (int i = 0; i < COUNT; i++) { |
107 | expected_dscr = rand() % DSCR_MAX; |
108 | set_dscr(expected_dscr); |
109 | |
110 | for (int j = rand() % 5; j > 0; --j) { |
111 | FAIL_IF_EXIT(get_dscr() != expected_dscr); |
112 | FAIL_IF_EXIT(get_dscr_usr() != expected_dscr); |
113 | |
114 | if (args->do_yields && rand() % 2) |
115 | sched_yield(); |
116 | } |
117 | |
118 | expected_dscr = rand() % DSCR_MAX; |
119 | set_dscr_usr(expected_dscr); |
120 | |
121 | for (int j = rand() % 5; j > 0; --j) { |
122 | FAIL_IF_EXIT(get_dscr() != expected_dscr); |
123 | FAIL_IF_EXIT(get_dscr_usr() != expected_dscr); |
124 | |
125 | if (args->do_yields && rand() % 2) |
126 | sched_yield(); |
127 | } |
128 | } |
129 | |
130 | return NULL; |
131 | } |
132 | |
133 | int dscr_explicit_random_test(void) |
134 | { |
135 | struct random_thread_args threads[THREADS]; |
136 | pthread_barrier_t barrier; |
137 | |
138 | SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR)); |
139 | |
140 | FAIL_IF(pthread_barrier_init(&barrier, NULL, THREADS)); |
141 | |
142 | for (int i = 0; i < THREADS; i++) { |
143 | threads[i].do_yields = i % 2 == 0; |
144 | threads[i].barrier = &barrier; |
145 | |
146 | FAIL_IF(pthread_create(&threads[i].thread_id, NULL, |
147 | dscr_explicit_random_thread, (void *)&threads[i])); |
148 | } |
149 | |
150 | for (int i = 0; i < THREADS; i++) |
151 | FAIL_IF(pthread_join(threads[i].thread_id, NULL)); |
152 | |
153 | FAIL_IF(pthread_barrier_destroy(&barrier)); |
154 | |
155 | return 0; |
156 | } |
157 | |
158 | int main(int argc, char *argv[]) |
159 | { |
160 | unsigned long orig_dscr_default = 0; |
161 | int err = 0; |
162 | |
163 | if (have_hwcap2(PPC_FEATURE2_DSCR)) |
164 | orig_dscr_default = get_default_dscr(); |
165 | |
166 | err |= test_harness(dscr_explicit_lockstep_test, "dscr_explicit_lockstep_test" ); |
167 | err |= test_harness(dscr_explicit_random_test, "dscr_explicit_random_test" ); |
168 | |
169 | if (have_hwcap2(PPC_FEATURE2_DSCR)) |
170 | set_default_dscr(orig_dscr_default); |
171 | |
172 | return err; |
173 | } |
174 | |