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