| // SPDX-License-Identifier: GPL-2.0 | |
| /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ | |
| #include <test_progs.h> | |
| #include <string.h> | |
| #include <stdio.h> | |
| #include "task_work.skel.h" | |
| #include "task_work_fail.skel.h" | |
| #include <linux/bpf.h> | |
| #include <linux/perf_event.h> | |
| #include <sys/syscall.h> | |
| #include <time.h> | |
| static int perf_event_open(__u32 type, __u64 config, int pid) | |
| { | |
| struct perf_event_attr attr = { | |
| .type = type, | |
| .config = config, | |
| .size = sizeof(struct perf_event_attr), | |
| .sample_period = 100000, | |
| }; | |
| return syscall(__NR_perf_event_open, &attr, pid, -1, -1, 0); | |
| } | |
| struct elem { | |
| char data[128]; | |
| struct bpf_task_work tw; | |
| }; | |
| static int verify_map(struct bpf_map *map, const char *expected_data) | |
| { | |
| int err; | |
| struct elem value; | |
| int processed_values = 0; | |
| int k, sz; | |
| sz = bpf_map__max_entries(map); | |
| for (k = 0; k < sz; ++k) { | |
| err = bpf_map__lookup_elem(map, &k, sizeof(int), &value, sizeof(struct elem), 0); | |
| if (err) | |
| continue; | |
| if (!ASSERT_EQ(strcmp(expected_data, value.data), 0, "map data")) { | |
| fprintf(stderr, "expected '%s', found '%s' in %s map", expected_data, | |
| value.data, bpf_map__name(map)); | |
| return 2; | |
| } | |
| processed_values++; | |
| } | |
| return processed_values == 0; | |
| } | |
| static void task_work_run(const char *prog_name, const char *map_name) | |
| { | |
| struct task_work *skel; | |
| struct bpf_program *prog; | |
| struct bpf_map *map; | |
| struct bpf_link *link = NULL; | |
| int err, pe_fd = -1, pid, status, pipefd[2]; | |
| char user_string[] = "hello world"; | |
| if (!ASSERT_NEQ(pipe(pipefd), -1, "pipe")) | |
| return; | |
| pid = fork(); | |
| if (pid == 0) { | |
| __u64 num = 1; | |
| int i; | |
| char buf; | |
| close(pipefd[1]); | |
| read(pipefd[0], &buf, sizeof(buf)); | |
| close(pipefd[0]); | |
| for (i = 0; i < 10000; ++i) | |
| num *= time(0) % 7; | |
| (void)num; | |
| exit(0); | |
| } | |
| if (!ASSERT_GT(pid, 0, "fork() failed")) { | |
| close(pipefd[0]); | |
| close(pipefd[1]); | |
| return; | |
| } | |
| skel = task_work__open(); | |
| if (!ASSERT_OK_PTR(skel, "task_work__open")) | |
| return; | |
| bpf_object__for_each_program(prog, skel->obj) { | |
| bpf_program__set_autoload(prog, false); | |
| } | |
| prog = bpf_object__find_program_by_name(skel->obj, prog_name); | |
| if (!ASSERT_OK_PTR(prog, "prog_name")) | |
| goto cleanup; | |
| bpf_program__set_autoload(prog, true); | |
| skel->bss->user_ptr = (char *)user_string; | |
| err = task_work__load(skel); | |
| if (!ASSERT_OK(err, "skel_load")) | |
| goto cleanup; | |
| pe_fd = perf_event_open(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, pid); | |
| if (pe_fd == -1 && (errno == ENOENT || errno == EOPNOTSUPP)) { | |
| printf("%s:SKIP:no PERF_COUNT_HW_CPU_CYCLES\n", __func__); | |
| test__skip(); | |
| goto cleanup; | |
| } | |
| if (!ASSERT_NEQ(pe_fd, -1, "pe_fd")) { | |
| fprintf(stderr, "perf_event_open errno: %d, pid: %d\n", errno, pid); | |
| goto cleanup; | |
| } | |
| link = bpf_program__attach_perf_event(prog, pe_fd); | |
| if (!ASSERT_OK_PTR(link, "attach_perf_event")) | |
| goto cleanup; | |
| /* perf event fd ownership is passed to bpf_link */ | |
| pe_fd = -1; | |
| close(pipefd[0]); | |
| write(pipefd[1], user_string, 1); | |
| close(pipefd[1]); | |
| /* Wait to collect some samples */ | |
| waitpid(pid, &status, 0); | |
| pid = 0; | |
| map = bpf_object__find_map_by_name(skel->obj, map_name); | |
| if (!ASSERT_OK_PTR(map, "find map_name")) | |
| goto cleanup; | |
| if (!ASSERT_OK(verify_map(map, user_string), "verify map")) | |
| goto cleanup; | |
| cleanup: | |
| if (pe_fd >= 0) | |
| close(pe_fd); | |
| bpf_link__destroy(link); | |
| task_work__destroy(skel); | |
| if (pid > 0) { | |
| close(pipefd[0]); | |
| write(pipefd[1], user_string, 1); | |
| close(pipefd[1]); | |
| waitpid(pid, &status, 0); | |
| } | |
| } | |
| void test_task_work(void) | |
| { | |
| if (test__start_subtest("test_task_work_hash_map")) | |
| task_work_run("oncpu_hash_map", "hmap"); | |
| if (test__start_subtest("test_task_work_array_map")) | |
| task_work_run("oncpu_array_map", "arrmap"); | |
| if (test__start_subtest("test_task_work_lru_map")) | |
| task_work_run("oncpu_lru_map", "lrumap"); | |
| RUN_TESTS(task_work_fail); | |
| } |