Skip to content

Commit

Permalink
xarray: Track free entries in an XArray
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Matthew Wilcox committed Oct 21, 2018
1 parent 3d5bd6e commit 371c752
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 7 deletions.
23 changes: 20 additions & 3 deletions Documentation/core-api/xarray.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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`
Expand All @@ -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`

Expand Down
101 changes: 101 additions & 0 deletions include/linux/xarray.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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. */

/*
Expand Down
61 changes: 61 additions & 0 deletions lib/test_xarray.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit 371c752

Please sign in to comment.