]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
xarray: Add xas_for_each_conflict
authorMatthew Wilcox <willy@infradead.org>
Sat, 2 Jun 2018 02:46:02 +0000 (22:46 -0400)
committerMatthew Wilcox <willy@infradead.org>
Sun, 21 Oct 2018 14:45:59 +0000 (10:45 -0400)
This iterator iterates over each entry that is stored in the index or
indices specified by the xa_state.  This is intended for use for a
conditional store of a multiindex entry, or to allow entries which are
about to be removed from the xarray to be disposed of properly.

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

index 381f94a667625bf3d29e066a817b17984a425004..2664e718dbd318e6da892a35949284697d0302ee 100644 (file)
@@ -878,6 +878,7 @@ static inline bool xas_retry(struct xa_state *xas, const void *entry)
 void *xas_load(struct xa_state *);
 void *xas_store(struct xa_state *, void *entry);
 void *xas_find(struct xa_state *, unsigned long max);
+void *xas_find_conflict(struct xa_state *);
 
 bool xas_get_mark(const struct xa_state *, xa_mark_t);
 void xas_set_mark(const struct xa_state *, xa_mark_t);
@@ -1088,6 +1089,22 @@ enum {
        for (entry = xas_find_marked(xas, max, mark); entry; \
             entry = xas_next_marked(xas, max, mark))
 
+/**
+ * xas_for_each_conflict() - Iterate over a range of an XArray.
+ * @xas: XArray operation state.
+ * @entry: Entry retrieved from the array.
+ *
+ * The loop body will be executed for each entry in the XArray that lies
+ * within the range specified by @xas.  If the loop completes successfully,
+ * any entries that lie in this range will be replaced by @entry.  The caller
+ * may break out of the loop; if they do so, the contents of the XArray will
+ * be unchanged.  The operation may fail due to an out of memory condition.
+ * The caller may also call xa_set_err() to exit the loop while setting an
+ * error to record the reason.
+ */
+#define xas_for_each_conflict(xas, entry) \
+       while ((entry = xas_find_conflict(xas)))
+
 void *__xas_next(struct xa_state *);
 void *__xas_prev(struct xa_state *);
 
index 85ef1882dd3ce8295542e36a930aaa983c5fb08a..8eba3de1baeae89b41b9b7f781bce34f843cf006 100644 (file)
@@ -365,6 +365,73 @@ static noinline void check_multi_store(struct xarray *xa)
 #endif
 }
 
+static noinline void __check_store_iter(struct xarray *xa, unsigned long start,
+                       unsigned int order, unsigned int present)
+{
+       XA_STATE_ORDER(xas, xa, start, order);
+       void *entry;
+       unsigned int count = 0;
+
+retry:
+       xas_lock(&xas);
+       xas_for_each_conflict(&xas, entry) {
+               XA_BUG_ON(xa, !xa_is_value(entry));
+               XA_BUG_ON(xa, entry < xa_mk_value(start));
+               XA_BUG_ON(xa, entry > xa_mk_value(start + (1UL << order) - 1));
+               count++;
+       }
+       xas_store(&xas, xa_mk_value(start));
+       xas_unlock(&xas);
+       if (xas_nomem(&xas, GFP_KERNEL)) {
+               count = 0;
+               goto retry;
+       }
+       XA_BUG_ON(xa, xas_error(&xas));
+       XA_BUG_ON(xa, count != present);
+       XA_BUG_ON(xa, xa_load(xa, start) != xa_mk_value(start));
+       XA_BUG_ON(xa, xa_load(xa, start + (1UL << order) - 1) !=
+                       xa_mk_value(start));
+       xa_erase_index(xa, start);
+}
+
+static noinline void check_store_iter(struct xarray *xa)
+{
+       unsigned int i, j;
+       unsigned int max_order = IS_ENABLED(CONFIG_XARRAY_MULTI) ? 20 : 1;
+
+       for (i = 0; i < max_order; i++) {
+               unsigned int min = 1 << i;
+               unsigned int max = (2 << i) - 1;
+               __check_store_iter(xa, 0, i, 0);
+               XA_BUG_ON(xa, !xa_empty(xa));
+               __check_store_iter(xa, min, i, 0);
+               XA_BUG_ON(xa, !xa_empty(xa));
+
+               xa_store_index(xa, min, GFP_KERNEL);
+               __check_store_iter(xa, min, i, 1);
+               XA_BUG_ON(xa, !xa_empty(xa));
+               xa_store_index(xa, max, GFP_KERNEL);
+               __check_store_iter(xa, min, i, 1);
+               XA_BUG_ON(xa, !xa_empty(xa));
+
+               for (j = 0; j < min; j++)
+                       xa_store_index(xa, j, GFP_KERNEL);
+               __check_store_iter(xa, 0, i, min);
+               XA_BUG_ON(xa, !xa_empty(xa));
+               for (j = 0; j < min; j++)
+                       xa_store_index(xa, min + j, GFP_KERNEL);
+               __check_store_iter(xa, min, i, min);
+               XA_BUG_ON(xa, !xa_empty(xa));
+       }
+#ifdef CONFIG_XARRAY_MULTI
+       xa_store_index(xa, 63, GFP_KERNEL);
+       xa_store_index(xa, 65, GFP_KERNEL);
+       __check_store_iter(xa, 64, 2, 1);
+       xa_erase_index(xa, 63);
+#endif
+       XA_BUG_ON(xa, !xa_empty(xa));
+}
+
 static noinline void check_multi_find(struct xarray *xa)
 {
 #ifdef CONFIG_XARRAY_MULTI
@@ -627,6 +694,7 @@ static int xarray_checks(void)
        check_find(&array);
        check_destroy(&array);
        check_move(&array);
+       check_store_iter(&array);
 
        printk("XArray: %u of %u tests passed\n", tests_passed, tests_run);
        return (tests_run == tests_passed) ? 0 : -EINVAL;
index 303c46579598d7719d98f4333c294d58340a3947..41f8ebc651f5d54a1acb31887906cbd34270b629 100644 (file)
@@ -1110,6 +1110,67 @@ void *xas_find_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark)
 }
 EXPORT_SYMBOL_GPL(xas_find_marked);
 
+/**
+ * xas_find_conflict() - Find the next present entry in a range.
+ * @xas: XArray operation state.
+ *
+ * The @xas describes both a range and a position within that range.
+ *
+ * Context: Any context.  Expects xa_lock to be held.
+ * Return: The next entry in the range covered by @xas or %NULL.
+ */
+void *xas_find_conflict(struct xa_state *xas)
+{
+       void *curr;
+
+       if (xas_error(xas))
+               return NULL;
+
+       if (!xas->xa_node)
+               return NULL;
+
+       if (xas_top(xas->xa_node)) {
+               curr = xas_start(xas);
+               if (!curr)
+                       return NULL;
+               while (xa_is_node(curr)) {
+                       struct xa_node *node = xa_to_node(curr);
+                       curr = xas_descend(xas, node);
+               }
+               if (curr)
+                       return curr;
+       }
+
+       if (xas->xa_node->shift > xas->xa_shift)
+               return NULL;
+
+       for (;;) {
+               if (xas->xa_node->shift == xas->xa_shift) {
+                       if ((xas->xa_offset & xas->xa_sibs) == xas->xa_sibs)
+                               break;
+               } else if (xas->xa_offset == XA_CHUNK_MASK) {
+                       xas->xa_offset = xas->xa_node->offset;
+                       xas->xa_node = xa_parent_locked(xas->xa, xas->xa_node);
+                       if (!xas->xa_node)
+                               break;
+                       continue;
+               }
+               curr = xa_entry_locked(xas->xa, xas->xa_node, ++xas->xa_offset);
+               if (xa_is_sibling(curr))
+                       continue;
+               while (xa_is_node(curr)) {
+                       xas->xa_node = xa_to_node(curr);
+                       xas->xa_offset = 0;
+                       curr = xa_entry_locked(xas->xa, xas->xa_node, 0);
+               }
+               if (curr)
+                       return curr;
+       }
+       xas->xa_offset -= xas->xa_sibs;
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(xas_find_conflict);
+
 /**
  * xa_init_flags() - Initialise an empty XArray with flags.
  * @xa: XArray.