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