-
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 a test with bpf_timer in inner map.
Check that map-in-map supports bpf timers. Check that indirect "recursion" of timer callbacks works: timer_cb1() { bpf_timer_set_callback(timer_cb2); } timer_cb2() { bpf_timer_set_callback(timer_cb1); } Check that bpf_map_release htab_free_prealloced_timers bpf_timer_cancel_and_free hrtimer_cancel works while timer cb is running. "while true; do ./test_progs -t timer_mim; done" is a great stress test. It caught missing timer cancel in htab->extra_elems. timer_mim_reject.c is a negative test that checks that timer<->map mismatch is prevented. Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Andrii Nakryiko <andrii@kernel.org> Acked-by: Toke Høiland-Jørgensen <toke@redhat.com> Link: https://lore.kernel.org/bpf/20210715005417.78572-12-alexei.starovoitov@gmail.com
- Loading branch information
Alexei Starovoitov
authored and
Daniel Borkmann
committed
Jul 15, 2021
1 parent
3540f7c
commit 61f71e7
Showing
3 changed files
with
231 additions
and
0 deletions.
There are no files selected for viewing
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,69 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2021 Facebook */ | ||
#include <test_progs.h> | ||
#include "timer_mim.skel.h" | ||
#include "timer_mim_reject.skel.h" | ||
|
||
static int timer_mim(struct timer_mim *timer_skel) | ||
{ | ||
__u32 duration = 0, retval; | ||
__u64 cnt1, cnt2; | ||
int err, prog_fd, key1 = 1; | ||
|
||
err = timer_mim__attach(timer_skel); | ||
if (!ASSERT_OK(err, "timer_attach")) | ||
return err; | ||
|
||
prog_fd = bpf_program__fd(timer_skel->progs.test1); | ||
err = bpf_prog_test_run(prog_fd, 1, NULL, 0, | ||
NULL, NULL, &retval, &duration); | ||
ASSERT_OK(err, "test_run"); | ||
ASSERT_EQ(retval, 0, "test_run"); | ||
timer_mim__detach(timer_skel); | ||
|
||
/* check that timer_cb[12] are incrementing 'cnt' */ | ||
cnt1 = READ_ONCE(timer_skel->bss->cnt); | ||
usleep(200); /* 100 times more than interval */ | ||
cnt2 = READ_ONCE(timer_skel->bss->cnt); | ||
ASSERT_GT(cnt2, cnt1, "cnt"); | ||
|
||
ASSERT_EQ(timer_skel->bss->err, 0, "err"); | ||
/* check that code paths completed */ | ||
ASSERT_EQ(timer_skel->bss->ok, 1 | 2, "ok"); | ||
|
||
close(bpf_map__fd(timer_skel->maps.inner_htab)); | ||
err = bpf_map_delete_elem(bpf_map__fd(timer_skel->maps.outer_arr), &key1); | ||
ASSERT_EQ(err, 0, "delete inner map"); | ||
|
||
/* check that timer_cb[12] are no longer running */ | ||
cnt1 = READ_ONCE(timer_skel->bss->cnt); | ||
usleep(200); | ||
cnt2 = READ_ONCE(timer_skel->bss->cnt); | ||
ASSERT_EQ(cnt2, cnt1, "cnt"); | ||
|
||
return 0; | ||
} | ||
|
||
void test_timer_mim(void) | ||
{ | ||
struct timer_mim_reject *timer_reject_skel = NULL; | ||
libbpf_print_fn_t old_print_fn = NULL; | ||
struct timer_mim *timer_skel = NULL; | ||
int err; | ||
|
||
old_print_fn = libbpf_set_print(NULL); | ||
timer_reject_skel = timer_mim_reject__open_and_load(); | ||
libbpf_set_print(old_print_fn); | ||
if (!ASSERT_ERR_PTR(timer_reject_skel, "timer_reject_skel_load")) | ||
goto cleanup; | ||
|
||
timer_skel = timer_mim__open_and_load(); | ||
if (!ASSERT_OK_PTR(timer_skel, "timer_skel_load")) | ||
goto cleanup; | ||
|
||
err = timer_mim(timer_skel); | ||
ASSERT_OK(err, "timer_mim"); | ||
cleanup: | ||
timer_mim__destroy(timer_skel); | ||
timer_mim_reject__destroy(timer_reject_skel); | ||
} |
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,88 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2021 Facebook */ | ||
#include <linux/bpf.h> | ||
#include <time.h> | ||
#include <errno.h> | ||
#include <bpf/bpf_helpers.h> | ||
#include "bpf_tcp_helpers.h" | ||
|
||
char _license[] SEC("license") = "GPL"; | ||
struct hmap_elem { | ||
int pad; /* unused */ | ||
struct bpf_timer timer; | ||
}; | ||
|
||
struct inner_map { | ||
__uint(type, BPF_MAP_TYPE_HASH); | ||
__uint(max_entries, 1024); | ||
__type(key, int); | ||
__type(value, struct hmap_elem); | ||
} inner_htab SEC(".maps"); | ||
|
||
#define ARRAY_KEY 1 | ||
#define HASH_KEY 1234 | ||
|
||
struct outer_arr { | ||
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); | ||
__uint(max_entries, 2); | ||
__uint(key_size, sizeof(int)); | ||
__uint(value_size, sizeof(int)); | ||
__array(values, struct inner_map); | ||
} outer_arr SEC(".maps") = { | ||
.values = { [ARRAY_KEY] = &inner_htab }, | ||
}; | ||
|
||
__u64 err; | ||
__u64 ok; | ||
__u64 cnt; | ||
|
||
static int timer_cb1(void *map, int *key, struct hmap_elem *val); | ||
|
||
static int timer_cb2(void *map, int *key, struct hmap_elem *val) | ||
{ | ||
cnt++; | ||
bpf_timer_set_callback(&val->timer, timer_cb1); | ||
if (bpf_timer_start(&val->timer, 1000, 0)) | ||
err |= 1; | ||
ok |= 1; | ||
return 0; | ||
} | ||
|
||
/* callback for inner hash map */ | ||
static int timer_cb1(void *map, int *key, struct hmap_elem *val) | ||
{ | ||
cnt++; | ||
bpf_timer_set_callback(&val->timer, timer_cb2); | ||
if (bpf_timer_start(&val->timer, 1000, 0)) | ||
err |= 2; | ||
/* Do a lookup to make sure 'map' and 'key' pointers are correct */ | ||
bpf_map_lookup_elem(map, key); | ||
ok |= 2; | ||
return 0; | ||
} | ||
|
||
SEC("fentry/bpf_fentry_test1") | ||
int BPF_PROG(test1, int a) | ||
{ | ||
struct hmap_elem init = {}; | ||
struct bpf_map *inner_map; | ||
struct hmap_elem *val; | ||
int array_key = ARRAY_KEY; | ||
int hash_key = HASH_KEY; | ||
|
||
inner_map = bpf_map_lookup_elem(&outer_arr, &array_key); | ||
if (!inner_map) | ||
return 0; | ||
|
||
bpf_map_update_elem(inner_map, &hash_key, &init, 0); | ||
val = bpf_map_lookup_elem(inner_map, &hash_key); | ||
if (!val) | ||
return 0; | ||
|
||
bpf_timer_init(&val->timer, inner_map, CLOCK_MONOTONIC); | ||
if (bpf_timer_set_callback(&val->timer, timer_cb1)) | ||
err |= 4; | ||
if (bpf_timer_start(&val->timer, 0, 0)) | ||
err |= 8; | ||
return 0; | ||
} |
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,74 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2021 Facebook */ | ||
#include <linux/bpf.h> | ||
#include <time.h> | ||
#include <errno.h> | ||
#include <bpf/bpf_helpers.h> | ||
#include "bpf_tcp_helpers.h" | ||
|
||
char _license[] SEC("license") = "GPL"; | ||
struct hmap_elem { | ||
int pad; /* unused */ | ||
struct bpf_timer timer; | ||
}; | ||
|
||
struct inner_map { | ||
__uint(type, BPF_MAP_TYPE_HASH); | ||
__uint(max_entries, 1024); | ||
__type(key, int); | ||
__type(value, struct hmap_elem); | ||
} inner_htab SEC(".maps"); | ||
|
||
#define ARRAY_KEY 1 | ||
#define ARRAY_KEY2 2 | ||
#define HASH_KEY 1234 | ||
|
||
struct outer_arr { | ||
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS); | ||
__uint(max_entries, 2); | ||
__uint(key_size, sizeof(int)); | ||
__uint(value_size, sizeof(int)); | ||
__array(values, struct inner_map); | ||
} outer_arr SEC(".maps") = { | ||
.values = { [ARRAY_KEY] = &inner_htab }, | ||
}; | ||
|
||
__u64 err; | ||
__u64 ok; | ||
__u64 cnt; | ||
|
||
/* callback for inner hash map */ | ||
static int timer_cb(void *map, int *key, struct hmap_elem *val) | ||
{ | ||
return 0; | ||
} | ||
|
||
SEC("fentry/bpf_fentry_test1") | ||
int BPF_PROG(test1, int a) | ||
{ | ||
struct hmap_elem init = {}; | ||
struct bpf_map *inner_map, *inner_map2; | ||
struct hmap_elem *val; | ||
int array_key = ARRAY_KEY; | ||
int array_key2 = ARRAY_KEY2; | ||
int hash_key = HASH_KEY; | ||
|
||
inner_map = bpf_map_lookup_elem(&outer_arr, &array_key); | ||
if (!inner_map) | ||
return 0; | ||
|
||
inner_map2 = bpf_map_lookup_elem(&outer_arr, &array_key2); | ||
if (!inner_map2) | ||
return 0; | ||
bpf_map_update_elem(inner_map, &hash_key, &init, 0); | ||
val = bpf_map_lookup_elem(inner_map, &hash_key); | ||
if (!val) | ||
return 0; | ||
|
||
bpf_timer_init(&val->timer, inner_map2, CLOCK_MONOTONIC); | ||
if (bpf_timer_set_callback(&val->timer, timer_cb)) | ||
err |= 4; | ||
if (bpf_timer_start(&val->timer, 0, 0)) | ||
err |= 8; | ||
return 0; | ||
} |