aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Wilcox <willy@infradead.org>2017-11-10 15:34:55 -0500
committerMatthew Wilcox <willy@infradead.org>2018-10-21 10:45:58 -0400
commit41aec91f55985e7f14ee75fe2f6e7bcfff0d0fae (patch)
tree4efdedeabd066e15a542b28f21a47731c5323b5b
parent58d6ea3085f2e53714810a513c61629f6d2be0a6 (diff)
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>
-rw-r--r--include/linux/xarray.h60
-rw-r--r--lib/test_xarray.c20
-rw-r--r--lib/xarray.c71
3 files changed, 151 insertions, 0 deletions
diff --git a/include/linux/xarray.h b/include/linux/xarray.h
index 15eab63286ed..7f740f675b96 100644
--- a/include/linux/xarray.h
+++ b/include/linux/xarray.h
@@ -275,6 +275,8 @@ struct xarray {
275void xa_init_flags(struct xarray *, gfp_t flags); 275void xa_init_flags(struct xarray *, gfp_t flags);
276void *xa_load(struct xarray *, unsigned long index); 276void *xa_load(struct xarray *, unsigned long index);
277void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); 277void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
278void *xa_cmpxchg(struct xarray *, unsigned long index,
279 void *old, void *entry, gfp_t);
278bool xa_get_mark(struct xarray *, unsigned long index, xa_mark_t); 280bool xa_get_mark(struct xarray *, unsigned long index, xa_mark_t);
279void xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); 281void xa_set_mark(struct xarray *, unsigned long index, xa_mark_t);
280void xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); 282void 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)
334 return xa_store(xa, index, NULL, 0); 336 return xa_store(xa, index, NULL, 0);
335} 337}
336 338
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 */
356static 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
337#define xa_trylock(xa) spin_trylock(&(xa)->xa_lock) 367#define xa_trylock(xa) spin_trylock(&(xa)->xa_lock)
338#define xa_lock(xa) spin_lock(&(xa)->xa_lock) 368#define xa_lock(xa) spin_lock(&(xa)->xa_lock)
339#define xa_unlock(xa) spin_unlock(&(xa)->xa_lock) 369#define xa_unlock(xa) spin_unlock(&(xa)->xa_lock)
@@ -355,10 +385,40 @@ static inline void *xa_erase(struct xarray *xa, unsigned long index)
355 */ 385 */
356void *__xa_erase(struct xarray *, unsigned long index); 386void *__xa_erase(struct xarray *, unsigned long index);
357void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); 387void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
388void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old,
389 void *entry, gfp_t);
358void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); 390void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t);
359void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); 391void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t);
360 392
361/** 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 */
410static 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
421/**
362 * xa_erase_bh() - Erase this entry from the XArray. 422 * xa_erase_bh() - Erase this entry from the XArray.
363 * @xa: XArray. 423 * @xa: XArray.
364 * @index: Index of entry. 424 * @index: Index of entry.
diff --git a/lib/test_xarray.c b/lib/test_xarray.c
index b711030fbc32..fb472258b639 100644
--- a/lib/test_xarray.c
+++ b/lib/test_xarray.c
@@ -198,6 +198,25 @@ static noinline void check_xa_shrink(struct xarray *xa)
198 XA_BUG_ON(xa, !xa_empty(xa)); 198 XA_BUG_ON(xa, !xa_empty(xa));
199} 199}
200 200
201static 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
201static noinline void check_multi_store(struct xarray *xa) 220static noinline void check_multi_store(struct xarray *xa)
202{ 221{
203#ifdef CONFIG_XARRAY_MULTI 222#ifdef CONFIG_XARRAY_MULTI
@@ -274,6 +293,7 @@ static int xarray_checks(void)
274 check_xa_load(&array); 293 check_xa_load(&array);
275 check_xa_mark(&array); 294 check_xa_mark(&array);
276 check_xa_shrink(&array); 295 check_xa_shrink(&array);
296 check_cmpxchg(&array);
277 check_multi_store(&array); 297 check_multi_store(&array);
278 298
279 printk("XArray: %u of %u tests passed\n", tests_passed, tests_run); 299 printk("XArray: %u of %u tests passed\n", tests_passed, tests_run);
diff --git a/lib/xarray.c b/lib/xarray.c
index 4596a95ed9cd..2ba5a98ec618 100644
--- a/lib/xarray.c
+++ b/lib/xarray.c
@@ -976,6 +976,77 @@ void *__xa_store(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
976EXPORT_SYMBOL(__xa_store); 976EXPORT_SYMBOL(__xa_store);
977 977
978/** 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 */
993void *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}
1012EXPORT_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 */
1030void *__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}
1047EXPORT_SYMBOL(__xa_cmpxchg);
1048
1049/**
979 * __xa_set_mark() - Set this mark on this entry while locked. 1050 * __xa_set_mark() - Set this mark on this entry while locked.
980 * @xa: XArray. 1051 * @xa: XArray.
981 * @index: Index of entry. 1052 * @index: Index of entry.