#include <bpf_atomic.h>
#include <libarena/common.h>
#include <libarena/asan.h>
#include <libarena/spmc.h>
static inline
u64 spmc_arr_size(volatile struct spmc_arr __arena *spmc_arr)
{
return SPMC_ARR_BASESZ << spmc_arr->order;
}
static inline
u64 spmc_arr_get(volatile struct spmc_arr __arena *spmc_arr, u64 ind)
{
u64 ret = READ_ONCE(spmc_arr->data[ind % spmc_arr_size(spmc_arr)]);
return ret;
}
static inline
void spmc_arr_put(volatile struct spmc_arr __arena *spmc_arr, u64 ind, u64 value)
{
WRITE_ONCE(spmc_arr->data[ind % spmc_arr_size(spmc_arr)], value);
}
static inline
void spmc_arr_copy(volatile struct spmc_arr __arena *dst,
volatile struct spmc_arr __arena *src, u64 b, u64 t)
{
u64 i;
for (i = t; i < b && can_loop; i++)
spmc_arr_put(dst, i, spmc_arr_get(src, i));
}
static inline
int spmc_order_init(struct spmc __arena *spmc, int order)
{
volatile struct spmc_arr __arena *arr = &spmc->arr[order];
if (unlikely(!spmc))
return -EINVAL;
if (order >= SPMC_ARR_ORDERS)
return -E2BIG;
if (arr->data)
return 0;
arr->data = arena_malloc((SPMC_ARR_BASESZ << order) * sizeof(*arr->data));
if (!arr->data)
return -ENOMEM;
return 0;
}
__weak
int spmc_owned_add(struct spmc __arena *spmc, u64 val)
{
volatile struct spmc_arr __arena *newarr;
volatile struct spmc_arr __arena *arr;
ssize_t sz;
u64 b, t;
int ret;
if (unlikely(!spmc))
return -EINVAL;
b = smp_load_acquire(&spmc->bottom);
t = READ_ONCE(spmc->top);
arr = READ_ONCE(spmc->cur);
sz = b - t;
if (sz >= spmc_arr_size(arr) - 1) {
ret = spmc_order_init(spmc, arr->order + 1);
if (ret)
return ret;
newarr = &spmc->arr[arr->order + 1];
spmc_arr_copy(newarr, arr, b, t);
smp_store_release(&spmc->cur, newarr);
arr = newarr;
}
spmc_arr_put(arr, b, val);
smp_store_release(&spmc->bottom, b + 1);
return 0;
}
__weak
int spmc_owned_remove(struct spmc __arena *spmc, u64 *val)
{
volatile struct spmc_arr __arena *arr;
int ret = 0;
ssize_t sz;
u64 value;
u64 b, t;
if (unlikely(!spmc || !val))
return -EINVAL;
b = READ_ONCE(spmc->bottom) - 1;
WRITE_ONCE(spmc->bottom, b);
smp_mb();
t = READ_ONCE(spmc->top);
arr = READ_ONCE(spmc->cur);
sz = b - t;
if (sz < 0) {
WRITE_ONCE(spmc->bottom, t);
return -ENOENT;
}
value = spmc_arr_get(arr, b);
if (sz > 0) {
*val = value;
return 0;
}
if (cmpxchg(&spmc->top, t, t + 1) != t)
ret = -EAGAIN;
WRITE_ONCE(spmc->bottom, t + 1);
if (ret)
return ret;
*val = value;
return 0;
}
__weak
int spmc_steal(struct spmc __arena *spmc, u64 *val)
{
volatile struct spmc_arr __arena *arr;
ssize_t sz;
u64 value;
u64 b, t;
if (unlikely(!spmc || !val))
return -EINVAL;
t = smp_load_acquire(&spmc->top);
b = smp_load_acquire(&spmc->bottom);
sz = b - t;
if (sz <= 0)
return -ENOENT;
arr = smp_load_acquire(&spmc->cur);
value = spmc_arr_get(arr, t);
if (cmpxchg(&spmc->top, t, t + 1) != t)
return -EAGAIN;
*val = value;
return 0;
}
__weak
struct spmc __arena *spmc_create(void)
{
struct spmc __arena *volatile spmc;
int ret, i;
spmc = arena_malloc(sizeof(*spmc));
if (!spmc)
return NULL;
spmc->bottom = 0;
spmc->top = 0;
for (i = 0; i < SPMC_ARR_ORDERS && can_loop; i++) {
spmc->arr[i].data = NULL;
spmc->arr[i].order = i;
}
ret = spmc_order_init((struct spmc __arena *)spmc, 0);
if (ret) {
arena_free(spmc);
return NULL;
}
spmc->cur = &spmc->arr[0];
return (struct spmc __arena *)spmc;
}
__weak
int spmc_destroy(struct spmc __arena *spmc)
{
int i;
if (unlikely(!spmc))
return -EINVAL;
for (i = 0; i < SPMC_ARR_ORDERS && can_loop; i++)
arena_free(spmc->arr[i].data);
arena_free(spmc);
return 0;
}