| // SPDX-License-Identifier: GPL-2.0 | |
| /* Copyright (c) 2021 Facebook */ | |
| #include "vmlinux.h" | |
| #include <bpf/bpf_helpers.h> | |
| #include "bpf_misc.h" | |
| char _license[] SEC("license") = "GPL"; | |
| struct callback_ctx { | |
| int output; | |
| }; | |
| struct { | |
| __uint(type, BPF_MAP_TYPE_HASH); | |
| __uint(max_entries, 32); | |
| __type(key, int); | |
| __type(value, int); | |
| } map1 SEC(".maps"); | |
| /* These should be set by the user program */ | |
| u32 nested_callback_nr_loops; | |
| u32 stop_index = -1; | |
| u32 nr_loops; | |
| int pid; | |
| int callback_selector; | |
| /* Making these global variables so that the userspace program | |
| * can verify the output through the skeleton | |
| */ | |
| int nr_loops_returned; | |
| int g_output; | |
| int err; | |
| static int callback(__u32 index, void *data) | |
| { | |
| struct callback_ctx *ctx = data; | |
| if (index >= stop_index) | |
| return 1; | |
| ctx->output += index; | |
| return 0; | |
| } | |
| static int empty_callback(__u32 index, void *data) | |
| { | |
| return 0; | |
| } | |
| static int nested_callback2(__u32 index, void *data) | |
| { | |
| nr_loops_returned += bpf_loop(nested_callback_nr_loops, callback, data, 0); | |
| return 0; | |
| } | |
| static int nested_callback1(__u32 index, void *data) | |
| { | |
| bpf_loop(nested_callback_nr_loops, nested_callback2, data, 0); | |
| return 0; | |
| } | |
| SEC("fentry/" SYS_PREFIX "sys_nanosleep") | |
| int test_prog(void *ctx) | |
| { | |
| struct callback_ctx data = {}; | |
| if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| return 0; | |
| nr_loops_returned = bpf_loop(nr_loops, callback, &data, 0); | |
| if (nr_loops_returned < 0) | |
| err = nr_loops_returned; | |
| else | |
| g_output = data.output; | |
| return 0; | |
| } | |
| SEC("fentry/" SYS_PREFIX "sys_nanosleep") | |
| int prog_null_ctx(void *ctx) | |
| { | |
| if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| return 0; | |
| nr_loops_returned = bpf_loop(nr_loops, empty_callback, NULL, 0); | |
| return 0; | |
| } | |
| SEC("fentry/" SYS_PREFIX "sys_nanosleep") | |
| int prog_invalid_flags(void *ctx) | |
| { | |
| struct callback_ctx data = {}; | |
| if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| return 0; | |
| err = bpf_loop(nr_loops, callback, &data, 1); | |
| return 0; | |
| } | |
| SEC("fentry/" SYS_PREFIX "sys_nanosleep") | |
| int prog_nested_calls(void *ctx) | |
| { | |
| struct callback_ctx data = {}; | |
| if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| return 0; | |
| nr_loops_returned = 0; | |
| bpf_loop(nr_loops, nested_callback1, &data, 0); | |
| g_output = data.output; | |
| return 0; | |
| } | |
| static int callback_set_f0(int i, void *ctx) | |
| { | |
| g_output = 0xF0; | |
| return 0; | |
| } | |
| static int callback_set_0f(int i, void *ctx) | |
| { | |
| g_output = 0x0F; | |
| return 0; | |
| } | |
| /* | |
| * non-constant callback is a corner case for bpf_loop inline logic | |
| */ | |
| SEC("fentry/" SYS_PREFIX "sys_nanosleep") | |
| int prog_non_constant_callback(void *ctx) | |
| { | |
| if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| return 0; | |
| int (*callback)(int i, void *ctx); | |
| g_output = 0; | |
| if (callback_selector == 0x0F) | |
| callback = callback_set_0f; | |
| else | |
| callback = callback_set_f0; | |
| bpf_loop(1, callback, NULL, 0); | |
| return 0; | |
| } | |
| static int stack_check_inner_callback(void *ctx) | |
| { | |
| return 0; | |
| } | |
| static int map1_lookup_elem(int key) | |
| { | |
| int *val = bpf_map_lookup_elem(&map1, &key); | |
| return val ? *val : -1; | |
| } | |
| static void map1_update_elem(int key, int val) | |
| { | |
| bpf_map_update_elem(&map1, &key, &val, BPF_ANY); | |
| } | |
| static int stack_check_outer_callback(void *ctx) | |
| { | |
| int a = map1_lookup_elem(1); | |
| int b = map1_lookup_elem(2); | |
| int c = map1_lookup_elem(3); | |
| int d = map1_lookup_elem(4); | |
| int e = map1_lookup_elem(5); | |
| int f = map1_lookup_elem(6); | |
| bpf_loop(1, stack_check_inner_callback, NULL, 0); | |
| map1_update_elem(1, a + 1); | |
| map1_update_elem(2, b + 1); | |
| map1_update_elem(3, c + 1); | |
| map1_update_elem(4, d + 1); | |
| map1_update_elem(5, e + 1); | |
| map1_update_elem(6, f + 1); | |
| return 0; | |
| } | |
| /* Some of the local variables in stack_check and | |
| * stack_check_outer_callback would be allocated on stack by | |
| * compiler. This test should verify that stack content for these | |
| * variables is preserved between calls to bpf_loop (might be an issue | |
| * if loop inlining allocates stack slots incorrectly). | |
| */ | |
| SEC("fentry/" SYS_PREFIX "sys_nanosleep") | |
| int stack_check(void *ctx) | |
| { | |
| if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| return 0; | |
| int a = map1_lookup_elem(7); | |
| int b = map1_lookup_elem(8); | |
| int c = map1_lookup_elem(9); | |
| int d = map1_lookup_elem(10); | |
| int e = map1_lookup_elem(11); | |
| int f = map1_lookup_elem(12); | |
| bpf_loop(1, stack_check_outer_callback, NULL, 0); | |
| map1_update_elem(7, a + 1); | |
| map1_update_elem(8, b + 1); | |
| map1_update_elem(9, c + 1); | |
| map1_update_elem(10, d + 1); | |
| map1_update_elem(11, e + 1); | |
| map1_update_elem(12, f + 1); | |
| return 0; | |
| } |