#include <sys/cdefs.h>
#include <linux/xarray.h>
#include <vm/vm_pageout.h>
#define NULL_VALUE (void *)0x1
void *
__xa_erase(struct xarray *xa, uint32_t index)
{
void *retval;
XA_ASSERT_LOCKED(xa);
retval = radix_tree_delete(&xa->xa_head, index);
if (retval == NULL_VALUE)
retval = NULL;
return (retval);
}
void *
xa_erase(struct xarray *xa, uint32_t index)
{
void *retval;
xa_lock(xa);
retval = __xa_erase(xa, index);
xa_unlock(xa);
return (retval);
}
void *
xa_load(struct xarray *xa, uint32_t index)
{
void *retval;
xa_lock(xa);
retval = radix_tree_lookup(&xa->xa_head, index);
xa_unlock(xa);
if (retval == NULL_VALUE)
retval = NULL;
return (retval);
}
static void
xa_vm_wait_locked(struct xarray *xa)
{
xa_unlock(xa);
vm_wait(NULL);
xa_lock(xa);
}
int
__xa_alloc(struct xarray *xa, uint32_t *pindex, void *ptr, struct xa_limit limit, gfp_t gfp)
{
int retval;
XA_ASSERT_LOCKED(xa);
MPASS(limit.max > limit.min);
*pindex = (xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0;
*pindex = MAX(*pindex, limit.min);
if (ptr == NULL)
ptr = NULL_VALUE;
retry:
retval = radix_tree_insert(&xa->xa_head, *pindex, ptr);
switch (retval) {
case -EEXIST:
if (likely(*pindex < limit.max)) {
(*pindex)++;
goto retry;
}
retval = -ENOMEM;
break;
case -ENOMEM:
if (likely(gfp & M_WAITOK)) {
xa_vm_wait_locked(xa);
goto retry;
}
break;
default:
break;
}
return (retval);
}
int
xa_alloc(struct xarray *xa, uint32_t *pindex, void *ptr, struct xa_limit limit, gfp_t gfp)
{
int retval;
if (ptr == NULL)
ptr = NULL_VALUE;
xa_lock(xa);
retval = __xa_alloc(xa, pindex, ptr, limit, gfp);
xa_unlock(xa);
return (retval);
}
int
__xa_alloc_cyclic(struct xarray *xa, uint32_t *pindex, void *ptr, struct xa_limit limit,
uint32_t *pnext_index, gfp_t gfp)
{
int retval;
int timeout = 1;
XA_ASSERT_LOCKED(xa);
MPASS(limit.max > limit.min);
*pnext_index = (xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0;
*pnext_index = MAX(*pnext_index, limit.min);
if (ptr == NULL)
ptr = NULL_VALUE;
retry:
retval = radix_tree_insert(&xa->xa_head, *pnext_index, ptr);
switch (retval) {
case -EEXIST:
if (unlikely(*pnext_index == limit.max) && !timeout--) {
retval = -ENOMEM;
break;
}
(*pnext_index)++;
if (*pnext_index > limit.max) {
*pnext_index = (xa->xa_flags & XA_FLAGS_ALLOC1) != 0 ? 1 : 0;
*pnext_index = MAX(*pnext_index, limit.min);
}
goto retry;
case -ENOMEM:
if (likely(gfp & M_WAITOK)) {
xa_vm_wait_locked(xa);
goto retry;
}
break;
default:
break;
}
*pindex = *pnext_index;
return (retval);
}
int
xa_alloc_cyclic(struct xarray *xa, uint32_t *pindex, void *ptr, struct xa_limit limit,
uint32_t *pnext_index, gfp_t gfp)
{
int retval;
xa_lock(xa);
retval = __xa_alloc_cyclic(xa, pindex, ptr, limit, pnext_index, gfp);
xa_unlock(xa);
return (retval);
}
int
xa_alloc_cyclic_irq(struct xarray *xa, uint32_t *pindex, void *ptr,
struct xa_limit limit, uint32_t *pnext_index, gfp_t gfp)
{
int retval;
xa_lock_irq(xa);
retval = __xa_alloc_cyclic(xa, pindex, ptr, limit, pnext_index, gfp);
xa_unlock_irq(xa);
return (retval);
}
int
__xa_insert(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)
{
int retval;
XA_ASSERT_LOCKED(xa);
if (ptr == NULL)
ptr = NULL_VALUE;
retry:
retval = radix_tree_insert(&xa->xa_head, index, ptr);
switch (retval) {
case -ENOMEM:
if (likely(gfp & M_WAITOK)) {
xa_vm_wait_locked(xa);
goto retry;
}
break;
default:
break;
}
return (retval);
}
int
xa_insert(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)
{
int retval;
xa_lock(xa);
retval = __xa_insert(xa, index, ptr, gfp);
xa_unlock(xa);
return (retval);
}
void *
__xa_store(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)
{
int retval;
XA_ASSERT_LOCKED(xa);
if (ptr == NULL)
ptr = NULL_VALUE;
retry:
retval = radix_tree_store(&xa->xa_head, index, &ptr);
switch (retval) {
case 0:
if (ptr == NULL_VALUE)
ptr = NULL;
break;
case -ENOMEM:
if (likely(gfp & M_WAITOK)) {
xa_vm_wait_locked(xa);
goto retry;
}
ptr = XA_ERROR(retval);
break;
default:
ptr = XA_ERROR(retval);
break;
}
return (ptr);
}
void *
xa_store(struct xarray *xa, uint32_t index, void *ptr, gfp_t gfp)
{
void *retval;
xa_lock(xa);
retval = __xa_store(xa, index, ptr, gfp);
xa_unlock(xa);
return (retval);
}
void
xa_init_flags(struct xarray *xa, uint32_t flags)
{
memset(xa, 0, sizeof(*xa));
mtx_init(&xa->xa_lock, "lkpi-xarray", NULL, MTX_DEF | MTX_RECURSE);
xa->xa_head.gfp_mask = GFP_NOWAIT;
xa->xa_flags = flags;
}
void
xa_destroy(struct xarray *xa)
{
struct radix_tree_iter iter;
void **ppslot;
xa_lock(xa);
radix_tree_for_each_slot(ppslot, &xa->xa_head, &iter, 0)
radix_tree_iter_delete(&xa->xa_head, &iter, ppslot);
xa_unlock(xa);
}
bool
__xa_empty(struct xarray *xa)
{
struct radix_tree_iter iter = {};
void **temp;
XA_ASSERT_LOCKED(xa);
return (!radix_tree_iter_find(&xa->xa_head, &iter, &temp, 0));
}
bool
xa_empty(struct xarray *xa)
{
bool retval;
xa_lock(xa);
retval = __xa_empty(xa);
xa_unlock(xa);
return (retval);
}
void *
__xa_next(struct xarray *xa, unsigned long *pindex, bool not_first)
{
struct radix_tree_iter iter = { .index = *pindex };
void **ppslot;
void *retval;
bool found;
XA_ASSERT_LOCKED(xa);
if (not_first) {
iter.index++;
if (iter.index == 0)
return (NULL);
}
found = radix_tree_iter_find(&xa->xa_head, &iter, &ppslot, 0);
if (likely(found)) {
retval = *ppslot;
if (retval == NULL_VALUE)
retval = NULL;
*pindex = iter.index;
} else {
retval = NULL;
}
return (retval);
}
void *
xa_next(struct xarray *xa, unsigned long *pindex, bool not_first)
{
void *retval;
xa_lock(xa);
retval = __xa_next(xa, pindex, not_first);
xa_unlock(xa);
return (retval);
}