xref: /kvm-unit-tests/powerpc/smp.c (revision c76b0d0a3842ba312a2d8512f7a3728f4598bf94)
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