aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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.