diff options
-rw-r--r-- | include/linux/xarray.h | 60 | ||||
-rw-r--r-- | lib/test_xarray.c | 20 | ||||
-rw-r--r-- | lib/xarray.c | 71 |
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 { | |||
275 | void xa_init_flags(struct xarray *, gfp_t flags); | 275 | void xa_init_flags(struct xarray *, gfp_t flags); |
276 | void *xa_load(struct xarray *, unsigned long index); | 276 | void *xa_load(struct xarray *, unsigned long index); |
277 | void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); | 277 | 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); | ||
278 | bool xa_get_mark(struct xarray *, unsigned long index, xa_mark_t); | 280 | bool xa_get_mark(struct xarray *, unsigned long index, xa_mark_t); |
279 | void xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); | 281 | void xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); |
280 | void xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); | 282 | 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) | |||
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 | */ | ||
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 | |||
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 | */ |
356 | void *__xa_erase(struct xarray *, unsigned long index); | 386 | void *__xa_erase(struct xarray *, unsigned long index); |
357 | void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); | 387 | 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); | ||
358 | void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); | 390 | void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t); |
359 | void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t); | 391 | void __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 | */ | ||
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 | |||
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 | ||
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 | |||
201 | static noinline void check_multi_store(struct xarray *xa) | 220 | static 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) | |||
976 | EXPORT_SYMBOL(__xa_store); | 976 | EXPORT_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 | */ | ||
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 | |||
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. |