xref: /linux/tools/testing/selftests/bpf/libarena/src/spmc.bpf.c (revision 57c6ace8395d53b9bae6fb21e0bd3f536342c16e)
1*57c6ace8SEmil Tsalapatis // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
2*57c6ace8SEmil Tsalapatis /*
3*57c6ace8SEmil Tsalapatis  * Copyright (c) 2025-2026 Meta Platforms, Inc. and affiliates.
4*57c6ace8SEmil Tsalapatis  * Copyright (c) 2025-2026 Emil Tsalapatis <etsal@meta.com>
5*57c6ace8SEmil Tsalapatis  */
6*57c6ace8SEmil Tsalapatis 
7*57c6ace8SEmil Tsalapatis #include <bpf_atomic.h>
8*57c6ace8SEmil Tsalapatis 
9*57c6ace8SEmil Tsalapatis #include <libarena/common.h>
10*57c6ace8SEmil Tsalapatis 
11*57c6ace8SEmil Tsalapatis #include <libarena/asan.h>
12*57c6ace8SEmil Tsalapatis #include <libarena/spmc.h>
13*57c6ace8SEmil Tsalapatis 
14*57c6ace8SEmil Tsalapatis static inline
15*57c6ace8SEmil Tsalapatis u64 spmc_arr_size(volatile struct spmc_arr __arena *spmc_arr)
16*57c6ace8SEmil Tsalapatis {
17*57c6ace8SEmil Tsalapatis 	return SPMC_ARR_BASESZ << spmc_arr->order;
18*57c6ace8SEmil Tsalapatis }
19*57c6ace8SEmil Tsalapatis 
20*57c6ace8SEmil Tsalapatis static inline
21*57c6ace8SEmil Tsalapatis u64 spmc_arr_get(volatile struct spmc_arr __arena *spmc_arr, u64 ind)
22*57c6ace8SEmil Tsalapatis {
23*57c6ace8SEmil Tsalapatis 	u64 ret = READ_ONCE(spmc_arr->data[ind % spmc_arr_size(spmc_arr)]);
24*57c6ace8SEmil Tsalapatis 
25*57c6ace8SEmil Tsalapatis 	return ret;
26*57c6ace8SEmil Tsalapatis }
27*57c6ace8SEmil Tsalapatis 
28*57c6ace8SEmil Tsalapatis static inline
29*57c6ace8SEmil Tsalapatis void spmc_arr_put(volatile struct spmc_arr __arena *spmc_arr, u64 ind, u64 value)
30*57c6ace8SEmil Tsalapatis {
31*57c6ace8SEmil Tsalapatis 	WRITE_ONCE(spmc_arr->data[ind % spmc_arr_size(spmc_arr)], value);
32*57c6ace8SEmil Tsalapatis }
33*57c6ace8SEmil Tsalapatis 
34*57c6ace8SEmil Tsalapatis static inline
35*57c6ace8SEmil Tsalapatis void spmc_arr_copy(volatile struct spmc_arr __arena *dst,
36*57c6ace8SEmil Tsalapatis 		   volatile struct spmc_arr __arena *src, u64 b, u64 t)
37*57c6ace8SEmil Tsalapatis {
38*57c6ace8SEmil Tsalapatis 	u64 i;
39*57c6ace8SEmil Tsalapatis 
40*57c6ace8SEmil Tsalapatis 	for (i = t; i < b && can_loop; i++)
41*57c6ace8SEmil Tsalapatis 		spmc_arr_put(dst, i, spmc_arr_get(src, i));
42*57c6ace8SEmil Tsalapatis }
43*57c6ace8SEmil Tsalapatis 
44*57c6ace8SEmil Tsalapatis static inline
45*57c6ace8SEmil Tsalapatis int spmc_order_init(struct spmc __arena *spmc, int order)
46*57c6ace8SEmil Tsalapatis {
47*57c6ace8SEmil Tsalapatis 	volatile struct spmc_arr __arena *arr = &spmc->arr[order];
48*57c6ace8SEmil Tsalapatis 
49*57c6ace8SEmil Tsalapatis 	if (unlikely(!spmc))
50*57c6ace8SEmil Tsalapatis 		return -EINVAL;
51*57c6ace8SEmil Tsalapatis 
52*57c6ace8SEmil Tsalapatis 	if (order >= SPMC_ARR_ORDERS)
53*57c6ace8SEmil Tsalapatis 		return -E2BIG;
54*57c6ace8SEmil Tsalapatis 
55*57c6ace8SEmil Tsalapatis 	/* Already allocated? */
56*57c6ace8SEmil Tsalapatis 	if (arr->data)
57*57c6ace8SEmil Tsalapatis 		return 0;
58*57c6ace8SEmil Tsalapatis 
59*57c6ace8SEmil Tsalapatis 	arr->data = arena_malloc((SPMC_ARR_BASESZ << order) * sizeof(*arr->data));
60*57c6ace8SEmil Tsalapatis 	if (!arr->data)
61*57c6ace8SEmil Tsalapatis 		return -ENOMEM;
62*57c6ace8SEmil Tsalapatis 
63*57c6ace8SEmil Tsalapatis 	return 0;
64*57c6ace8SEmil Tsalapatis }
65*57c6ace8SEmil Tsalapatis 
66*57c6ace8SEmil Tsalapatis __weak
67*57c6ace8SEmil Tsalapatis int spmc_owned_add(struct spmc __arena *spmc, u64 val)
68*57c6ace8SEmil Tsalapatis {
69*57c6ace8SEmil Tsalapatis 	volatile struct spmc_arr __arena *newarr;
70*57c6ace8SEmil Tsalapatis 	volatile struct spmc_arr __arena *arr;
71*57c6ace8SEmil Tsalapatis 	ssize_t sz;
72*57c6ace8SEmil Tsalapatis 	u64 b, t;
73*57c6ace8SEmil Tsalapatis 	int ret;
74*57c6ace8SEmil Tsalapatis 
75*57c6ace8SEmil Tsalapatis 	if (unlikely(!spmc))
76*57c6ace8SEmil Tsalapatis 		return -EINVAL;
77*57c6ace8SEmil Tsalapatis 
78*57c6ace8SEmil Tsalapatis 	/*
79*57c6ace8SEmil Tsalapatis 	 * Bottom must always be read first, also
80*57c6ace8SEmil Tsalapatis 	 * see spmc_steal().
81*57c6ace8SEmil Tsalapatis 	 */
82*57c6ace8SEmil Tsalapatis 	b = smp_load_acquire(&spmc->bottom);
83*57c6ace8SEmil Tsalapatis 	t = READ_ONCE(spmc->top);
84*57c6ace8SEmil Tsalapatis 	arr = READ_ONCE(spmc->cur);
85*57c6ace8SEmil Tsalapatis 
86*57c6ace8SEmil Tsalapatis 	sz = b - t;
87*57c6ace8SEmil Tsalapatis 	if (sz >= spmc_arr_size(arr) - 1) {
88*57c6ace8SEmil Tsalapatis 		ret = spmc_order_init(spmc, arr->order + 1);
89*57c6ace8SEmil Tsalapatis 		if (ret)
90*57c6ace8SEmil Tsalapatis 			return ret;
91*57c6ace8SEmil Tsalapatis 
92*57c6ace8SEmil Tsalapatis 		newarr = &spmc->arr[arr->order + 1];
93*57c6ace8SEmil Tsalapatis 
94*57c6ace8SEmil Tsalapatis 		spmc_arr_copy(newarr, arr, b, t);
95*57c6ace8SEmil Tsalapatis 		smp_store_release(&spmc->cur, newarr);
96*57c6ace8SEmil Tsalapatis 		arr = newarr;
97*57c6ace8SEmil Tsalapatis 	}
98*57c6ace8SEmil Tsalapatis 
99*57c6ace8SEmil Tsalapatis 	spmc_arr_put(arr, b, val);
100*57c6ace8SEmil Tsalapatis 	smp_store_release(&spmc->bottom, b + 1);
101*57c6ace8SEmil Tsalapatis 
102*57c6ace8SEmil Tsalapatis 	return 0;
103*57c6ace8SEmil Tsalapatis }
104*57c6ace8SEmil Tsalapatis 
105*57c6ace8SEmil Tsalapatis 
106*57c6ace8SEmil Tsalapatis __weak
107*57c6ace8SEmil Tsalapatis int spmc_owned_remove(struct spmc __arena *spmc, u64 *val)
108*57c6ace8SEmil Tsalapatis {
109*57c6ace8SEmil Tsalapatis 	volatile struct spmc_arr __arena *arr;
110*57c6ace8SEmil Tsalapatis 	int ret = 0;
111*57c6ace8SEmil Tsalapatis 	ssize_t sz;
112*57c6ace8SEmil Tsalapatis 	u64 value;
113*57c6ace8SEmil Tsalapatis 	u64 b, t;
114*57c6ace8SEmil Tsalapatis 
115*57c6ace8SEmil Tsalapatis 	if (unlikely(!spmc || !val))
116*57c6ace8SEmil Tsalapatis 		return -EINVAL;
117*57c6ace8SEmil Tsalapatis 
118*57c6ace8SEmil Tsalapatis 	b = READ_ONCE(spmc->bottom) - 1;
119*57c6ace8SEmil Tsalapatis 	WRITE_ONCE(spmc->bottom, b);
120*57c6ace8SEmil Tsalapatis 	smp_mb();
121*57c6ace8SEmil Tsalapatis 
122*57c6ace8SEmil Tsalapatis 	t = READ_ONCE(spmc->top);
123*57c6ace8SEmil Tsalapatis 	arr = READ_ONCE(spmc->cur);
124*57c6ace8SEmil Tsalapatis 
125*57c6ace8SEmil Tsalapatis 	sz = b - t;
126*57c6ace8SEmil Tsalapatis 	if (sz < 0) {
127*57c6ace8SEmil Tsalapatis 		WRITE_ONCE(spmc->bottom, t);
128*57c6ace8SEmil Tsalapatis 		return -ENOENT;
129*57c6ace8SEmil Tsalapatis 	}
130*57c6ace8SEmil Tsalapatis 
131*57c6ace8SEmil Tsalapatis 	value = spmc_arr_get(arr, b);
132*57c6ace8SEmil Tsalapatis 	if (sz > 0) {
133*57c6ace8SEmil Tsalapatis 		*val = value;
134*57c6ace8SEmil Tsalapatis 		return 0;
135*57c6ace8SEmil Tsalapatis 	}
136*57c6ace8SEmil Tsalapatis 
137*57c6ace8SEmil Tsalapatis 	if (cmpxchg(&spmc->top, t, t + 1) != t)
138*57c6ace8SEmil Tsalapatis 		ret = -EAGAIN;
139*57c6ace8SEmil Tsalapatis 
140*57c6ace8SEmil Tsalapatis 	WRITE_ONCE(spmc->bottom, t + 1);
141*57c6ace8SEmil Tsalapatis 
142*57c6ace8SEmil Tsalapatis 	if (ret)
143*57c6ace8SEmil Tsalapatis 		return ret;
144*57c6ace8SEmil Tsalapatis 
145*57c6ace8SEmil Tsalapatis 	*val = value;
146*57c6ace8SEmil Tsalapatis 
147*57c6ace8SEmil Tsalapatis 	return 0;
148*57c6ace8SEmil Tsalapatis }
149*57c6ace8SEmil Tsalapatis 
150*57c6ace8SEmil Tsalapatis __weak
151*57c6ace8SEmil Tsalapatis int spmc_steal(struct spmc __arena *spmc, u64 *val)
152*57c6ace8SEmil Tsalapatis {
153*57c6ace8SEmil Tsalapatis 	volatile struct spmc_arr __arena *arr;
154*57c6ace8SEmil Tsalapatis 	ssize_t sz;
155*57c6ace8SEmil Tsalapatis 	u64 value;
156*57c6ace8SEmil Tsalapatis 	u64 b, t;
157*57c6ace8SEmil Tsalapatis 
158*57c6ace8SEmil Tsalapatis 	if (unlikely(!spmc || !val))
159*57c6ace8SEmil Tsalapatis 		return -EINVAL;
160*57c6ace8SEmil Tsalapatis 
161*57c6ace8SEmil Tsalapatis 	/*
162*57c6ace8SEmil Tsalapatis 	 * It is important that t is read before b for
163*57c6ace8SEmil Tsalapatis 	 * stealers to avoid racing with the owner.
164*57c6ace8SEmil Tsalapatis 	 * Races between stealers are dealt with using
165*57c6ace8SEmil Tsalapatis 	 * CAS to increment the top value below.
166*57c6ace8SEmil Tsalapatis 	 */
167*57c6ace8SEmil Tsalapatis 	t = smp_load_acquire(&spmc->top);
168*57c6ace8SEmil Tsalapatis 	b = smp_load_acquire(&spmc->bottom);
169*57c6ace8SEmil Tsalapatis 
170*57c6ace8SEmil Tsalapatis 	sz = b - t;
171*57c6ace8SEmil Tsalapatis 	if (sz <= 0)
172*57c6ace8SEmil Tsalapatis 		return -ENOENT;
173*57c6ace8SEmil Tsalapatis 
174*57c6ace8SEmil Tsalapatis 	arr = smp_load_acquire(&spmc->cur);
175*57c6ace8SEmil Tsalapatis 	value = spmc_arr_get(arr, t);
176*57c6ace8SEmil Tsalapatis 
177*57c6ace8SEmil Tsalapatis 	if (cmpxchg(&spmc->top, t, t + 1) != t)
178*57c6ace8SEmil Tsalapatis 		return -EAGAIN;
179*57c6ace8SEmil Tsalapatis 
180*57c6ace8SEmil Tsalapatis 	*val = value;
181*57c6ace8SEmil Tsalapatis 
182*57c6ace8SEmil Tsalapatis 	return 0;
183*57c6ace8SEmil Tsalapatis }
184*57c6ace8SEmil Tsalapatis 
185*57c6ace8SEmil Tsalapatis 
186*57c6ace8SEmil Tsalapatis __weak
187*57c6ace8SEmil Tsalapatis struct spmc __arena *spmc_create(void)
188*57c6ace8SEmil Tsalapatis {
189*57c6ace8SEmil Tsalapatis 	/*
190*57c6ace8SEmil Tsalapatis 	 * Marked as volatile because otherwise the array
191*57c6ace8SEmil Tsalapatis 	 * reference in the internal loop gets demoted to
192*57c6ace8SEmil Tsalapatis 	 * scalar and the program fails verification.
193*57c6ace8SEmil Tsalapatis 	 */
194*57c6ace8SEmil Tsalapatis 	struct spmc __arena *volatile spmc;
195*57c6ace8SEmil Tsalapatis 	int ret, i;
196*57c6ace8SEmil Tsalapatis 
197*57c6ace8SEmil Tsalapatis 	spmc = arena_malloc(sizeof(*spmc));
198*57c6ace8SEmil Tsalapatis 	if (!spmc)
199*57c6ace8SEmil Tsalapatis 		return NULL;
200*57c6ace8SEmil Tsalapatis 
201*57c6ace8SEmil Tsalapatis 	spmc->bottom = 0;
202*57c6ace8SEmil Tsalapatis 	spmc->top = 0;
203*57c6ace8SEmil Tsalapatis 
204*57c6ace8SEmil Tsalapatis 	for (i = 0; i < SPMC_ARR_ORDERS && can_loop; i++) {
205*57c6ace8SEmil Tsalapatis 		spmc->arr[i].data = NULL;
206*57c6ace8SEmil Tsalapatis 		spmc->arr[i].order = i;
207*57c6ace8SEmil Tsalapatis 	}
208*57c6ace8SEmil Tsalapatis 
209*57c6ace8SEmil Tsalapatis 	ret = spmc_order_init((struct spmc __arena *)spmc, 0);
210*57c6ace8SEmil Tsalapatis 	if (ret) {
211*57c6ace8SEmil Tsalapatis 		arena_free(spmc);
212*57c6ace8SEmil Tsalapatis 		return NULL;
213*57c6ace8SEmil Tsalapatis 	}
214*57c6ace8SEmil Tsalapatis 
215*57c6ace8SEmil Tsalapatis 	spmc->cur = &spmc->arr[0];
216*57c6ace8SEmil Tsalapatis 
217*57c6ace8SEmil Tsalapatis 	return (struct spmc __arena *)spmc;
218*57c6ace8SEmil Tsalapatis }
219*57c6ace8SEmil Tsalapatis 
220*57c6ace8SEmil Tsalapatis __weak
221*57c6ace8SEmil Tsalapatis int spmc_destroy(struct spmc __arena *spmc)
222*57c6ace8SEmil Tsalapatis {
223*57c6ace8SEmil Tsalapatis 	int i;
224*57c6ace8SEmil Tsalapatis 
225*57c6ace8SEmil Tsalapatis 	if (unlikely(!spmc))
226*57c6ace8SEmil Tsalapatis 		return -EINVAL;
227*57c6ace8SEmil Tsalapatis 
228*57c6ace8SEmil Tsalapatis 	for (i = 0; i < SPMC_ARR_ORDERS && can_loop; i++)
229*57c6ace8SEmil Tsalapatis 		arena_free(spmc->arr[i].data);
230*57c6ace8SEmil Tsalapatis 
231*57c6ace8SEmil Tsalapatis 	arena_free(spmc);
232*57c6ace8SEmil Tsalapatis 
233*57c6ace8SEmil Tsalapatis 	return 0;
234*57c6ace8SEmil Tsalapatis }
235