]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
maple_tree: Fix crash on spanning rebalance all the way to root.
authorLiam R. Howlett <Liam.Howlett@Oracle.com>
Tue, 4 Aug 2020 19:40:32 +0000 (15:40 -0400)
committerLiam R. Howlett <Liam.Howlett@Oracle.com>
Fri, 30 Oct 2020 19:06:34 +0000 (15:06 -0400)
It is possible to have a spanning rebalance that causes 3 new nodes per
level and a new level added in a new root node.  This means that
allocations need to be 3 * height + 1, and this situation needs to be
detected so that dead root isn't attempted to ascend looking for more
data to merge in.

Signed-off-by: Liam R. Howlett <Liam.Howlett@Oracle.com>
lib/maple_tree.c
lib/test_maple_tree.c

index 742c5454d2b3618ae6201fee3133d561909cff57..ee319fe06c5c3ada3dfc4e16e5e1c0822cad4448 100644 (file)
@@ -255,6 +255,11 @@ static inline bool mte_is_root(const struct maple_enode *node)
        return ma_is_root(mte_to_node(node));
 }
 
+static inline bool mas_is_root_limits(const struct ma_state *mas)
+{
+       return !mas->min && mas->max == ULONG_MAX;
+}
+
 static inline bool mt_is_alloc(struct maple_tree *mt)
 {
        return (mt->ma_flags & MAPLE_ALLOC_RANGE);
@@ -2121,7 +2126,7 @@ static inline void mas_wmb_replace(struct ma_state *mas,
 static inline bool mast_new_root(struct maple_subtree_state *mast,
                                 struct ma_state *mas)
 {
-       if (mast->l->min || mast->l->max != ULONG_MAX)
+       if (!mas_is_root_limits(mast->l))
                return false;
 
        mas_mn(mast->l)->parent =
@@ -2294,6 +2299,9 @@ static inline int mas_spanning_rebalance(struct ma_state *mas,
                if (mast_sufficient(mast))
                        continue;
 
+               if (mas_is_root_limits(mast->orig_l)) // new root without a node.
+                       break;
+
                // Try to get enough data for the next iteration.
                if (!mast_rebalance_from_siblings(mast))
                        if (!mast_rebalance_from_cousins(mast))
@@ -2312,14 +2320,16 @@ static inline int mas_spanning_rebalance(struct ma_state *mas,
        if (right)
                mte_set_parent(right, l_mas.node, ++slot);
 
-       if (!mast->bn->b_end) {
+       if (mas_is_root_limits(mast->orig_l)) {
                mas_mn(&l_mas)->parent =
                    ma_parent_ptr(((unsigned long)mas->tree | MA_ROOT_PARENT));
        } else {
                mas_mn(&l_mas)->parent = mas_mn(mast->orig_l)->parent;
        }
 
-       mat_add(&free, mast->orig_l->node);
+       if (!mte_dead_node(mast->orig_l->node))
+               mat_add(&free, mast->orig_l->node);
+
        mas_dup_state(mast->orig_l, &l_mas);
        mas->depth = mast->orig_l->depth;
 
@@ -2882,7 +2892,7 @@ static inline int mas_spanning_store(struct ma_state *mas, void *entry)
        /* Node rebalancing may occur due to this store, so there may be two new
         * entries per level plus a new root.
         */
-       node_cnt += 1 + mas->tree->ma_height * 2;
+       node_cnt += 1 + mas->tree->ma_height * 3;
        mas_node_cnt(mas, node_cnt);
        if (mas_is_err(mas))
                return 0;
@@ -2891,17 +2901,6 @@ static inline int mas_spanning_store(struct ma_state *mas, void *entry)
        mast.orig_l = &l_mas;
        mast.orig_r = &r_mas;
 
-       // FIXME: Is this needed?
-#if 0
-       mas_dup_state(&l_mas, mas);
-       mas->last = mas->index;
-       mas_node_walk(mas, mte_node_type(mas->node), &range_min, &range_max);
-       mas->index = mas->last = l_mas.last;
-       mas_node_walk(mas, mte_node_type(mas->node), &range_min, &range_max);
-       mas_dup_state(mas, &l_mas);
-#endif
-
-
        // Set up right side.
        mas_dup_state(&r_mas, mas);
        r_mas.depth = mas->depth;
index e81c8d585ba4fa9a12addb13abcc804830288b1a..c8cb13d29f524eaf89840d77c12b27cacf62f3f0 100644 (file)
@@ -31503,6 +31503,14 @@ static noinline void check_ranges(struct maple_tree *mt)
                118, 128,
                        };
 
+       unsigned long overflow[] = {
+               1317,
+               1321,
+               1351,
+               1352,
+               1365,
+       };
+
        MT_BUG_ON(mt, !mtree_empty(mt));
        check_insert_range(mt, r[0], r[1], xa_mk_value(r[0]), 0);
        check_insert_range(mt, r[2], r[3], xa_mk_value(r[2]), 0);
@@ -31728,6 +31736,81 @@ static noinline void check_ranges(struct maple_tree *mt)
        check_store_range(mt, 1792, 1799, NULL, 0);
        mt_validate(mt);
        mtree_destroy(mt);
+
+       mtree_init(mt, MAPLE_ALLOC_RANGE);
+       for (i = 0; i <= 200; i++) {
+               val = i*10;
+               val2 = (i+1)*10;
+               check_store_range(mt, val, val2, xa_mk_value(val), 0);
+       }
+
+       for (i = 10; i <= 19; i++) {
+               val = i*100 + 5;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val++;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val += 10;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+
+               val += 39;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val++;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val += 10;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+       }
+
+       for (i = 13; i <= 14; i++) {
+               val = i*100 + 75;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val++;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val += 9;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val++;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val += 9;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val++;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+       }
+       for (i = 0; i <= 3; i++) {
+               val = 1200 + i*10;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val++;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+       }
+       for (i = 0; i <= 5; i++) {
+               val = 1270 + i;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val+= 10;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+       }
+       for (i = 0; i <= 6; i++) {
+               val = 1400 + i*10;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val++;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+       }
+
+       for (i = 0; i <= 5; i++) {
+               val = 1370 + i;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val+= 10;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+               val+= 10;
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+       }
+       for (i = 0; i < ARRAY_SIZE(overflow); i++) {
+               val = overflow[i];
+               check_store_range(mt, val, val, xa_mk_value(val), 0);
+       }
+
+
+       //  Cause a 3 child split all the way up the tree.
+       check_store_range(mt, 1349, 1350, NULL, 0);
+       mt_validate(mt);
+       mtree_destroy(mt);
 }
 
 static noinline void check_next_entry(struct maple_tree *mt)