]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - tools/testing/radix-tree/multiorder.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[mirror_ubuntu-zesty-kernel.git] / tools / testing / radix-tree / multiorder.c
index fa6effe997a3da99daa9c529156f1d50ae6f8195..f79812a5e0708b9146c2dcb479d804637262b1b8 100644 (file)
@@ -75,8 +75,27 @@ static void __multiorder_tag_test(int index, int order)
        item_kill_tree(&tree);
 }
 
+static void __multiorder_tag_test2(unsigned order, unsigned long index2)
+{
+       RADIX_TREE(tree, GFP_KERNEL);
+       unsigned long index = (1 << order);
+       index2 += index;
+
+       assert(item_insert_order(&tree, 0, order) == 0);
+       assert(item_insert(&tree, index2) == 0);
+
+       assert(radix_tree_tag_set(&tree, 0, 0));
+       assert(radix_tree_tag_set(&tree, index2, 0));
+
+       assert(tag_tagged_items(&tree, NULL, 0, ~0UL, 10, 0, 1) == 2);
+
+       item_kill_tree(&tree);
+}
+
 static void multiorder_tag_tests(void)
 {
+       int i, j;
+
        /* test multi-order entry for indices 0-7 with no sibling pointers */
        __multiorder_tag_test(0, 3);
        __multiorder_tag_test(5, 3);
@@ -116,6 +135,10 @@ static void multiorder_tag_tests(void)
        __multiorder_tag_test(300, 8);
 
        __multiorder_tag_test(0x12345678UL, 8);
+
+       for (i = 1; i < 10; i++)
+               for (j = 0; j < (10 << i); j++)
+                       __multiorder_tag_test2(i, j);
 }
 
 static void multiorder_check(unsigned long index, int order)
@@ -332,7 +355,7 @@ void multiorder_tagged_iteration(void)
        item_kill_tree(&tree);
 }
 
-static void __multiorder_join(unsigned long index,
+static void multiorder_join1(unsigned long index,
                                unsigned order1, unsigned order2)
 {
        unsigned long loc;
@@ -350,7 +373,7 @@ static void __multiorder_join(unsigned long index,
        item_kill_tree(&tree);
 }
 
-static void __multiorder_join2(unsigned order1, unsigned order2)
+static void multiorder_join2(unsigned order1, unsigned order2)
 {
        RADIX_TREE(tree, GFP_KERNEL);
        struct radix_tree_node *node;
@@ -370,6 +393,39 @@ static void __multiorder_join2(unsigned order1, unsigned order2)
        item_kill_tree(&tree);
 }
 
+/*
+ * This test revealed an accounting bug for exceptional entries at one point.
+ * Nodes were being freed back into the pool with an elevated exception count
+ * by radix_tree_join() and then radix_tree_split() was failing to zero the
+ * count of exceptional entries.
+ */
+static void multiorder_join3(unsigned int order)
+{
+       RADIX_TREE(tree, GFP_KERNEL);
+       struct radix_tree_node *node;
+       void **slot;
+       struct radix_tree_iter iter;
+       unsigned long i;
+
+       for (i = 0; i < (1 << order); i++) {
+               radix_tree_insert(&tree, i, (void *)0x12UL);
+       }
+
+       radix_tree_join(&tree, 0, order, (void *)0x16UL);
+       rcu_barrier();
+
+       radix_tree_split(&tree, 0, 0);
+
+       radix_tree_for_each_slot(slot, &tree, &iter, 0) {
+               radix_tree_iter_replace(&tree, &iter, slot, (void *)0x12UL);
+       }
+
+       __radix_tree_lookup(&tree, 0, &node, NULL);
+       assert(node->exceptional == node->count);
+
+       item_kill_tree(&tree);
+}
+
 static void multiorder_join(void)
 {
        int i, j, idx;
@@ -377,35 +433,74 @@ static void multiorder_join(void)
        for (idx = 0; idx < 1024; idx = idx * 2 + 3) {
                for (i = 1; i < 15; i++) {
                        for (j = 0; j < i; j++) {
-                               __multiorder_join(idx, i, j);
+                               multiorder_join1(idx, i, j);
                        }
                }
        }
 
        for (i = 1; i < 15; i++) {
                for (j = 0; j < i; j++) {
-                       __multiorder_join2(i, j);
+                       multiorder_join2(i, j);
                }
        }
+
+       for (i = 3; i < 10; i++) {
+               multiorder_join3(i);
+       }
+}
+
+static void check_mem(unsigned old_order, unsigned new_order, unsigned alloc)
+{
+       struct radix_tree_preload *rtp = &radix_tree_preloads;
+       if (rtp->nr != 0)
+               printf("split(%u %u) remaining %u\n", old_order, new_order,
+                                                       rtp->nr);
+       /*
+        * Can't check for equality here as some nodes may have been
+        * RCU-freed while we ran.  But we should never finish with more
+        * nodes allocated since they should have all been preloaded.
+        */
+       if (nr_allocated > alloc)
+               printf("split(%u %u) allocated %u %u\n", old_order, new_order,
+                                                       alloc, nr_allocated);
 }
 
 static void __multiorder_split(int old_order, int new_order)
 {
-       RADIX_TREE(tree, GFP_KERNEL);
+       RADIX_TREE(tree, GFP_ATOMIC);
        void **slot;
        struct radix_tree_iter iter;
-       struct radix_tree_node *node;
-       void *item;
+       unsigned alloc;
+
+       radix_tree_preload(GFP_KERNEL);
+       assert(item_insert_order(&tree, 0, old_order) == 0);
+       radix_tree_preload_end();
+
+       /* Wipe out the preloaded cache or it'll confuse check_mem() */
+       radix_tree_cpu_dead(0);
 
-       item_insert_order(&tree, 0, old_order);
        radix_tree_tag_set(&tree, 0, 2);
+
+       radix_tree_split_preload(old_order, new_order, GFP_KERNEL);
+       alloc = nr_allocated;
        radix_tree_split(&tree, 0, new_order);
+       check_mem(old_order, new_order, alloc);
        radix_tree_for_each_slot(slot, &tree, &iter, 0) {
                radix_tree_iter_replace(&tree, &iter, slot,
                                        item_create(iter.index, new_order));
        }
+       radix_tree_preload_end();
 
        item_kill_tree(&tree);
+}
+
+static void __multiorder_split2(int old_order, int new_order)
+{
+       RADIX_TREE(tree, GFP_KERNEL);
+       void **slot;
+       struct radix_tree_iter iter;
+       struct radix_tree_node *node;
+       void *item;
 
        __radix_tree_insert(&tree, 0, old_order, (void *)0x12);
 
@@ -424,6 +519,15 @@ static void __multiorder_split(int old_order, int new_order)
        assert(node->exceptional == 0);
 
        item_kill_tree(&tree);
+}
+
+static void __multiorder_split3(int old_order, int new_order)
+{
+       RADIX_TREE(tree, GFP_KERNEL);
+       void **slot;
+       struct radix_tree_iter iter;
+       struct radix_tree_node *node;
+       void *item;
 
        __radix_tree_insert(&tree, 0, old_order, (void *)0x12);
 
@@ -441,15 +545,69 @@ static void __multiorder_split(int old_order, int new_order)
        assert(node->exceptional > 0);
 
        item_kill_tree(&tree);
+
+       __radix_tree_insert(&tree, 0, old_order, (void *)0x12);
+
+       item = __radix_tree_lookup(&tree, 0, &node, NULL);
+       assert(item == (void *)0x12);
+       assert(node->exceptional > 0);
+
+       radix_tree_split(&tree, 0, new_order);
+       radix_tree_for_each_slot(slot, &tree, &iter, 0) {
+               if (iter.index == (1 << new_order))
+                       radix_tree_iter_replace(&tree, &iter, slot,
+                                               (void *)0x16);
+               else
+                       radix_tree_iter_replace(&tree, &iter, slot, NULL);
+       }
+
+       item = __radix_tree_lookup(&tree, 1 << new_order, &node, NULL);
+       assert(item == (void *)0x16);
+       assert(node->count == node->exceptional);
+       do {
+               node = node->parent;
+               if (!node)
+                       break;
+               assert(node->count == 1);
+               assert(node->exceptional == 0);
+       } while (1);
+
+       item_kill_tree(&tree);
 }
 
 static void multiorder_split(void)
 {
        int i, j;
 
-       for (i = 9; i < 19; i++)
-               for (j = 0; j < i; j++)
+       for (i = 3; i < 11; i++)
+               for (j = 0; j < i; j++) {
                        __multiorder_split(i, j);
+                       __multiorder_split2(i, j);
+                       __multiorder_split3(i, j);
+               }
+}
+
+static void multiorder_account(void)
+{
+       RADIX_TREE(tree, GFP_KERNEL);
+       struct radix_tree_node *node;
+       void **slot;
+
+       item_insert_order(&tree, 0, 5);
+
+       __radix_tree_insert(&tree, 1 << 5, 5, (void *)0x12);
+       __radix_tree_lookup(&tree, 0, &node, NULL);
+       assert(node->count == node->exceptional * 2);
+       radix_tree_delete(&tree, 1 << 5);
+       assert(node->exceptional == 0);
+
+       __radix_tree_insert(&tree, 1 << 5, 5, (void *)0x12);
+       __radix_tree_lookup(&tree, 1 << 5, &node, &slot);
+       assert(node->count == node->exceptional * 2);
+       __radix_tree_replace(&tree, node, slot, NULL, NULL, NULL);
+       assert(node->exceptional == 0);
+
+       item_kill_tree(&tree);
 }
 
 void multiorder_checks(void)
@@ -471,4 +629,7 @@ void multiorder_checks(void)
        multiorder_tagged_iteration();
        multiorder_join();
        multiorder_split();
+       multiorder_account();
+
+       radix_tree_cpu_dead(0);
 }