Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <joannekoong@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20211130030622.4131246-3-joannekoong@fb.com

authored by

Joanne Koong and committed by
Alexei Starovoitov
4e5070b6 e6f2dd0f

+257
+145
tools/testing/selftests/bpf/prog_tests/bpf_loop.c
··· 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 + struct bpf_link *link; 11 + 12 + link = bpf_program__attach(skel->progs.test_prog); 13 + if (!ASSERT_OK_PTR(link, "link")) 14 + return; 15 + 16 + /* test 0 loops */ 17 + skel->bss->nr_loops = 0; 18 + 19 + usleep(1); 20 + 21 + ASSERT_EQ(skel->bss->nr_loops_returned, skel->bss->nr_loops, 22 + "0 loops"); 23 + 24 + /* test 500 loops */ 25 + skel->bss->nr_loops = 500; 26 + 27 + usleep(1); 28 + 29 + ASSERT_EQ(skel->bss->nr_loops_returned, skel->bss->nr_loops, 30 + "500 loops"); 31 + ASSERT_EQ(skel->bss->g_output, (500 * 499) / 2, "g_output"); 32 + 33 + /* test exceeding the max limit */ 34 + skel->bss->nr_loops = -1; 35 + 36 + usleep(1); 37 + 38 + ASSERT_EQ(skel->bss->err, -E2BIG, "over max limit"); 39 + 40 + bpf_link__destroy(link); 41 + } 42 + 43 + static void check_callback_fn_stop(struct bpf_loop *skel) 44 + { 45 + struct bpf_link *link; 46 + 47 + link = bpf_program__attach(skel->progs.test_prog); 48 + if (!ASSERT_OK_PTR(link, "link")) 49 + return; 50 + 51 + /* testing that loop is stopped when callback_fn returns 1 */ 52 + skel->bss->nr_loops = 400; 53 + skel->data->stop_index = 50; 54 + 55 + usleep(1); 56 + 57 + ASSERT_EQ(skel->bss->nr_loops_returned, skel->data->stop_index + 1, 58 + "nr_loops_returned"); 59 + ASSERT_EQ(skel->bss->g_output, (50 * 49) / 2, 60 + "g_output"); 61 + 62 + bpf_link__destroy(link); 63 + } 64 + 65 + static void check_null_callback_ctx(struct bpf_loop *skel) 66 + { 67 + struct bpf_link *link; 68 + 69 + /* check that user is able to pass in a null callback_ctx */ 70 + link = bpf_program__attach(skel->progs.prog_null_ctx); 71 + if (!ASSERT_OK_PTR(link, "link")) 72 + return; 73 + 74 + skel->bss->nr_loops = 10; 75 + 76 + usleep(1); 77 + 78 + ASSERT_EQ(skel->bss->nr_loops_returned, skel->bss->nr_loops, 79 + "nr_loops_returned"); 80 + 81 + bpf_link__destroy(link); 82 + } 83 + 84 + static void check_invalid_flags(struct bpf_loop *skel) 85 + { 86 + struct bpf_link *link; 87 + 88 + /* check that passing in non-zero flags returns -EINVAL */ 89 + link = bpf_program__attach(skel->progs.prog_invalid_flags); 90 + if (!ASSERT_OK_PTR(link, "link")) 91 + return; 92 + 93 + usleep(1); 94 + 95 + ASSERT_EQ(skel->bss->err, -EINVAL, "err"); 96 + 97 + bpf_link__destroy(link); 98 + } 99 + 100 + static void check_nested_calls(struct bpf_loop *skel) 101 + { 102 + __u32 nr_loops = 100, nested_callback_nr_loops = 4; 103 + struct bpf_link *link; 104 + 105 + /* check that nested calls are supported */ 106 + link = bpf_program__attach(skel->progs.prog_nested_calls); 107 + if (!ASSERT_OK_PTR(link, "link")) 108 + return; 109 + 110 + skel->bss->nr_loops = nr_loops; 111 + skel->bss->nested_callback_nr_loops = nested_callback_nr_loops; 112 + 113 + usleep(1); 114 + 115 + ASSERT_EQ(skel->bss->nr_loops_returned, nr_loops * nested_callback_nr_loops 116 + * nested_callback_nr_loops, "nr_loops_returned"); 117 + ASSERT_EQ(skel->bss->g_output, (4 * 3) / 2 * nested_callback_nr_loops 118 + * nr_loops, "g_output"); 119 + 120 + bpf_link__destroy(link); 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 + skel->bss->pid = getpid(); 132 + 133 + if (test__start_subtest("check_nr_loops")) 134 + check_nr_loops(skel); 135 + if (test__start_subtest("check_callback_fn_stop")) 136 + check_callback_fn_stop(skel); 137 + if (test__start_subtest("check_null_callback_ctx")) 138 + check_null_callback_ctx(skel); 139 + if (test__start_subtest("check_invalid_flags")) 140 + check_invalid_flags(skel); 141 + if (test__start_subtest("check_nested_calls")) 142 + check_nested_calls(skel); 143 + 144 + bpf_loop__destroy(skel); 145 + }
+112
tools/testing/selftests/bpf/progs/bpf_loop.c
··· 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 + int pid; 18 + 19 + /* Making these global variables so that the userspace program 20 + * can verify the output through the skeleton 21 + */ 22 + int nr_loops_returned; 23 + int g_output; 24 + int err; 25 + 26 + static int callback(__u32 index, void *data) 27 + { 28 + struct callback_ctx *ctx = data; 29 + 30 + if (index >= stop_index) 31 + return 1; 32 + 33 + ctx->output += index; 34 + 35 + return 0; 36 + } 37 + 38 + static int empty_callback(__u32 index, void *data) 39 + { 40 + return 0; 41 + } 42 + 43 + static int nested_callback2(__u32 index, void *data) 44 + { 45 + nr_loops_returned += bpf_loop(nested_callback_nr_loops, callback, data, 0); 46 + 47 + return 0; 48 + } 49 + 50 + static int nested_callback1(__u32 index, void *data) 51 + { 52 + bpf_loop(nested_callback_nr_loops, nested_callback2, data, 0); 53 + return 0; 54 + } 55 + 56 + SEC("fentry/__x64_sys_nanosleep") 57 + int test_prog(void *ctx) 58 + { 59 + struct callback_ctx data = {}; 60 + 61 + if (bpf_get_current_pid_tgid() >> 32 != pid) 62 + return 0; 63 + 64 + nr_loops_returned = bpf_loop(nr_loops, callback, &data, 0); 65 + 66 + if (nr_loops_returned < 0) 67 + err = nr_loops_returned; 68 + else 69 + g_output = data.output; 70 + 71 + return 0; 72 + } 73 + 74 + SEC("fentry/__x64_sys_nanosleep") 75 + int prog_null_ctx(void *ctx) 76 + { 77 + if (bpf_get_current_pid_tgid() >> 32 != pid) 78 + return 0; 79 + 80 + nr_loops_returned = bpf_loop(nr_loops, empty_callback, NULL, 0); 81 + 82 + return 0; 83 + } 84 + 85 + SEC("fentry/__x64_sys_nanosleep") 86 + int prog_invalid_flags(void *ctx) 87 + { 88 + struct callback_ctx data = {}; 89 + 90 + if (bpf_get_current_pid_tgid() >> 32 != pid) 91 + return 0; 92 + 93 + err = bpf_loop(nr_loops, callback, &data, 1); 94 + 95 + return 0; 96 + } 97 + 98 + SEC("fentry/__x64_sys_nanosleep") 99 + int prog_nested_calls(void *ctx) 100 + { 101 + struct callback_ctx data = {}; 102 + 103 + if (bpf_get_current_pid_tgid() >> 32 != pid) 104 + return 0; 105 + 106 + nr_loops_returned = 0; 107 + bpf_loop(nr_loops, nested_callback1, &data, 0); 108 + 109 + g_output = data.output; 110 + 111 + return 0; 112 + }