mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									173f4c5ebb
								
							
						
					
					
						commit
						e454cf5958
					
				
					 1 changed files with 77 additions and 3 deletions
				
			
		| 
						 | 
					@ -389,10 +389,84 @@ static int trie_update_elem(struct bpf_map *map,
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int trie_delete_elem(struct bpf_map *map, void *key)
 | 
					/* Called from syscall or from eBPF program */
 | 
				
			||||||
 | 
					static int trie_delete_elem(struct bpf_map *map, void *_key)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* TODO */
 | 
						struct lpm_trie *trie = container_of(map, struct lpm_trie, map);
 | 
				
			||||||
	return -ENOSYS;
 | 
						struct bpf_lpm_trie_key *key = _key;
 | 
				
			||||||
 | 
						struct lpm_trie_node __rcu **trim;
 | 
				
			||||||
 | 
						struct lpm_trie_node *node;
 | 
				
			||||||
 | 
						unsigned long irq_flags;
 | 
				
			||||||
 | 
						unsigned int next_bit;
 | 
				
			||||||
 | 
						size_t matchlen = 0;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (key->prefixlen > trie->max_prefixlen)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						raw_spin_lock_irqsave(&trie->lock, irq_flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Walk the tree looking for an exact key/length match and keeping
 | 
				
			||||||
 | 
						 * track of where we could begin trimming the tree.  The trim-point
 | 
				
			||||||
 | 
						 * is the sub-tree along the walk consisting of only single-child
 | 
				
			||||||
 | 
						 * intermediate nodes and ending at a leaf node that we want to
 | 
				
			||||||
 | 
						 * remove.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						trim = &trie->root;
 | 
				
			||||||
 | 
						node = rcu_dereference_protected(
 | 
				
			||||||
 | 
							trie->root, lockdep_is_held(&trie->lock));
 | 
				
			||||||
 | 
						while (node) {
 | 
				
			||||||
 | 
							matchlen = longest_prefix_match(trie, node, key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (node->prefixlen != matchlen ||
 | 
				
			||||||
 | 
							    node->prefixlen == key->prefixlen)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							next_bit = extract_bit(key->data, node->prefixlen);
 | 
				
			||||||
 | 
							/* If we hit a node that has more than one child or is a valid
 | 
				
			||||||
 | 
							 * prefix itself, do not remove it. Reset the root of the trim
 | 
				
			||||||
 | 
							 * path to its descendant on our path.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (!(node->flags & LPM_TREE_NODE_FLAG_IM) ||
 | 
				
			||||||
 | 
							    (node->child[0] && node->child[1]))
 | 
				
			||||||
 | 
								trim = &node->child[next_bit];
 | 
				
			||||||
 | 
							node = rcu_dereference_protected(
 | 
				
			||||||
 | 
								node->child[next_bit], lockdep_is_held(&trie->lock));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!node || node->prefixlen != key->prefixlen ||
 | 
				
			||||||
 | 
						    (node->flags & LPM_TREE_NODE_FLAG_IM)) {
 | 
				
			||||||
 | 
							ret = -ENOENT;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trie->n_entries--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If the node we are removing is not a leaf node, simply mark it
 | 
				
			||||||
 | 
						 * as intermediate and we are done.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (rcu_access_pointer(node->child[0]) ||
 | 
				
			||||||
 | 
						    rcu_access_pointer(node->child[1])) {
 | 
				
			||||||
 | 
							node->flags |= LPM_TREE_NODE_FLAG_IM;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* trim should now point to the slot holding the start of a path from
 | 
				
			||||||
 | 
						 * zero or more intermediate nodes to our leaf node for deletion.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						while ((node = rcu_dereference_protected(
 | 
				
			||||||
 | 
								*trim, lockdep_is_held(&trie->lock)))) {
 | 
				
			||||||
 | 
							RCU_INIT_POINTER(*trim, NULL);
 | 
				
			||||||
 | 
							trim = rcu_access_pointer(node->child[0]) ?
 | 
				
			||||||
 | 
								&node->child[0] :
 | 
				
			||||||
 | 
								&node->child[1];
 | 
				
			||||||
 | 
							kfree_rcu(node, rcu);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						raw_spin_unlock_irqrestore(&trie->lock, irq_flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define LPM_DATA_SIZE_MAX	256
 | 
					#define LPM_DATA_SIZE_MAX	256
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue