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