]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
XArray: Redesign xa_alloc API
authorMatthew Wilcox <willy@infradead.org>
Mon, 31 Dec 2018 15:41:01 +0000 (10:41 -0500)
committerMatthew Wilcox <willy@infradead.org>
Wed, 6 Feb 2019 18:32:23 +0000 (13:32 -0500)
It was too easy to forget to initialise the start index.  Add an
xa_limit data structure which can be used to pass min & max, and
define a couple of special values for common cases.  Also add some
more tests cribbed from the IDR test suite.  Change the return value
from -ENOSPC to -EBUSY to match xa_insert().

Signed-off-by: Matthew Wilcox <willy@infradead.org>
include/linux/xarray.h
lib/test_xarray.c
lib/xarray.c

index 99dd0838b4ba827dd64a88f17b952bb32a611df1..883bb958e4627c4e60bbb39e5d8f2af332a87a1b 100644 (file)
@@ -200,6 +200,27 @@ static inline int xa_err(void *entry)
        return 0;
 }
 
+/**
+ * struct xa_limit - Represents a range of IDs.
+ * @min: The lowest ID to allocate (inclusive).
+ * @max: The maximum ID to allocate (inclusive).
+ *
+ * This structure is used either directly or via the XA_LIMIT() macro
+ * to communicate the range of IDs that are valid for allocation.
+ * Two common ranges are predefined for you:
+ *  * xa_limit_32b     - [0 - UINT_MAX]
+ *  * xa_limit_31b     - [0 - INT_MAX]
+ */
+struct xa_limit {
+       u32 max;
+       u32 min;
+};
+
+#define XA_LIMIT(_min, _max) (struct xa_limit) { .min = _min, .max = _max }
+
+#define xa_limit_32b   XA_LIMIT(0, UINT_MAX)
+#define xa_limit_31b   XA_LIMIT(0, INT_MAX)
+
 typedef unsigned __bitwise xa_mark_t;
 #define XA_MARK_0              ((__force xa_mark_t)0U)
 #define XA_MARK_1              ((__force xa_mark_t)1U)
@@ -476,7 +497,8 @@ void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
 void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old,
                void *entry, gfp_t);
 int __xa_insert(struct xarray *, unsigned long index, void *entry, gfp_t);
-int __xa_alloc(struct xarray *, u32 *id, u32 max, void *entry, gfp_t);
+int __must_check __xa_alloc(struct xarray *, u32 *id, void *entry,
+               struct xa_limit, gfp_t);
 int __xa_reserve(struct xarray *, unsigned long index, gfp_t);
 void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t);
 void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t);
@@ -753,26 +775,26 @@ static inline int xa_insert_irq(struct xarray *xa, unsigned long index,
  * xa_alloc() - Find somewhere to store this entry in the XArray.
  * @xa: XArray.
  * @id: Pointer to ID.
- * @max: Maximum ID to allocate (inclusive).
  * @entry: New entry.
+ * @limit: Range of ID to allocate.
  * @gfp: Memory allocation flags.
  *
- * Allocates an unused ID in the range specified by @id and @max.
- * Updates the @id pointer with the index, then stores the entry at that
- * index.  A concurrent lookup will not see an uninitialised @id.
+ * Finds an empty entry in @xa between @limit.min and @limit.max,
+ * stores the index into the @id pointer, then stores the entry at
+ * that index.  A concurrent lookup will not see an uninitialised @id.
  *
- * Context: Process context.  Takes and releases the xa_lock.  May sleep if
+ * Context: Any context.  Takes and releases the xa_lock.  May sleep if
  * the @gfp flags permit.
- * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if
- * there is no more space in the XArray.
+ * Return: 0 on success, -ENOMEM if memory could not be allocated or
+ * -EBUSY if there are no free entries in @limit.
  */
-static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry,
-               gfp_t gfp)
+static inline __must_check int xa_alloc(struct xarray *xa, u32 *id,
+               void *entry, struct xa_limit limit, gfp_t gfp)
 {
        int err;
 
        xa_lock(xa);
-       err = __xa_alloc(xa, id, max, entry, gfp);
+       err = __xa_alloc(xa, id, entry, limit, gfp);
        xa_unlock(xa);
 
        return err;
@@ -782,26 +804,26 @@ static inline int xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry,
  * xa_alloc_bh() - Find somewhere to store this entry in the XArray.
  * @xa: XArray.
  * @id: Pointer to ID.
- * @max: Maximum ID to allocate (inclusive).
  * @entry: New entry.
+ * @limit: Range of ID to allocate.
  * @gfp: Memory allocation flags.
  *
- * Allocates an unused ID in the range specified by @id and @max.
- * Updates the @id pointer with the index, then stores the entry at that
- * index.  A concurrent lookup will not see an uninitialised @id.
+ * Finds an empty entry in @xa between @limit.min and @limit.max,
+ * stores the index into the @id pointer, then stores the entry at
+ * that index.  A concurrent lookup will not see an uninitialised @id.
  *
  * Context: Any context.  Takes and releases the xa_lock while
  * disabling softirqs.  May sleep if the @gfp flags permit.
- * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if
- * there is no more space in the XArray.
+ * Return: 0 on success, -ENOMEM if memory could not be allocated or
+ * -EBUSY if there are no free entries in @limit.
  */
-static inline int xa_alloc_bh(struct xarray *xa, u32 *id, u32 max, void *entry,
-               gfp_t gfp)
+static inline int __must_check xa_alloc_bh(struct xarray *xa, u32 *id,
+               void *entry, struct xa_limit limit, gfp_t gfp)
 {
        int err;
 
        xa_lock_bh(xa);
-       err = __xa_alloc(xa, id, max, entry, gfp);
+       err = __xa_alloc(xa, id, entry, limit, gfp);
        xa_unlock_bh(xa);
 
        return err;
@@ -811,26 +833,26 @@ static inline int xa_alloc_bh(struct xarray *xa, u32 *id, u32 max, void *entry,
  * xa_alloc_irq() - Find somewhere to store this entry in the XArray.
  * @xa: XArray.
  * @id: Pointer to ID.
- * @max: Maximum ID to allocate (inclusive).
  * @entry: New entry.
+ * @limit: Range of ID to allocate.
  * @gfp: Memory allocation flags.
  *
- * Allocates an unused ID in the range specified by @id and @max.
- * Updates the @id pointer with the index, then stores the entry at that
- * index.  A concurrent lookup will not see an uninitialised @id.
+ * Finds an empty entry in @xa between @limit.min and @limit.max,
+ * stores the index into the @id pointer, then stores the entry at
+ * that index.  A concurrent lookup will not see an uninitialised @id.
  *
  * Context: Process context.  Takes and releases the xa_lock while
  * disabling interrupts.  May sleep if the @gfp flags permit.
- * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if
- * there is no more space in the XArray.
+ * Return: 0 on success, -ENOMEM if memory could not be allocated or
+ * -EBUSY if there are no free entries in @limit.
  */
-static inline int xa_alloc_irq(struct xarray *xa, u32 *id, u32 max, void *entry,
-               gfp_t gfp)
+static inline int __must_check xa_alloc_irq(struct xarray *xa, u32 *id,
+               void *entry, struct xa_limit limit, gfp_t gfp)
 {
        int err;
 
        xa_lock_irq(xa);
-       err = __xa_alloc(xa, id, max, entry, gfp);
+       err = __xa_alloc(xa, id, entry, limit, gfp);
        xa_unlock_irq(xa);
 
        return err;
index cd74f8f32abe38837c312bf060dcaa765146c3e5..b5a6b981454d68d3e72ba305a821e191295ff287 100644 (file)
@@ -40,9 +40,9 @@ static void *xa_store_index(struct xarray *xa, unsigned long index, gfp_t gfp)
 
 static void xa_alloc_index(struct xarray *xa, unsigned long index, gfp_t gfp)
 {
-       u32 id = 0;
+       u32 id;
 
-       XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(index),
+       XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(index), xa_limit_32b,
                                gfp) != 0);
        XA_BUG_ON(xa, id != index);
 }
@@ -640,28 +640,81 @@ static noinline void check_xa_alloc_1(struct xarray *xa, unsigned int base)
        xa_destroy(xa);
 
        /* Check that we fail properly at the limit of allocation */
-       id = 0xfffffffeU;
-       XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id),
+       XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(UINT_MAX - 1),
+                               XA_LIMIT(UINT_MAX - 1, UINT_MAX),
                                GFP_KERNEL) != 0);
        XA_BUG_ON(xa, id != 0xfffffffeU);
-       XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id),
+       XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(UINT_MAX),
+                               XA_LIMIT(UINT_MAX - 1, UINT_MAX),
                                GFP_KERNEL) != 0);
        XA_BUG_ON(xa, id != 0xffffffffU);
-       XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, xa_mk_index(id),
-                               GFP_KERNEL) != -ENOSPC);
-       XA_BUG_ON(xa, id != 0xffffffffU);
+       id = 3;
+       XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(0),
+                               XA_LIMIT(UINT_MAX - 1, UINT_MAX),
+                               GFP_KERNEL) != -EBUSY);
+       XA_BUG_ON(xa, id != 3);
        xa_destroy(xa);
 
-       id = 10;
-       XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id),
-                               GFP_KERNEL) != -ENOSPC);
+       XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(10), XA_LIMIT(10, 5),
+                               GFP_KERNEL) != -EBUSY);
        XA_BUG_ON(xa, xa_store_index(xa, 3, GFP_KERNEL) != 0);
-       XA_BUG_ON(xa, xa_alloc(xa, &id, 5, xa_mk_index(id),
-                               GFP_KERNEL) != -ENOSPC);
+       XA_BUG_ON(xa, xa_alloc(xa, &id, xa_mk_index(10), XA_LIMIT(10, 5),
+                               GFP_KERNEL) != -EBUSY);
        xa_erase_index(xa, 3);
        XA_BUG_ON(xa, !xa_empty(xa));
 }
 
+static noinline void check_xa_alloc_2(struct xarray *xa, unsigned int base)
+{
+       unsigned int i, id;
+       unsigned long index;
+       void *entry;
+
+       /* Allocate and free a NULL and check xa_empty() behaves */
+       XA_BUG_ON(xa, !xa_empty(xa));
+       XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0);
+       XA_BUG_ON(xa, id != base);
+       XA_BUG_ON(xa, xa_empty(xa));
+       XA_BUG_ON(xa, xa_erase(xa, id) != NULL);
+       XA_BUG_ON(xa, !xa_empty(xa));
+
+       /* Ditto, but check destroy instead of erase */
+       XA_BUG_ON(xa, !xa_empty(xa));
+       XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0);
+       XA_BUG_ON(xa, id != base);
+       XA_BUG_ON(xa, xa_empty(xa));
+       xa_destroy(xa);
+       XA_BUG_ON(xa, !xa_empty(xa));
+
+       for (i = base; i < base + 10; i++) {
+               XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b,
+                                       GFP_KERNEL) != 0);
+               XA_BUG_ON(xa, id != i);
+       }
+
+       XA_BUG_ON(xa, xa_store(xa, 3, xa_mk_index(3), GFP_KERNEL) != NULL);
+       XA_BUG_ON(xa, xa_store(xa, 4, xa_mk_index(4), GFP_KERNEL) != NULL);
+       XA_BUG_ON(xa, xa_store(xa, 4, NULL, GFP_KERNEL) != xa_mk_index(4));
+       XA_BUG_ON(xa, xa_erase(xa, 5) != NULL);
+       XA_BUG_ON(xa, xa_alloc(xa, &id, NULL, xa_limit_32b, GFP_KERNEL) != 0);
+       XA_BUG_ON(xa, id != 5);
+
+       xa_for_each(xa, index, entry) {
+               xa_erase_index(xa, index);
+       }
+
+       for (i = base; i < base + 9; i++) {
+               XA_BUG_ON(xa, xa_erase(xa, i) != NULL);
+               XA_BUG_ON(xa, xa_empty(xa));
+       }
+       XA_BUG_ON(xa, xa_erase(xa, 8) != NULL);
+       XA_BUG_ON(xa, xa_empty(xa));
+       XA_BUG_ON(xa, xa_erase(xa, base + 9) != NULL);
+       XA_BUG_ON(xa, !xa_empty(xa));
+
+       xa_destroy(xa);
+}
+
 static DEFINE_XARRAY_ALLOC(xa0);
 static DEFINE_XARRAY_ALLOC1(xa1);
 
@@ -669,6 +722,8 @@ static noinline void check_xa_alloc(void)
 {
        check_xa_alloc_1(&xa0, 0);
        check_xa_alloc_1(&xa1, 1);
+       check_xa_alloc_2(&xa0, 0);
+       check_xa_alloc_2(&xa1, 1);
 }
 
 static noinline void __check_store_iter(struct xarray *xa, unsigned long start,
@@ -1219,9 +1274,8 @@ static void check_align_1(struct xarray *xa, char *name)
        void *entry;
 
        for (i = 0; i < 8; i++) {
-               id = 0;
-               XA_BUG_ON(xa, xa_alloc(xa, &id, UINT_MAX, name + i, GFP_KERNEL)
-                               != 0);
+               XA_BUG_ON(xa, xa_alloc(xa, &id, name + i, xa_limit_32b,
+                                       GFP_KERNEL) != 0);
                XA_BUG_ON(xa, id != i);
        }
        xa_for_each(xa, index, entry)
index 468fb7b7963fed5b0b7b3473043ebba003c57f75..c707388fb05ee44278ad471b2b3ec74553ccbd31 100644 (file)
@@ -1615,23 +1615,23 @@ EXPORT_SYMBOL(xa_store_range);
  * __xa_alloc() - Find somewhere to store this entry in the XArray.
  * @xa: XArray.
  * @id: Pointer to ID.
- * @max: Maximum ID to allocate (inclusive).
+ * @limit: Range for allocated ID.
  * @entry: New entry.
  * @gfp: Memory allocation flags.
  *
- * Allocates an unused ID in the range specified by @id and @max.
- * Updates the @id pointer with the index, then stores the entry at that
- * index.  A concurrent lookup will not see an uninitialised @id.
+ * Finds an empty entry in @xa between @limit.min and @limit.max,
+ * stores the index into the @id pointer, then stores the entry at
+ * that index.  A concurrent lookup will not see an uninitialised @id.
  *
  * Context: Any context.  Expects xa_lock to be held on entry.  May
  * release and reacquire xa_lock if @gfp flags permit.
- * Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if
- * there is no more space in the XArray.
+ * Return: 0 on success, -ENOMEM if memory could not be allocated or
+ * -EBUSY if there are no free entries in @limit.
  */
-int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp)
+int __xa_alloc(struct xarray *xa, u32 *id, void *entry,
+               struct xa_limit limit, gfp_t gfp)
 {
        XA_STATE(xas, xa, 0);
-       int err;
 
        if (WARN_ON_ONCE(xa_is_advanced(entry)))
                return -EINVAL;
@@ -1642,18 +1642,17 @@ int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp)
                entry = XA_ZERO_ENTRY;
 
        do {
-               xas.xa_index = *id;
-               xas_find_marked(&xas, max, XA_FREE_MARK);
+               xas.xa_index = limit.min;
+               xas_find_marked(&xas, limit.max, XA_FREE_MARK);
                if (xas.xa_node == XAS_RESTART)
-                       xas_set_err(&xas, -ENOSPC);
+                       xas_set_err(&xas, -EBUSY);
+               else
+                       *id = xas.xa_index;
                xas_store(&xas, entry);
                xas_clear_mark(&xas, XA_FREE_MARK);
        } while (__xas_nomem(&xas, gfp));
 
-       err = xas_error(&xas);
-       if (!err)
-               *id = xas.xa_index;
-       return err;
+       return xas_error(&xas);
 }
 EXPORT_SYMBOL(__xa_alloc);