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.
This commit is contained in:
Willy Tarreau 2017-11-13 16:16:09 +01:00
parent fa5c812a6b
commit d19ec7d502

View File

@ -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 <sub> 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;