Commit a3e4d3f9 authored by Matthew Wilcox's avatar Matthew Wilcox
Browse files

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: default avatarMatthew Wilcox <willy@infradead.org>
parent 3ccaf57a
Loading
Loading
Loading
Loading
+51 −29
Original line number Diff line number Diff line
@@ -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;
+70 −16
Original line number Diff line number Diff line
@@ -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)
+14 −15
Original line number Diff line number Diff line
@@ -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);