-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests/bpf: Add bpf_lookup_and_delete_elem tests
Add bpf selftests and extend existing ones for a new function bpf_lookup_and_delete_elem() for (percpu) hash and (percpu) LRU hash map types. In test_lru_map and test_maps we add an element, lookup_and_delete it, then check whether it's deleted. The newly added lookup_and_delete prog tests practically do the same thing but additionally use a BPF program to change the value of the element for LRU maps. Signed-off-by: Denis Salopek <denis.salopek@sartura.hr> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Acked-by: Yonghong Song <yhs@fb.com> Link: https://lore.kernel.org/bpf/d30d3e0060c1f750e133579623cf1c60ff58f3d9.1620763117.git.denis.salopek@sartura.hr
- Loading branch information
Denis Salopek
authored and
Andrii Nakryiko
committed
May 24, 2021
1 parent
d59b9f2
commit 49c299b
Showing
4 changed files
with
339 additions
and
0 deletions.
There are no files selected for viewing
288 changes: 288 additions & 0 deletions
288
tools/testing/selftests/bpf/prog_tests/lookup_and_delete.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
|
||
#include <test_progs.h> | ||
#include "test_lookup_and_delete.skel.h" | ||
|
||
#define START_VALUE 1234 | ||
#define NEW_VALUE 4321 | ||
#define MAX_ENTRIES 2 | ||
|
||
static int duration; | ||
static int nr_cpus; | ||
|
||
static int fill_values(int map_fd) | ||
{ | ||
__u64 key, value = START_VALUE; | ||
int err; | ||
|
||
for (key = 1; key < MAX_ENTRIES + 1; key++) { | ||
err = bpf_map_update_elem(map_fd, &key, &value, BPF_NOEXIST); | ||
if (!ASSERT_OK(err, "bpf_map_update_elem")) | ||
return -1; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int fill_values_percpu(int map_fd) | ||
{ | ||
__u64 key, value[nr_cpus]; | ||
int i, err; | ||
|
||
for (i = 0; i < nr_cpus; i++) | ||
value[i] = START_VALUE; | ||
|
||
for (key = 1; key < MAX_ENTRIES + 1; key++) { | ||
err = bpf_map_update_elem(map_fd, &key, value, BPF_NOEXIST); | ||
if (!ASSERT_OK(err, "bpf_map_update_elem")) | ||
return -1; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static struct test_lookup_and_delete *setup_prog(enum bpf_map_type map_type, | ||
int *map_fd) | ||
{ | ||
struct test_lookup_and_delete *skel; | ||
int err; | ||
|
||
skel = test_lookup_and_delete__open(); | ||
if (!ASSERT_OK_PTR(skel, "test_lookup_and_delete__open")) | ||
return NULL; | ||
|
||
err = bpf_map__set_type(skel->maps.hash_map, map_type); | ||
if (!ASSERT_OK(err, "bpf_map__set_type")) | ||
goto cleanup; | ||
|
||
err = bpf_map__set_max_entries(skel->maps.hash_map, MAX_ENTRIES); | ||
if (!ASSERT_OK(err, "bpf_map__set_max_entries")) | ||
goto cleanup; | ||
|
||
err = test_lookup_and_delete__load(skel); | ||
if (!ASSERT_OK(err, "test_lookup_and_delete__load")) | ||
goto cleanup; | ||
|
||
*map_fd = bpf_map__fd(skel->maps.hash_map); | ||
if (!ASSERT_GE(*map_fd, 0, "bpf_map__fd")) | ||
goto cleanup; | ||
|
||
return skel; | ||
|
||
cleanup: | ||
test_lookup_and_delete__destroy(skel); | ||
return NULL; | ||
} | ||
|
||
/* Triggers BPF program that updates map with given key and value */ | ||
static int trigger_tp(struct test_lookup_and_delete *skel, __u64 key, | ||
__u64 value) | ||
{ | ||
int err; | ||
|
||
skel->bss->set_pid = getpid(); | ||
skel->bss->set_key = key; | ||
skel->bss->set_value = value; | ||
|
||
err = test_lookup_and_delete__attach(skel); | ||
if (!ASSERT_OK(err, "test_lookup_and_delete__attach")) | ||
return -1; | ||
|
||
syscall(__NR_getpgid); | ||
|
||
test_lookup_and_delete__detach(skel); | ||
|
||
return 0; | ||
} | ||
|
||
static void test_lookup_and_delete_hash(void) | ||
{ | ||
struct test_lookup_and_delete *skel; | ||
__u64 key, value; | ||
int map_fd, err; | ||
|
||
/* Setup program and fill the map. */ | ||
skel = setup_prog(BPF_MAP_TYPE_HASH, &map_fd); | ||
if (!ASSERT_OK_PTR(skel, "setup_prog")) | ||
return; | ||
|
||
err = fill_values(map_fd); | ||
if (!ASSERT_OK(err, "fill_values")) | ||
goto cleanup; | ||
|
||
/* Lookup and delete element. */ | ||
key = 1; | ||
err = bpf_map_lookup_and_delete_elem(map_fd, &key, &value); | ||
if (!ASSERT_OK(err, "bpf_map_lookup_and_delete_elem")) | ||
goto cleanup; | ||
|
||
/* Fetched value should match the initially set value. */ | ||
if (CHECK(value != START_VALUE, "bpf_map_lookup_and_delete_elem", | ||
"unexpected value=%lld\n", value)) | ||
goto cleanup; | ||
|
||
/* Check that the entry is non existent. */ | ||
err = bpf_map_lookup_elem(map_fd, &key, &value); | ||
if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) | ||
goto cleanup; | ||
|
||
cleanup: | ||
test_lookup_and_delete__destroy(skel); | ||
} | ||
|
||
static void test_lookup_and_delete_percpu_hash(void) | ||
{ | ||
struct test_lookup_and_delete *skel; | ||
__u64 key, val, value[nr_cpus]; | ||
int map_fd, err, i; | ||
|
||
/* Setup program and fill the map. */ | ||
skel = setup_prog(BPF_MAP_TYPE_PERCPU_HASH, &map_fd); | ||
if (!ASSERT_OK_PTR(skel, "setup_prog")) | ||
return; | ||
|
||
err = fill_values_percpu(map_fd); | ||
if (!ASSERT_OK(err, "fill_values_percpu")) | ||
goto cleanup; | ||
|
||
/* Lookup and delete element. */ | ||
key = 1; | ||
err = bpf_map_lookup_and_delete_elem(map_fd, &key, value); | ||
if (!ASSERT_OK(err, "bpf_map_lookup_and_delete_elem")) | ||
goto cleanup; | ||
|
||
for (i = 0; i < nr_cpus; i++) { | ||
val = value[i]; | ||
|
||
/* Fetched value should match the initially set value. */ | ||
if (CHECK(val != START_VALUE, "map value", | ||
"unexpected for cpu %d: %lld\n", i, val)) | ||
goto cleanup; | ||
} | ||
|
||
/* Check that the entry is non existent. */ | ||
err = bpf_map_lookup_elem(map_fd, &key, value); | ||
if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) | ||
goto cleanup; | ||
|
||
cleanup: | ||
test_lookup_and_delete__destroy(skel); | ||
} | ||
|
||
static void test_lookup_and_delete_lru_hash(void) | ||
{ | ||
struct test_lookup_and_delete *skel; | ||
__u64 key, value; | ||
int map_fd, err; | ||
|
||
/* Setup program and fill the LRU map. */ | ||
skel = setup_prog(BPF_MAP_TYPE_LRU_HASH, &map_fd); | ||
if (!ASSERT_OK_PTR(skel, "setup_prog")) | ||
return; | ||
|
||
err = fill_values(map_fd); | ||
if (!ASSERT_OK(err, "fill_values")) | ||
goto cleanup; | ||
|
||
/* Insert new element at key=3, should reuse LRU element. */ | ||
key = 3; | ||
err = trigger_tp(skel, key, NEW_VALUE); | ||
if (!ASSERT_OK(err, "trigger_tp")) | ||
goto cleanup; | ||
|
||
/* Lookup and delete element 3. */ | ||
err = bpf_map_lookup_and_delete_elem(map_fd, &key, &value); | ||
if (!ASSERT_OK(err, "bpf_map_lookup_and_delete_elem")) | ||
goto cleanup; | ||
|
||
/* Value should match the new value. */ | ||
if (CHECK(value != NEW_VALUE, "bpf_map_lookup_and_delete_elem", | ||
"unexpected value=%lld\n", value)) | ||
goto cleanup; | ||
|
||
/* Check that entries 3 and 1 are non existent. */ | ||
err = bpf_map_lookup_elem(map_fd, &key, &value); | ||
if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) | ||
goto cleanup; | ||
|
||
key = 1; | ||
err = bpf_map_lookup_elem(map_fd, &key, &value); | ||
if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) | ||
goto cleanup; | ||
|
||
cleanup: | ||
test_lookup_and_delete__destroy(skel); | ||
} | ||
|
||
static void test_lookup_and_delete_lru_percpu_hash(void) | ||
{ | ||
struct test_lookup_and_delete *skel; | ||
__u64 key, val, value[nr_cpus]; | ||
int map_fd, err, i, cpucnt = 0; | ||
|
||
/* Setup program and fill the LRU map. */ | ||
skel = setup_prog(BPF_MAP_TYPE_LRU_PERCPU_HASH, &map_fd); | ||
if (!ASSERT_OK_PTR(skel, "setup_prog")) | ||
return; | ||
|
||
err = fill_values_percpu(map_fd); | ||
if (!ASSERT_OK(err, "fill_values_percpu")) | ||
goto cleanup; | ||
|
||
/* Insert new element at key=3, should reuse LRU element 1. */ | ||
key = 3; | ||
err = trigger_tp(skel, key, NEW_VALUE); | ||
if (!ASSERT_OK(err, "trigger_tp")) | ||
goto cleanup; | ||
|
||
/* Clean value. */ | ||
for (i = 0; i < nr_cpus; i++) | ||
value[i] = 0; | ||
|
||
/* Lookup and delete element 3. */ | ||
err = bpf_map_lookup_and_delete_elem(map_fd, &key, value); | ||
if (!ASSERT_OK(err, "bpf_map_lookup_and_delete_elem")) { | ||
goto cleanup; | ||
} | ||
|
||
/* Check if only one CPU has set the value. */ | ||
for (i = 0; i < nr_cpus; i++) { | ||
val = value[i]; | ||
if (val) { | ||
if (CHECK(val != NEW_VALUE, "map value", | ||
"unexpected for cpu %d: %lld\n", i, val)) | ||
goto cleanup; | ||
cpucnt++; | ||
} | ||
} | ||
if (CHECK(cpucnt != 1, "map value", "set for %d CPUs instead of 1!\n", | ||
cpucnt)) | ||
goto cleanup; | ||
|
||
/* Check that entries 3 and 1 are non existent. */ | ||
err = bpf_map_lookup_elem(map_fd, &key, &value); | ||
if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) | ||
goto cleanup; | ||
|
||
key = 1; | ||
err = bpf_map_lookup_elem(map_fd, &key, &value); | ||
if (!ASSERT_ERR(err, "bpf_map_lookup_elem")) | ||
goto cleanup; | ||
|
||
cleanup: | ||
test_lookup_and_delete__destroy(skel); | ||
} | ||
|
||
void test_lookup_and_delete(void) | ||
{ | ||
nr_cpus = bpf_num_possible_cpus(); | ||
|
||
if (test__start_subtest("lookup_and_delete")) | ||
test_lookup_and_delete_hash(); | ||
if (test__start_subtest("lookup_and_delete_percpu")) | ||
test_lookup_and_delete_percpu_hash(); | ||
if (test__start_subtest("lookup_and_delete_lru")) | ||
test_lookup_and_delete_lru_hash(); | ||
if (test__start_subtest("lookup_and_delete_lru_percpu")) | ||
test_lookup_and_delete_lru_percpu_hash(); | ||
} |
26 changes: 26 additions & 0 deletions
26
tools/testing/selftests/bpf/progs/test_lookup_and_delete.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
#include "vmlinux.h" | ||
#include <bpf/bpf_helpers.h> | ||
|
||
__u32 set_pid = 0; | ||
__u64 set_key = 0; | ||
__u64 set_value = 0; | ||
|
||
struct { | ||
__uint(type, BPF_MAP_TYPE_HASH); | ||
__uint(max_entries, 2); | ||
__type(key, __u64); | ||
__type(value, __u64); | ||
} hash_map SEC(".maps"); | ||
|
||
SEC("tp/syscalls/sys_enter_getpgid") | ||
int bpf_lookup_and_delete_test(const void *ctx) | ||
{ | ||
if (set_pid == bpf_get_current_pid_tgid() >> 32) | ||
bpf_map_update_elem(&hash_map, &set_key, &set_value, BPF_NOEXIST); | ||
|
||
return 0; | ||
} | ||
|
||
char _license[] SEC("license") = "GPL"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters