summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Wilcox <willy@infradead.org>2018-10-26 14:43:22 -0400
committerMatthew Wilcox <willy@infradead.org>2019-02-06 13:13:24 -0500
commit3ccaf57a6a63ad171a951dcaddffc453b2414c7b (patch)
treefc2202432a5b50ee5507a2e240b439b2993c2c3f
parentfd9dc93e36231fb6d520e0edd467058fad4fd12d (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.rst10
-rw-r--r--include/linux/xarray.h14
-rw-r--r--lib/test_xarray.c88
-rw-r--r--lib/xarray.c11
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
131initialise it by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`, 131initialise it by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`,
132the XArray changes to track whether entries are in use or not. 132the XArray changes to track whether entries are in use or not.
133 133
134You can call :c:func:`xa_alloc` to store the entry at any unused index 134You can call :c:func:`xa_alloc` to store the entry at an unused index
135in the XArray. If you need to modify the array from interrupt context, 135in the XArray. If you need to modify the array from interrupt context,
136you can use :c:func:`xa_alloc_bh` or :c:func:`xa_alloc_irq` to disable 136you can use :c:func:`xa_alloc_bh` or :c:func:`xa_alloc_irq` to disable
137interrupts while allocating the ID. 137interrupts while allocating the ID.
138 138
139Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert` 139Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert` will
140will mark the entry as being allocated. Unlike a normal XArray, storing 140also 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`.
142To free an entry, use :c:func:`xa_erase` (or :c:func:`xa_release` if 142To free an entry, use :c:func:`xa_erase` (or :c:func:`xa_release` if
143you only want to free the entry if it's ``NULL``). 143you only want to free the entry if it's ``NULL``).
144 144
145By default, the lowest free entry is allocated starting from 0. If you
146want to allocate entries starting at 1, it is more efficient to use
147:c:func:`DEFINE_XARRAY_ALLOC1` or ``XA_FLAGS_ALLOC1``.
148
145You cannot use ``XA_MARK_0`` with an allocating XArray as this mark 149You cannot use ``XA_MARK_0`` with an allocating XArray as this mark
146is used to track whether an entry is free or not. The other marks are 150is used to track whether an entry is free or not. The other marks are
147available for your use. 151available 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
290void *xa_load(struct xarray *, unsigned long index); 302void *xa_load(struct xarray *, unsigned long index);
291void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t); 303void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
292void *xa_erase(struct xarray *, unsigned long index); 304void *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
592static DEFINE_XARRAY_ALLOC(xa0); 592static noinline void check_xa_alloc_1(struct xarray *xa, unsigned int base)
593
594static 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
665static DEFINE_XARRAY_ALLOC(xa0);
666static DEFINE_XARRAY_ALLOC1(xa1);
667
668static 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
652static noinline void __check_store_iter(struct xarray *xa, unsigned long start, 674static 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
60static inline bool xa_zero_busy(const struct xarray *xa)
61{
62 return xa->xa_flags & XA_FLAGS_ZERO_BUSY;
63}
64
60static inline void xa_mark_set(struct xarray *xa, xa_mark_t mark) 65static 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));