From d19ec7d5021ec2a813ec9eed245f20bfade22e97 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 13 Nov 2017 16:16:09 +0100 Subject: [PATCH] BUG/MAJOR: ebtree/scope: fix insertion and removal of duplicates in scope-aware trees Commit ca30839 and following ("MINOR: ebtree: implement the scope-aware functions for eb32") improperly dealt with the scope in duplicate trees. The insertion was too lenient in that it would always mark the whole rightmost chain below the insertion point, and the removal could leave marks of non-existing scopes causing next()/first() to visit the wrong branch and return NULL. For insertion, we must only tag the nodes between the head of the dup tree and the insertion point which is the top of the lowest subtree. For removal, the new scope must be be calculated by oring the scopes of the two new branches and is irrelevant to the previous values. No backport is needed, this is purely 1.8-specific. --- ebtree/eb32sctree.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/ebtree/eb32sctree.c b/ebtree/eb32sctree.c index 4f48d58358..1386ebdd26 100644 --- a/ebtree/eb32sctree.c +++ b/ebtree/eb32sctree.c @@ -40,13 +40,23 @@ REGPRM1 struct eb32sc_node *eb32sc_insert_dup(struct eb_node *sub, struct eb_nod head = container_of(eb_untag(head->branches.b[EB_RGHT], EB_NODE), struct eb_node, branches); + + if (unlikely(head->bit > last->bit + 1)) { + /* there's a hole here, we must assign the top of the + * following sub-tree to and mark all intermediate + * nodes with the scope mask. + */ + do { + eb32 = container_of(sub, struct eb32sc_node, node); + if (!(eb32->node_s & scope)) + eb32->node_s |= scope; + + sub = container_of(eb_untag(sub->branches.b[EB_RGHT], EB_NODE), + struct eb_node, branches); + } while (sub != head); + } + eb32 = container_of(head, struct eb32sc_node, node); - - if (head->bit > last->bit + 1) - sub = head; /* there's a hole here */ - - if ((eb32->node_s | scope) != eb32->node_s) - eb32->node_s |= scope; } /* Here we have a leaf attached to (head)->b[EB_RGHT] */ @@ -423,6 +433,7 @@ void eb32sc_delete(struct eb32sc_node *eb32) unsigned int pside, gpside, sibtype; struct eb_node *parent; struct eb_root *gparent; + unsigned long scope; if (!node->leaf_p) return; @@ -486,7 +497,6 @@ void eb32sc_delete(struct eb32sc_node *eb32) parent->node_p = node->node_p; parent->branches = node->branches; parent->bit = node->bit; - container_of(parent, struct eb32sc_node, node)->node_s |= eb32->node_s; /* We must now update the new node's parent... */ gpside = eb_gettag(parent->node_p); @@ -494,15 +504,20 @@ void eb32sc_delete(struct eb32sc_node *eb32) gparent->b[gpside] = eb_dotag(&parent->branches, EB_NODE); /* ... and its branches */ + scope = 0; for (pside = 0; pside <= 1; pside++) { if (eb_gettag(parent->branches.b[pside]) == EB_NODE) { eb_root_to_node(eb_untag(parent->branches.b[pside], EB_NODE))->node_p = eb_dotag(&parent->branches, pside); + scope |= container_of(eb_untag(parent->branches.b[pside], EB_NODE), struct eb32sc_node, node.branches)->node_s; } else { eb_root_to_node(eb_untag(parent->branches.b[pside], EB_LEAF))->leaf_p = eb_dotag(&parent->branches, pside); + scope |= container_of(eb_untag(parent->branches.b[pside], EB_LEAF), struct eb32sc_node, node.branches)->leaf_s; } } + container_of(parent, struct eb32sc_node, node)->node_s = scope; + delete_unlink: /* Now the node has been completely unlinked */ node->leaf_p = NULL;