* [PATCH bpf v4 0/3] bpf: Fix incorrect handling of user flags by percpu map updates
@ 2026-10-04 11:10 Masoud Aghasi
2026-10-04 11:10 ` [PATCH bpf v4 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update Masoud Aghasi
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Masoud Aghasi @ 2026-10-04 11:10 UTC (permalink / raw)
To: bpf
Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang,
Masoud Aghasi
For BPF_MAP_TYPE_PERCPU_ARRAY map, bpf_percpu_array_update()
is not considering the possibility of a combination of
(BPF_NOEXIST, BPF_EXIST) flags with (BPF_F_CPU, BPF_F_ALL_CPUS) flags.
This causes the (BPF_NOEXIST, BPF_EXIST) flags to lose their effect
in some cases.
For BPF_MAP_TYPE_PERCPU_HASH and BPF_MAP_TYPE_LRU_PERCPU_HASH maps,
htab_map_check_update_flags() and check_flags() are not considering
the possibility of a combination of (BPF_NOEXIST, BPF_EXIST) flags
with (BPF_F_CPU, BPF_F_ALL_CPUS) flags. This causes the
(BPF_NOEXIST, BPF_EXIST) flags to lose their effect in some cases.
For example, when using (BPF_F_CPU | BPF_EXIST) flag combination
with bpf_map_update_elem() on a BPF_MAP_TYPE_PERCPU_HASH map, the
BPF_EXIST flag does not prevent new insertions as expected.
This series fixes the bug by adding proper flag validations and checks
and adds regression tests.
V4 changes:
- Edit selftest to use proper value_sz in the new cgroup map test
- Edit selftest to pin the current pid to the current cpu for LRU map
- Edit commit messages to include user-visible changes
V3 changes:
- Split the fix into two separate patches for array map and hash maps
- Extend the selftest to cover all percpu map types
- Extend the selftest to cover all applicable flag combinations
- Extend the selftest to cover the unnecessary delete issue of LRU map
V2 changes:
- Fix a BPF_EXIST flag check in __htab_lru_percpu_map_update_elem()
- Add additional test for BPF_MAP_TYPE_LRU_PERCPU_HASH map
- Add check for BPF_EXIST, BPF_NOEXIST combination in percpu array map
Masoud Aghasi (3):
bpf: Fix incorrect handling of user flags in bpf_percpu_array_update
bpf: Fix incorrect handling of user flags by percpu hash map updates
selftests/bpf: add tests for percpu map flags combination
kernel/bpf/arraymap.c | 5 +-
kernel/bpf/hashtab.c | 11 +-
.../selftests/bpf/prog_tests/percpu_alloc.c | 200 ++++++++++++++++++
3 files changed, 210 insertions(+), 6 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH bpf v4 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update
2026-10-04 11:10 [PATCH bpf v4 0/3] bpf: Fix incorrect handling of user flags by percpu map updates Masoud Aghasi
@ 2026-10-04 11:10 ` Masoud Aghasi
2026-10-05 3:10 ` Leon Hwang
2026-10-04 11:10 ` [PATCH bpf v4 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates Masoud Aghasi
2026-10-04 11:10 ` [PATCH bpf v4 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
2 siblings, 1 reply; 9+ messages in thread
From: Masoud Aghasi @ 2026-10-04 11:10 UTC (permalink / raw)
To: bpf
Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang,
Masoud Aghasi
For BPF_MAP_TYPE_PERCPU_ARRAY map, bpf_percpu_array_update()
is not considering the possibility of a combination of
(BPF_NOEXIST, BPF_EXIST) flags with (BPF_F_CPU, BPF_F_ALL_CPUS) flags.
This causes the (BPF_NOEXIST, BPF_EXIST) flags to lose their effect
in some cases.
For example, using the (BPF_F_ALL_CPUS | BPF_EXIST) flag combination
with bpf_map_update_elem() results in an incorrect EINVAL error
response, even though the flag combination is valid.
This patch fixes the bug by adding proper flag validations and checks.
Before this patch, bpf_percpu_array_update() rejected
BPF_F_ALL_CPUS | BPF_EXIST and BPF_F_ALL_CPUS | BPF_NOEXIST with
-EINVAL. Also the BPF_F_CPU | BPF_NOEXIST were accepted.
After the patch BPF_F_ALL_CPUS | BPF_EXIST is accepted and
BPF_F_ALL_CPUS | BPF_NOEXIST and BPF_F_CPU | BPF_NOEXIST
result in -EEXIST.
Fixes: 8eb76cb03f0f ("bpf: Add BPF_F_CPU and BPF_F_ALL_CPUS flags support for percpu_array maps")
Signed-off-by: Masoud Aghasi <maghasi@disroot.org>
---
kernel/bpf/arraymap.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 0fe9afd4a591..4edfde6a624c 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -438,7 +438,8 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
u32 size;
int cpu, off = 0;
- if (unlikely((map_flags & BPF_F_LOCK) || (u32)map_flags > BPF_F_ALL_CPUS))
+ if (unlikely((map_flags & BPF_EXIST) && (map_flags & BPF_NOEXIST)) ||
+ unlikely((u32)map_flags & ~(BPF_EXIST | BPF_NOEXIST | BPF_F_CPU | BPF_F_ALL_CPUS)))
/* unknown flags */
return -EINVAL;
@@ -446,7 +447,7 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
/* all elements were pre-allocated, cannot insert a new one */
return -E2BIG;
- if (unlikely(map_flags == BPF_NOEXIST))
+ if (unlikely(map_flags & BPF_NOEXIST))
/* all elements already exist */
return -EEXIST;
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH bpf v4 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates
2026-10-04 11:10 [PATCH bpf v4 0/3] bpf: Fix incorrect handling of user flags by percpu map updates Masoud Aghasi
2026-10-04 11:10 ` [PATCH bpf v4 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update Masoud Aghasi
@ 2026-10-04 11:10 ` Masoud Aghasi
2026-10-04 11:10 ` [PATCH bpf v4 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
2 siblings, 0 replies; 9+ messages in thread
From: Masoud Aghasi @ 2026-10-04 11:10 UTC (permalink / raw)
To: bpf
Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang,
Masoud Aghasi
For BPF_MAP_TYPE_PERCPU_HASH and BPF_MAP_TYPE_LRU_PERCPU_HASH maps,
htab_map_check_update_flags() and check_flags() are not considering
the possibility of a combination of (BPF_NOEXIST, BPF_EXIST) flags
with (BPF_F_CPU, BPF_F_ALL_CPUS) flags. This causes the
(BPF_NOEXIST, BPF_EXIST) flags to lose their effect in some cases.
For example, when using (BPF_F_CPU | BPF_EXIST) or
(BPF_F_CPU | BPF_NOEXIST) flag combinations with bpf_map_update_elem()
on a percpu hash map, the BPF_EXIST flag does not prevent new
insertions as expected and BPF_NOEXIST flag does not prevent
modification of existing entries as expected.
This patch fixes the bug by adding proper flag validations and checks.
Before this patch, htab_map_check_update_flags() rejected
(BPF_F_ALL_CPUS | BPF_EXIST) and (BPF_F_ALL_CPUS | BPF_NOEXIST) with
-EINVAL. After the patch those combinations are accepted.
This patch also starts returning -EINVAL for (BPF_EXIST | BPF_NOEXIST),
with or without BPF_F_CPU, which was previously accepted.
Fixes: c6936161fd55 ("bpf: Add BPF_F_CPU and BPF_F_ALL_CPUS flags support for percpu_hash and lru_percpu_hash maps")
Signed-off-by: Masoud Aghasi <maghasi@disroot.org>
---
kernel/bpf/hashtab.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 53c99fe4f176..2106b82894b2 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -1196,11 +1196,11 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
static int check_flags(struct bpf_htab *htab, struct htab_elem *l_old,
u64 map_flags)
{
- if (l_old && (map_flags & ~BPF_F_LOCK) == BPF_NOEXIST)
+ if (l_old && (map_flags & BPF_NOEXIST))
/* elem already exists */
return -EEXIST;
- if (!l_old && (map_flags & ~BPF_F_LOCK) == BPF_EXIST)
+ if (!l_old && (map_flags & BPF_EXIST))
/* elem doesn't exist, cannot update it */
return -ENOENT;
@@ -1383,9 +1383,12 @@ static long htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value
static int htab_map_check_update_flags(bool onallcpus, u64 map_flags)
{
+ if (unlikely((map_flags & BPF_EXIST) && (map_flags & BPF_NOEXIST)))
+ return -EINVAL;
if (unlikely(!onallcpus && map_flags > BPF_EXIST))
return -EINVAL;
- if (unlikely(onallcpus && ((map_flags & BPF_F_LOCK) || (u32)map_flags > BPF_F_ALL_CPUS)))
+ if (unlikely(onallcpus &&
+ ((u32)map_flags & ~(BPF_EXIST | BPF_NOEXIST | BPF_F_CPU | BPF_F_ALL_CPUS))))
return -EINVAL;
return 0;
}
@@ -1483,7 +1486,7 @@ static long __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key,
* to remove older elem from htab and this removal
* operation will need a bucket lock.
*/
- if (map_flags != BPF_EXIST) {
+ if (!(map_flags & BPF_EXIST)) {
l_new = prealloc_lru_pop(htab, key, hash);
if (!l_new)
return -ENOMEM;
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH bpf v4 3/3] selftests/bpf: add tests for percpu map flags combination
2026-10-04 11:10 [PATCH bpf v4 0/3] bpf: Fix incorrect handling of user flags by percpu map updates Masoud Aghasi
2026-10-04 11:10 ` [PATCH bpf v4 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update Masoud Aghasi
2026-10-04 11:10 ` [PATCH bpf v4 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates Masoud Aghasi
@ 2026-10-04 11:10 ` Masoud Aghasi
2026-10-05 3:12 ` Leon Hwang
2 siblings, 1 reply; 9+ messages in thread
From: Masoud Aghasi @ 2026-10-04 11:10 UTC (permalink / raw)
To: bpf
Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, leon.hwang,
Masoud Aghasi
All possible combinations of (BPF_EXIST, BPF_NOEXIST) and
(BPF_F_ALL_CPUS, BPF_F_CPU) flags are covered for all percpu map types.
As BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE does not support BPF_NOEXIST and
also in order to reduce the amount of code duplicates, I added its
new test scenarios to the existing cpu_flag_percpu_cgroup_storage test.
But for other percpu map types, I added new tests to be able to
exercise all flag combinations and the edge cases such as percpu LRU
unnecessary deletion issue.
Signed-off-by: Masoud Aghasi <maghasi@disroot.org>
---
.../selftests/bpf/prog_tests/percpu_alloc.c | 200 ++++++++++++++++++
1 file changed, 200 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
index 7b4a1e24363b..1f33ea20d845 100644
--- a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
+++ b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
@@ -449,6 +449,48 @@ static void test_lru_percpu_hash_cpu_flag_create(void)
test_percpu_map_cpu_flag_create(BPF_MAP_TYPE_LRU_PERCPU_HASH, 0);
}
+static void test_percpu_cgroup_storage_flags_combination(struct bpf_map *map, int nr_cpus,
+ struct bpf_cgroup_storage_key *key)
+{
+ int err;
+ u64 flags = 0;
+ size_t value_sz = sizeof(u32);
+ size_t elem_sz = roundup(value_sz, 8);
+ u32 *values = NULL;
+
+ values = calloc(nr_cpus, elem_sz);
+ if (!ASSERT_OK_PTR(values, "calloc values"))
+ return;
+
+ flags = BPF_NOEXIST | BPF_EXIST;
+ err = bpf_map__update_elem(map, key, sizeof(*key), values, elem_sz * nr_cpus, flags);
+ if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem noexist|exist"))
+ goto out;
+
+ flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
+ err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
+ if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem all_cpus|noexist"))
+ goto out;
+
+ flags = BPF_F_CPU | BPF_NOEXIST;
+ err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
+ if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem cpu|noexist"))
+ goto out;
+
+ flags = BPF_F_ALL_CPUS | BPF_EXIST;
+ err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
+ if (!ASSERT_OK(err, "bpf_map__update_elem all_cpus|exist"))
+ goto out;
+
+ flags = BPF_F_CPU | BPF_EXIST;
+ err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
+ if (!ASSERT_OK(err, "bpf_map__update_elem cpu|exist"))
+ goto out;
+
+out:
+ free(values);
+}
+
static void test_percpu_cgroup_storage_cpu_flag(void)
{
struct percpu_alloc_array *skel = NULL;
@@ -489,6 +531,7 @@ static void test_percpu_cgroup_storage_cpu_flag(void)
goto out;
test_percpu_map_op_cpu_flag(map, &key, sizeof(key), 1, nr_cpus, false);
+ test_percpu_cgroup_storage_flags_combination(map, nr_cpus, &key);
out:
bpf_prog_detach2(-1, cgroup, BPF_CGROUP_INET_EGRESS);
close(cgroup);
@@ -537,6 +580,157 @@ static void test_hash_cpu_flag(void)
test_map_op_cpu_flag(BPF_MAP_TYPE_HASH);
}
+static void test_percpu_map_flags_combination(enum bpf_map_type map_type)
+{
+ size_t value_sz = 8;
+ void *values = NULL;
+ u32 max_entries = 3, key = 0;
+ u64 flags = 0;
+ int err, map_fd, nr_cpus;
+ bool is_hash_map = (map_type == BPF_MAP_TYPE_PERCPU_HASH ||
+ map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH);
+
+ nr_cpus = libbpf_num_possible_cpus();
+ if (!ASSERT_GT(nr_cpus, 0, "libbpf_num_possible_cpus"))
+ return;
+
+ values = calloc(nr_cpus, value_sz);
+ if (!ASSERT_OK_PTR(values, "calloc values"))
+ return;
+
+ map_fd = bpf_map_create(map_type, "test_flags_combination_map",
+ sizeof(u32), value_sz, max_entries, NULL);
+ if (!ASSERT_GE(map_fd, 0, "bpf_map_create")) {
+ free(values);
+ return;
+ }
+
+ flags = BPF_NOEXIST | BPF_EXIST;
+ err = bpf_map_update_elem(map_fd, &key, values, flags);
+ if (!ASSERT_EQ(err, -EINVAL, "bpf_map_update_elem noexist|exist"))
+ goto out;
+
+ flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
+ key = 0;
+ err = bpf_map_update_elem(map_fd, &key, values, flags);
+ if (is_hash_map) {
+ if (!ASSERT_OK(err, "bpf_map_update_elem all_cpus|noexist"))
+ goto out;
+
+ err = bpf_map_update_elem(map_fd, &key, values, flags);
+ if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem all_cpus|noexist"))
+ goto out;
+ } else {
+ if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem all_cpus|noexist"))
+ goto out;
+ }
+
+ flags = BPF_F_CPU | BPF_NOEXIST;
+ key = 1;
+ err = bpf_map_update_elem(map_fd, &key, values, flags);
+ if (is_hash_map) {
+ if (!ASSERT_OK(err, "bpf_map_update_elem cpu|noexist"))
+ goto out;
+
+ err = bpf_map_update_elem(map_fd, &key, values, flags);
+ if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem cpu|noexist"))
+ goto out;
+ } else {
+ if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem cpu|noexist"))
+ goto out;
+ }
+
+ flags = BPF_F_ALL_CPUS | BPF_EXIST;
+ key = 2;
+ err = bpf_map_update_elem(map_fd, &key, values, flags);
+ if (is_hash_map) {
+ if (!ASSERT_EQ(err, -ENOENT, "bpf_map_update_elem all_cpus|exist"))
+ goto out;
+ } else {
+ if (!ASSERT_OK(err, "bpf_map_update_elem all_cpus|exist"))
+ goto out;
+ }
+
+ flags = BPF_F_CPU | BPF_EXIST;
+ err = bpf_map_update_elem(map_fd, &key, values, flags);
+ if (is_hash_map) {
+ if (!ASSERT_EQ(err, -ENOENT, "bpf_map_update_elem cpu|exist"))
+ goto out;
+ } else {
+ if (!ASSERT_OK(err, "bpf_map_update_elem cpu|exist"))
+ goto out;
+ }
+
+ if (map_type != BPF_MAP_TYPE_LRU_PERCPU_HASH)
+ goto out;
+
+ /* Percpu LRU hash map should not delete old entries unnecessarily */
+ flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
+ key = 2;
+ err = bpf_map_update_elem(map_fd, &key, values, flags);
+ if (!ASSERT_OK(err, "bpf_map_update_elem unnecessary_deletion"))
+ goto out;
+
+ flags = BPF_F_ALL_CPUS | BPF_EXIST;
+ err = bpf_map_update_elem(map_fd, &key, values, flags);
+ if (!ASSERT_OK(err, "bpf_map_update_elem unnecessary_deletion"))
+ goto out;
+
+ key = 0;
+ err = bpf_map_lookup_elem(map_fd, &key, values);
+ if (!ASSERT_OK(err, "bpf_map_lookup_elem unnecessary_deletion"))
+ goto out;
+
+out:
+ close(map_fd);
+ free(values);
+}
+
+static int pin_current_cpu(cpu_set_t *old_mask)
+{
+ int err, cpu;
+ cpu_set_t new_mask;
+
+ err = sched_getaffinity(0, sizeof(*old_mask), old_mask);
+ if (!ASSERT_OK(err, "sched_getaffinity"))
+ return -1;
+
+ cpu = sched_getcpu();
+ if (!ASSERT_GE(cpu, 0, "sched_getcpu"))
+ return -1;
+
+ CPU_ZERO(&new_mask);
+ CPU_SET(cpu, &new_mask);
+
+ err = sched_setaffinity(0, sizeof(new_mask), &new_mask);
+ if (!ASSERT_OK(err, "sched_setaffinity"))
+ return -1;
+
+ return 0;
+}
+
+static void test_percpu_hash_flags_combination(void)
+{
+ test_percpu_map_flags_combination(BPF_MAP_TYPE_PERCPU_HASH);
+}
+
+static void test_lru_percpu_hash_flags_combination(void)
+{
+ cpu_set_t old_mask;
+
+ if (pin_current_cpu(&old_mask))
+ return;
+
+ test_percpu_map_flags_combination(BPF_MAP_TYPE_LRU_PERCPU_HASH);
+
+ sched_setaffinity(0, sizeof(old_mask), &old_mask);
+}
+
+static void test_percpu_array_flags_combination(void)
+{
+ test_percpu_map_flags_combination(BPF_MAP_TYPE_PERCPU_ARRAY);
+}
+
void test_percpu_alloc(void)
{
if (test__start_subtest("array"))
@@ -565,4 +759,10 @@ void test_percpu_alloc(void)
test_array_cpu_flag();
if (test__start_subtest("cpu_flag_hash"))
test_hash_cpu_flag();
+ if (test__start_subtest("cpu_flag_combination_percpu_hash"))
+ test_percpu_hash_flags_combination();
+ if (test__start_subtest("cpu_flag_combination_lru_percpu_hash"))
+ test_lru_percpu_hash_flags_combination();
+ if (test__start_subtest("cpu_flag_combination_percpu_array"))
+ test_percpu_array_flags_combination();
}
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH bpf v4 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update
2026-10-04 11:10 ` [PATCH bpf v4 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update Masoud Aghasi
@ 2026-10-05 3:10 ` Leon Hwang
2026-10-05 14:58 ` Masoud Aghasi
0 siblings, 1 reply; 9+ messages in thread
From: Leon Hwang @ 2026-10-05 3:10 UTC (permalink / raw)
To: Masoud Aghasi, bpf
Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai
Hi Masoud,
Please wait some time, say 24 hours, for human reviews before sending
the next revision.
On 4/10/26 19:10, Masoud Aghasi wrote:
> For BPF_MAP_TYPE_PERCPU_ARRAY map, bpf_percpu_array_update()
> is not considering the possibility of a combination of
> (BPF_NOEXIST, BPF_EXIST) flags with (BPF_F_CPU, BPF_F_ALL_CPUS) flags.
> This causes the (BPF_NOEXIST, BPF_EXIST) flags to lose their effect
> in some cases.
[...]
> @@ -438,7 +438,8 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
> u32 size;
> int cpu, off = 0;
>
> - if (unlikely((map_flags & BPF_F_LOCK) || (u32)map_flags > BPF_F_ALL_CPUS))
> + if (unlikely((map_flags & BPF_EXIST) && (map_flags & BPF_NOEXIST)) ||
> + unlikely((u32)map_flags & ~(BPF_EXIST | BPF_NOEXIST | BPF_F_CPU | BPF_F_ALL_CPUS)))
Probably, we can add these two macros in include/linux/bpf.h.
#define BPF_EXIST_FLAGS (BPF_EXIST | BPF_NOEXIST)
#define BPF_CPU_FLAGS (BPF_F_CPU | BPF_F_ALL_CPUS)
Then, this 'if' can be simplified to
if (unlikely(((map_flags & BPF_EXIST_FLAGS) == BPF_EXIST_FLAGS) ||
((u32)map_flags & ~(BPF_EXIST_FLAGS | BPF_CPU_FLAGS)))
Also simplify htab_map_check_update_flags() in the next patch by the
same way.
Thanks,
Leon
> /* unknown flags */
> return -EINVAL;
>
> @@ -446,7 +447,7 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
> /* all elements were pre-allocated, cannot insert a new one */
> return -E2BIG;
>
> - if (unlikely(map_flags == BPF_NOEXIST))
> + if (unlikely(map_flags & BPF_NOEXIST))
> /* all elements already exist */
> return -EEXIST;
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf v4 3/3] selftests/bpf: add tests for percpu map flags combination
2026-10-04 11:10 ` [PATCH bpf v4 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
@ 2026-10-05 3:12 ` Leon Hwang
2026-10-05 15:19 ` Masoud Aghasi
0 siblings, 1 reply; 9+ messages in thread
From: Leon Hwang @ 2026-10-05 3:12 UTC (permalink / raw)
To: Masoud Aghasi, bpf
Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai
On 4/10/26 19:10, Masoud Aghasi wrote:
> All possible combinations of (BPF_EXIST, BPF_NOEXIST) and
> (BPF_F_ALL_CPUS, BPF_F_CPU) flags are covered for all percpu map types.
>
> As BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE does not support BPF_NOEXIST and
> also in order to reduce the amount of code duplicates, I added its
> new test scenarios to the existing cpu_flag_percpu_cgroup_storage test.
>
> But for other percpu map types, I added new tests to be able to
> exercise all flag combinations and the edge cases such as percpu LRU
> unnecessary deletion issue.
>
> Signed-off-by: Masoud Aghasi <maghasi@disroot.org>
> ---
> .../selftests/bpf/prog_tests/percpu_alloc.c | 200 ++++++++++++++++++
> 1 file changed, 200 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
> index 7b4a1e24363b..1f33ea20d845 100644
> --- a/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
> +++ b/tools/testing/selftests/bpf/prog_tests/percpu_alloc.c
> @@ -449,6 +449,48 @@ static void test_lru_percpu_hash_cpu_flag_create(void)
> test_percpu_map_cpu_flag_create(BPF_MAP_TYPE_LRU_PERCPU_HASH, 0);
> }
>
> +static void test_percpu_cgroup_storage_flags_combination(struct bpf_map *map, int nr_cpus,
> + struct bpf_cgroup_storage_key *key)
> +{
> + int err;
> + u64 flags = 0;
> + size_t value_sz = sizeof(u32);
> + size_t elem_sz = roundup(value_sz, 8);
> + u32 *values = NULL;
Pls keep these lines in inverted Christmas tree style.
> +
> + values = calloc(nr_cpus, elem_sz);
> + if (!ASSERT_OK_PTR(values, "calloc values"))
> + return;
> +
> + flags = BPF_NOEXIST | BPF_EXIST;
> + err = bpf_map__update_elem(map, key, sizeof(*key), values, elem_sz * nr_cpus, flags);
> + if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem noexist|exist"))
> + goto out;
> +
> + flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
> + err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
> + if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem all_cpus|noexist"))
> + goto out;
> +
> + flags = BPF_F_CPU | BPF_NOEXIST;
> + err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
> + if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem cpu|noexist"))
> + goto out;
> +
> + flags = BPF_F_ALL_CPUS | BPF_EXIST;
> + err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
> + if (!ASSERT_OK(err, "bpf_map__update_elem all_cpus|exist"))
> + goto out;
> +
> + flags = BPF_F_CPU | BPF_EXIST;
> + err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
> + if (!ASSERT_OK(err, "bpf_map__update_elem cpu|exist"))
> + goto out;
This 'goto out' is unnecessary.
For these two bpf_map__update_elem() calls, better to set values before
updating, then to verify the values after updating.
> +
> +out:
> + free(values);
> +}
> +
> static void test_percpu_cgroup_storage_cpu_flag(void)
> {
> struct percpu_alloc_array *skel = NULL;
> @@ -489,6 +531,7 @@ static void test_percpu_cgroup_storage_cpu_flag(void)
> goto out;
>
> test_percpu_map_op_cpu_flag(map, &key, sizeof(key), 1, nr_cpus, false);
> + test_percpu_cgroup_storage_flags_combination(map, nr_cpus, &key);
> out:
> bpf_prog_detach2(-1, cgroup, BPF_CGROUP_INET_EGRESS);
> close(cgroup);
> @@ -537,6 +580,157 @@ static void test_hash_cpu_flag(void)
> test_map_op_cpu_flag(BPF_MAP_TYPE_HASH);
> }
>
> +static void test_percpu_map_flags_combination(enum bpf_map_type map_type)
> +{
> + size_t value_sz = 8;
> + void *values = NULL;
> + u32 max_entries = 3, key = 0;
> + u64 flags = 0;
> + int err, map_fd, nr_cpus;
> + bool is_hash_map = (map_type == BPF_MAP_TYPE_PERCPU_HASH ||
> + map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH);
Ditto the style.
We can pass 'bool is_hash_map' via parameter.
> +
> + nr_cpus = libbpf_num_possible_cpus();
> + if (!ASSERT_GT(nr_cpus, 0, "libbpf_num_possible_cpus"))
> + return;
> +
> + values = calloc(nr_cpus, value_sz);
> + if (!ASSERT_OK_PTR(values, "calloc values"))
> + return;
> +
> + map_fd = bpf_map_create(map_type, "test_flags_combination_map",
> + sizeof(u32), value_sz, max_entries, NULL);
> + if (!ASSERT_GE(map_fd, 0, "bpf_map_create")) {
> + free(values);
> + return;
> + }
> +
> + flags = BPF_NOEXIST | BPF_EXIST;
> + err = bpf_map_update_elem(map_fd, &key, values, flags);
> + if (!ASSERT_EQ(err, -EINVAL, "bpf_map_update_elem noexist|exist"))
> + goto out;
> +
> + flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
> + key = 0;
> + err = bpf_map_update_elem(map_fd, &key, values, flags);
> + if (is_hash_map) {
> + if (!ASSERT_OK(err, "bpf_map_update_elem all_cpus|noexist"))
> + goto out;
> +
> + err = bpf_map_update_elem(map_fd, &key, values, flags);
> + if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem all_cpus|noexist"))
> + goto out;
> + } else {
> + if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem all_cpus|noexist"))
> + goto out;
> + }
The 'if (!ASSERT_EQ(err, ...))' can be moved after 'if (is_hash_map)'.
if (is_hash_map) {
if (!ASSERT_EQ(err, ...))
goto out;
err = bpf_map_update_elem(...);
}
if (!ASSERT_EQ(err, ...))
goto out;
> +
> + flags = BPF_F_CPU | BPF_NOEXIST;
> + key = 1;
> + err = bpf_map_update_elem(map_fd, &key, values, flags);
> + if (is_hash_map) {
> + if (!ASSERT_OK(err, "bpf_map_update_elem cpu|noexist"))
> + goto out;
> +
> + err = bpf_map_update_elem(map_fd, &key, values, flags);
> + if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem cpu|noexist"))
> + goto out;
> + } else {
> + if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem cpu|noexist"))
> + goto out;
> + }
> +
> + flags = BPF_F_ALL_CPUS | BPF_EXIST;
> + key = 2;
> + err = bpf_map_update_elem(map_fd, &key, values, flags);
> + if (is_hash_map) {
> + if (!ASSERT_EQ(err, -ENOENT, "bpf_map_update_elem all_cpus|exist"))
> + goto out;
> + } else {
> + if (!ASSERT_OK(err, "bpf_map_update_elem all_cpus|exist"))
> + goto out;
> + }
> +
> + flags = BPF_F_CPU | BPF_EXIST;
> + err = bpf_map_update_elem(map_fd, &key, values, flags);
> + if (is_hash_map) {
> + if (!ASSERT_EQ(err, -ENOENT, "bpf_map_update_elem cpu|exist"))
> + goto out;
> + } else {
> + if (!ASSERT_OK(err, "bpf_map_update_elem cpu|exist"))
> + goto out;
> + }
> +
> + if (map_type != BPF_MAP_TYPE_LRU_PERCPU_HASH)
> + goto out;
> +
> + /* Percpu LRU hash map should not delete old entries unnecessarily */
> + flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
> + key = 2;
> + err = bpf_map_update_elem(map_fd, &key, values, flags);
> + if (!ASSERT_OK(err, "bpf_map_update_elem unnecessary_deletion"))
> + goto out;
> +
> + flags = BPF_F_ALL_CPUS | BPF_EXIST;
> + err = bpf_map_update_elem(map_fd, &key, values, flags);
> + if (!ASSERT_OK(err, "bpf_map_update_elem unnecessary_deletion"))
> + goto out;
> +
> + key = 0;
> + err = bpf_map_lookup_elem(map_fd, &key, values);
> + if (!ASSERT_OK(err, "bpf_map_lookup_elem unnecessary_deletion"))
> + goto out;
This 'goto' is unnecessary.
> +
> +out:
> + close(map_fd);
> + free(values);
> +}
After reading test_percpu_map_flags_combination(), two helpers are
needed to verify the updated values. One for updating with BPF_F_CPU,
another one for updating with BPF_F_ALL_CPUS.
Thanks,
Leon
> [...]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf v4 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update
2026-10-05 3:10 ` Leon Hwang
@ 2026-10-05 14:58 ` Masoud Aghasi
0 siblings, 0 replies; 9+ messages in thread
From: Masoud Aghasi @ 2026-10-05 14:58 UTC (permalink / raw)
To: Leon Hwang, bpf
Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai
On 05/10/2026 04:10, Leon Hwang wrote:
> Hi Masoud,
>
> Please wait some time, say 24 hours, for human reviews before sending
> the next revision.
Hi Leon, noted. thanks for the review.
>
>> @@ -438,7 +438,8 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
>> u32 size;
>> int cpu, off = 0;
>>
>> - if (unlikely((map_flags & BPF_F_LOCK) || (u32)map_flags > BPF_F_ALL_CPUS))
>> + if (unlikely((map_flags & BPF_EXIST) && (map_flags & BPF_NOEXIST)) ||
>> + unlikely((u32)map_flags & ~(BPF_EXIST | BPF_NOEXIST | BPF_F_CPU | BPF_F_ALL_CPUS)))
>
> Probably, we can add these two macros in include/linux/bpf.h.
>
> #define BPF_EXIST_FLAGS (BPF_EXIST | BPF_NOEXIST)
> #define BPF_CPU_FLAGS (BPF_F_CPU | BPF_F_ALL_CPUS)
>
> Then, this 'if' can be simplified to
>
> if (unlikely(((map_flags & BPF_EXIST_FLAGS) == BPF_EXIST_FLAGS) ||
> ((u32)map_flags & ~(BPF_EXIST_FLAGS | BPF_CPU_FLAGS)))
>
> Also simplify htab_map_check_update_flags() in the next patch by the
> same way.
>
> Thanks,
> Leon
Sounds good to me. I will apply it to the v5 if there are no objections.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf v4 3/3] selftests/bpf: add tests for percpu map flags combination
2026-10-05 3:12 ` Leon Hwang
@ 2026-10-05 15:19 ` Masoud Aghasi
2026-10-06 2:12 ` Leon Hwang
0 siblings, 1 reply; 9+ messages in thread
From: Masoud Aghasi @ 2026-10-05 15:19 UTC (permalink / raw)
To: Leon Hwang, bpf
Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai
On 05/10/2026 04:12, Leon Hwang wrote:
> On 4/10/26 19:10, Masoud Aghasi wrote:
[...]
>> +{
>> + int err;
>> + u64 flags = 0;
>> + size_t value_sz = sizeof(u32);
>> + size_t elem_sz = roundup(value_sz, 8);
>> + u32 *values = NULL;
>
> Pls keep these lines in inverted Christmas tree style.
>
[...]
>> + flags = BPF_F_CPU | BPF_EXIST;
>> + err = bpf_map__update_elem(map, key, sizeof(*key), values, value_sz, flags);
>> + if (!ASSERT_OK(err, "bpf_map__update_elem cpu|exist"))
>> + goto out;
>
> This 'goto out' is unnecessary.
>
> For these two bpf_map__update_elem() calls, better to set values before
> updating, then to verify the values after updating.
>
[...]
>> +static void test_percpu_map_flags_combination(enum bpf_map_type map_type)
>> +{
>> + size_t value_sz = 8;
>> + void *values = NULL;
>> + u32 max_entries = 3, key = 0;
>> + u64 flags = 0;
>> + int err, map_fd, nr_cpus;
>> + bool is_hash_map = (map_type == BPF_MAP_TYPE_PERCPU_HASH ||
>> + map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH);
>
> Ditto the style.
>
> We can pass 'bool is_hash_map' via parameter.
>
[...]
>> + flags = BPF_F_ALL_CPUS | BPF_NOEXIST;
>> + key = 0;
>> + err = bpf_map_update_elem(map_fd, &key, values, flags);
>> + if (is_hash_map) {
>> + if (!ASSERT_OK(err, "bpf_map_update_elem all_cpus|noexist"))
>> + goto out;
>> +
>> + err = bpf_map_update_elem(map_fd, &key, values, flags);
>> + if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem all_cpus|noexist"))
>> + goto out;
>> + } else {
>> + if (!ASSERT_EQ(err, -EEXIST, "bpf_map_update_elem all_cpus|noexist"))
>> + goto out;
>> + }
>
> The 'if (!ASSERT_EQ(err, ...))' can be moved after 'if (is_hash_map)'.
>
> if (is_hash_map) {
> if (!ASSERT_EQ(err, ...))
> goto out;
>
> err = bpf_map_update_elem(...);
> }
> if (!ASSERT_EQ(err, ...))
> goto out;
>
[...]
>> + key = 0;
>> + err = bpf_map_lookup_elem(map_fd, &key, values);
>> + if (!ASSERT_OK(err, "bpf_map_lookup_elem unnecessary_deletion"))
>> + goto out;
>
> This 'goto' is unnecessary.
>
>> +
>> +out:
>> + close(map_fd);
>> + free(values);
>> +}
>
> After reading test_percpu_map_flags_combination(), two helpers are
> needed to verify the updated values. One for updating with BPF_F_CPU,
> another one for updating with BPF_F_ALL_CPUS.
>
> Thanks,
> Leon
Thanks, I will apply your comments to the v5.
just regarding the is_hash_map, isn't it better to keep it as an
implementation detail inside the function and avoid introducing an
unnecessary invariant for callers to maintain?
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf v4 3/3] selftests/bpf: add tests for percpu map flags combination
2026-10-05 15:19 ` Masoud Aghasi
@ 2026-10-06 2:12 ` Leon Hwang
0 siblings, 0 replies; 9+ messages in thread
From: Leon Hwang @ 2026-10-06 2:12 UTC (permalink / raw)
To: Masoud Aghasi, bpf
Cc: andrii, eddyz87, ast, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai
On 5/10/26 23:19, Masoud Aghasi wrote:
> On 05/10/2026 04:12, Leon Hwang wrote:
>> On 4/10/26 19:10, Masoud Aghasi wrote:
[...]
>>
>> After reading test_percpu_map_flags_combination(), two helpers are
>> needed to verify the updated values. One for updating with BPF_F_CPU,
>> another one for updating with BPF_F_ALL_CPUS.
>>
>> Thanks,
>> Leon
>
> Thanks, I will apply your comments to the v5.
> just regarding the is_hash_map, isn't it better to keep it as an
> implementation detail inside the function and avoid introducing an
> unnecessary invariant for callers to maintain?
I think either way is fine in this selftest.
Thanks,
Leon
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-10-06 2:12 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-04 11:10 [PATCH bpf v4 0/3] bpf: Fix incorrect handling of user flags by percpu map updates Masoud Aghasi
2026-10-04 11:10 ` [PATCH bpf v4 1/3] bpf: Fix incorrect handling of user flags in bpf_percpu_array_update Masoud Aghasi
2026-10-05 3:10 ` Leon Hwang
2026-10-05 14:58 ` Masoud Aghasi
2026-10-04 11:10 ` [PATCH bpf v4 2/3] bpf: Fix incorrect handling of user flags by percpu hash map updates Masoud Aghasi
2026-10-04 11:10 ` [PATCH bpf v4 3/3] selftests/bpf: add tests for percpu map flags combination Masoud Aghasi
2026-10-05 3:12 ` Leon Hwang
2026-10-05 15:19 ` Masoud Aghasi
2026-10-06 2:12 ` Leon Hwang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).