selftests/bpf: Add tests for percpu map flags combination

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>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Leon Hwang <leon.hwang@linux.dev>
Link: https://patch.msgid.link/20261008120734.1565972-4-maghasi@disroot.org
This commit is contained in:
Masoud Aghasi 2026-10-08 13:07:34 +01:00 committed by Alexei Starovoitov
parent 4a9b25fe8d
commit 22392799e2
No known key found for this signature in database

View File

@ -449,6 +449,95 @@ static void test_lru_percpu_hash_cpu_flag_create(void)
test_percpu_map_cpu_flag_create(BPF_MAP_TYPE_LRU_PERCPU_HASH, 0);
}
static bool verify_map_value_all_cpus(int map_fd, void *key, void *values, size_t elem_sz,
u32 expected_value, int nr_cpus)
{
int i, err;
err = bpf_map_lookup_elem(map_fd, key, values);
if (!ASSERT_OK(err, "bpf_map_lookup_elem all_cpus"))
return false;
for (i = 0 ; i < nr_cpus ; i++) {
if (!ASSERT_EQ(*(u32 *)(values + elem_sz * i), expected_value,
"bpf_map_lookup_elem value all_cpus"))
return false;
}
return true;
}
static bool verify_map_value_first_cpu(int map_fd, void *key, void *values, size_t elem_sz,
u32 expected_value, int nr_cpus)
{
int i, err;
err = bpf_map_lookup_elem(map_fd, key, values);
if (!ASSERT_OK(err, "bpf_map_lookup_elem cpu"))
return false;
if (!ASSERT_EQ(*(u32 *)values, expected_value, "bpf_map_lookup_elem value cpu"))
return false;
for (i = 1 ; i < nr_cpus ; i++) {
if (!ASSERT_NEQ(*(u32 *)(values + elem_sz * i), expected_value,
"bpf_map_lookup_elem value cpu"))
return false;
}
return true;
}
static void test_percpu_cgroup_storage_flags_combination(struct bpf_map *map, int nr_cpus,
struct bpf_cgroup_storage_key *key)
{
size_t value_sz = sizeof(u32), key_sz, elem_sz;
u32 *values, test_value = 0xAABBCCDD;
int err, map_fd;
u64 flags;
key_sz = sizeof(*key);
elem_sz = roundup(value_sz, 8);
map_fd = bpf_map__fd(map);
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, key_sz, 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, key_sz, 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, key_sz, values, value_sz, flags);
if (!ASSERT_EQ(err, -EINVAL, "bpf_map__update_elem cpu|noexist"))
goto out;
flags = BPF_F_ALL_CPUS | BPF_EXIST;
values[0] = test_value;
err = bpf_map__update_elem(map, key, key_sz, values, value_sz, flags);
if (!ASSERT_OK(err, "bpf_map__update_elem all_cpus|exist"))
goto out;
if (!verify_map_value_all_cpus(map_fd, key, values, elem_sz, test_value, nr_cpus))
goto out;
flags = BPF_F_CPU | BPF_EXIST;
values[0] = --test_value;
err = bpf_map__update_elem(map, key, key_sz, values, value_sz, flags);
if (!ASSERT_OK(err, "bpf_map__update_elem cpu|exist"))
goto out;
verify_map_value_first_cpu(map_fd, key, values, elem_sz, test_value, nr_cpus);
out:
free(values);
}
static void test_percpu_cgroup_storage_cpu_flag(void)
{
struct percpu_alloc_array *skel = NULL;
@ -489,6 +578,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 +627,164 @@ 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)
{
u32 max_entries = 3, key = 0, first_value = 0xAABBCCDD, test_value = first_value;
size_t value_sz = sizeof(u32), elem_sz;
int err, map_fd, nr_cpus;
bool is_hash_map;
u32 *values;
u64 flags;
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;
elem_sz = roundup(value_sz, 8);
values = calloc(nr_cpus, elem_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;
values[0] = test_value;
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;
if (!verify_map_value_all_cpus(map_fd, &key, values, elem_sz, test_value, nr_cpus))
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;
flags = BPF_F_CPU | BPF_NOEXIST;
values[0] = --test_value;
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;
if (!verify_map_value_first_cpu(map_fd, &key, values, elem_sz, test_value, nr_cpus))
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;
flags = BPF_F_ALL_CPUS | BPF_EXIST;
values[0] = --test_value;
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;
if (!verify_map_value_all_cpus(map_fd, &key, values, elem_sz, test_value, nr_cpus))
goto out;
}
flags = BPF_F_CPU | BPF_EXIST;
values[0] = --test_value;
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 (!verify_map_value_first_cpu(map_fd, &key, values, elem_sz, test_value, nr_cpus))
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 lru_percpu_hash all_cpus|noexist"))
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 lru_percpu_hash all_cpus|exist"))
goto out;
key = 0;
verify_map_value_all_cpus(map_fd, &key, values, elem_sz, first_value, nr_cpus);
out:
close(map_fd);
free(values);
}
static bool pin_current_cpu(cpu_set_t *old_mask)
{
cpu_set_t new_mask;
int err, cpu;
err = sched_getaffinity(0, sizeof(*old_mask), old_mask);
if (!ASSERT_OK(err, "sched_getaffinity"))
return false;
cpu = sched_getcpu();
if (!ASSERT_GE(cpu, 0, "sched_getcpu"))
return false;
CPU_ZERO(&new_mask);
CPU_SET(cpu, &new_mask);
err = sched_setaffinity(0, sizeof(new_mask), &new_mask);
return ASSERT_OK(err, "sched_setaffinity");
}
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 +813,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();
}