Commit 9d9f63c5 authored by Hou Tao's avatar Hou Tao Committed by Tengda Wu
Browse files

bpf: Handle in-place update for full LPM trie correctly

stable inclusion
from stable-v6.6.66
commit 2e9ff3f4834c873b4c768091a83322f4ed4c95cf
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IBFB6E

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=2e9ff3f4834c873b4c768091a83322f4ed4c95cf



--------------------------------

[ 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: b95a5c4d ("bpf: add a longest prefix match trie map implementation")
Reviewed-by: default avatarToke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: default avatarHou Tao <houtao1@huawei.com>
Link: https://lore.kernel.org/r/20241206110622.1161752-5-houtao@huaweicloud.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Conflicts:
	kernel/bpf/lpm_trie.c
[The cleanup patch 3d5611b4d7ef did not backport, causing conflicts]
Signed-off-by: default avatarTengda Wu <wutengda2@huawei.com>
parent 6d436902
Loading
Loading
Loading
Loading
+20 −21
Original line number Diff line number Diff line
@@ -302,6 +302,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)
@@ -325,20 +335,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);
@@ -368,10 +370,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;
	}
@@ -385,9 +387,9 @@ static long trie_update_elem(struct bpf_map *map,
				ret = -EEXIST;
				goto out;
			}
			trie->n_entries--;
		} else if (flags == BPF_EXIST) {
			ret = -ENOENT;
		} else {
			ret = trie_check_add_elem(trie, flags);
			if (ret)
				goto out;
		}

@@ -400,10 +402,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.
@@ -417,6 +418,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;
	}
@@ -439,9 +441,6 @@ static long trie_update_elem(struct bpf_map *map,

out:
	if (ret) {
		if (new_node)
			trie->n_entries--;

		kfree(new_node);
		kfree(im_node);
	}