179e7ec00SYonghong Song // SPDX-License-Identifier: GPL-2.0 279e7ec00SYonghong Song /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ 379e7ec00SYonghong Song 479e7ec00SYonghong Song #include <vmlinux.h> 579e7ec00SYonghong Song #include <stdbool.h> 679e7ec00SYonghong Song #include <bpf/bpf_helpers.h> 779e7ec00SYonghong Song #include "bpf_kfuncs.h" 879e7ec00SYonghong Song 979e7ec00SYonghong Song #define CLOCK_MONOTONIC 1 1079e7ec00SYonghong Song 1179e7ec00SYonghong Song struct timer_elem { 1279e7ec00SYonghong Song struct bpf_timer timer; 1379e7ec00SYonghong Song }; 1479e7ec00SYonghong Song 1579e7ec00SYonghong Song struct { 1679e7ec00SYonghong Song __uint(type, BPF_MAP_TYPE_ARRAY); 1779e7ec00SYonghong Song __uint(max_entries, 1); 1879e7ec00SYonghong Song __type(key, int); 1979e7ec00SYonghong Song __type(value, struct timer_elem); 2079e7ec00SYonghong Song } timer_map SEC(".maps"); 2179e7ec00SYonghong Song 2279e7ec00SYonghong Song int timer_result; 2379e7ec00SYonghong Song 24*90e43f1bSPuranjay Mohan #if (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64)) && \ 25*90e43f1bSPuranjay Mohan defined(__BPF_FEATURE_STACK_ARGUMENT) 2679e7ec00SYonghong Song 2779e7ec00SYonghong Song const volatile bool has_stack_arg = true; 2879e7ec00SYonghong Song 2979e7ec00SYonghong Song __noinline static int static_func_many_args(int a, int b, int c, int d, 3079e7ec00SYonghong Song int e, int f, int g, int h) 3179e7ec00SYonghong Song { 3279e7ec00SYonghong Song return a + b + c + d + e + f + g + h; 3379e7ec00SYonghong Song } 3479e7ec00SYonghong Song 3579e7ec00SYonghong Song __noinline int global_calls_many_args(int a, int b, int c) 3679e7ec00SYonghong Song { 3779e7ec00SYonghong Song return static_func_many_args(a, b, c, 4, 5, 6, 7, 8); 3879e7ec00SYonghong Song } 3979e7ec00SYonghong Song 4079e7ec00SYonghong Song SEC("tc") 4179e7ec00SYonghong Song int test_global_many_args(void) 4279e7ec00SYonghong Song { 4379e7ec00SYonghong Song return global_calls_many_args(1, 2, 3); 4479e7ec00SYonghong Song } 4579e7ec00SYonghong Song 4679e7ec00SYonghong Song struct test_data { 4779e7ec00SYonghong Song long x; 4879e7ec00SYonghong Song long y; 4979e7ec00SYonghong Song }; 5079e7ec00SYonghong Song 5179e7ec00SYonghong Song /* 1 + 2 + 3 + 4 + 5 + 10 + 20 = 45 */ 5279e7ec00SYonghong Song __noinline static long func_with_ptr_stack_arg(long a, long b, long c, long d, 5379e7ec00SYonghong Song long e, struct test_data *p) 5479e7ec00SYonghong Song { 5579e7ec00SYonghong Song return a + b + c + d + e + p->x + p->y; 5679e7ec00SYonghong Song } 5779e7ec00SYonghong Song 5879e7ec00SYonghong Song __noinline long global_ptr_stack_arg(long a, long b, long c, long d, long e) 5979e7ec00SYonghong Song { 6079e7ec00SYonghong Song struct test_data data = { .x = 10, .y = 20 }; 6179e7ec00SYonghong Song 6279e7ec00SYonghong Song return func_with_ptr_stack_arg(a, b, c, d, e, &data); 6379e7ec00SYonghong Song } 6479e7ec00SYonghong Song 6579e7ec00SYonghong Song SEC("tc") 6679e7ec00SYonghong Song int test_bpf2bpf_ptr_stack_arg(void) 6779e7ec00SYonghong Song { 6879e7ec00SYonghong Song return global_ptr_stack_arg(1, 2, 3, 4, 5); 6979e7ec00SYonghong Song } 7079e7ec00SYonghong Song 7179e7ec00SYonghong Song /* 1 + 2 + 3 + 4 + 5 + 10 + 6 + 20 = 51 */ 7279e7ec00SYonghong Song __noinline static long func_with_mix_stack_args(long a, long b, long c, long d, 7379e7ec00SYonghong Song long e, struct test_data *p, 7479e7ec00SYonghong Song long f, struct test_data *q) 7579e7ec00SYonghong Song { 7679e7ec00SYonghong Song return a + b + c + d + e + p->x + f + q->y; 7779e7ec00SYonghong Song } 7879e7ec00SYonghong Song 7979e7ec00SYonghong Song __noinline long global_mix_stack_args(long a, long b, long c, long d, long e) 8079e7ec00SYonghong Song { 8179e7ec00SYonghong Song struct test_data p = { .x = 10 }; 8279e7ec00SYonghong Song struct test_data q = { .y = 20 }; 8379e7ec00SYonghong Song 8479e7ec00SYonghong Song return func_with_mix_stack_args(a, b, c, d, e, &p, e + 1, &q); 8579e7ec00SYonghong Song } 8679e7ec00SYonghong Song 8779e7ec00SYonghong Song SEC("tc") 8879e7ec00SYonghong Song int test_bpf2bpf_mix_stack_args(void) 8979e7ec00SYonghong Song { 9079e7ec00SYonghong Song return global_mix_stack_args(1, 2, 3, 4, 5); 9179e7ec00SYonghong Song } 9279e7ec00SYonghong Song 9379e7ec00SYonghong Song /* 9479e7ec00SYonghong Song * Nesting test: func_outer calls func_inner, both with struct pointer 9579e7ec00SYonghong Song * as stack arg. 9679e7ec00SYonghong Song * 9779e7ec00SYonghong Song * func_inner: (a+1) + (b+1) + (c+1) + (d+1) + (e+1) + p->x + p->y 9879e7ec00SYonghong Song * = 2 + 3 + 4 + 5 + 6 + 10 + 20 = 50 9979e7ec00SYonghong Song */ 10079e7ec00SYonghong Song __noinline static long func_inner_ptr(long a, long b, long c, long d, 10179e7ec00SYonghong Song long e, struct test_data *p) 10279e7ec00SYonghong Song { 10379e7ec00SYonghong Song return a + b + c + d + e + p->x + p->y; 10479e7ec00SYonghong Song } 10579e7ec00SYonghong Song 10679e7ec00SYonghong Song __noinline static long func_outer_ptr(long a, long b, long c, long d, 10779e7ec00SYonghong Song long e, struct test_data *p) 10879e7ec00SYonghong Song { 10979e7ec00SYonghong Song return func_inner_ptr(a + 1, b + 1, c + 1, d + 1, e + 1, p); 11079e7ec00SYonghong Song } 11179e7ec00SYonghong Song 11279e7ec00SYonghong Song __noinline long global_nesting_ptr(long a, long b, long c, long d, long e) 11379e7ec00SYonghong Song { 11479e7ec00SYonghong Song struct test_data data = { .x = 10, .y = 20 }; 11579e7ec00SYonghong Song 11679e7ec00SYonghong Song return func_outer_ptr(a, b, c, d, e, &data); 11779e7ec00SYonghong Song } 11879e7ec00SYonghong Song 11979e7ec00SYonghong Song SEC("tc") 12079e7ec00SYonghong Song int test_bpf2bpf_nesting_stack_arg(void) 12179e7ec00SYonghong Song { 12279e7ec00SYonghong Song return global_nesting_ptr(1, 2, 3, 4, 5); 12379e7ec00SYonghong Song } 12479e7ec00SYonghong Song 12579e7ec00SYonghong Song /* 1 + 2 + 3 + 4 + 5 + sizeof(pkt_v4) = 15 + 54 = 69 */ 12679e7ec00SYonghong Song __noinline static long func_with_dynptr(long a, long b, long c, long d, 12779e7ec00SYonghong Song long e, struct bpf_dynptr *ptr) 12879e7ec00SYonghong Song { 12979e7ec00SYonghong Song return a + b + c + d + e + bpf_dynptr_size(ptr); 13079e7ec00SYonghong Song } 13179e7ec00SYonghong Song 13279e7ec00SYonghong Song __noinline long global_dynptr_stack_arg(void *ctx __arg_ctx, long a, long b, 13379e7ec00SYonghong Song long c, long d) 13479e7ec00SYonghong Song { 13579e7ec00SYonghong Song struct bpf_dynptr ptr; 13679e7ec00SYonghong Song 13779e7ec00SYonghong Song bpf_dynptr_from_skb(ctx, 0, &ptr); 13879e7ec00SYonghong Song return func_with_dynptr(a, b, c, d, d + 1, &ptr); 13979e7ec00SYonghong Song } 14079e7ec00SYonghong Song 14179e7ec00SYonghong Song SEC("tc") 14279e7ec00SYonghong Song int test_bpf2bpf_dynptr_stack_arg(struct __sk_buff *skb) 14379e7ec00SYonghong Song { 14479e7ec00SYonghong Song return global_dynptr_stack_arg(skb, 1, 2, 3, 4); 14579e7ec00SYonghong Song } 14679e7ec00SYonghong Song 14779e7ec00SYonghong Song /* foo1: a+b+c+d+e+f+g+h */ 14879e7ec00SYonghong Song __noinline static int foo1(int a, int b, int c, int d, 14979e7ec00SYonghong Song int e, int f, int g, int h) 15079e7ec00SYonghong Song { 15179e7ec00SYonghong Song return a + b + c + d + e + f + g + h; 15279e7ec00SYonghong Song } 15379e7ec00SYonghong Song 15479e7ec00SYonghong Song /* foo2: a+b+c+d+e+f+g+h+i+j */ 15579e7ec00SYonghong Song __noinline static int foo2(int a, int b, int c, int d, int e, 15679e7ec00SYonghong Song int f, int g, int h, int i, int j) 15779e7ec00SYonghong Song { 15879e7ec00SYonghong Song return a + b + c + d + e + f + g + h + i + j; 15979e7ec00SYonghong Song } 16079e7ec00SYonghong Song 16179e7ec00SYonghong Song /* global_two_callees calls foo1 (3 stack args) and foo2 (5 stack args). 16279e7ec00SYonghong Song * The outgoing stack arg area is sized for foo2 (the larger callee). 16379e7ec00SYonghong Song * Stores for foo1 are a subset of the area used by foo2. 16479e7ec00SYonghong Song * Result: foo1(1,2,3,4,5,6,7,8) + foo2(1,2,3,4,5,6,7,8,9,10) = 36 + 55 = 91 16579e7ec00SYonghong Song * 16679e7ec00SYonghong Song * Pass a-e through so the compiler can't constant-fold the stack args away. 16779e7ec00SYonghong Song */ 16879e7ec00SYonghong Song __noinline int global_two_callees(int a, int b, int c, int d, int e) 16979e7ec00SYonghong Song { 17079e7ec00SYonghong Song int ret; 17179e7ec00SYonghong Song 17279e7ec00SYonghong Song ret = foo1(a, b, c, d, e, a + 5, a + 6, a + 7); 17379e7ec00SYonghong Song ret += foo2(a, b, c, d, e, a + 5, a + 6, a + 7, a + 8, a + 9); 17479e7ec00SYonghong Song return ret; 17579e7ec00SYonghong Song } 17679e7ec00SYonghong Song 17779e7ec00SYonghong Song SEC("tc") 17879e7ec00SYonghong Song int test_two_callees(void) 17979e7ec00SYonghong Song { 18079e7ec00SYonghong Song return global_two_callees(1, 2, 3, 4, 5); 18179e7ec00SYonghong Song } 18279e7ec00SYonghong Song 18379e7ec00SYonghong Song static int timer_cb_many_args(void *map, int *key, struct bpf_timer *timer) 18479e7ec00SYonghong Song { 18579e7ec00SYonghong Song timer_result = static_func_many_args(10, 20, 30, 40, 50, 60, 70, 80); 18679e7ec00SYonghong Song return 0; 18779e7ec00SYonghong Song } 18879e7ec00SYonghong Song 18979e7ec00SYonghong Song SEC("tc") 19079e7ec00SYonghong Song int test_async_cb_many_args(void) 19179e7ec00SYonghong Song { 19279e7ec00SYonghong Song struct timer_elem *elem; 19379e7ec00SYonghong Song int key = 0; 19479e7ec00SYonghong Song 19579e7ec00SYonghong Song elem = bpf_map_lookup_elem(&timer_map, &key); 19679e7ec00SYonghong Song if (!elem) 19779e7ec00SYonghong Song return -1; 19879e7ec00SYonghong Song 19979e7ec00SYonghong Song bpf_timer_init(&elem->timer, &timer_map, CLOCK_MONOTONIC); 20079e7ec00SYonghong Song bpf_timer_set_callback(&elem->timer, timer_cb_many_args); 20179e7ec00SYonghong Song bpf_timer_start(&elem->timer, 1, 0); 20279e7ec00SYonghong Song return 0; 20379e7ec00SYonghong Song } 20479e7ec00SYonghong Song 20579e7ec00SYonghong Song #else 20679e7ec00SYonghong Song 20779e7ec00SYonghong Song const volatile bool has_stack_arg = false; 20879e7ec00SYonghong Song 20979e7ec00SYonghong Song SEC("tc") 21079e7ec00SYonghong Song int test_global_many_args(void) 21179e7ec00SYonghong Song { 21279e7ec00SYonghong Song return 0; 21379e7ec00SYonghong Song } 21479e7ec00SYonghong Song 21579e7ec00SYonghong Song SEC("tc") 21679e7ec00SYonghong Song int test_bpf2bpf_ptr_stack_arg(void) 21779e7ec00SYonghong Song { 21879e7ec00SYonghong Song return 0; 21979e7ec00SYonghong Song } 22079e7ec00SYonghong Song 22179e7ec00SYonghong Song SEC("tc") 22279e7ec00SYonghong Song int test_bpf2bpf_mix_stack_args(void) 22379e7ec00SYonghong Song { 22479e7ec00SYonghong Song return 0; 22579e7ec00SYonghong Song } 22679e7ec00SYonghong Song 22779e7ec00SYonghong Song SEC("tc") 22879e7ec00SYonghong Song int test_bpf2bpf_nesting_stack_arg(void) 22979e7ec00SYonghong Song { 23079e7ec00SYonghong Song return 0; 23179e7ec00SYonghong Song } 23279e7ec00SYonghong Song 23379e7ec00SYonghong Song SEC("tc") 23479e7ec00SYonghong Song int test_bpf2bpf_dynptr_stack_arg(struct __sk_buff *skb) 23579e7ec00SYonghong Song { 23679e7ec00SYonghong Song return 0; 23779e7ec00SYonghong Song } 23879e7ec00SYonghong Song 23979e7ec00SYonghong Song SEC("tc") 24079e7ec00SYonghong Song int test_two_callees(void) 24179e7ec00SYonghong Song { 24279e7ec00SYonghong Song return 0; 24379e7ec00SYonghong Song } 24479e7ec00SYonghong Song 24579e7ec00SYonghong Song SEC("tc") 24679e7ec00SYonghong Song int test_async_cb_many_args(void) 24779e7ec00SYonghong Song { 24879e7ec00SYonghong Song return 0; 24979e7ec00SYonghong Song } 25079e7ec00SYonghong Song 25179e7ec00SYonghong Song #endif 25279e7ec00SYonghong Song 25379e7ec00SYonghong Song char _license[] SEC("license") = "GPL"; 254