xref: /freebsd/tools/test/buf_ring/buf_ring_test.c (revision 8e95079e175854b84e57c521b34b4b52f7ae9cb1)
1*8e95079eSAndrew Turner /*-
2*8e95079eSAndrew Turner  * SPDX-License-Identifier: BSD-2-Clause
3*8e95079eSAndrew Turner  *
4*8e95079eSAndrew Turner  * Copyright (c) 2024 Arm Ltd.
5*8e95079eSAndrew Turner  *
6*8e95079eSAndrew Turner  * Redistribution and use in source and binary forms, with or without
7*8e95079eSAndrew Turner  * modification, are permitted provided that the following conditions
8*8e95079eSAndrew Turner  * are met:
9*8e95079eSAndrew Turner  * 1. Redistributions of source code must retain the above copyright
10*8e95079eSAndrew Turner  *    notice, this list of conditions and the following disclaimer.
11*8e95079eSAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
12*8e95079eSAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
13*8e95079eSAndrew Turner  *    documentation and/or other materials provided with the distribution.
14*8e95079eSAndrew Turner  *
15*8e95079eSAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*8e95079eSAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*8e95079eSAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*8e95079eSAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*8e95079eSAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*8e95079eSAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*8e95079eSAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*8e95079eSAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*8e95079eSAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*8e95079eSAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*8e95079eSAndrew Turner  * SUCH DAMAGE.
26*8e95079eSAndrew Turner  *
27*8e95079eSAndrew Turner  */
28*8e95079eSAndrew Turner 
29*8e95079eSAndrew Turner #include <sys/types.h>
30*8e95079eSAndrew Turner #include <sys/errno.h>
31*8e95079eSAndrew Turner 
32*8e95079eSAndrew Turner #include <machine/atomic.h>
33*8e95079eSAndrew Turner #include <machine/cpu.h>
34*8e95079eSAndrew Turner #include <machine/cpufunc.h>
35*8e95079eSAndrew Turner 
36*8e95079eSAndrew Turner #include <assert.h>
37*8e95079eSAndrew Turner #include <err.h>
38*8e95079eSAndrew Turner #include <getopt.h>
39*8e95079eSAndrew Turner #include <pthread.h>
40*8e95079eSAndrew Turner #include <stdarg.h>
41*8e95079eSAndrew Turner #include <stdatomic.h>
42*8e95079eSAndrew Turner #include <stdbool.h>
43*8e95079eSAndrew Turner #include <stdio.h>
44*8e95079eSAndrew Turner #include <stdlib.h>
45*8e95079eSAndrew Turner #include <string.h>
46*8e95079eSAndrew Turner #include <unistd.h>
47*8e95079eSAndrew Turner 
48*8e95079eSAndrew Turner /* #define DEBUG_BUFRING */
49*8e95079eSAndrew Turner 
50*8e95079eSAndrew Turner #ifdef DEBUG_BUFRING
51*8e95079eSAndrew Turner static void
panic(const char * fmt,...)52*8e95079eSAndrew Turner panic(const char *fmt, ...)
53*8e95079eSAndrew Turner {
54*8e95079eSAndrew Turner 	va_list ap;
55*8e95079eSAndrew Turner 
56*8e95079eSAndrew Turner 	va_start(ap, fmt);
57*8e95079eSAndrew Turner 	vfprintf(stderr, fmt, ap);
58*8e95079eSAndrew Turner 	va_end(ap);
59*8e95079eSAndrew Turner 	fprintf(stderr, "\n");
60*8e95079eSAndrew Turner 	exit(1);
61*8e95079eSAndrew Turner }
62*8e95079eSAndrew Turner #endif
63*8e95079eSAndrew Turner 
64*8e95079eSAndrew Turner static void
critical_enter(void)65*8e95079eSAndrew Turner critical_enter(void)
66*8e95079eSAndrew Turner {
67*8e95079eSAndrew Turner }
68*8e95079eSAndrew Turner 
69*8e95079eSAndrew Turner static void
critical_exit(void)70*8e95079eSAndrew Turner critical_exit(void)
71*8e95079eSAndrew Turner {
72*8e95079eSAndrew Turner }
73*8e95079eSAndrew Turner 
74*8e95079eSAndrew Turner #include "../../../sys/sys/buf_ring.h"
75*8e95079eSAndrew Turner 
76*8e95079eSAndrew Turner #define	PROD_ITERATIONS	100000000
77*8e95079eSAndrew Turner 
78*8e95079eSAndrew Turner static enum {
79*8e95079eSAndrew Turner 	CT_UNKNOWN,
80*8e95079eSAndrew Turner 	CT_MC,
81*8e95079eSAndrew Turner 	CT_MC_MT,
82*8e95079eSAndrew Turner 	CT_SC,
83*8e95079eSAndrew Turner 	CT_PEEK,
84*8e95079eSAndrew Turner 	CT_PEEK_CLEAR,
85*8e95079eSAndrew Turner } cons_type = CT_UNKNOWN;
86*8e95079eSAndrew Turner 
87*8e95079eSAndrew Turner static unsigned int prod_count;
88*8e95079eSAndrew Turner 
89*8e95079eSAndrew Turner static struct buf_ring *br;
90*8e95079eSAndrew Turner static _Atomic bool prod_done = false;
91*8e95079eSAndrew Turner static _Atomic int prod_done_count = 0;
92*8e95079eSAndrew Turner static _Atomic size_t total_cons_count = 0;
93*8e95079eSAndrew Turner 
94*8e95079eSAndrew Turner static uint64_t *mt_seen;
95*8e95079eSAndrew Turner 
96*8e95079eSAndrew Turner static void *
producer(void * arg)97*8e95079eSAndrew Turner producer(void *arg)
98*8e95079eSAndrew Turner {
99*8e95079eSAndrew Turner 	int id, rv;
100*8e95079eSAndrew Turner 
101*8e95079eSAndrew Turner 	id = (int)(uintptr_t)arg;
102*8e95079eSAndrew Turner 
103*8e95079eSAndrew Turner 	for (size_t i = 0; i < PROD_ITERATIONS;) {
104*8e95079eSAndrew Turner 		rv = buf_ring_enqueue(br, (void *)(i * prod_count + 1 + id));
105*8e95079eSAndrew Turner 		if (rv == 0) {
106*8e95079eSAndrew Turner 			i++;
107*8e95079eSAndrew Turner 		}
108*8e95079eSAndrew Turner 	}
109*8e95079eSAndrew Turner 	if ((unsigned int)atomic_fetch_add(&prod_done_count, 1) ==
110*8e95079eSAndrew Turner 	    (prod_count - 1))
111*8e95079eSAndrew Turner 		atomic_store(&prod_done, true);
112*8e95079eSAndrew Turner 
113*8e95079eSAndrew Turner 	return (NULL);
114*8e95079eSAndrew Turner }
115*8e95079eSAndrew Turner 
116*8e95079eSAndrew Turner static void *
consumer(void * arg)117*8e95079eSAndrew Turner consumer(void *arg)
118*8e95079eSAndrew Turner {
119*8e95079eSAndrew Turner 	void *val;
120*8e95079eSAndrew Turner 	size_t *max_vals;
121*8e95079eSAndrew Turner 	size_t consume_count, curr;
122*8e95079eSAndrew Turner 	int id;
123*8e95079eSAndrew Turner 
124*8e95079eSAndrew Turner 	(void)arg;
125*8e95079eSAndrew Turner 
126*8e95079eSAndrew Turner 	max_vals = calloc(prod_count, sizeof(*max_vals));
127*8e95079eSAndrew Turner 	assert(max_vals != NULL);
128*8e95079eSAndrew Turner 
129*8e95079eSAndrew Turner 	/* Set the initial value to be the expected value */
130*8e95079eSAndrew Turner 	for (unsigned int i = 1; i < prod_count; i++) {
131*8e95079eSAndrew Turner 		max_vals[i] = (int)(i - prod_count);
132*8e95079eSAndrew Turner 	}
133*8e95079eSAndrew Turner 
134*8e95079eSAndrew Turner 	consume_count = 0;
135*8e95079eSAndrew Turner 	while (!atomic_load(&prod_done) || !buf_ring_empty(br)) {
136*8e95079eSAndrew Turner 		switch(cons_type) {
137*8e95079eSAndrew Turner 		case CT_MC:
138*8e95079eSAndrew Turner 		case CT_MC_MT:
139*8e95079eSAndrew Turner 			val = buf_ring_dequeue_mc(br);
140*8e95079eSAndrew Turner 			break;
141*8e95079eSAndrew Turner 		case CT_SC:
142*8e95079eSAndrew Turner 			val = buf_ring_dequeue_sc(br);
143*8e95079eSAndrew Turner 			break;
144*8e95079eSAndrew Turner 		case CT_PEEK:
145*8e95079eSAndrew Turner 			val = buf_ring_peek(br);
146*8e95079eSAndrew Turner 			if (val != NULL)
147*8e95079eSAndrew Turner 				buf_ring_advance_sc(br);
148*8e95079eSAndrew Turner 			break;
149*8e95079eSAndrew Turner 		case CT_PEEK_CLEAR:
150*8e95079eSAndrew Turner 			val = buf_ring_peek_clear_sc(br);
151*8e95079eSAndrew Turner 			if (val != NULL)
152*8e95079eSAndrew Turner 				buf_ring_advance_sc(br);
153*8e95079eSAndrew Turner 			break;
154*8e95079eSAndrew Turner 		case CT_UNKNOWN:
155*8e95079eSAndrew Turner 			__unreachable();
156*8e95079eSAndrew Turner 		}
157*8e95079eSAndrew Turner 		if (val != NULL) {
158*8e95079eSAndrew Turner 			consume_count++;
159*8e95079eSAndrew Turner 			curr = (size_t)(uintptr_t)val;
160*8e95079eSAndrew Turner 			id = curr % prod_count;
161*8e95079eSAndrew Turner 			if (cons_type != CT_MC_MT) {
162*8e95079eSAndrew Turner 				if (curr != max_vals[id] + prod_count)
163*8e95079eSAndrew Turner 					printf("Incorrect val: %zu Expect: %zu "
164*8e95079eSAndrew Turner 					    "Difference: %zd\n", curr,
165*8e95079eSAndrew Turner 					    max_vals[id] + prod_count,
166*8e95079eSAndrew Turner 					    curr - max_vals[id] - prod_count);
167*8e95079eSAndrew Turner 			} else {
168*8e95079eSAndrew Turner 				size_t idx, bit;
169*8e95079eSAndrew Turner 
170*8e95079eSAndrew Turner 				idx = ((size_t)(uintptr_t)val - 1) /
171*8e95079eSAndrew Turner 				    (sizeof(*mt_seen) * NBBY);
172*8e95079eSAndrew Turner 				bit = ((size_t)(uintptr_t)val - 1) %
173*8e95079eSAndrew Turner 				    (sizeof(*mt_seen) * NBBY);
174*8e95079eSAndrew Turner 
175*8e95079eSAndrew Turner 				if (atomic_testandset_64(&mt_seen[idx], bit))
176*8e95079eSAndrew Turner 					printf("Repeat ID: %zx\n", (size_t)(uintptr_t)val);
177*8e95079eSAndrew Turner 			}
178*8e95079eSAndrew Turner 
179*8e95079eSAndrew Turner 			max_vals[id] = (uintptr_t)val;
180*8e95079eSAndrew Turner 		}
181*8e95079eSAndrew Turner 	}
182*8e95079eSAndrew Turner 
183*8e95079eSAndrew Turner 	atomic_fetch_add(&total_cons_count, consume_count);
184*8e95079eSAndrew Turner 
185*8e95079eSAndrew Turner 	for (unsigned int i = 0; i < prod_count; i++)
186*8e95079eSAndrew Turner 		printf("max[%d] = %zu\n", i, max_vals[i]);
187*8e95079eSAndrew Turner 
188*8e95079eSAndrew Turner 	return (NULL);
189*8e95079eSAndrew Turner }
190*8e95079eSAndrew Turner 
191*8e95079eSAndrew Turner static struct option longopts[] = {
192*8e95079eSAndrew Turner 	{ "buf-size",	required_argument,	NULL,	'b'	},
193*8e95079eSAndrew Turner 	{ "cons-type",	required_argument,	NULL,	'c'	},
194*8e95079eSAndrew Turner 	{ "prod-count",	required_argument,	NULL,	'p'	},
195*8e95079eSAndrew Turner 	{ "help",	no_argument,		NULL,	'h'	},
196*8e95079eSAndrew Turner 	{ NULL,		0,			NULL,	 0	},
197*8e95079eSAndrew Turner };
198*8e95079eSAndrew Turner 
199*8e95079eSAndrew Turner static void
usage(void)200*8e95079eSAndrew Turner usage(void)
201*8e95079eSAndrew Turner {
202*8e95079eSAndrew Turner 	errx(1, "test --cons-type=<mc|mc-mt|sc|peek|peek-clear> --prod-count=<prod thread count> [--buf-size=<buf_ring size>]");
203*8e95079eSAndrew Turner }
204*8e95079eSAndrew Turner 
205*8e95079eSAndrew Turner static uint32_t
next_power_of_2(uint32_t x)206*8e95079eSAndrew Turner next_power_of_2(uint32_t x)
207*8e95079eSAndrew Turner {
208*8e95079eSAndrew Turner 	x--;
209*8e95079eSAndrew Turner 	x |= x >> 1;
210*8e95079eSAndrew Turner 	x |= x >> 2;
211*8e95079eSAndrew Turner 	x |= x >> 4;
212*8e95079eSAndrew Turner 	x |= x >> 8;
213*8e95079eSAndrew Turner 	x |= x >> 16;
214*8e95079eSAndrew Turner 	x++;
215*8e95079eSAndrew Turner 	return (x);
216*8e95079eSAndrew Turner }
217*8e95079eSAndrew Turner 
218*8e95079eSAndrew Turner int
main(int argc,char * argv[])219*8e95079eSAndrew Turner main(int argc, char *argv[])
220*8e95079eSAndrew Turner {
221*8e95079eSAndrew Turner 	pthread_t *prod;
222*8e95079eSAndrew Turner 	pthread_t cons[2];
223*8e95079eSAndrew Turner 	const char *errstr;
224*8e95079eSAndrew Turner 	uint32_t size;
225*8e95079eSAndrew Turner 	int ch, ret;
226*8e95079eSAndrew Turner 
227*8e95079eSAndrew Turner 	size = 0;
228*8e95079eSAndrew Turner 	while ((ch = getopt_long(argc, argv, "bf:", longopts, NULL)) != -1) {
229*8e95079eSAndrew Turner 		switch(ch) {
230*8e95079eSAndrew Turner 		case 'b':
231*8e95079eSAndrew Turner 			errstr = NULL;
232*8e95079eSAndrew Turner 			size = strtonum(optarg, 1, UINT_MAX, &errstr);
233*8e95079eSAndrew Turner 			if (errstr != NULL) {
234*8e95079eSAndrew Turner 				errx(1, "--bufsize=%s: %s", optarg, errstr);
235*8e95079eSAndrew Turner 			}
236*8e95079eSAndrew Turner 			if (!powerof2(size)) {
237*8e95079eSAndrew Turner 				errx(1, "--bufsize needs a power of 2 size");
238*8e95079eSAndrew Turner 			}
239*8e95079eSAndrew Turner 			break;
240*8e95079eSAndrew Turner 		case 'c':
241*8e95079eSAndrew Turner 			if (strcmp(optarg, "mc") == 0) {
242*8e95079eSAndrew Turner 				cons_type = CT_MC;
243*8e95079eSAndrew Turner 			} else if (strcmp(optarg, "mc-mt") == 0) {
244*8e95079eSAndrew Turner 				cons_type = CT_MC_MT;
245*8e95079eSAndrew Turner 			} else if (strcmp(optarg, "sc") == 0) {
246*8e95079eSAndrew Turner 				cons_type = CT_SC;
247*8e95079eSAndrew Turner 			} else if (strcmp(optarg, "peek") == 0) {
248*8e95079eSAndrew Turner 				cons_type = CT_PEEK;
249*8e95079eSAndrew Turner 			} else if (strcmp(optarg, "peek-clear") == 0) {
250*8e95079eSAndrew Turner 				cons_type = CT_PEEK_CLEAR;
251*8e95079eSAndrew Turner 			} else {
252*8e95079eSAndrew Turner 				errx(1, "Unknown --cons-type: %s", optarg);
253*8e95079eSAndrew Turner 			}
254*8e95079eSAndrew Turner 			break;
255*8e95079eSAndrew Turner 		case 'p':
256*8e95079eSAndrew Turner 			errstr = NULL;
257*8e95079eSAndrew Turner 			prod_count = strtonum(optarg, 1, UINT_MAX, &errstr);
258*8e95079eSAndrew Turner 			if (errstr != NULL) {
259*8e95079eSAndrew Turner 				errx(1, "--prod-count=%s: %s", optarg, errstr);
260*8e95079eSAndrew Turner 			}
261*8e95079eSAndrew Turner 			break;
262*8e95079eSAndrew Turner 		case 'h':
263*8e95079eSAndrew Turner 		default:
264*8e95079eSAndrew Turner 			usage();
265*8e95079eSAndrew Turner 		}
266*8e95079eSAndrew Turner 	}
267*8e95079eSAndrew Turner 	argc -= optind;
268*8e95079eSAndrew Turner 	argv += optind;
269*8e95079eSAndrew Turner 
270*8e95079eSAndrew Turner 	if (cons_type == CT_UNKNOWN)
271*8e95079eSAndrew Turner 		errx(1, "No cons-type set");
272*8e95079eSAndrew Turner 
273*8e95079eSAndrew Turner 	if (prod_count == 0)
274*8e95079eSAndrew Turner 		errx(1, "prod-count is not set");
275*8e95079eSAndrew Turner 
276*8e95079eSAndrew Turner 	if (size == 0)
277*8e95079eSAndrew Turner 		size = next_power_of_2(prod_count);
278*8e95079eSAndrew Turner 
279*8e95079eSAndrew Turner 	if (cons_type == CT_MC_MT) {
280*8e95079eSAndrew Turner 		size_t entries;
281*8e95079eSAndrew Turner 
282*8e95079eSAndrew Turner 		entries = (size_t)PROD_ITERATIONS * prod_count;
283*8e95079eSAndrew Turner 		entries = roundup2(entries, sizeof(*mt_seen));
284*8e95079eSAndrew Turner 		mt_seen = calloc(entries / (sizeof(*mt_seen) * NBBY),
285*8e95079eSAndrew Turner 		    sizeof(*mt_seen));
286*8e95079eSAndrew Turner 	}
287*8e95079eSAndrew Turner 
288*8e95079eSAndrew Turner 	br = buf_ring_alloc(size);
289*8e95079eSAndrew Turner 
290*8e95079eSAndrew Turner 	ret = pthread_create(&cons[0], NULL, consumer, NULL);
291*8e95079eSAndrew Turner 	assert(ret == 0);
292*8e95079eSAndrew Turner 	if (cons_type == CT_MC_MT) {
293*8e95079eSAndrew Turner 		ret = pthread_create(&cons[1], NULL, consumer, NULL);
294*8e95079eSAndrew Turner 		assert(ret == 0);
295*8e95079eSAndrew Turner 	}
296*8e95079eSAndrew Turner 
297*8e95079eSAndrew Turner 	prod = calloc(prod_count, sizeof(*prod));
298*8e95079eSAndrew Turner 	assert(prod != NULL);
299*8e95079eSAndrew Turner 	for (unsigned i = 0; i < prod_count; i++) {
300*8e95079eSAndrew Turner 		ret = pthread_create(&prod[i], NULL, producer,
301*8e95079eSAndrew Turner 		    (void *)(uintptr_t)i);
302*8e95079eSAndrew Turner 		assert(ret == 0);
303*8e95079eSAndrew Turner 	}
304*8e95079eSAndrew Turner 
305*8e95079eSAndrew Turner 	for (unsigned int i = 0; i < prod_count; i++) {
306*8e95079eSAndrew Turner 		ret = pthread_join(prod[i], NULL);
307*8e95079eSAndrew Turner 		assert(ret == 0);
308*8e95079eSAndrew Turner 	}
309*8e95079eSAndrew Turner 	ret = pthread_join(cons[0], NULL);
310*8e95079eSAndrew Turner 	assert(ret == 0);
311*8e95079eSAndrew Turner 	if (cons_type == CT_MC_MT) {
312*8e95079eSAndrew Turner 		ret = pthread_join(cons[1], NULL);
313*8e95079eSAndrew Turner 		assert(ret == 0);
314*8e95079eSAndrew Turner 	}
315*8e95079eSAndrew Turner 
316*8e95079eSAndrew Turner 	printf("Expected: %zu\n", (size_t)PROD_ITERATIONS * prod_count);
317*8e95079eSAndrew Turner 	printf("Received: %zu\n", total_cons_count);
318*8e95079eSAndrew Turner 
319*8e95079eSAndrew Turner 	buf_ring_free(br);
320*8e95079eSAndrew Turner 
321*8e95079eSAndrew Turner 	return (0);
322*8e95079eSAndrew Turner }
323