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 static void check_non_constant_callback(struct bpf_loop *skel) 124 { 125 struct bpf_link *link = 126 bpf_program__attach(skel->progs.prog_non_constant_callback); 127 128 if (!ASSERT_OK_PTR(link, "link")) 129 return; 130 131 skel->bss->callback_selector = 0x0F; 132 usleep(1); 133 ASSERT_EQ(skel->bss->g_output, 0x0F, "g_output #1"); 134 135 skel->bss->callback_selector = 0xF0; 136 usleep(1); 137 ASSERT_EQ(skel->bss->g_output, 0xF0, "g_output #2"); 138 139 bpf_link__destroy(link); 140 } 141 142 static void check_stack(struct bpf_loop *skel) 143 { 144 struct bpf_link *link = bpf_program__attach(skel->progs.stack_check); 145 const int max_key = 12; 146 int key; 147 int map_fd; 148 149 if (!ASSERT_OK_PTR(link, "link")) 150 return; 151 152 map_fd = bpf_map__fd(skel->maps.map1); 153 154 if (!ASSERT_GE(map_fd, 0, "bpf_map__fd")) 155 goto out; 156 157 for (key = 1; key <= max_key; ++key) { 158 int val = key; 159 int err = bpf_map_update_elem(map_fd, &key, &val, BPF_NOEXIST); 160 161 if (!ASSERT_OK(err, "bpf_map_update_elem")) 162 goto out; 163 } 164 165 usleep(1); 166 167 for (key = 1; key <= max_key; ++key) { 168 int val; 169 int err = bpf_map_lookup_elem(map_fd, &key, &val); 170 171 if (!ASSERT_OK(err, "bpf_map_lookup_elem")) 172 goto out; 173 if (!ASSERT_EQ(val, key + 1, "bad value in the map")) 174 goto out; 175 } 176 177 out: 178 bpf_link__destroy(link); 179 } 180 181 void test_bpf_loop(void) 182 { 183 struct bpf_loop *skel; 184 185 skel = bpf_loop__open_and_load(); 186 if (!ASSERT_OK_PTR(skel, "bpf_loop__open_and_load")) 187 return; 188 189 skel->bss->pid = getpid(); 190 191 if (test__start_subtest("check_nr_loops")) 192 check_nr_loops(skel); 193 if (test__start_subtest("check_callback_fn_stop")) 194 check_callback_fn_stop(skel); 195 if (test__start_subtest("check_null_callback_ctx")) 196 check_null_callback_ctx(skel); 197 if (test__start_subtest("check_invalid_flags")) 198 check_invalid_flags(skel); 199 if (test__start_subtest("check_nested_calls")) 200 check_nested_calls(skel); 201 if (test__start_subtest("check_non_constant_callback")) 202 check_non_constant_callback(skel); 203 if (test__start_subtest("check_stack")) 204 check_stack(skel); 205 206 bpf_loop__destroy(skel); 207 } 208