diff options
author | Matthew Wilcox <willy@infradead.org> | 2018-10-26 14:43:22 -0400 |
---|---|---|
committer | Matthew Wilcox <willy@infradead.org> | 2019-02-06 13:13:24 -0500 |
commit | 3ccaf57a6a63ad171a951dcaddffc453b2414c7b (patch) | |
tree | fc2202432a5b50ee5507a2e240b439b2993c2c3f | |
parent | fd9dc93e36231fb6d520e0edd467058fad4fd12d (diff) |
XArray: Add support for 1s-based allocation
A lot of places want to allocate IDs starting at 1 instead of 0.
While the xa_alloc() API supports this, it's not very efficient if lots
of IDs are allocated, due to having to walk down to the bottom of the
tree to see if ID 1 is available, then all the way over to the next
non-allocated ID. This method marks ID 0 as being occupied which wastes
one slot in the XArray, but preserves xa_empty() as working.
Signed-off-by: Matthew Wilcox <willy@infradead.org>
-rw-r--r-- | Documentation/core-api/xarray.rst | 10 | ||||
-rw-r--r-- | include/linux/xarray.h | 14 | ||||
-rw-r--r-- | lib/test_xarray.c | 88 | ||||
-rw-r--r-- | lib/xarray.c | 11 |
4 files changed, 86 insertions, 37 deletions
diff --git a/Documentation/core-api/xarray.rst b/Documentation/core-api/xarray.rst index 42bb1a62650f..e90c4925cd37 100644 --- a/Documentation/core-api/xarray.rst +++ b/Documentation/core-api/xarray.rst | |||
@@ -131,17 +131,21 @@ If you use :c:func:`DEFINE_XARRAY_ALLOC` to define the XArray, or | |||
131 | initialise it by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`, | 131 | initialise it by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`, |
132 | the XArray changes to track whether entries are in use or not. | 132 | the XArray changes to track whether entries are in use or not. |
133 | 133 | ||
134 | You can call :c:func:`xa_alloc` to store the entry at any unused index | 134 | You can call :c:func:`xa_alloc` to store the entry at an unused index |
135 | in the XArray. If you need to modify the array from interrupt context, | 135 | in the XArray. If you need to modify the array from interrupt context, |
136 | you can use :c:func:`xa_alloc_bh` or :c:func:`xa_alloc_irq` to disable | 136 | you can use :c:func:`xa_alloc_bh` or :c:func:`xa_alloc_irq` to disable |
137 | interrupts while allocating the ID. | 137 | interrupts while allocating the ID. |
138 | 138 | ||
139 | Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert` | 139 | Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert` will |
140 | will mark the entry as being allocated. Unlike a normal XArray, storing | 140 | also mark the entry as being allocated. Unlike a normal XArray, storing |
141 | ``NULL`` will mark the entry as being in use, like :c:func:`xa_reserve`. | 141 | ``NULL`` will mark the entry as being in use, like :c:func:`xa_reserve`. |
142 | To free an entry, use :c:func:`xa_erase` (or :c:func:`xa_release` if | 142 | To free an entry, use :c:func:`xa_erase` (or :c:func:`xa_release` if |
143 | you only want to free the entry if it's ``NULL``). | 143 | you only want to free the entry if it's ``NULL``). |
144 | 144 | ||
145 | By default, the lowest free entry is allocated starting from 0. If you | ||
146 | want to allocate entries starting at 1, it is more efficient to use | ||
147 | :c:func:`DEFINE_XARRAY_ALLOC1` or ``XA_FLAGS_ALLOC1``. | ||
148 | |||
145 | You cannot use ``XA_MARK_0`` with an allocating XArray as this mark | 149 | You cannot use ``XA_MARK_0`` with an allocating XArray as this mark |
146 | is used to track whether an entry is free or not. The other marks are | 150 | is used to track whether an entry is free or not. The other marks are |
147 | available for your use. | 151 | available for your use. |
diff --git a/include/linux/xarray.h b/include/linux/xarray.h index 57cf35c4d094..99dd0838b4ba 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h | |||
@@ -220,10 +220,13 @@ enum xa_lock_type { | |||
220 | #define XA_FLAGS_LOCK_IRQ ((__force gfp_t)XA_LOCK_IRQ) | 220 | #define XA_FLAGS_LOCK_IRQ ((__force gfp_t)XA_LOCK_IRQ) |
221 | #define XA_FLAGS_LOCK_BH ((__force gfp_t)XA_LOCK_BH) | 221 | #define XA_FLAGS_LOCK_BH ((__force gfp_t)XA_LOCK_BH) |
222 | #define XA_FLAGS_TRACK_FREE ((__force gfp_t)4U) | 222 | #define XA_FLAGS_TRACK_FREE ((__force gfp_t)4U) |
223 | #define XA_FLAGS_ZERO_BUSY ((__force gfp_t)8U) | ||
223 | #define XA_FLAGS_MARK(mark) ((__force gfp_t)((1U << __GFP_BITS_SHIFT) << \ | 224 | #define XA_FLAGS_MARK(mark) ((__force gfp_t)((1U << __GFP_BITS_SHIFT) << \ |
224 | (__force unsigned)(mark))) | 225 | (__force unsigned)(mark))) |
225 | 226 | ||
227 | /* ALLOC is for a normal 0-based alloc. ALLOC1 is for an 1-based alloc */ | ||
226 | #define XA_FLAGS_ALLOC (XA_FLAGS_TRACK_FREE | XA_FLAGS_MARK(XA_FREE_MARK)) | 228 | #define XA_FLAGS_ALLOC (XA_FLAGS_TRACK_FREE | XA_FLAGS_MARK(XA_FREE_MARK)) |
229 | #define XA_FLAGS_ALLOC1 (XA_FLAGS_TRACK_FREE | XA_FLAGS_ZERO_BUSY) | ||
227 | 230 | ||
228 | /** | 231 | /** |
229 | * struct xarray - The anchor of the XArray. | 232 | * struct xarray - The anchor of the XArray. |
@@ -279,7 +282,7 @@ struct xarray { | |||
279 | #define DEFINE_XARRAY(name) DEFINE_XARRAY_FLAGS(name, 0) | 282 | #define DEFINE_XARRAY(name) DEFINE_XARRAY_FLAGS(name, 0) |
280 | 283 | ||
281 | /** | 284 | /** |
282 | * DEFINE_XARRAY_ALLOC() - Define an XArray which can allocate IDs. | 285 | * DEFINE_XARRAY_ALLOC() - Define an XArray which allocates IDs starting at 0. |
283 | * @name: A string that names your XArray. | 286 | * @name: A string that names your XArray. |
284 | * | 287 | * |
285 | * This is intended for file scope definitions of allocating XArrays. | 288 | * This is intended for file scope definitions of allocating XArrays. |
@@ -287,6 +290,15 @@ struct xarray { | |||
287 | */ | 290 | */ |
288 | #define DEFINE_XARRAY_ALLOC(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC) | 291 | #define DEFINE_XARRAY_ALLOC(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC) |
289 | 292 | ||
293 | /** | ||
294 | * DEFINE_XARRAY_ALLOC1() - Define an XArray which allocates IDs starting at 1. | ||
295 | * @name: A string that names your XArray. | ||
296 | * | ||
297 | * This is intended for file scope definitions of allocating XArrays. | ||
298 | * See also DEFINE_XARRAY(). | ||
299 | */ | ||
300 | #define DEFINE_XARRAY_ALLOC1(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC1) | ||
301 | |||
290 | void *xa_load(struct xarray *, unsigned long index); | 302 | void *xa_load(struct xarray *, unsigned long index); |
291 | void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); | 303 | void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); |
292 | void *xa_erase(struct xarray *, unsigned long index); | 304 | void *xa_erase(struct xarray *, unsigned long index); |
diff --git a/lib/test_xarray.c b/lib/test_xarray.c index 9d894e93456c..cd74f8f32abe 100644 --- a/lib/test_xarray.c +++ b/lib/test_xarray.c | |||
@@ -589,64 +589,86 @@ static noinline void check_multi_store(struct xarray *xa) | |||
589 | #endif | 589 | #endif |
590 | } | 590 | } |
591 | 591 | ||
592 | static DEFINE_XARRAY_ALLOC(xa0); | 592 | static noinline void check_xa_alloc_1(struct xarray *xa, unsigned int base) |
593 | |||
594 | static noinline void check_xa_alloc(void) | ||
595 | { | 593 | { |
596 | int i; | 594 | int i; |
597 | u32 id; | 595 | u32 id; |
598 | 596 | ||
599 | /* An empty array should assign 0 to the first alloc */ | 597 | XA_BUG_ON(xa, !xa_empty(xa)); |
600 | xa_alloc_index(&xa0, 0, GFP_KERNEL); | 598 | /* An empty array should assign %base to the first alloc */ |
599 | xa_alloc_index(xa, base, GFP_KERNEL); | ||
601 | 600 | ||
602 | /* Erasing it should make the array empty again */ | 601 | /* Erasing it should make the array empty again */ |
603 | xa_erase_index(&xa0, 0); | 602 | xa_erase_index(xa, base); |
604 | XA_BUG_ON(&xa0, !xa_empty(&xa0)); | 603 | XA_BUG_ON(xa, !xa_empty(xa)); |
604 | |||
605 | /* And it should assign %base again */ | ||
606 | xa_alloc_index(xa, base, GFP_KERNEL); | ||
607 | |||
608 | /* Allocating and then erasing a lot should not lose base */ | ||
609 | for (i = base + 1; i < 2 * XA_CHUNK_SIZE; i++) | ||
610 | xa_alloc_index(xa, i, GFP_KERNEL); | ||
611 | for (i = base; i < 2 * XA_CHUNK_SIZE; i++) | ||
612 | xa_erase_index(xa, i); | ||
613 | xa_alloc_index(xa, base, GFP_KERNEL); | ||
614 | |||
615 | /* Destroying the array should do the same as erasing */ | ||
616 | xa_destroy(xa); | ||
605 | 617 | ||
606 | /* And it should assign 0 again */ | 618 | /* And it should assign %base again */ |
607 | xa_alloc_index(&xa0, 0, GFP_KERNEL); | 619 | xa_alloc_index(xa, base, GFP_KERNEL); |
608 | 620 | ||
609 | /* The next assigned ID should be 1 */ | 621 | /* The next assigned ID should be base+1 */ |
610 | xa_alloc_index(&xa0, 1, GFP_KERNEL); | 622 | xa_alloc_index(xa, base + 1, GFP_KERNEL); |
611 | xa_erase_index(&xa0, 1); | 623 | xa_erase_index(xa, base + 1); |
612 | 624 | ||
613 | /* Storing a value should mark it used */ | 625 | /* Storing a value should mark it used */ |
614 | xa_store_index(&xa0, 1, GFP_KERNEL); | 626 | xa_store_index(xa, base + 1, GFP_KERNEL); |
615 | xa_alloc_index(&xa0, 2, GFP_KERNEL); | 627 | xa_alloc_index(xa, base + 2, GFP_KERNEL); |
616 | 628 | ||
617 | /* If we then erase 0, it should be free */ | 629 | /* If we then erase base, it should be free */ |
618 | xa_erase_index(&xa0, 0); | 630 | xa_erase_index(xa, base); |
619 | xa_alloc_index(&xa0, 0, GFP_KERNEL); | 631 | xa_alloc_index(xa, base, GFP_KERNEL); |
620 | 632 | ||
621 | xa_erase_index(&xa0, 1); | 633 | xa_erase_index(xa, base + 1); |
622 | xa_erase_index(&xa0, 2); | 634 | xa_erase_index(xa, base + 2); |
623 | 635 | ||
624 | for (i = 1; i < 5000; i++) { | 636 | for (i = 1; i < 5000; i++) { |
625 | xa_alloc_index(&xa0, i, GFP_KERNEL); | 637 | xa_alloc_index(xa, base + i, GFP_KERNEL); |
626 | } | 638 | } |
627 | 639 | ||
628 | xa_destroy(&xa0); | 640 | xa_destroy(xa); |
629 | 641 | ||
642 | /* Check that we fail properly at the limit of allocation */ | ||
630 | id = 0xfffffffeU; | 643 | id = 0xfffffffeU; |
631 | XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_index(id), | 644 | XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), |
632 | GFP_KERNEL) != 0); | 645 | GFP_KERNEL) != 0); |
633 | XA_BUG_ON(&xa0, id != 0xfffffffeU); | 646 | XA_BUG_ON(xa, id != 0xfffffffeU); |
634 | XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_index(id), | 647 | XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), |
635 | GFP_KERNEL) != 0); | 648 | GFP_KERNEL) != 0); |
636 | XA_BUG_ON(&xa0, id != 0xffffffffU); | 649 | XA_BUG_ON(xa, id != 0xffffffffU); |
637 | XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, UINT_MAX, xa_mk_index(id), | 650 | XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id), |
638 | GFP_KERNEL) != -ENOSPC); | 651 | GFP_KERNEL) != -ENOSPC); |
639 | XA_BUG_ON(&xa0, id != 0xffffffffU); | 652 | XA_BUG_ON(xa, id != 0xffffffffU); |
640 | xa_destroy(&xa0); | 653 | xa_destroy(xa); |
641 | 654 | ||
642 | id = 10; | 655 | id = 10; |
643 | XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, 5, xa_mk_index(id), | 656 | XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id), |
644 | GFP_KERNEL) != -ENOSPC); | 657 | GFP_KERNEL) != -ENOSPC); |
645 | XA_BUG_ON(&xa0, xa_store_index(&xa0, 3, GFP_KERNEL) != 0); | 658 | XA_BUG_ON(xa, xa_store_index(xa, 3, GFP_KERNEL) != 0); |
646 | XA_BUG_ON(&xa0, xa_alloc(&xa0, &id, 5, xa_mk_index(id), | 659 | XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id), |
647 | GFP_KERNEL) != -ENOSPC); | 660 | GFP_KERNEL) != -ENOSPC); |
648 | xa_erase_index(&xa0, 3); | 661 | xa_erase_index(xa, 3); |
649 | XA_BUG_ON(&xa0, !xa_empty(&xa0)); | 662 | XA_BUG_ON(xa, !xa_empty(xa)); |
663 | } | ||
664 | |||
665 | static DEFINE_XARRAY_ALLOC(xa0); | ||
666 | static DEFINE_XARRAY_ALLOC1(xa1); | ||
667 | |||
668 | static noinline void check_xa_alloc(void) | ||
669 | { | ||
670 | check_xa_alloc_1(&xa0, 0); | ||
671 | check_xa_alloc_1(&xa1, 1); | ||
650 | } | 672 | } |
651 | 673 | ||
652 | static noinline void __check_store_iter(struct xarray *xa, unsigned long start, | 674 | static noinline void __check_store_iter(struct xarray *xa, unsigned long start, |
diff --git a/lib/xarray.c b/lib/xarray.c index 1b97ca58bd15..468fb7b7963f 100644 --- a/lib/xarray.c +++ b/lib/xarray.c | |||
@@ -57,6 +57,11 @@ static inline bool xa_track_free(const struct xarray *xa) | |||
57 | return xa->xa_flags & XA_FLAGS_TRACK_FREE; | 57 | return xa->xa_flags & XA_FLAGS_TRACK_FREE; |
58 | } | 58 | } |
59 | 59 | ||
60 | static inline bool xa_zero_busy(const struct xarray *xa) | ||
61 | { | ||
62 | return xa->xa_flags & XA_FLAGS_ZERO_BUSY; | ||
63 | } | ||
64 | |||
60 | static inline void xa_mark_set(struct xarray *xa, xa_mark_t mark) | 65 | static inline void xa_mark_set(struct xarray *xa, xa_mark_t mark) |
61 | { | 66 | { |
62 | if (!(xa->xa_flags & XA_FLAGS_MARK(mark))) | 67 | if (!(xa->xa_flags & XA_FLAGS_MARK(mark))) |
@@ -432,6 +437,8 @@ static void xas_shrink(struct xa_state *xas) | |||
432 | break; | 437 | break; |
433 | if (!xa_is_node(entry) && node->shift) | 438 | if (!xa_is_node(entry) && node->shift) |
434 | break; | 439 | break; |
440 | if (xa_is_zero(entry) && xa_zero_busy(xa)) | ||
441 | entry = NULL; | ||
435 | xas->xa_node = XAS_BOUNDS; | 442 | xas->xa_node = XAS_BOUNDS; |
436 | 443 | ||
437 | RCU_INIT_POINTER(xa->xa_head, entry); | 444 | RCU_INIT_POINTER(xa->xa_head, entry); |
@@ -628,6 +635,8 @@ static void *xas_create(struct xa_state *xas, bool allow_root) | |||
628 | if (xas_top(node)) { | 635 | if (xas_top(node)) { |
629 | entry = xa_head_locked(xa); | 636 | entry = xa_head_locked(xa); |
630 | xas->xa_node = NULL; | 637 | xas->xa_node = NULL; |
638 | if (!entry && xa_zero_busy(xa)) | ||
639 | entry = XA_ZERO_ENTRY; | ||
631 | shift = xas_expand(xas, entry); | 640 | shift = xas_expand(xas, entry); |
632 | if (shift < 0) | 641 | if (shift < 0) |
633 | return NULL; | 642 | return NULL; |
@@ -1942,6 +1951,8 @@ void xa_destroy(struct xarray *xa) | |||
1942 | entry = xa_head_locked(xa); | 1951 | entry = xa_head_locked(xa); |
1943 | RCU_INIT_POINTER(xa->xa_head, NULL); | 1952 | RCU_INIT_POINTER(xa->xa_head, NULL); |
1944 | xas_init_marks(&xas); | 1953 | xas_init_marks(&xas); |
1954 | if (xa_zero_busy(xa)) | ||
1955 | xa_mark_clear(xa, XA_FREE_MARK); | ||
1945 | /* lockdep checks we're still holding the lock in xas_free_nodes() */ | 1956 | /* lockdep checks we're still holding the lock in xas_free_nodes() */ |
1946 | if (xa_is_node(entry)) | 1957 | if (xa_is_node(entry)) |
1947 | xas_free_nodes(&xas, xa_to_node(entry)); | 1958 | xas_free_nodes(&xas, xa_to_node(entry)); |