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

selftests/bpf: Test parameterized task BPF iterators.

Test iterators of vma, files and tasks.

Ensure the API works appropriately to visit all tasks,
tasks in a process, or a particular task.

Signed-off-by: Kui-Feng Lee <kuifeng@fb.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Yonghong Song <yhs@fb.com>
Acked-by: Martin KaFai Lau <martin.lau@kernel.org>
Link: https://lore.kernel.org/bpf/20220926184957.208194-5-kuifeng@fb.com

authored by

Kui-Feng Lee and committed by
Andrii Nakryiko
b3e1331e 2c4fe44f

+322 -24
+261 -21
tools/testing/selftests/bpf/prog_tests/bpf_iter.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 /* Copyright (c) 2020 Facebook */ 3 3 #include <test_progs.h> 4 + #include <unistd.h> 5 + #include <sys/syscall.h> 4 6 #include "bpf_iter_ipv6_route.skel.h" 5 7 #include "bpf_iter_netlink.skel.h" 6 8 #include "bpf_iter_bpf_map.skel.h" ··· 16 14 #include "bpf_iter_udp4.skel.h" 17 15 #include "bpf_iter_udp6.skel.h" 18 16 #include "bpf_iter_unix.skel.h" 17 + #include "bpf_iter_vma_offset.skel.h" 19 18 #include "bpf_iter_test_kern1.skel.h" 20 19 #include "bpf_iter_test_kern2.skel.h" 21 20 #include "bpf_iter_test_kern3.skel.h" ··· 46 43 } 47 44 } 48 45 49 - static void do_dummy_read(struct bpf_program *prog) 46 + static void do_dummy_read_opts(struct bpf_program *prog, struct bpf_iter_attach_opts *opts) 50 47 { 51 48 struct bpf_link *link; 52 49 char buf[16] = {}; 53 50 int iter_fd, len; 54 51 55 - link = bpf_program__attach_iter(prog, NULL); 52 + link = bpf_program__attach_iter(prog, opts); 56 53 if (!ASSERT_OK_PTR(link, "attach_iter")) 57 54 return; 58 55 ··· 69 66 70 67 free_link: 71 68 bpf_link__destroy(link); 69 + } 70 + 71 + static void do_dummy_read(struct bpf_program *prog) 72 + { 73 + do_dummy_read_opts(prog, NULL); 72 74 } 73 75 74 76 static void do_read_map_iter_fd(struct bpf_object_skeleton **skel, struct bpf_program *prog, ··· 175 167 bpf_iter_bpf_map__destroy(skel); 176 168 } 177 169 178 - static void test_task(void) 170 + static int pidfd_open(pid_t pid, unsigned int flags) 171 + { 172 + return syscall(SYS_pidfd_open, pid, flags); 173 + } 174 + 175 + static void check_bpf_link_info(const struct bpf_program *prog) 176 + { 177 + LIBBPF_OPTS(bpf_iter_attach_opts, opts); 178 + union bpf_iter_link_info linfo; 179 + struct bpf_link_info info = {}; 180 + struct bpf_link *link; 181 + __u32 info_len; 182 + int err; 183 + 184 + memset(&linfo, 0, sizeof(linfo)); 185 + linfo.task.tid = getpid(); 186 + opts.link_info = &linfo; 187 + opts.link_info_len = sizeof(linfo); 188 + 189 + link = bpf_program__attach_iter(prog, &opts); 190 + if (!ASSERT_OK_PTR(link, "attach_iter")) 191 + return; 192 + 193 + info_len = sizeof(info); 194 + err = bpf_obj_get_info_by_fd(bpf_link__fd(link), &info, &info_len); 195 + ASSERT_OK(err, "bpf_obj_get_info_by_fd"); 196 + ASSERT_EQ(info.iter.task.tid, getpid(), "check_task_tid"); 197 + 198 + bpf_link__destroy(link); 199 + } 200 + 201 + static pthread_mutex_t do_nothing_mutex; 202 + 203 + static void *do_nothing_wait(void *arg) 204 + { 205 + pthread_mutex_lock(&do_nothing_mutex); 206 + pthread_mutex_unlock(&do_nothing_mutex); 207 + 208 + pthread_exit(arg); 209 + } 210 + 211 + static void test_task_common_nocheck(struct bpf_iter_attach_opts *opts, 212 + int *num_unknown, int *num_known) 179 213 { 180 214 struct bpf_iter_task *skel; 215 + pthread_t thread_id; 216 + void *ret; 181 217 182 218 skel = bpf_iter_task__open_and_load(); 183 219 if (!ASSERT_OK_PTR(skel, "bpf_iter_task__open_and_load")) 184 220 return; 185 221 186 - do_dummy_read(skel->progs.dump_task); 222 + ASSERT_OK(pthread_mutex_lock(&do_nothing_mutex), "pthread_mutex_lock"); 223 + 224 + ASSERT_OK(pthread_create(&thread_id, NULL, &do_nothing_wait, NULL), 225 + "pthread_create"); 226 + 227 + skel->bss->tid = getpid(); 228 + 229 + do_dummy_read_opts(skel->progs.dump_task, opts); 230 + 231 + *num_unknown = skel->bss->num_unknown_tid; 232 + *num_known = skel->bss->num_known_tid; 233 + 234 + ASSERT_OK(pthread_mutex_unlock(&do_nothing_mutex), "pthread_mutex_unlock"); 235 + ASSERT_FALSE(pthread_join(thread_id, &ret) || ret != NULL, 236 + "pthread_join"); 187 237 188 238 bpf_iter_task__destroy(skel); 239 + } 240 + 241 + static void test_task_common(struct bpf_iter_attach_opts *opts, int num_unknown, int num_known) 242 + { 243 + int num_unknown_tid, num_known_tid; 244 + 245 + test_task_common_nocheck(opts, &num_unknown_tid, &num_known_tid); 246 + ASSERT_EQ(num_unknown_tid, num_unknown, "check_num_unknown_tid"); 247 + ASSERT_EQ(num_known_tid, num_known, "check_num_known_tid"); 248 + } 249 + 250 + static void test_task_tid(void) 251 + { 252 + LIBBPF_OPTS(bpf_iter_attach_opts, opts); 253 + union bpf_iter_link_info linfo; 254 + int num_unknown_tid, num_known_tid; 255 + 256 + memset(&linfo, 0, sizeof(linfo)); 257 + linfo.task.tid = getpid(); 258 + opts.link_info = &linfo; 259 + opts.link_info_len = sizeof(linfo); 260 + test_task_common(&opts, 0, 1); 261 + 262 + linfo.task.tid = 0; 263 + linfo.task.pid = getpid(); 264 + test_task_common(&opts, 1, 1); 265 + 266 + test_task_common_nocheck(NULL, &num_unknown_tid, &num_known_tid); 267 + ASSERT_GT(num_unknown_tid, 1, "check_num_unknown_tid"); 268 + ASSERT_EQ(num_known_tid, 1, "check_num_known_tid"); 269 + } 270 + 271 + static void test_task_pid(void) 272 + { 273 + LIBBPF_OPTS(bpf_iter_attach_opts, opts); 274 + union bpf_iter_link_info linfo; 275 + 276 + memset(&linfo, 0, sizeof(linfo)); 277 + linfo.task.pid = getpid(); 278 + opts.link_info = &linfo; 279 + opts.link_info_len = sizeof(linfo); 280 + 281 + test_task_common(&opts, 1, 1); 282 + } 283 + 284 + static void test_task_pidfd(void) 285 + { 286 + LIBBPF_OPTS(bpf_iter_attach_opts, opts); 287 + union bpf_iter_link_info linfo; 288 + int pidfd; 289 + 290 + pidfd = pidfd_open(getpid(), 0); 291 + if (!ASSERT_GT(pidfd, 0, "pidfd_open")) 292 + return; 293 + 294 + memset(&linfo, 0, sizeof(linfo)); 295 + linfo.task.pid_fd = pidfd; 296 + opts.link_info = &linfo; 297 + opts.link_info_len = sizeof(linfo); 298 + 299 + test_task_common(&opts, 1, 1); 300 + 301 + close(pidfd); 189 302 } 190 303 191 304 static void test_task_sleepable(void) ··· 341 212 bpf_iter_task_stack__destroy(skel); 342 213 } 343 214 344 - static void *do_nothing(void *arg) 345 - { 346 - pthread_exit(arg); 347 - } 348 - 349 215 static void test_task_file(void) 350 216 { 217 + LIBBPF_OPTS(bpf_iter_attach_opts, opts); 351 218 struct bpf_iter_task_file *skel; 219 + union bpf_iter_link_info linfo; 352 220 pthread_t thread_id; 353 221 void *ret; 354 222 ··· 355 229 356 230 skel->bss->tgid = getpid(); 357 231 358 - if (!ASSERT_OK(pthread_create(&thread_id, NULL, &do_nothing, NULL), 359 - "pthread_create")) 360 - goto done; 232 + ASSERT_OK(pthread_mutex_lock(&do_nothing_mutex), "pthread_mutex_lock"); 233 + 234 + ASSERT_OK(pthread_create(&thread_id, NULL, &do_nothing_wait, NULL), 235 + "pthread_create"); 236 + 237 + memset(&linfo, 0, sizeof(linfo)); 238 + linfo.task.tid = getpid(); 239 + opts.link_info = &linfo; 240 + opts.link_info_len = sizeof(linfo); 241 + 242 + do_dummy_read_opts(skel->progs.dump_task_file, &opts); 243 + 244 + ASSERT_EQ(skel->bss->count, 0, "check_count"); 245 + ASSERT_EQ(skel->bss->unique_tgid_count, 1, "check_unique_tgid_count"); 246 + 247 + skel->bss->last_tgid = 0; 248 + skel->bss->count = 0; 249 + skel->bss->unique_tgid_count = 0; 361 250 362 251 do_dummy_read(skel->progs.dump_task_file); 363 252 364 - if (!ASSERT_FALSE(pthread_join(thread_id, &ret) || ret != NULL, 365 - "pthread_join")) 366 - goto done; 367 - 368 253 ASSERT_EQ(skel->bss->count, 0, "check_count"); 254 + ASSERT_GT(skel->bss->unique_tgid_count, 1, "check_unique_tgid_count"); 369 255 370 - done: 256 + check_bpf_link_info(skel->progs.dump_task_file); 257 + 258 + ASSERT_OK(pthread_mutex_unlock(&do_nothing_mutex), "pthread_mutex_unlock"); 259 + ASSERT_OK(pthread_join(thread_id, &ret), "pthread_join"); 260 + ASSERT_NULL(ret, "pthread_join"); 261 + 371 262 bpf_iter_task_file__destroy(skel); 372 263 } 373 264 ··· 1392 1249 *dst = '\0'; 1393 1250 } 1394 1251 1395 - static void test_task_vma(void) 1252 + static void test_task_vma_common(struct bpf_iter_attach_opts *opts) 1396 1253 { 1397 1254 int err, iter_fd = -1, proc_maps_fd = -1; 1398 1255 struct bpf_iter_task_vma *skel; ··· 1404 1261 return; 1405 1262 1406 1263 skel->bss->pid = getpid(); 1264 + skel->bss->one_task = opts ? 1 : 0; 1407 1265 1408 1266 err = bpf_iter_task_vma__load(skel); 1409 1267 if (!ASSERT_OK(err, "bpf_iter_task_vma__load")) 1410 1268 goto out; 1411 1269 1412 1270 skel->links.proc_maps = bpf_program__attach_iter( 1413 - skel->progs.proc_maps, NULL); 1271 + skel->progs.proc_maps, opts); 1414 1272 1415 1273 if (!ASSERT_OK_PTR(skel->links.proc_maps, "bpf_program__attach_iter")) { 1416 1274 skel->links.proc_maps = NULL; ··· 1435 1291 goto out; 1436 1292 len += err; 1437 1293 } 1294 + if (opts) 1295 + ASSERT_EQ(skel->bss->one_task_error, 0, "unexpected task"); 1438 1296 1439 1297 /* read CMP_BUFFER_SIZE (1kB) from /proc/pid/maps */ 1440 1298 snprintf(maps_path, 64, "/proc/%u/maps", skel->bss->pid); ··· 1452 1306 str_strip_first_line(proc_maps_output); 1453 1307 1454 1308 ASSERT_STREQ(task_vma_output, proc_maps_output, "compare_output"); 1309 + 1310 + check_bpf_link_info(skel->progs.proc_maps); 1311 + 1455 1312 out: 1456 1313 close(proc_maps_fd); 1457 1314 close(iter_fd); ··· 1474 1325 bpf_iter_sockmap__destroy(skel); 1475 1326 } 1476 1327 1328 + static void test_task_vma(void) 1329 + { 1330 + LIBBPF_OPTS(bpf_iter_attach_opts, opts); 1331 + union bpf_iter_link_info linfo; 1332 + 1333 + memset(&linfo, 0, sizeof(linfo)); 1334 + linfo.task.tid = getpid(); 1335 + opts.link_info = &linfo; 1336 + opts.link_info_len = sizeof(linfo); 1337 + 1338 + test_task_vma_common(&opts); 1339 + test_task_vma_common(NULL); 1340 + } 1341 + 1342 + /* uprobe attach point */ 1343 + static noinline int trigger_func(int arg) 1344 + { 1345 + asm volatile (""); 1346 + return arg + 1; 1347 + } 1348 + 1349 + static void test_task_vma_offset_common(struct bpf_iter_attach_opts *opts, bool one_proc) 1350 + { 1351 + struct bpf_iter_vma_offset *skel; 1352 + struct bpf_link *link; 1353 + char buf[16] = {}; 1354 + int iter_fd, len; 1355 + int pgsz, shift; 1356 + 1357 + skel = bpf_iter_vma_offset__open_and_load(); 1358 + if (!ASSERT_OK_PTR(skel, "bpf_iter_vma_offset__open_and_load")) 1359 + return; 1360 + 1361 + skel->bss->pid = getpid(); 1362 + skel->bss->address = (uintptr_t)trigger_func; 1363 + for (pgsz = getpagesize(), shift = 0; pgsz > 1; pgsz >>= 1, shift++) 1364 + ; 1365 + skel->bss->page_shift = shift; 1366 + 1367 + link = bpf_program__attach_iter(skel->progs.get_vma_offset, opts); 1368 + if (!ASSERT_OK_PTR(link, "attach_iter")) 1369 + return; 1370 + 1371 + iter_fd = bpf_iter_create(bpf_link__fd(link)); 1372 + if (!ASSERT_GT(iter_fd, 0, "create_iter")) 1373 + goto exit; 1374 + 1375 + while ((len = read(iter_fd, buf, sizeof(buf))) > 0) 1376 + ; 1377 + buf[15] = 0; 1378 + ASSERT_EQ(strcmp(buf, "OK\n"), 0, "strcmp"); 1379 + 1380 + ASSERT_EQ(skel->bss->offset, get_uprobe_offset(trigger_func), "offset"); 1381 + if (one_proc) 1382 + ASSERT_EQ(skel->bss->unique_tgid_cnt, 1, "unique_tgid_count"); 1383 + else 1384 + ASSERT_GT(skel->bss->unique_tgid_cnt, 1, "unique_tgid_count"); 1385 + 1386 + close(iter_fd); 1387 + 1388 + exit: 1389 + bpf_link__destroy(link); 1390 + } 1391 + 1392 + static void test_task_vma_offset(void) 1393 + { 1394 + LIBBPF_OPTS(bpf_iter_attach_opts, opts); 1395 + union bpf_iter_link_info linfo; 1396 + 1397 + memset(&linfo, 0, sizeof(linfo)); 1398 + linfo.task.pid = getpid(); 1399 + opts.link_info = &linfo; 1400 + opts.link_info_len = sizeof(linfo); 1401 + 1402 + test_task_vma_offset_common(&opts, true); 1403 + 1404 + linfo.task.pid = 0; 1405 + linfo.task.tid = getpid(); 1406 + test_task_vma_offset_common(&opts, true); 1407 + 1408 + test_task_vma_offset_common(NULL, false); 1409 + } 1410 + 1477 1411 void test_bpf_iter(void) 1478 1412 { 1413 + ASSERT_OK(pthread_mutex_init(&do_nothing_mutex, NULL), "pthread_mutex_init"); 1414 + 1479 1415 if (test__start_subtest("btf_id_or_null")) 1480 1416 test_btf_id_or_null(); 1481 1417 if (test__start_subtest("ipv6_route")) ··· 1569 1335 test_netlink(); 1570 1336 if (test__start_subtest("bpf_map")) 1571 1337 test_bpf_map(); 1572 - if (test__start_subtest("task")) 1573 - test_task(); 1338 + if (test__start_subtest("task_tid")) 1339 + test_task_tid(); 1340 + if (test__start_subtest("task_pid")) 1341 + test_task_pid(); 1342 + if (test__start_subtest("task_pidfd")) 1343 + test_task_pidfd(); 1574 1344 if (test__start_subtest("task_sleepable")) 1575 1345 test_task_sleepable(); 1576 1346 if (test__start_subtest("task_stack")) ··· 1635 1397 test_ksym_iter(); 1636 1398 if (test__start_subtest("bpf_sockmap_map_iter_fd")) 1637 1399 test_bpf_sockmap_map_iter_fd(); 1400 + if (test__start_subtest("vma_offset")) 1401 + test_task_vma_offset(); 1638 1402 }
+1 -1
tools/testing/selftests/bpf/prog_tests/btf_dump.c
··· 764 764 765 765 /* union with nested struct */ 766 766 TEST_BTF_DUMP_DATA(btf, d, "union", str, union bpf_iter_link_info, BTF_F_COMPACT, 767 - "(union bpf_iter_link_info){.map = (struct){.map_fd = (__u32)1,},.cgroup = (struct){.order = (enum bpf_cgroup_iter_order)BPF_CGROUP_ITER_SELF_ONLY,.cgroup_fd = (__u32)1,},}", 767 + "(union bpf_iter_link_info){.map = (struct){.map_fd = (__u32)1,},.cgroup = (struct){.order = (enum bpf_cgroup_iter_order)BPF_CGROUP_ITER_SELF_ONLY,.cgroup_fd = (__u32)1,},.task = (struct){.tid = (__u32)1,.pid = (__u32)1,},}", 768 768 { .cgroup = { .order = 1, .cgroup_fd = 1, }}); 769 769 770 770 /* struct skb with nested structs/unions; because type output is so
+9
tools/testing/selftests/bpf/progs/bpf_iter_task.c
··· 6 6 7 7 char _license[] SEC("license") = "GPL"; 8 8 9 + uint32_t tid = 0; 10 + int num_unknown_tid = 0; 11 + int num_known_tid = 0; 12 + 9 13 SEC("iter/task") 10 14 int dump_task(struct bpf_iter__task *ctx) 11 15 { ··· 21 17 BPF_SEQ_PRINTF(seq, "%s\n", info); 22 18 return 0; 23 19 } 20 + 21 + if (task->pid != tid) 22 + num_unknown_tid++; 23 + else 24 + num_known_tid++; 24 25 25 26 if (ctx->meta->seq_num == 0) 26 27 BPF_SEQ_PRINTF(seq, " tgid gid\n");
+8 -1
tools/testing/selftests/bpf/progs/bpf_iter_task_file.c
··· 7 7 8 8 int count = 0; 9 9 int tgid = 0; 10 + int last_tgid = 0; 11 + int unique_tgid_count = 0; 10 12 11 13 SEC("iter/task_file") 12 14 int dump_task_file(struct bpf_iter__task_file *ctx) 13 15 { 14 16 struct seq_file *seq = ctx->meta->seq; 15 17 struct task_struct *task = ctx->task; 16 - __u32 fd = ctx->fd; 17 18 struct file *file = ctx->file; 19 + __u32 fd = ctx->fd; 18 20 19 21 if (task == (void *)0 || file == (void *)0) 20 22 return 0; ··· 28 26 29 27 if (tgid == task->tgid && task->tgid != task->pid) 30 28 count++; 29 + 30 + if (last_tgid != task->tgid) { 31 + last_tgid = task->tgid; 32 + unique_tgid_count++; 33 + } 31 34 32 35 BPF_SEQ_PRINTF(seq, "%8d %8d %8d %lx\n", task->tgid, task->pid, fd, 33 36 (long)file->f_op);
+6 -1
tools/testing/selftests/bpf/progs/bpf_iter_task_vma.c
··· 20 20 #define D_PATH_BUF_SIZE 1024 21 21 char d_path_buf[D_PATH_BUF_SIZE] = {}; 22 22 __u32 pid = 0; 23 + __u32 one_task = 0; 24 + __u32 one_task_error = 0; 23 25 24 26 SEC("iter/task_vma") int proc_maps(struct bpf_iter__task_vma *ctx) 25 27 { ··· 35 33 return 0; 36 34 37 35 file = vma->vm_file; 38 - if (task->tgid != pid) 36 + if (task->tgid != pid) { 37 + if (one_task) 38 + one_task_error = 1; 39 39 return 0; 40 + } 40 41 perm_str[0] = (vma->vm_flags & VM_READ) ? 'r' : '-'; 41 42 perm_str[1] = (vma->vm_flags & VM_WRITE) ? 'w' : '-'; 42 43 perm_str[2] = (vma->vm_flags & VM_EXEC) ? 'x' : '-';
+37
tools/testing/selftests/bpf/progs/bpf_iter_vma_offset.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ 3 + #include "bpf_iter.h" 4 + #include <bpf/bpf_helpers.h> 5 + 6 + char _license[] SEC("license") = "GPL"; 7 + 8 + __u32 unique_tgid_cnt = 0; 9 + uintptr_t address = 0; 10 + uintptr_t offset = 0; 11 + __u32 last_tgid = 0; 12 + __u32 pid = 0; 13 + __u32 page_shift = 0; 14 + 15 + SEC("iter/task_vma") 16 + int get_vma_offset(struct bpf_iter__task_vma *ctx) 17 + { 18 + struct vm_area_struct *vma = ctx->vma; 19 + struct seq_file *seq = ctx->meta->seq; 20 + struct task_struct *task = ctx->task; 21 + 22 + if (task == NULL || vma == NULL) 23 + return 0; 24 + 25 + if (last_tgid != task->tgid) 26 + unique_tgid_cnt++; 27 + last_tgid = task->tgid; 28 + 29 + if (task->tgid != pid) 30 + return 0; 31 + 32 + if (vma->vm_start <= address && vma->vm_end > address) { 33 + offset = address - vma->vm_start + (vma->vm_pgoff << page_shift); 34 + BPF_SEQ_PRINTF(seq, "OK\n"); 35 + } 36 + return 0; 37 + }