Skip to content

Commit e454cf5

Browse files
kraigatgoogdavem330
authored andcommitted
bpf: Implement map_delete_elem for BPF_MAP_TYPE_LPM_TRIE
This is a simple non-recursive delete operation. It prunes paths of empty nodes in the tree, but it does not try to further compress the tree as nodes are removed. Signed-off-by: Craig Gallek <kraig@google.com> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 173f4c5 commit e454cf5

File tree

1 file changed

+77
-3
lines changed

1 file changed

+77
-3
lines changed

kernel/bpf/lpm_trie.c

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,10 +389,84 @@ static int trie_update_elem(struct bpf_map *map,
389389
return ret;
390390
}
391391

392-
static int trie_delete_elem(struct bpf_map *map, void *key)
392+
/* Called from syscall or from eBPF program */
393+
static int trie_delete_elem(struct bpf_map *map, void *_key)
393394
{
394-
/* TODO */
395-
return -ENOSYS;
395+
struct lpm_trie *trie = container_of(map, struct lpm_trie, map);
396+
struct bpf_lpm_trie_key *key = _key;
397+
struct lpm_trie_node __rcu **trim;
398+
struct lpm_trie_node *node;
399+
unsigned long irq_flags;
400+
unsigned int next_bit;
401+
size_t matchlen = 0;
402+
int ret = 0;
403+
404+
if (key->prefixlen > trie->max_prefixlen)
405+
return -EINVAL;
406+
407+
raw_spin_lock_irqsave(&trie->lock, irq_flags);
408+
409+
/* Walk the tree looking for an exact key/length match and keeping
410+
* track of where we could begin trimming the tree. The trim-point
411+
* is the sub-tree along the walk consisting of only single-child
412+
* intermediate nodes and ending at a leaf node that we want to
413+
* remove.
414+
*/
415+
trim = &trie->root;
416+
node = rcu_dereference_protected(
417+
trie->root, lockdep_is_held(&trie->lock));
418+
while (node) {
419+
matchlen = longest_prefix_match(trie, node, key);
420+
421+
if (node->prefixlen != matchlen ||
422+
node->prefixlen == key->prefixlen)
423+
break;
424+
425+
next_bit = extract_bit(key->data, node->prefixlen);
426+
/* If we hit a node that has more than one child or is a valid
427+
* prefix itself, do not remove it. Reset the root of the trim
428+
* path to its descendant on our path.
429+
*/
430+
if (!(node->flags & LPM_TREE_NODE_FLAG_IM) ||
431+
(node->child[0] && node->child[1]))
432+
trim = &node->child[next_bit];
433+
node = rcu_dereference_protected(
434+
node->child[next_bit], lockdep_is_held(&trie->lock));
435+
}
436+
437+
if (!node || node->prefixlen != key->prefixlen ||
438+
(node->flags & LPM_TREE_NODE_FLAG_IM)) {
439+
ret = -ENOENT;
440+
goto out;
441+
}
442+
443+
trie->n_entries--;
444+
445+
/* If the node we are removing is not a leaf node, simply mark it
446+
* as intermediate and we are done.
447+
*/
448+
if (rcu_access_pointer(node->child[0]) ||
449+
rcu_access_pointer(node->child[1])) {
450+
node->flags |= LPM_TREE_NODE_FLAG_IM;
451+
goto out;
452+
}
453+
454+
/* trim should now point to the slot holding the start of a path from
455+
* zero or more intermediate nodes to our leaf node for deletion.
456+
*/
457+
while ((node = rcu_dereference_protected(
458+
*trim, lockdep_is_held(&trie->lock)))) {
459+
RCU_INIT_POINTER(*trim, NULL);
460+
trim = rcu_access_pointer(node->child[0]) ?
461+
&node->child[0] :
462+
&node->child[1];
463+
kfree_rcu(node, rcu);
464+
}
465+
466+
out:
467+
raw_spin_unlock_irqrestore(&trie->lock, irq_flags);
468+
469+
return ret;
396470
}
397471

398472
#define LPM_DATA_SIZE_MAX 256

0 commit comments

Comments
 (0)