/* Determine the longest prefix of @node that matches @key. *Ifit'sthemaximumpossibleprefixforthistrie,wehave *anexactmatchandcanreturnitdirectly.
*/
matchlen = __longest_prefix_match(trie, node, key); if (matchlen == trie->max_prefixlen) {
found = node; break;
}
/* If the number of bits that match is smaller than the prefix *lengthof@node,bailoutandreturnthenodewehaveseen *lastinthetraversal(ie,theparent).
*/ if (matchlen < node->prefixlen) break;
/* Consider this node as return candidate unless it is an *artificiallyaddedintermediateone.
*/ if (!(node->flags & LPM_TREE_NODE_FLAG_IM))
found = node;
/* If the node match is fully satisfied, let's see if we can *becomemorespecific.Determinethenextbitinthekeyand *traversedown.
*/
next_bit = extract_bit(key->data, node->prefixlen);
node = rcu_dereference_check(node->child[next_bit],
rcu_read_lock_bh_held());
}
/* Now find a slot to attach the new node. To do that, walk the tree *fromtherootandmatchasmanybitsaspossibleforeachnodeuntil *weeitherfindanemptyslotoraslotthatneedstobereplacedby *anintermediatenode.
*/
slot = &trie->root;
while ((node = rcu_dereference(*slot))) {
matchlen = longest_prefix_match(trie, node, key);
if (node->prefixlen != matchlen ||
node->prefixlen == key->prefixlen) break;
/* If the slot is empty (a free child pointer or an empty root), *simplyassignthe@new_nodetothatslotandbedone.
*/ if (!node) {
ret = trie_check_add_elem(trie, flags); if (ret) goto out;
rcu_assign_pointer(*slot, new_node); goto out;
}
/* If the slot we picked already exists, replace it with @new_node *whichalreadyhasthecorrectdataarrayset.
*/ if (node->prefixlen == matchlen) { if (!(node->flags & LPM_TREE_NODE_FLAG_IM)) { if (flags == BPF_NOEXIST) {
ret = -EEXIST; goto out;
}
} else {
ret = trie_check_add_elem(trie, flags); if (ret) goto out;
}
ret = trie_check_add_elem(trie, flags); if (ret) goto out;
/* If the new node matches the prefix completely, it must be inserted *asanancestor.Simplyinsertitbetween@nodeand*@slot.
*/ if (matchlen == key->prefixlen) {
next_bit = extract_bit(node->data, matchlen);
rcu_assign_pointer(new_node->child[next_bit], node);
rcu_assign_pointer(*slot, new_node); goto out;
}
im_node = lpm_trie_node_alloc(trie, NULL); if (!im_node) {
trie->n_entries--;
ret = -ENOMEM; goto out;
}
/* Now determine which child to install in which slot */ if (extract_bit(key->data, matchlen)) {
rcu_assign_pointer(im_node->child[0], node);
rcu_assign_pointer(im_node->child[1], new_node);
} else {
rcu_assign_pointer(im_node->child[0], new_node);
rcu_assign_pointer(im_node->child[1], node);
}
/* Finally, assign the intermediate node to the determined slot */
rcu_assign_pointer(*slot, im_node);
out:
raw_res_spin_unlock_irqrestore(&trie->lock, irq_flags);
out_free: if (ret)
bpf_mem_cache_free(&trie->ma, new_node);
bpf_mem_cache_free_rcu(&trie->ma, free_node);
return ret;
}
/* Called from syscall or from eBPF program */ staticlong trie_delete_elem(struct bpf_map *map, void *_key)
{ struct lpm_trie *trie = container_of(map, struct lpm_trie, map); struct lpm_trie_node *free_node = NULL, *free_parent = NULL; struct bpf_lpm_trie_key_u8 *key = _key; struct lpm_trie_node __rcu **trim, **trim2; struct lpm_trie_node *node, *parent; unsignedlong irq_flags; unsignedint next_bit;
size_t matchlen = 0; int ret = 0;
if (key->prefixlen > trie->max_prefixlen) return -EINVAL;
ret = raw_res_spin_lock_irqsave(&trie->lock, irq_flags); if (ret) return ret;
/* Walk the tree looking for an exact key/length match and keeping *trackofthepathwetraverse.Wewillneedtoknowthenode *wewishtodelete,andtheslotthatpointstothenodewewant *todelete.Wemayalsoneedtoknowthenodesparentandthe *slotthatcontainsit.
*/
trim = &trie->root;
trim2 = trim;
parent = NULL; while ((node = rcu_dereference(*trim))) {
matchlen = longest_prefix_match(trie, node, key);
if (node->prefixlen != matchlen ||
node->prefixlen == key->prefixlen) break;
/* If the node we are removing has two children, simply mark it *asintermediateandwearedone.
*/ if (rcu_access_pointer(node->child[0]) &&
rcu_access_pointer(node->child[1])) {
node->flags |= LPM_TREE_NODE_FLAG_IM; goto out;
}
/* If the parent of the node we are about to delete is an intermediate *node,andthedeletednodedoesn'thaveanychildren,wecandelete *theintermediateparentaswellandpromoteitsotherchild *upthetree.Doingthismaintainstheinvariantthatall *intermediatenodeshaveexactly2childrenandthatthereareno *unnecessaryintermediatenodesinthetree.
*/ if (parent && (parent->flags & LPM_TREE_NODE_FLAG_IM) &&
!node->child[0] && !node->child[1]) { if (node == rcu_access_pointer(parent->child[0]))
rcu_assign_pointer(
*trim2, rcu_access_pointer(parent->child[1])); else
rcu_assign_pointer(
*trim2, rcu_access_pointer(parent->child[0]));
free_parent = parent;
free_node = node; goto out;
}
/* The node we are removing has either zero or one child. If there *isachild,moveitintotheremovednode'sslotthendelete *thenode.Otherwisejustcleartheslotanddeletethenode.
*/ if (node->child[0])
rcu_assign_pointer(*trim, rcu_access_pointer(node->child[0])); elseif (node->child[1])
rcu_assign_pointer(*trim, rcu_access_pointer(node->child[1])); else
RCU_INIT_POINTER(*trim, NULL);
free_node = node;
/* Always start at the root and walk down to a node that has no *children.Thenfreethatnode,nullifyitsreferenceintheparent *andstartover.
*/
for (;;) {
slot = &trie->root;
for (;;) {
node = rcu_dereference_protected(*slot, 1); if (!node) goto out;
if (rcu_access_pointer(node->child[0])) {
slot = &node->child[0]; continue;
}
if (rcu_access_pointer(node->child[1])) {
slot = &node->child[1]; continue;
}
/* No bpf program may access the map, so freeing the *nodewithoutwaitingfortheextraRCUGP.
*/
bpf_mem_cache_raw_free(node);
RCU_INIT_POINTER(*slot, NULL); break;
}
}
/* The get_next_key follows postorder. For the 4 node example in *thetopofthisfile,thetrie_get_next_key()returnsthefollowing *oneafteranother: *192.168.0.0/24 *192.168.1.0/24 *192.168.128.0/24 *192.168.0.0/16 * *Theideaistoreturnmorespecifickeysbeforelessspecificones.
*/
/* Try to find the exact node for the given key */ for (node = search_root; node;) {
node_stack[++stack_ptr] = node;
matchlen = longest_prefix_match(trie, node, key); if (node->prefixlen != matchlen ||
node->prefixlen == key->prefixlen) break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.