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