Skip to content

Commit 41aec91

Browse files
author
Matthew Wilcox
committed
xarray: Add XArray conditional store operations
Like cmpxchg(), xa_cmpxchg will only store to the index if the current entry matches the old entry. It returns the current entry, which is usually more useful than the errno returned by radix_tree_insert(). For the users who really only want the errno, the xa_insert() wrapper provides a more convenient calling convention. Signed-off-by: Matthew Wilcox <willy@infradead.org>
1 parent 58d6ea3 commit 41aec91

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed

include/linux/xarray.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ struct xarray {
275275
void xa_init_flags(struct xarray *, gfp_t flags);
276276
void *xa_load(struct xarray *, unsigned long index);
277277
void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
278+
void *xa_cmpxchg(struct xarray *, unsigned long index,
279+
void *old, void *entry, gfp_t);
278280
bool xa_get_mark(struct xarray *, unsigned long index, xa_mark_t);
279281
void xa_set_mark(struct xarray *, unsigned long index, xa_mark_t);
280282
void xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t);
@@ -334,6 +336,34 @@ static inline void *xa_erase(struct xarray *xa, unsigned long index)
334336
return xa_store(xa, index, NULL, 0);
335337
}
336338

339+
/**
340+
* xa_insert() - Store this entry in the XArray unless another entry is
341+
* already present.
342+
* @xa: XArray.
343+
* @index: Index into array.
344+
* @entry: New entry.
345+
* @gfp: Memory allocation flags.
346+
*
347+
* If you would rather see the existing entry in the array, use xa_cmpxchg().
348+
* This function is for users who don't care what the entry is, only that
349+
* one is present.
350+
*
351+
* Context: Process context. Takes and releases the xa_lock.
352+
* May sleep if the @gfp flags permit.
353+
* Return: 0 if the store succeeded. -EEXIST if another entry was present.
354+
* -ENOMEM if memory could not be allocated.
355+
*/
356+
static inline int xa_insert(struct xarray *xa, unsigned long index,
357+
void *entry, gfp_t gfp)
358+
{
359+
void *curr = xa_cmpxchg(xa, index, NULL, entry, gfp);
360+
if (!curr)
361+
return 0;
362+
if (xa_is_err(curr))
363+
return xa_err(curr);
364+
return -EEXIST;
365+
}
366+
337367
#define xa_trylock(xa) spin_trylock(&(xa)->xa_lock)
338368
#define xa_lock(xa) spin_lock(&(xa)->xa_lock)
339369
#define xa_unlock(xa) spin_unlock(&(xa)->xa_lock)
@@ -355,9 +385,39 @@ static inline void *xa_erase(struct xarray *xa, unsigned long index)
355385
*/
356386
void *__xa_erase(struct xarray *, unsigned long index);
357387
void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
388+
void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old,
389+
void *entry, gfp_t);
358390
void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t);
359391
void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t);
360392

393+
/**
394+
* __xa_insert() - Store this entry in the XArray unless another entry is
395+
* already present.
396+
* @xa: XArray.
397+
* @index: Index into array.
398+
* @entry: New entry.
399+
* @gfp: Memory allocation flags.
400+
*
401+
* If you would rather see the existing entry in the array, use __xa_cmpxchg().
402+
* This function is for users who don't care what the entry is, only that
403+
* one is present.
404+
*
405+
* Context: Any context. Expects xa_lock to be held on entry. May
406+
* release and reacquire xa_lock if the @gfp flags permit.
407+
* Return: 0 if the store succeeded. -EEXIST if another entry was present.
408+
* -ENOMEM if memory could not be allocated.
409+
*/
410+
static inline int __xa_insert(struct xarray *xa, unsigned long index,
411+
void *entry, gfp_t gfp)
412+
{
413+
void *curr = __xa_cmpxchg(xa, index, NULL, entry, gfp);
414+
if (!curr)
415+
return 0;
416+
if (xa_is_err(curr))
417+
return xa_err(curr);
418+
return -EEXIST;
419+
}
420+
361421
/**
362422
* xa_erase_bh() - Erase this entry from the XArray.
363423
* @xa: XArray.

lib/test_xarray.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,25 @@ static noinline void check_xa_shrink(struct xarray *xa)
198198
XA_BUG_ON(xa, !xa_empty(xa));
199199
}
200200

201+
static noinline void check_cmpxchg(struct xarray *xa)
202+
{
203+
void *FIVE = xa_mk_value(5);
204+
void *SIX = xa_mk_value(6);
205+
void *LOTS = xa_mk_value(12345678);
206+
207+
XA_BUG_ON(xa, !xa_empty(xa));
208+
XA_BUG_ON(xa, xa_store_index(xa, 12345678, GFP_KERNEL) != NULL);
209+
XA_BUG_ON(xa, xa_insert(xa, 12345678, xa, GFP_KERNEL) != -EEXIST);
210+
XA_BUG_ON(xa, xa_cmpxchg(xa, 12345678, SIX, FIVE, GFP_KERNEL) != LOTS);
211+
XA_BUG_ON(xa, xa_cmpxchg(xa, 12345678, LOTS, FIVE, GFP_KERNEL) != LOTS);
212+
XA_BUG_ON(xa, xa_cmpxchg(xa, 12345678, FIVE, LOTS, GFP_KERNEL) != FIVE);
213+
XA_BUG_ON(xa, xa_cmpxchg(xa, 5, FIVE, NULL, GFP_KERNEL) != NULL);
214+
XA_BUG_ON(xa, xa_cmpxchg(xa, 5, NULL, FIVE, GFP_KERNEL) != NULL);
215+
xa_erase_index(xa, 12345678);
216+
xa_erase_index(xa, 5);
217+
XA_BUG_ON(xa, !xa_empty(xa));
218+
}
219+
201220
static noinline void check_multi_store(struct xarray *xa)
202221
{
203222
#ifdef CONFIG_XARRAY_MULTI
@@ -274,6 +293,7 @@ static int xarray_checks(void)
274293
check_xa_load(&array);
275294
check_xa_mark(&array);
276295
check_xa_shrink(&array);
296+
check_cmpxchg(&array);
277297
check_multi_store(&array);
278298

279299
printk("XArray: %u of %u tests passed\n", tests_passed, tests_run);

lib/xarray.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,77 @@ void *__xa_store(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
975975
}
976976
EXPORT_SYMBOL(__xa_store);
977977

978+
/**
979+
* xa_cmpxchg() - Conditionally replace an entry in the XArray.
980+
* @xa: XArray.
981+
* @index: Index into array.
982+
* @old: Old value to test against.
983+
* @entry: New value to place in array.
984+
* @gfp: Memory allocation flags.
985+
*
986+
* If the entry at @index is the same as @old, replace it with @entry.
987+
* If the return value is equal to @old, then the exchange was successful.
988+
*
989+
* Context: Process context. Takes and releases the xa_lock. May sleep
990+
* if the @gfp flags permit.
991+
* Return: The old value at this index or xa_err() if an error happened.
992+
*/
993+
void *xa_cmpxchg(struct xarray *xa, unsigned long index,
994+
void *old, void *entry, gfp_t gfp)
995+
{
996+
XA_STATE(xas, xa, index);
997+
void *curr;
998+
999+
if (WARN_ON_ONCE(xa_is_internal(entry)))
1000+
return XA_ERROR(-EINVAL);
1001+
1002+
do {
1003+
xas_lock(&xas);
1004+
curr = xas_load(&xas);
1005+
if (curr == old)
1006+
xas_store(&xas, entry);
1007+
xas_unlock(&xas);
1008+
} while (xas_nomem(&xas, gfp));
1009+
1010+
return xas_result(&xas, curr);
1011+
}
1012+
EXPORT_SYMBOL(xa_cmpxchg);
1013+
1014+
/**
1015+
* __xa_cmpxchg() - Store this entry in the XArray.
1016+
* @xa: XArray.
1017+
* @index: Index into array.
1018+
* @old: Old value to test against.
1019+
* @entry: New entry.
1020+
* @gfp: Memory allocation flags.
1021+
*
1022+
* You must already be holding the xa_lock when calling this function.
1023+
* It will drop the lock if needed to allocate memory, and then reacquire
1024+
* it afterwards.
1025+
*
1026+
* Context: Any context. Expects xa_lock to be held on entry. May
1027+
* release and reacquire xa_lock if @gfp flags permit.
1028+
* Return: The old entry at this index or xa_err() if an error happened.
1029+
*/
1030+
void *__xa_cmpxchg(struct xarray *xa, unsigned long index,
1031+
void *old, void *entry, gfp_t gfp)
1032+
{
1033+
XA_STATE(xas, xa, index);
1034+
void *curr;
1035+
1036+
if (WARN_ON_ONCE(xa_is_internal(entry)))
1037+
return XA_ERROR(-EINVAL);
1038+
1039+
do {
1040+
curr = xas_load(&xas);
1041+
if (curr == old)
1042+
xas_store(&xas, entry);
1043+
} while (__xas_nomem(&xas, gfp));
1044+
1045+
return xas_result(&xas, curr);
1046+
}
1047+
EXPORT_SYMBOL(__xa_cmpxchg);
1048+
9781049
/**
9791050
* __xa_set_mark() - Set this mark on this entry while locked.
9801051
* @xa: XArray.

0 commit comments

Comments
 (0)