summaryrefslogtreecommitdiff
path: root/kernel/bpf
diff options
context:
space:
mode:
authorHou Tao <houtao1@huawei.com>2024-12-06 19:06:17 +0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-12-14 20:03:19 +0100
commit7218e441ad9d8f76cd76402035713c3c53865129 (patch)
tree2095e94072e4c38f22701e9647c67ce8ffdf3aa9 /kernel/bpf
parent6dc076a257a5343308eeb5e94f11112f608d929e (diff)
downloadlinux-7218e441ad9d8f76cd76402035713c3c53865129.tar.gz
linux-7218e441ad9d8f76cd76402035713c3c53865129.tar.bz2
linux-7218e441ad9d8f76cd76402035713c3c53865129.zip
bpf: Handle in-place update for full LPM trie correctly
[ Upstream commit 532d6b36b2bfac5514426a97a4df8d103d700d43 ] When a LPM trie is full, in-place updates of existing elements incorrectly return -ENOSPC. Fix this by deferring the check of trie->n_entries. For new insertions, n_entries must not exceed max_entries. However, in-place updates are allowed even when the trie is full. Fixes: b95a5c4db09b ("bpf: add a longest prefix match trie map implementation") Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com> Signed-off-by: Hou Tao <houtao1@huawei.com> Link: https://lore.kernel.org/r/20241206110622.1161752-5-houtao@huaweicloud.com Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'kernel/bpf')
-rw-r--r--kernel/bpf/lpm_trie.c44
1 files changed, 21 insertions, 23 deletions
diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c
index 8a040f123e04..fcc5cd358d7f 100644
--- a/kernel/bpf/lpm_trie.c
+++ b/kernel/bpf/lpm_trie.c
@@ -310,6 +310,16 @@ static struct lpm_trie_node *lpm_trie_node_alloc(const struct lpm_trie *trie,
return node;
}
+static int trie_check_add_elem(struct lpm_trie *trie, u64 flags)
+{
+ if (flags == BPF_EXIST)
+ return -ENOENT;
+ if (trie->n_entries == trie->map.max_entries)
+ return -ENOSPC;
+ trie->n_entries++;
+ return 0;
+}
+
/* Called from syscall or from eBPF program */
static long trie_update_elem(struct bpf_map *map,
void *_key, void *value, u64 flags)
@@ -333,20 +343,12 @@ static long trie_update_elem(struct bpf_map *map,
spin_lock_irqsave(&trie->lock, irq_flags);
/* Allocate and fill a new node */
-
- if (trie->n_entries == trie->map.max_entries) {
- ret = -ENOSPC;
- goto out;
- }
-
new_node = lpm_trie_node_alloc(trie, value);
if (!new_node) {
ret = -ENOMEM;
goto out;
}
- trie->n_entries++;
-
new_node->prefixlen = key->prefixlen;
RCU_INIT_POINTER(new_node->child[0], NULL);
RCU_INIT_POINTER(new_node->child[1], NULL);
@@ -376,10 +378,10 @@ static long trie_update_elem(struct bpf_map *map,
* simply assign the @new_node to that slot and be done.
*/
if (!node) {
- if (flags == BPF_EXIST) {
- ret = -ENOENT;
+ ret = trie_check_add_elem(trie, flags);
+ if (ret)
goto out;
- }
+
rcu_assign_pointer(*slot, new_node);
goto out;
}
@@ -393,10 +395,10 @@ static long trie_update_elem(struct bpf_map *map,
ret = -EEXIST;
goto out;
}
- trie->n_entries--;
- } else if (flags == BPF_EXIST) {
- ret = -ENOENT;
- goto out;
+ } else {
+ ret = trie_check_add_elem(trie, flags);
+ if (ret)
+ goto out;
}
new_node->child[0] = node->child[0];
@@ -408,10 +410,9 @@ static long trie_update_elem(struct bpf_map *map,
goto out;
}
- if (flags == BPF_EXIST) {
- ret = -ENOENT;
+ ret = trie_check_add_elem(trie, flags);
+ if (ret)
goto out;
- }
/* If the new node matches the prefix completely, it must be inserted
* as an ancestor. Simply insert it between @node and *@slot.
@@ -425,6 +426,7 @@ static long trie_update_elem(struct bpf_map *map,
im_node = lpm_trie_node_alloc(trie, NULL);
if (!im_node) {
+ trie->n_entries--;
ret = -ENOMEM;
goto out;
}
@@ -446,12 +448,8 @@ static long trie_update_elem(struct bpf_map *map,
rcu_assign_pointer(*slot, im_node);
out:
- if (ret) {
- if (new_node)
- trie->n_entries--;
+ if (ret)
kfree(new_node);
- }
-
spin_unlock_irqrestore(&trie->lock, irq_flags);
kfree_rcu(free_node, rcu);