mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
xarray: Track free entries in an XArray
Add the optional ability to track which entries in an XArray are free and provide xa_alloc() to replace most of the functionality of the IDR. Signed-off-by: Matthew Wilcox <willy@infradead.org>
This commit is contained in:
parent
3d5bd6e1a0
commit
371c752dc6
@ -103,12 +103,25 @@ Finally, you can remove all entries from an XArray by calling
|
||||
to free the entries first. You can do this by iterating over all present
|
||||
entries in the XArray using the :c:func:`xa_for_each` iterator.
|
||||
|
||||
ID assignment
|
||||
-------------
|
||||
|
||||
You can call :c:func:`xa_alloc` to store the entry at any unused index
|
||||
in the XArray. If you need to modify the array from interrupt context,
|
||||
you can use :c:func:`xa_alloc_bh` or :c:func:`xa_alloc_irq` to disable
|
||||
interrupts while allocating the ID. Unlike :c:func:`xa_store`, allocating
|
||||
a ``NULL`` pointer does not delete an entry. Instead it reserves an
|
||||
entry like :c:func:`xa_reserve` and you can release it using either
|
||||
:c:func:`xa_erase` or :c:func:`xa_release`. To use ID assignment, the
|
||||
XArray must be defined with :c:func:`DEFINE_XARRAY_ALLOC`, or initialised
|
||||
by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`,
|
||||
|
||||
Memory allocation
|
||||
-----------------
|
||||
|
||||
The :c:func:`xa_store`, :c:func:`xa_cmpxchg`, :c:func:`xa_reserve`
|
||||
and :c:func:`xa_insert` functions take a gfp_t parameter in case
|
||||
the XArray needs to allocate memory to store this entry.
|
||||
The :c:func:`xa_store`, :c:func:`xa_cmpxchg`, :c:func:`xa_alloc`,
|
||||
:c:func:`xa_reserve` and :c:func:`xa_insert` functions take a gfp_t
|
||||
parameter in case the XArray needs to allocate memory to store this entry.
|
||||
If the entry is being deleted, no memory allocation needs to be performed,
|
||||
and the GFP flags specified will be ignored.
|
||||
|
||||
@ -143,6 +156,9 @@ Takes xa_lock internally:
|
||||
* :c:func:`xa_erase_bh`
|
||||
* :c:func:`xa_erase_irq`
|
||||
* :c:func:`xa_cmpxchg`
|
||||
* :c:func:`xa_alloc`
|
||||
* :c:func:`xa_alloc_bh`
|
||||
* :c:func:`xa_alloc_irq`
|
||||
* :c:func:`xa_destroy`
|
||||
* :c:func:`xa_set_mark`
|
||||
* :c:func:`xa_clear_mark`
|
||||
@ -152,6 +168,7 @@ Assumes xa_lock held on entry:
|
||||
* :c:func:`__xa_insert`
|
||||
* :c:func:`__xa_erase`
|
||||
* :c:func:`__xa_cmpxchg`
|
||||
* :c:func:`__xa_alloc`
|
||||
* :c:func:`__xa_set_mark`
|
||||
* :c:func:`__xa_clear_mark`
|
||||
|
||||
|
@ -205,6 +205,7 @@ typedef unsigned __bitwise xa_mark_t;
|
||||
#define XA_MARK_2 ((__force xa_mark_t)2U)
|
||||
#define XA_PRESENT ((__force xa_mark_t)8U)
|
||||
#define XA_MARK_MAX XA_MARK_2
|
||||
#define XA_FREE_MARK XA_MARK_0
|
||||
|
||||
enum xa_lock_type {
|
||||
XA_LOCK_IRQ = 1,
|
||||
@ -217,9 +218,12 @@ enum xa_lock_type {
|
||||
*/
|
||||
#define XA_FLAGS_LOCK_IRQ ((__force gfp_t)XA_LOCK_IRQ)
|
||||
#define XA_FLAGS_LOCK_BH ((__force gfp_t)XA_LOCK_BH)
|
||||
#define XA_FLAGS_TRACK_FREE ((__force gfp_t)4U)
|
||||
#define XA_FLAGS_MARK(mark) ((__force gfp_t)((1U << __GFP_BITS_SHIFT) << \
|
||||
(__force unsigned)(mark)))
|
||||
|
||||
#define XA_FLAGS_ALLOC (XA_FLAGS_TRACK_FREE | XA_FLAGS_MARK(XA_FREE_MARK))
|
||||
|
||||
/**
|
||||
* struct xarray - The anchor of the XArray.
|
||||
* @xa_lock: Lock that protects the contents of the XArray.
|
||||
@ -273,6 +277,15 @@ struct xarray {
|
||||
*/
|
||||
#define DEFINE_XARRAY(name) DEFINE_XARRAY_FLAGS(name, 0)
|
||||
|
||||
/**
|
||||
* DEFINE_XARRAY_ALLOC() - Define an XArray which can allocate IDs.
|
||||
* @name: A string that names your XArray.
|
||||
*
|
||||
* This is intended for file scope definitions of allocating XArrays.
|
||||
* See also DEFINE_XARRAY().
|
||||
*/
|
||||
#define DEFINE_XARRAY_ALLOC(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC)
|
||||
|
||||
void xa_init_flags(struct xarray *, gfp_t flags);
|
||||
void *xa_load(struct xarray *, unsigned long index);
|
||||
void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
|
||||
@ -439,6 +452,7 @@ void *__xa_erase(struct xarray *, unsigned long index);
|
||||
void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
|
||||
void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old,
|
||||
void *entry, gfp_t);
|
||||
int __xa_alloc(struct xarray *, u32 *id, u32 max, void *entry, gfp_t);
|
||||
void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t);
|
||||
void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t);
|
||||
|
||||
@ -518,6 +532,93 @@ static inline void *xa_erase_irq(struct xarray *xa, unsigned long index)
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* xa_alloc() - Find somewhere to store this entry in the XArray.
|
||||
* @xa: XArray.
|
||||
* @id: Pointer to ID.
|
||||
* @max: Maximum ID to allocate (inclusive).
|
||||
* @entry: New entry.
|
||||
* @gfp: Memory allocation flags.
|
||||
*
|
||||
* Allocates an unused ID in the range specified by @id and @max.
|
||||
* Updates the @id pointer with the index, then stores the entry at that
|
||||
* index. A concurrent lookup will not see an uninitialised @id.
|
||||
*
|
||||
* Context: Process context. Takes and releases the xa_lock. May sleep if
|
||||
* the @gfp flags permit.
|
||||
* Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if
|
||||
* there is no more space in the XArray.
|
||||
*/
|
||||
static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry,
|
||||
gfp_t gfp)
|
||||
{
|
||||
int err;
|
||||
|
||||
xa_lock(xa);
|
||||
err = __xa_alloc(xa, id, max, entry, gfp);
|
||||
xa_unlock(xa);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* xa_alloc_bh() - Find somewhere to store this entry in the XArray.
|
||||
* @xa: XArray.
|
||||
* @id: Pointer to ID.
|
||||
* @max: Maximum ID to allocate (inclusive).
|
||||
* @entry: New entry.
|
||||
* @gfp: Memory allocation flags.
|
||||
*
|
||||
* Allocates an unused ID in the range specified by @id and @max.
|
||||
* Updates the @id pointer with the index, then stores the entry at that
|
||||
* index. A concurrent lookup will not see an uninitialised @id.
|
||||
*
|
||||
* Context: Process context. Takes and releases the xa_lock while
|
||||
* disabling softirqs. May sleep if the @gfp flags permit.
|
||||
* Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if
|
||||
* there is no more space in the XArray.
|
||||
*/
|
||||
static inline int xa_alloc_bh(struct xarray *xa, u32 *id, u32 max, void *entry,
|
||||
gfp_t gfp)
|
||||
{
|
||||
int err;
|
||||
|
||||
xa_lock_bh(xa);
|
||||
err = __xa_alloc(xa, id, max, entry, gfp);
|
||||
xa_unlock_bh(xa);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* xa_alloc_irq() - Find somewhere to store this entry in the XArray.
|
||||
* @xa: XArray.
|
||||
* @id: Pointer to ID.
|
||||
* @max: Maximum ID to allocate (inclusive).
|
||||
* @entry: New entry.
|
||||
* @gfp: Memory allocation flags.
|
||||
*
|
||||
* Allocates an unused ID in the range specified by @id and @max.
|
||||
* Updates the @id pointer with the index, then stores the entry at that
|
||||
* index. A concurrent lookup will not see an uninitialised @id.
|
||||
*
|
||||
* Context: Process context. Takes and releases the xa_lock while
|
||||
* disabling interrupts. May sleep if the @gfp flags permit.
|
||||
* Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if
|
||||
* there is no more space in the XArray.
|
||||
*/
|
||||
static inline int xa_alloc_irq(struct xarray *xa, u32 *id, u32 max, void *entry,
|
||||
gfp_t gfp)
|
||||
{
|
||||
int err;
|
||||
|
||||
xa_lock_irq(xa);
|
||||
err = __xa_alloc(xa, id, max, entry, gfp);
|
||||
xa_unlock_irq(xa);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Everything below here is the Advanced API. Proceed with caution. */
|
||||
|
||||
/*
|
||||
|
@ -33,6 +33,15 @@ static void *xa_store_index(struct xarray *xa, unsigned long index, gfp_t gfp)
|
||||
return xa_store(xa, index, xa_mk_value(index & LONG_MAX), gfp);
|
||||
}
|
||||
|
||||
static void xa_alloc_index(struct xarray *xa, unsigned long index, gfp_t gfp)
|
||||
{
|
||||
u32 id = 0;
|
||||
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_value(index & LONG_MAX),
|
||||
gfp) != 0);
|
||||
XA_BUG_ON(xa, id != index);
|
||||
}
|
||||
|
||||
static void xa_erase_index(struct xarray *xa, unsigned long index)
|
||||
{
|
||||
XA_BUG_ON(xa, xa_erase(xa, index) != xa_mk_value(index & LONG_MAX));
|
||||
@ -404,6 +413,57 @@ static noinline void check_multi_store(struct xarray *xa)
|
||||
#endif
|
||||
}
|
||||
|
||||
static DEFINE_XARRAY_ALLOC(xa0);
|
||||
|
||||
static noinline void check_xa_alloc(void)
|
||||
{
|
||||
int i;
|
||||
u32 id;
|
||||
|
||||
/* An empty array should assign 0 to the first alloc */
|
||||
xa_alloc_index(&xa0, 0, GFP_KERNEL);
|
||||
|
||||
/* Erasing it should make the array empty again */
|
||||
xa_erase_index(&xa0, 0);
|
||||
XA_BUG_ON(&xa0, !xa_empty(&xa0));
|
||||
|
||||
/* And it should assign 0 again */
|
||||
xa_alloc_index(&xa0, 0, GFP_KERNEL);
|
||||
|
||||
/* The next assigned ID should be 1 */
|
||||
xa_alloc_index(&xa0, 1, GFP_KERNEL);
|
||||
xa_erase_index(&xa0, 1);
|
||||
|
||||
/* Storing a value should mark it used */
|
||||
xa_store_index(&xa0, 1, GFP_KERNEL);
|
||||
xa_alloc_index(&xa0, 2, GFP_KERNEL);
|
||||
|
||||
/* If we then erase 0, it should be free */
|
||||
xa_erase_index(&xa0, 0);
|
||||
xa_alloc_index(&xa0, 0, GFP_KERNEL);
|
||||
|
||||
xa_erase_index(&xa0, 1);
|
||||
xa_erase_index(&xa0, 2);
|
||||
|
||||
for (i = 1; i < 5000; i++) {
|
||||
xa_alloc_index(&xa0, i, GFP_KERNEL);
|
||||
}
|
||||
|
||||
xa_destroy(&xa0);
|
||||
|
||||
id = 0xfffffffeU;
|
||||
XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_value(0),
|
||||
GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(&xa0, id != 0xfffffffeU);
|
||||
XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_value(0),
|
||||
GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(&xa0, id != 0xffffffffU);
|
||||
XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_value(0),
|
||||
GFP_KERNEL) != -ENOSPC);
|
||||
XA_BUG_ON(&xa0, id != 0xffffffffU);
|
||||
xa_destroy(&xa0);
|
||||
}
|
||||
|
||||
static noinline void __check_store_iter(struct xarray *xa, unsigned long start,
|
||||
unsigned int order, unsigned int present)
|
||||
{
|
||||
@ -849,6 +909,7 @@ static int xarray_checks(void)
|
||||
check_cmpxchg(&array);
|
||||
check_reserve(&array);
|
||||
check_multi_store(&array);
|
||||
check_xa_alloc();
|
||||
check_find(&array);
|
||||
check_destroy(&array);
|
||||
check_move(&array);
|
||||
|
88
lib/xarray.c
88
lib/xarray.c
@ -52,6 +52,11 @@ static inline void xas_unlock_type(struct xa_state *xas, unsigned int lock_type)
|
||||
xas_unlock(xas);
|
||||
}
|
||||
|
||||
static inline bool xa_track_free(const struct xarray *xa)
|
||||
{
|
||||
return xa->xa_flags & XA_FLAGS_TRACK_FREE;
|
||||
}
|
||||
|
||||
static inline void xa_mark_set(struct xarray *xa, xa_mark_t mark)
|
||||
{
|
||||
if (!(xa->xa_flags & XA_FLAGS_MARK(mark)))
|
||||
@ -94,6 +99,11 @@ static inline bool node_any_mark(struct xa_node *node, xa_mark_t mark)
|
||||
return !bitmap_empty(node_marks(node, mark), XA_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
static inline void node_mark_all(struct xa_node *node, xa_mark_t mark)
|
||||
{
|
||||
bitmap_fill(node_marks(node, mark), XA_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
#define mark_inc(mark) do { \
|
||||
mark = (__force xa_mark_t)((__force unsigned)(mark) + 1); \
|
||||
} while (0)
|
||||
@ -416,6 +426,8 @@ static void xas_shrink(struct xa_state *xas)
|
||||
xas->xa_node = XAS_BOUNDS;
|
||||
|
||||
RCU_INIT_POINTER(xa->xa_head, entry);
|
||||
if (xa_track_free(xa) && !node_get_mark(node, 0, XA_FREE_MARK))
|
||||
xa_mark_clear(xa, XA_FREE_MARK);
|
||||
|
||||
node->count = 0;
|
||||
node->nr_values = 0;
|
||||
@ -549,8 +561,15 @@ static int xas_expand(struct xa_state *xas, void *head)
|
||||
|
||||
/* Propagate the aggregated mark info to the new child */
|
||||
for (;;) {
|
||||
if (xa_marked(xa, mark))
|
||||
if (xa_track_free(xa) && mark == XA_FREE_MARK) {
|
||||
node_mark_all(node, XA_FREE_MARK);
|
||||
if (!xa_marked(xa, XA_FREE_MARK)) {
|
||||
node_clear_mark(node, 0, XA_FREE_MARK);
|
||||
xa_mark_set(xa, XA_FREE_MARK);
|
||||
}
|
||||
} else if (xa_marked(xa, mark)) {
|
||||
node_set_mark(node, 0, mark);
|
||||
}
|
||||
if (mark == XA_MARK_MAX)
|
||||
break;
|
||||
mark_inc(mark);
|
||||
@ -624,6 +643,8 @@ static void *xas_create(struct xa_state *xas)
|
||||
node = xas_alloc(xas, shift);
|
||||
if (!node)
|
||||
break;
|
||||
if (xa_track_free(xa))
|
||||
node_mark_all(node, XA_FREE_MARK);
|
||||
rcu_assign_pointer(*slot, xa_mk_node(node));
|
||||
} else if (xa_is_node(entry)) {
|
||||
node = xa_to_node(entry);
|
||||
@ -882,7 +903,10 @@ void xas_init_marks(const struct xa_state *xas)
|
||||
xa_mark_t mark = 0;
|
||||
|
||||
for (;;) {
|
||||
xas_clear_mark(xas, mark);
|
||||
if (xa_track_free(xas->xa) && mark == XA_FREE_MARK)
|
||||
xas_set_mark(xas, mark);
|
||||
else
|
||||
xas_clear_mark(xas, mark);
|
||||
if (mark == XA_MARK_MAX)
|
||||
break;
|
||||
mark_inc(mark);
|
||||
@ -1333,6 +1357,8 @@ void *xa_store(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
|
||||
do {
|
||||
xas_lock(&xas);
|
||||
curr = xas_store(&xas, entry);
|
||||
if (xa_track_free(xa) && entry)
|
||||
xas_clear_mark(&xas, XA_FREE_MARK);
|
||||
xas_unlock(&xas);
|
||||
} while (xas_nomem(&xas, gfp));
|
||||
|
||||
@ -1365,6 +1391,8 @@ void *__xa_store(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
|
||||
|
||||
do {
|
||||
curr = xas_store(&xas, entry);
|
||||
if (xa_track_free(xa) && entry)
|
||||
xas_clear_mark(&xas, XA_FREE_MARK);
|
||||
} while (__xas_nomem(&xas, gfp));
|
||||
|
||||
return xas_result(&xas, curr);
|
||||
@ -1400,8 +1428,11 @@ void *xa_cmpxchg(struct xarray *xa, unsigned long index,
|
||||
curr = xas_load(&xas);
|
||||
if (curr == XA_ZERO_ENTRY)
|
||||
curr = NULL;
|
||||
if (curr == old)
|
||||
if (curr == old) {
|
||||
xas_store(&xas, entry);
|
||||
if (xa_track_free(xa) && entry)
|
||||
xas_clear_mark(&xas, XA_FREE_MARK);
|
||||
}
|
||||
xas_unlock(&xas);
|
||||
} while (xas_nomem(&xas, gfp));
|
||||
|
||||
@ -1438,8 +1469,11 @@ void *__xa_cmpxchg(struct xarray *xa, unsigned long index,
|
||||
curr = xas_load(&xas);
|
||||
if (curr == XA_ZERO_ENTRY)
|
||||
curr = NULL;
|
||||
if (curr == old)
|
||||
if (curr == old) {
|
||||
xas_store(&xas, entry);
|
||||
if (xa_track_free(xa) && entry)
|
||||
xas_clear_mark(&xas, XA_FREE_MARK);
|
||||
}
|
||||
} while (__xas_nomem(&xas, gfp));
|
||||
|
||||
return xas_result(&xas, curr);
|
||||
@ -1483,6 +1517,52 @@ int xa_reserve(struct xarray *xa, unsigned long index, gfp_t gfp)
|
||||
}
|
||||
EXPORT_SYMBOL(xa_reserve);
|
||||
|
||||
/**
|
||||
* __xa_alloc() - Find somewhere to store this entry in the XArray.
|
||||
* @xa: XArray.
|
||||
* @id: Pointer to ID.
|
||||
* @max: Maximum ID to allocate (inclusive).
|
||||
* @entry: New entry.
|
||||
* @gfp: Memory allocation flags.
|
||||
*
|
||||
* Allocates an unused ID in the range specified by @id and @max.
|
||||
* Updates the @id pointer with the index, then stores the entry at that
|
||||
* index. A concurrent lookup will not see an uninitialised @id.
|
||||
*
|
||||
* Context: Any context. Expects xa_lock to be held on entry. May
|
||||
* release and reacquire xa_lock if @gfp flags permit.
|
||||
* Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if
|
||||
* there is no more space in the XArray.
|
||||
*/
|
||||
int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp)
|
||||
{
|
||||
XA_STATE(xas, xa, 0);
|
||||
int err;
|
||||
|
||||
if (WARN_ON_ONCE(xa_is_internal(entry)))
|
||||
return -EINVAL;
|
||||
if (WARN_ON_ONCE(!xa_track_free(xa)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!entry)
|
||||
entry = XA_ZERO_ENTRY;
|
||||
|
||||
do {
|
||||
xas.xa_index = *id;
|
||||
xas_find_marked(&xas, max, XA_FREE_MARK);
|
||||
if (xas.xa_node == XAS_RESTART)
|
||||
xas_set_err(&xas, -ENOSPC);
|
||||
xas_store(&xas, entry);
|
||||
xas_clear_mark(&xas, XA_FREE_MARK);
|
||||
} while (__xas_nomem(&xas, gfp));
|
||||
|
||||
err = xas_error(&xas);
|
||||
if (!err)
|
||||
*id = xas.xa_index;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(__xa_alloc);
|
||||
|
||||
/**
|
||||
* __xa_set_mark() - Set this mark on this entry while locked.
|
||||
* @xa: XArray.
|
||||
|
Loading…
Reference in New Issue
Block a user