1 /* SPDX-License-Identifier: LGPL-2.0-only */
2 /*
3 * SMP and IPI Tests
4 *
5 * Copyright 2024 Nicholas Piggin, IBM Corp.
6 */
7 #include <libcflat.h>
8 #include <asm/atomic.h>
9 #include <asm/barrier.h>
10 #include <asm/processor.h>
11 #include <asm/time.h>
12 #include <asm/smp.h>
13 #include <asm/setup.h>
14 #include <asm/ppc_asm.h>
15 #include <devicetree.h>
16
17 static volatile bool start_test_running = true;
18 static volatile int nr_cpus_started;
19
start_fn(int cpu_id)20 static void start_fn(int cpu_id)
21 {
22 atomic_fetch_inc(&nr_cpus_started);
23 while (start_test_running)
24 cpu_relax();
25 atomic_fetch_dec(&nr_cpus_started);
26 }
27
test_start_cpus(int argc,char ** argv)28 static void test_start_cpus(int argc, char **argv)
29 {
30 uint64_t tb;
31
32 if (argc > 2)
33 report_abort("Unsupported argument: '%s'", argv[2]);
34
35 nr_cpus_started = 1;
36 if (!start_all_cpus(start_fn))
37 report_abort("Failed to start secondary cpus");
38
39 tb = get_tb();
40 while (nr_cpus_started < nr_cpus_present) {
41 cpu_relax();
42 if (get_tb() - tb > tb_hz * 5)
43 report_abort("Failed to start all secondaries");
44 }
45
46 if (nr_cpus_started != nr_cpus_online)
47 report_abort("Started CPUs does not match online");
48
49 barrier();
50 start_test_running = false;
51 barrier();
52
53 tb = get_tb();
54 while (nr_cpus_started > 1) {
55 cpu_relax();
56 if (get_tb() - tb > tb_hz * 5)
57 report_abort("Failed to stop all secondaries");
58 }
59
60 stop_all_cpus();
61
62 report(true, "start cpus");
63 }
64
65 static volatile int nr_cpus_ipi = 0;
66
ipi_handler(struct pt_regs * regs,void * data)67 static void ipi_handler(struct pt_regs *regs, void *data)
68 {
69 atomic_fetch_inc(&nr_cpus_ipi);
70 }
71
72 static volatile bool ipi_test_running = true;
73
ipi_fn(int cpu_id)74 static void ipi_fn(int cpu_id)
75 {
76 local_ipi_enable();
77
78 mtspr(SPR_DEC, 0x7fffffff);
79 local_irq_enable();
80 while (ipi_test_running)
81 cpu_relax();
82 local_irq_disable();
83
84 local_ipi_disable();
85 }
86
test_ipi_cpus(int argc,char ** argv)87 static void test_ipi_cpus(int argc, char **argv)
88 {
89 uint64_t tb;
90 int i;
91
92 if (argc > 2)
93 report_abort("Unsupported argument: '%s'", argv[2]);
94
95 if (nr_cpus_present < 2) {
96 report_skip("Requires SMP (2 or more CPUs)");
97 return;
98 }
99
100 register_ipi(ipi_handler, NULL);
101
102 if (!start_all_cpus(ipi_fn))
103 report_abort("Failed to start secondary cpus");
104
105 for (i = 1; i < nr_cpus_online; i++)
106 send_ipi(cpus[i].server_no);
107
108 tb = get_tb();
109 while (nr_cpus_ipi < nr_cpus_online - 1) {
110 cpu_relax();
111 if (get_tb() - tb > tb_hz * 5)
112 report_abort("Secondaries failed to respond to IPIs");
113 }
114
115 send_ipi(cpus[1].server_no);
116
117 tb = get_tb();
118 while (nr_cpus_ipi < nr_cpus_online) {
119 cpu_relax();
120 if (get_tb() - tb > tb_hz * 5)
121 report_abort("Secondaries failed to respond to IPIs");
122 }
123
124 ipi_test_running = false;
125
126 stop_all_cpus();
127
128 assert(nr_cpus_ipi == nr_cpus_present);
129
130 unregister_ipi();
131
132 report(true, "IPI cpus");
133 }
134
135 static uint64_t time;
136 static bool time_went_backward;
137
check_and_record_time(void)138 static void check_and_record_time(void)
139 {
140 uint64_t tb;
141 uint64_t t;
142 uint64_t old;
143
144 t = time;
145 again:
146 barrier();
147 tb = get_tb();
148 asm volatile("1: ldarx %0,0,%1 ; cmpd %0,%2 ; bne 2f ; stdcx. %3,0,%1 ; bne- 1b; 2:" : "=&r"(old) : "r"(&time), "r"(t), "r"(tb) : "memory", "cr0");
149 assert(tb >= t);
150 if (old != t) {
151 t = old;
152 goto again;
153 }
154 if (old > tb)
155 time_went_backward = true;
156 }
157
update_time(int64_t tb_offset)158 static void update_time(int64_t tb_offset)
159 {
160 uint64_t new_tb;
161
162 new_tb = get_tb() + tb_offset;
163 mtspr(SPR_TBU40, new_tb);
164 if ((get_tb() & 0xFFFFFF) < (new_tb & 0xFFFFFF)) {
165 new_tb += 0x1000000;
166 mtspr(SPR_TBU40, new_tb);
167 }
168 }
169
time_sync_fn(int cpu_id)170 static void time_sync_fn(int cpu_id)
171 {
172 uint64_t start = get_tb();
173
174 while (!time_went_backward && get_tb() - start < tb_hz*2) {
175 check_and_record_time();
176 cpu_relax();
177 }
178
179 while (!time_went_backward && get_tb() - start < tb_hz*2) {
180 check_and_record_time();
181 udelay(1);
182 }
183
184 if (machine_is_powernv()) {
185 while (!time_went_backward && get_tb() - start < tb_hz*2) {
186 check_and_record_time();
187 update_time(0x1234000000);
188 cpu_relax();
189 update_time(-0x1234000000);
190 }
191 }
192 }
193
test_time_sync(int argc,char ** argv)194 static void test_time_sync(int argc, char **argv)
195 {
196 if (argc > 2)
197 report_abort("Unsupported argument: '%s'", argv[2]);
198
199 if (nr_cpus_present < 2) {
200 report_skip("Requires SMP (2 or more CPUs)");
201 return;
202 }
203
204 time_went_backward = false;
205
206 if (!start_all_cpus(time_sync_fn))
207 report_abort("Failed to start secondary cpus");
208
209 time_sync_fn(-1);
210
211 stop_all_cpus();
212
213 report(!time_went_backward, "time sync");
214 }
215
216 static volatile bool relax_test_running = true;
217
218 static int relax_loop_count[NR_CPUS];
219
relax_fn(int cpu_id)220 static void relax_fn(int cpu_id)
221 {
222 volatile int i = 0;
223
224 while (relax_test_running) {
225 cpu_relax();
226 i++;
227 }
228
229 relax_loop_count[cpu_id] = i;
230 }
231
232 #define ITERS 1000000
233
test_relax(int argc,char ** argv)234 static void test_relax(int argc, char **argv)
235 {
236 volatile int i;
237 int count;
238
239 if (argc > 2)
240 report_abort("Unsupported argument: '%s'", argv[2]);
241
242 if (nr_cpus_present < 2) {
243 report_skip("Requires SMP (2 or more CPUs)");
244 return;
245 }
246
247 if (!start_all_cpus(relax_fn))
248 report_abort("Failed to start secondary cpus");
249
250 for (i = 0; i < ITERS; i++)
251 ;
252
253 relax_test_running = false;
254
255 stop_all_cpus();
256
257 count = 0;
258 for (i = 0; i < NR_CPUS; i++)
259 count += relax_loop_count[i];
260 if (count == 0)
261 count = 1;
262
263 report(true, "busy-loops on CPU:%d vs cpu_relax-loops on others %ld%%", smp_processor_id(), (long)ITERS * 100 / count);
264 }
265
266 static volatile bool pause_test_running = true;
267
268 static int pause_loop_count[NR_CPUS];
269
pause_fn(int cpu_id)270 static void pause_fn(int cpu_id)
271 {
272 volatile int i = 0;
273
274 while (pause_test_running) {
275 pause_short();
276 i++;
277 }
278
279 pause_loop_count[cpu_id] = i;
280 }
281
282 #define ITERS 1000000
283
test_pause(int argc,char ** argv)284 static void test_pause(int argc, char **argv)
285 {
286 volatile int i;
287 int count;
288
289 if (argc > 2)
290 report_abort("Unsupported argument: '%s'", argv[2]);
291
292 if (!cpu_has_pause_short)
293 return;
294
295 if (nr_cpus_present < 2) {
296 report_skip("Requires SMP (2 or more CPUs)");
297 return;
298 }
299
300 if (!start_all_cpus(pause_fn))
301 report_abort("Failed to start secondary cpus");
302
303 for (i = 0; i < ITERS; i++)
304 ;
305
306 pause_test_running = false;
307
308 stop_all_cpus();
309
310 count = 0;
311 for (i = 0; i < NR_CPUS; i++)
312 count += pause_loop_count[i];
313
314 report(true, "busy-loops on CPU:%d vs pause_short-loops on others %ld%%", smp_processor_id(), (long)ITERS * 100 / count);
315 }
316
317 struct {
318 const char *name;
319 void (*func)(int argc, char **argv);
320 } hctests[] = {
321 { "start_cpus", test_start_cpus },
322 { "ipi_cpus", test_ipi_cpus },
323 { "time_sync", test_time_sync },
324 { "cpu_relax", test_relax },
325 { "pause", test_pause },
326 { NULL, NULL }
327 };
328
main(int argc,char ** argv)329 int main(int argc, char **argv)
330 {
331 bool all;
332 int i;
333
334 all = argc == 1 || !strcmp(argv[1], "all");
335
336 report_prefix_push("smp");
337
338 for (i = 0; hctests[i].name != NULL; i++) {
339 if (all || strcmp(argv[1], hctests[i].name) == 0) {
340 report_prefix_push(hctests[i].name);
341 hctests[i].func(argc, argv);
342 report_prefix_pop();
343 }
344 }
345
346 report_prefix_pop();
347 return report_summary();
348 }
349