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

XArray: Honour reserved entries in xa_insert



xa_insert() should treat reserved entries as occupied, not as available.
Also, it should treat requests to insert a NULL pointer as a request
to reserve the slot.  Add xa_insert_bh() and xa_insert_irq() for
completeness.

Signed-off-by: default avatarMatthew Wilcox <willy@infradead.org>
parent 76b4e529
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -108,12 +108,13 @@ some, but not all of the other indices changing.

Sometimes you need to ensure that a subsequent call to :c:func:`xa_store`
will not need to allocate memory.  The :c:func:`xa_reserve` function
will store a reserved entry at the indicated index.  Users of the normal
API will see this entry as containing ``NULL``.  If you do not need to
use the reserved entry, you can call :c:func:`xa_release` to remove the
unused entry.  If another user has stored to the entry in the meantime,
:c:func:`xa_release` will do nothing; if instead you want the entry to
become ``NULL``, you should use :c:func:`xa_erase`.
will store a reserved entry at the indicated index.  Users of the
normal API will see this entry as containing ``NULL``.  If you do
not need to use the reserved entry, you can call :c:func:`xa_release`
to remove the unused entry.  If another user has stored to the entry
in the meantime, :c:func:`xa_release` will do nothing; if instead you
want the entry to become ``NULL``, you should use :c:func:`xa_erase`.
Using :c:func:`xa_insert` on a reserved entry will fail.

If all entries in the array are ``NULL``, the :c:func:`xa_empty` function
will return ``true``.
@@ -183,6 +184,8 @@ Takes xa_lock internally:
 * :c:func:`xa_store_bh`
 * :c:func:`xa_store_irq`
 * :c:func:`xa_insert`
 * :c:func:`xa_insert_bh`
 * :c:func:`xa_insert_irq`
 * :c:func:`xa_erase`
 * :c:func:`xa_erase_bh`
 * :c:func:`xa_erase_irq`
+71 −39
Original line number Diff line number Diff line
@@ -463,39 +463,12 @@ 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_insert(struct xarray *, unsigned long index, void *entry, gfp_t);
int __xa_alloc(struct xarray *, u32 *id, u32 max, void *entry, 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);

/**
 * __xa_insert() - Store this entry in the XArray unless another entry is
 *			already present.
 * @xa: XArray.
 * @index: Index into array.
 * @entry: New entry.
 * @gfp: Memory allocation flags.
 *
 * If you would rather see the existing entry in the array, use __xa_cmpxchg().
 * This function is for users who don't care what the entry is, only that
 * one is present.
 *
 * Context: Any context.  Expects xa_lock to be held on entry.  May
 *	    release and reacquire xa_lock if the @gfp flags permit.
 * Return: 0 if the store succeeded.  -EEXIST if another entry was present.
 * -ENOMEM if memory could not be allocated.
 */
static inline int __xa_insert(struct xarray *xa, unsigned long index,
		void *entry, gfp_t gfp)
{
	void *curr = __xa_cmpxchg(xa, index, NULL, entry, gfp);
	if (!curr)
		return 0;
	if (xa_is_err(curr))
		return xa_err(curr);
	return -EEXIST;
}

/**
 * xa_store_bh() - Store this entry in the XArray.
 * @xa: XArray.
@@ -685,24 +658,83 @@ static inline void *xa_cmpxchg_irq(struct xarray *xa, unsigned long index,
 * @entry: New entry.
 * @gfp: Memory allocation flags.
 *
 * If you would rather see the existing entry in the array, use xa_cmpxchg().
 * This function is for users who don't care what the entry is, only that
 * one is present.
 * Inserting a NULL entry will store a reserved entry (like xa_reserve())
 * if no entry is present.  Inserting will fail if a reserved entry is
 * present, even though loading from this index will return NULL.
 *
 * Context: Process context.  Takes and releases the xa_lock.
 *	    May sleep if the @gfp flags permit.
 * Context: Any context.  Takes and releases the xa_lock.  May sleep if
 * the @gfp flags permit.
 * Return: 0 if the store succeeded.  -EEXIST if another entry was present.
 * -ENOMEM if memory could not be allocated.
 */
static inline int xa_insert(struct xarray *xa, unsigned long index,
		void *entry, gfp_t gfp)
{
	void *curr = xa_cmpxchg(xa, index, NULL, entry, gfp);
	if (!curr)
		return 0;
	if (xa_is_err(curr))
		return xa_err(curr);
	return -EEXIST;
	int err;

	xa_lock(xa);
	err = __xa_insert(xa, index, entry, gfp);
	xa_unlock(xa);

	return err;
}

/**
 * xa_insert_bh() - Store this entry in the XArray unless another entry is
 *			already present.
 * @xa: XArray.
 * @index: Index into array.
 * @entry: New entry.
 * @gfp: Memory allocation flags.
 *
 * Inserting a NULL entry will store a reserved entry (like xa_reserve())
 * if no entry is present.  Inserting will fail if a reserved entry is
 * present, even though loading from this index will return NULL.
 *
 * Context: Any context.  Takes and releases the xa_lock while
 * disabling softirqs.  May sleep if the @gfp flags permit.
 * Return: 0 if the store succeeded.  -EEXIST if another entry was present.
 * -ENOMEM if memory could not be allocated.
 */
static inline int xa_insert_bh(struct xarray *xa, unsigned long index,
		void *entry, gfp_t gfp)
{
	int err;

	xa_lock_bh(xa);
	err = __xa_insert(xa, index, entry, gfp);
	xa_unlock_bh(xa);

	return err;
}

/**
 * xa_insert_irq() - Store this entry in the XArray unless another entry is
 *			already present.
 * @xa: XArray.
 * @index: Index into array.
 * @entry: New entry.
 * @gfp: Memory allocation flags.
 *
 * Inserting a NULL entry will store a reserved entry (like xa_reserve())
 * if no entry is present.  Inserting will fail if a reserved entry is
 * present, even though loading from this index will return NULL.
 *
 * Context: Process context.  Takes and releases the xa_lock while
 * disabling interrupts.  May sleep if the @gfp flags permit.
 * Return: 0 if the store succeeded.  -EEXIST if another entry was present.
 * -ENOMEM if memory could not be allocated.
 */
static inline int xa_insert_irq(struct xarray *xa, unsigned long index,
		void *entry, gfp_t gfp)
{
	int err;

	xa_lock_irq(xa);
	err = __xa_insert(xa, index, entry, gfp);
	xa_unlock_irq(xa);

	return err;
}

/**
+5 −3
Original line number Diff line number Diff line
@@ -382,10 +382,12 @@ static noinline void check_reserve(struct xarray *xa)
	xa_erase_index(xa, 12345678);
	XA_BUG_ON(xa, !xa_empty(xa));

	/* And so does xa_insert */
	/* But xa_insert does not */
	xa_reserve(xa, 12345678, GFP_KERNEL);
	XA_BUG_ON(xa, xa_insert(xa, 12345678, xa_mk_value(12345678), 0) != 0);
	xa_erase_index(xa, 12345678);
	XA_BUG_ON(xa, xa_insert(xa, 12345678, xa_mk_value(12345678), 0) !=
			-EEXIST);
	XA_BUG_ON(xa, xa_empty(xa));
	XA_BUG_ON(xa, xa_erase(xa, 12345678) != NULL);
	XA_BUG_ON(xa, !xa_empty(xa));

	/* Can iterate through a reserved entry */
+41 −0
Original line number Diff line number Diff line
@@ -1439,6 +1439,47 @@ void *__xa_cmpxchg(struct xarray *xa, unsigned long index,
}
EXPORT_SYMBOL(__xa_cmpxchg);

/**
 * __xa_insert() - Store this entry in the XArray if no entry is present.
 * @xa: XArray.
 * @index: Index into array.
 * @entry: New entry.
 * @gfp: Memory allocation flags.
 *
 * Inserting a NULL entry will store a reserved entry (like xa_reserve())
 * if no entry is present.  Inserting will fail if a reserved entry is
 * present, even though loading from this index will return NULL.
 *
 * Context: Any context.  Expects xa_lock to be held on entry.  May
 * release and reacquire xa_lock if @gfp flags permit.
 * Return: 0 if the store succeeded.  -EEXIST if another entry was present.
 * -ENOMEM if memory could not be allocated.
 */
int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
{
	XA_STATE(xas, xa, index);
	void *curr;

	if (WARN_ON_ONCE(xa_is_advanced(entry)))
		return -EINVAL;
	if (!entry)
		entry = XA_ZERO_ENTRY;

	do {
		curr = xas_load(&xas);
		if (!curr) {
			xas_store(&xas, entry);
			if (xa_track_free(xa))
				xas_clear_mark(&xas, XA_FREE_MARK);
		} else {
			xas_set_err(&xas, -EEXIST);
		}
	} while (__xas_nomem(&xas, gfp));

	return xas_error(&xas);
}
EXPORT_SYMBOL(__xa_insert);

/**
 * __xa_reserve() - Reserve this index in the XArray.
 * @xa: XArray.