Skip to content

Commit ed6ffe9

Browse files
jkoong-fbintel-lab-lkp
authored andcommitted
selftests/bpf: Add bpf_loop test
Add test for bpf_loop testing a variety of cases: various nr_loops, null callback ctx, invalid flags, nested callbacks. Signed-off-by: Joanne Koong <[email protected]>
1 parent 36deec7 commit ed6ffe9

File tree

2 files changed

+237
-0
lines changed

2 files changed

+237
-0
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2021 Facebook */
3+
4+
#include <test_progs.h>
5+
#include <network_helpers.h>
6+
#include "bpf_loop.skel.h"
7+
8+
static void check_nr_loops(struct bpf_loop *skel)
9+
{
10+
__u32 retval, duration;
11+
int err;
12+
13+
/* test 0 loops */
14+
skel->bss->nr_loops = 0;
15+
err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_prog),
16+
1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
17+
&retval, &duration);
18+
if (!ASSERT_OK(err, "err") || !ASSERT_OK(retval, "retval"))
19+
return;
20+
ASSERT_EQ(skel->bss->nr_loops_returned, skel->bss->nr_loops,
21+
"0 loops");
22+
23+
/* test 500 loops */
24+
skel->bss->nr_loops = 500;
25+
err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_prog),
26+
1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
27+
&retval, &duration);
28+
if (!ASSERT_OK(err, "err") ||
29+
!ASSERT_OK(retval, "retval"))
30+
return;
31+
ASSERT_EQ(skel->bss->nr_loops_returned, skel->bss->nr_loops,
32+
"500 loops");
33+
ASSERT_EQ(skel->bss->g_output, (500 * 499) / 2, "g_output");
34+
35+
/* test exceeding the max limit */
36+
skel->bss->nr_loops = -1;
37+
err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_prog),
38+
1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
39+
&retval, &duration);
40+
if (!ASSERT_OK(err, "err") || !ASSERT_OK(retval, "retval"))
41+
return;
42+
ASSERT_EQ(skel->bss->err, -E2BIG, "over max limit");
43+
}
44+
45+
static void check_callback_fn_stop(struct bpf_loop *skel)
46+
{
47+
__u32 retval, duration;
48+
int err;
49+
50+
skel->bss->nr_loops = 400;
51+
skel->data->stop_index = 50;
52+
53+
/* testing that loop is stopped when callback_fn returns 1 */
54+
err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_prog),
55+
1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
56+
&retval, &duration);
57+
58+
if (!ASSERT_OK(err, "err") || !ASSERT_OK(retval, "retval"))
59+
return;
60+
61+
ASSERT_EQ(skel->bss->nr_loops_returned, skel->data->stop_index + 1,
62+
"nr_loops_returned");
63+
ASSERT_EQ(skel->bss->g_output, (50 * 49) / 2,
64+
"g_output");
65+
}
66+
67+
static void check_null_callback_ctx(struct bpf_loop *skel)
68+
{
69+
__u32 retval, duration;
70+
int err;
71+
72+
skel->bss->nr_loops = 10;
73+
74+
/* check that user is able to pass in a null callback_ctx */
75+
err = bpf_prog_test_run(bpf_program__fd(skel->progs.prog_null_ctx),
76+
1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
77+
&retval, &duration);
78+
79+
if (!ASSERT_OK(err, "err") || !ASSERT_OK(retval, "retval"))
80+
return;
81+
82+
ASSERT_EQ(skel->bss->nr_loops_returned, skel->bss->nr_loops,
83+
"nr_loops_returned");
84+
}
85+
86+
static void check_invalid_flags(struct bpf_loop *skel)
87+
{
88+
__u32 retval, duration;
89+
int err;
90+
91+
/* check that passing in non-zero flags returns -EINVAL */
92+
err = bpf_prog_test_run(bpf_program__fd(skel->progs.prog_invalid_flags),
93+
1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
94+
&retval, &duration);
95+
96+
if (!ASSERT_OK(err, "err") || !ASSERT_OK(retval, "retval"))
97+
return;
98+
99+
ASSERT_EQ(skel->bss->err, -EINVAL, "err");
100+
}
101+
102+
static void check_nested_calls(struct bpf_loop *skel)
103+
{
104+
__u32 nr_loops = 100, nested_callback_nr_loops = 4;
105+
__u32 retval, duration;
106+
int err;
107+
108+
skel->bss->nr_loops = nr_loops;
109+
skel->bss->nested_callback_nr_loops = nested_callback_nr_loops;
110+
111+
/* check that nested calls are supported */
112+
err = bpf_prog_test_run(bpf_program__fd(skel->progs.prog_nested_calls),
113+
1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
114+
&retval, &duration);
115+
if (!ASSERT_OK(err, "err") || !ASSERT_OK(retval, "retval"))
116+
return;
117+
ASSERT_EQ(skel->bss->nr_loops_returned, nr_loops * nested_callback_nr_loops
118+
* nested_callback_nr_loops, "nr_loops_returned");
119+
ASSERT_EQ(skel->bss->g_output, (4 * 3) / 2 * nested_callback_nr_loops
120+
* nr_loops, "g_output");
121+
}
122+
123+
void test_bpf_loop(void)
124+
{
125+
struct bpf_loop *skel;
126+
127+
skel = bpf_loop__open_and_load();
128+
if (!ASSERT_OK_PTR(skel, "bpf_loop__open_and_load"))
129+
return;
130+
131+
check_nr_loops(skel);
132+
check_callback_fn_stop(skel);
133+
check_null_callback_ctx(skel);
134+
check_invalid_flags(skel);
135+
check_nested_calls(skel);
136+
137+
bpf_loop__destroy(skel);
138+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2021 Facebook */
3+
4+
#include "vmlinux.h"
5+
#include <bpf/bpf_helpers.h>
6+
7+
char _license[] SEC("license") = "GPL";
8+
9+
struct callback_ctx {
10+
int output;
11+
};
12+
13+
/* These should be set by the user program */
14+
u32 nested_callback_nr_loops;
15+
u32 stop_index = -1;
16+
u32 nr_loops;
17+
18+
/* Making these global variables so that the userspace program
19+
* can verify the output through the skeleton
20+
*/
21+
int nr_loops_returned;
22+
int g_output;
23+
int err;
24+
25+
static int callback(__u32 index, void *data)
26+
{
27+
struct callback_ctx *ctx = data;
28+
29+
if (index >= stop_index)
30+
return 1;
31+
32+
ctx->output += index;
33+
34+
return 0;
35+
}
36+
37+
static int empty_callback(__u32 index, void *data)
38+
{
39+
return 0;
40+
}
41+
42+
static int nested_callback2(__u32 index, void *data)
43+
{
44+
nr_loops_returned += bpf_loop(nested_callback_nr_loops, callback, data, 0);
45+
46+
return 0;
47+
}
48+
49+
static int nested_callback1(__u32 index, void *data)
50+
{
51+
bpf_loop(nested_callback_nr_loops, nested_callback2, data, 0);
52+
return 0;
53+
}
54+
55+
SEC("tc")
56+
int test_prog(struct __sk_buff *skb)
57+
{
58+
struct callback_ctx data = {};
59+
60+
nr_loops_returned = bpf_loop(nr_loops, callback, &data, 0);
61+
62+
if (nr_loops_returned < 0)
63+
err = nr_loops_returned;
64+
else
65+
g_output = data.output;
66+
67+
return 0;
68+
}
69+
70+
SEC("tc")
71+
int prog_null_ctx(struct __sk_buff *skb)
72+
{
73+
nr_loops_returned = bpf_loop(nr_loops, empty_callback, NULL, 0);
74+
75+
return 0;
76+
}
77+
78+
SEC("tc")
79+
int prog_invalid_flags(struct __sk_buff *skb)
80+
{
81+
struct callback_ctx data = {};
82+
83+
err = bpf_loop(nr_loops, callback, &data, 1);
84+
85+
return 0;
86+
}
87+
88+
SEC("tc")
89+
int prog_nested_calls(struct __sk_buff *skb)
90+
{
91+
struct callback_ctx data = {};
92+
93+
nr_loops_returned = 0;
94+
bpf_loop(nr_loops, nested_callback1, &data, 0);
95+
96+
g_output = data.output;
97+
98+
return 0;
99+
}

0 commit comments

Comments
 (0)