XArray: Redesign xa_alloc API
It was too easy to forget to initialise the start index. Add an xa_limit data structure which can be used to pass min & max, and define a couple of special values for common cases. Also add some more tests cribbed from the IDR test suite. Change the return value from -ENOSPC to -EBUSY to match xa_insert(). Signed-off-by: Matthew Wilcox <willy@infradead.org>
This commit is contained in:
parent
3ccaf57a6a
commit
a3e4d3f97e
|
@ -200,6 +200,27 @@ static inline int xa_err(void *entry)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct xa_limit - Represents a range of IDs.
|
||||
* @min: The lowest ID to allocate (inclusive).
|
||||
* @max: The maximum ID to allocate (inclusive).
|
||||
*
|
||||
* This structure is used either directly or via the XA_LIMIT() macro
|
||||
* to communicate the range of IDs that are valid for allocation.
|
||||
* Two common ranges are predefined for you:
|
||||
* * xa_limit_32b - [0 - UINT_MAX]
|
||||
* * xa_limit_31b - [0 - INT_MAX]
|
||||
*/
|
||||
struct xa_limit {
|
||||
u32 max;
|
||||
u32 min;
|
||||
};
|
||||
|
||||
#define XA_LIMIT(_min, _max) (struct xa_limit) { .min = _min, .max = _max }
|
||||
|
||||
#define xa_limit_32b XA_LIMIT(0, UINT_MAX)
|
||||
#define xa_limit_31b XA_LIMIT(0, INT_MAX)
|
||||
|
||||
typedef unsigned __bitwise xa_mark_t;
|
||||
#define XA_MARK_0 ((__force xa_mark_t)0U)
|
||||
#define XA_MARK_1 ((__force xa_mark_t)1U)
|
||||
|
@ -476,7 +497,8 @@ 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_insert(struct xarray *, unsigned long index, void *entry, gfp_t);
|
||||
int __xa_alloc(struct xarray *, u32 *id, u32 max, void *entry, gfp_t);
|
||||
int __must_check __xa_alloc(struct xarray *, u32 *id, void *entry,
|
||||
struct xa_limit, gfp_t);
|
||||
int __xa_reserve(struct xarray *, unsigned long index, 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);
|
||||
|
@ -753,26 +775,26 @@ static inline int xa_insert_irq(struct xarray *xa, unsigned long index,
|
|||
* 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.
|
||||
* @limit: Range of ID to allocate.
|
||||
* @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.
|
||||
* Finds an empty entry in @xa between @limit.min and @limit.max,
|
||||
* stores the index into the @id pointer, 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
|
||||
* Context: Any 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.
|
||||
* Return: 0 on success, -ENOMEM if memory could not be allocated or
|
||||
* -EBUSY if there are no free entries in @limit.
|
||||
*/
|
||||
static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry,
|
||||
gfp_t gfp)
|
||||
static inline __must_check int xa_alloc(struct xarray *xa, u32 *id,
|
||||
void *entry, struct xa_limit limit, gfp_t gfp)
|
||||
{
|
||||
int err;
|
||||
|
||||
xa_lock(xa);
|
||||
err = __xa_alloc(xa, id, max, entry, gfp);
|
||||
err = __xa_alloc(xa, id, entry, limit, gfp);
|
||||
xa_unlock(xa);
|
||||
|
||||
return err;
|
||||
|
@ -782,26 +804,26 @@ static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry,
|
|||
* 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.
|
||||
* @limit: Range of ID to allocate.
|
||||
* @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.
|
||||
* Finds an empty entry in @xa between @limit.min and @limit.max,
|
||||
* stores the index into the @id pointer, then stores the entry at
|
||||
* that index. A concurrent lookup will not see an uninitialised @id.
|
||||
*
|
||||
* Context: Any 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.
|
||||
* Return: 0 on success, -ENOMEM if memory could not be allocated or
|
||||
* -EBUSY if there are no free entries in @limit.
|
||||
*/
|
||||
static inline int xa_alloc_bh(struct xarray *xa, u32 *id, u32 max, void *entry,
|
||||
gfp_t gfp)
|
||||
static inline int __must_check xa_alloc_bh(struct xarray *xa, u32 *id,
|
||||
void *entry, struct xa_limit limit, gfp_t gfp)
|
||||
{
|
||||
int err;
|
||||
|
||||
xa_lock_bh(xa);
|
||||
err = __xa_alloc(xa, id, max, entry, gfp);
|
||||
err = __xa_alloc(xa, id, entry, limit, gfp);
|
||||
xa_unlock_bh(xa);
|
||||
|
||||
return err;
|
||||
|
@ -811,26 +833,26 @@ static inline int xa_alloc_bh(struct xarray *xa, u32 *id, u32 max, void *entry,
|
|||
* 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.
|
||||
* @limit: Range of ID to allocate.
|
||||
* @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.
|
||||
* Finds an empty entry in @xa between @limit.min and @limit.max,
|
||||
* stores the index into the @id pointer, 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.
|
||||
* Return: 0 on success, -ENOMEM if memory could not be allocated or
|
||||
* -EBUSY if there are no free entries in @limit.
|
||||
*/
|
||||
static inline int xa_alloc_irq(struct xarray *xa, u32 *id, u32 max, void *entry,
|
||||
gfp_t gfp)
|
||||
static inline int __must_check xa_alloc_irq(struct xarray *xa, u32 *id,
|
||||
void *entry, struct xa_limit limit, gfp_t gfp)
|
||||
{
|
||||
int err;
|
||||
|
||||
xa_lock_irq(xa);
|
||||
err = __xa_alloc(xa, id, max, entry, gfp);
|
||||
err = __xa_alloc(xa, id, entry, limit, gfp);
|
||||
xa_unlock_irq(xa);
|
||||
|
||||
return err;
|
||||
|
|
|
@ -40,9 +40,9 @@ static void *xa_store_index(struct xarray *xa, unsigned long index, gfp_t gfp)
|
|||
|
||||
static void xa_alloc_index(struct xarray *xa, unsigned long index, gfp_t gfp)
|
||||
{
|
||||
u32 id = 0;
|
||||
u32 id;
|
||||
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(index),
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(index), xa_limit_32b,
|
||||
gfp) != 0);
|
||||
XA_BUG_ON(xa, id != index);
|
||||
}
|
||||
|
@ -640,28 +640,81 @@ static noinline void check_xa_alloc_1(struct xarray *xa, unsigned int base)
|
|||
xa_destroy(xa);
|
||||
|
||||
/* Check that we fail properly at the limit of allocation */
|
||||
id = 0xfffffffeU;
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id),
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(UINT_MAX - 1),
|
||||
XA_LIMIT(UINT_MAX - 1, UINT_MAX),
|
||||
GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(xa, id != 0xfffffffeU);
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id),
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(UINT_MAX),
|
||||
XA_LIMIT(UINT_MAX - 1, UINT_MAX),
|
||||
GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(xa, id != 0xffffffffU);
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id),
|
||||
GFP_KERNEL) != -ENOSPC);
|
||||
XA_BUG_ON(xa, id != 0xffffffffU);
|
||||
id = 3;
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(0),
|
||||
XA_LIMIT(UINT_MAX - 1, UINT_MAX),
|
||||
GFP_KERNEL) != -EBUSY);
|
||||
XA_BUG_ON(xa, id != 3);
|
||||
xa_destroy(xa);
|
||||
|
||||
id = 10;
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id),
|
||||
GFP_KERNEL) != -ENOSPC);
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(10), XA_LIMIT(10, 5),
|
||||
GFP_KERNEL) != -EBUSY);
|
||||
XA_BUG_ON(xa, xa_store_index(xa, 3, GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id),
|
||||
GFP_KERNEL) != -ENOSPC);
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(10), XA_LIMIT(10, 5),
|
||||
GFP_KERNEL) != -EBUSY);
|
||||
xa_erase_index(xa, 3);
|
||||
XA_BUG_ON(xa, !xa_empty(xa));
|
||||
}
|
||||
|
||||
static noinline void check_xa_alloc_2(struct xarray *xa, unsigned int base)
|
||||
{
|
||||
unsigned int i, id;
|
||||
unsigned long index;
|
||||
void *entry;
|
||||
|
||||
/* Allocate and free a NULL and check xa_empty() behaves */
|
||||
XA_BUG_ON(xa, !xa_empty(xa));
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(xa, id != base);
|
||||
XA_BUG_ON(xa, xa_empty(xa));
|
||||
XA_BUG_ON(xa, xa_erase(xa, id) != NULL);
|
||||
XA_BUG_ON(xa, !xa_empty(xa));
|
||||
|
||||
/* Ditto, but check destroy instead of erase */
|
||||
XA_BUG_ON(xa, !xa_empty(xa));
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(xa, id != base);
|
||||
XA_BUG_ON(xa, xa_empty(xa));
|
||||
xa_destroy(xa);
|
||||
XA_BUG_ON(xa, !xa_empty(xa));
|
||||
|
||||
for (i = base; i < base + 10; i++) {
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b,
|
||||
GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(xa, id != i);
|
||||
}
|
||||
|
||||
XA_BUG_ON(xa, xa_store(xa, 3, xa_mk_index(3), GFP_KERNEL) != NULL);
|
||||
XA_BUG_ON(xa, xa_store(xa, 4, xa_mk_index(4), GFP_KERNEL) != NULL);
|
||||
XA_BUG_ON(xa, xa_store(xa, 4, NULL, GFP_KERNEL) != xa_mk_index(4));
|
||||
XA_BUG_ON(xa, xa_erase(xa, 5) != NULL);
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(xa, id != 5);
|
||||
|
||||
xa_for_each(xa, index, entry) {
|
||||
xa_erase_index(xa, index);
|
||||
}
|
||||
|
||||
for (i = base; i < base + 9; i++) {
|
||||
XA_BUG_ON(xa, xa_erase(xa, i) != NULL);
|
||||
XA_BUG_ON(xa, xa_empty(xa));
|
||||
}
|
||||
XA_BUG_ON(xa, xa_erase(xa, 8) != NULL);
|
||||
XA_BUG_ON(xa, xa_empty(xa));
|
||||
XA_BUG_ON(xa, xa_erase(xa, base + 9) != NULL);
|
||||
XA_BUG_ON(xa, !xa_empty(xa));
|
||||
|
||||
xa_destroy(xa);
|
||||
}
|
||||
|
||||
static DEFINE_XARRAY_ALLOC(xa0);
|
||||
static DEFINE_XARRAY_ALLOC1(xa1);
|
||||
|
||||
|
@ -669,6 +722,8 @@ static noinline void check_xa_alloc(void)
|
|||
{
|
||||
check_xa_alloc_1(&xa0, 0);
|
||||
check_xa_alloc_1(&xa1, 1);
|
||||
check_xa_alloc_2(&xa0, 0);
|
||||
check_xa_alloc_2(&xa1, 1);
|
||||
}
|
||||
|
||||
static noinline void __check_store_iter(struct xarray *xa, unsigned long start,
|
||||
|
@ -1219,9 +1274,8 @@ static void check_align_1(struct xarray *xa, char *name)
|
|||
void *entry;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
id = 0;
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, name + i, GFP_KERNEL)
|
||||
!= 0);
|
||||
XA_BUG_ON(xa, xa_alloc(xa, &id, name + i, xa_limit_32b,
|
||||
GFP_KERNEL) != 0);
|
||||
XA_BUG_ON(xa, id != i);
|
||||
}
|
||||
xa_for_each(xa, index, entry)
|
||||
|
|
29
lib/xarray.c
29
lib/xarray.c
|
@ -1615,23 +1615,23 @@ EXPORT_SYMBOL(xa_store_range);
|
|||
* __xa_alloc() - Find somewhere to store this entry in the XArray.
|
||||
* @xa: XArray.
|
||||
* @id: Pointer to ID.
|
||||
* @max: Maximum ID to allocate (inclusive).
|
||||
* @limit: Range for allocated ID.
|
||||
* @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.
|
||||
* Finds an empty entry in @xa between @limit.min and @limit.max,
|
||||
* stores the index into the @id pointer, 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.
|
||||
* Return: 0 on success, -ENOMEM if memory could not be allocated or
|
||||
* -EBUSY if there are no free entries in @limit.
|
||||
*/
|
||||
int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp)
|
||||
int __xa_alloc(struct xarray *xa, u32 *id, void *entry,
|
||||
struct xa_limit limit, gfp_t gfp)
|
||||
{
|
||||
XA_STATE(xas, xa, 0);
|
||||
int err;
|
||||
|
||||
if (WARN_ON_ONCE(xa_is_advanced(entry)))
|
||||
return -EINVAL;
|
||||
|
@ -1642,18 +1642,17 @@ int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp)
|
|||
entry = XA_ZERO_ENTRY;
|
||||
|
||||
do {
|
||||
xas.xa_index = *id;
|
||||
xas_find_marked(&xas, max, XA_FREE_MARK);
|
||||
xas.xa_index = limit.min;
|
||||
xas_find_marked(&xas, limit.max, XA_FREE_MARK);
|
||||
if (xas.xa_node == XAS_RESTART)
|
||||
xas_set_err(&xas, -ENOSPC);
|
||||
xas_set_err(&xas, -EBUSY);
|
||||
else
|
||||
*id = xas.xa_index;
|
||||
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;
|
||||
return xas_error(&xas);
|
||||
}
|
||||
EXPORT_SYMBOL(__xa_alloc);
|
||||
|
||||
|
|
Loading…
Reference in New Issue