1*79e7ec00SYonghong Song // SPDX-License-Identifier: GPL-2.0 2*79e7ec00SYonghong Song /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ 3*79e7ec00SYonghong Song 4*79e7ec00SYonghong Song #include <vmlinux.h> 5*79e7ec00SYonghong Song #include <stdbool.h> 6*79e7ec00SYonghong Song #include <bpf/bpf_helpers.h> 7*79e7ec00SYonghong Song #include "bpf_kfuncs.h" 8*79e7ec00SYonghong Song 9*79e7ec00SYonghong Song #define CLOCK_MONOTONIC 1 10*79e7ec00SYonghong Song 11*79e7ec00SYonghong Song struct timer_elem { 12*79e7ec00SYonghong Song struct bpf_timer timer; 13*79e7ec00SYonghong Song }; 14*79e7ec00SYonghong Song 15*79e7ec00SYonghong Song struct { 16*79e7ec00SYonghong Song __uint(type, BPF_MAP_TYPE_ARRAY); 17*79e7ec00SYonghong Song __uint(max_entries, 1); 18*79e7ec00SYonghong Song __type(key, int); 19*79e7ec00SYonghong Song __type(value, struct timer_elem); 20*79e7ec00SYonghong Song } timer_map SEC(".maps"); 21*79e7ec00SYonghong Song 22*79e7ec00SYonghong Song int timer_result; 23*79e7ec00SYonghong Song 24*79e7ec00SYonghong Song #if defined(__TARGET_ARCH_x86) && defined(__BPF_FEATURE_STACK_ARGUMENT) 25*79e7ec00SYonghong Song 26*79e7ec00SYonghong Song const volatile bool has_stack_arg = true; 27*79e7ec00SYonghong Song 28*79e7ec00SYonghong Song __noinline static int static_func_many_args(int a, int b, int c, int d, 29*79e7ec00SYonghong Song int e, int f, int g, int h) 30*79e7ec00SYonghong Song { 31*79e7ec00SYonghong Song return a + b + c + d + e + f + g + h; 32*79e7ec00SYonghong Song } 33*79e7ec00SYonghong Song 34*79e7ec00SYonghong Song __noinline int global_calls_many_args(int a, int b, int c) 35*79e7ec00SYonghong Song { 36*79e7ec00SYonghong Song return static_func_many_args(a, b, c, 4, 5, 6, 7, 8); 37*79e7ec00SYonghong Song } 38*79e7ec00SYonghong Song 39*79e7ec00SYonghong Song SEC("tc") 40*79e7ec00SYonghong Song int test_global_many_args(void) 41*79e7ec00SYonghong Song { 42*79e7ec00SYonghong Song return global_calls_many_args(1, 2, 3); 43*79e7ec00SYonghong Song } 44*79e7ec00SYonghong Song 45*79e7ec00SYonghong Song struct test_data { 46*79e7ec00SYonghong Song long x; 47*79e7ec00SYonghong Song long y; 48*79e7ec00SYonghong Song }; 49*79e7ec00SYonghong Song 50*79e7ec00SYonghong Song /* 1 + 2 + 3 + 4 + 5 + 10 + 20 = 45 */ 51*79e7ec00SYonghong Song __noinline static long func_with_ptr_stack_arg(long a, long b, long c, long d, 52*79e7ec00SYonghong Song long e, struct test_data *p) 53*79e7ec00SYonghong Song { 54*79e7ec00SYonghong Song return a + b + c + d + e + p->x + p->y; 55*79e7ec00SYonghong Song } 56*79e7ec00SYonghong Song 57*79e7ec00SYonghong Song __noinline long global_ptr_stack_arg(long a, long b, long c, long d, long e) 58*79e7ec00SYonghong Song { 59*79e7ec00SYonghong Song struct test_data data = { .x = 10, .y = 20 }; 60*79e7ec00SYonghong Song 61*79e7ec00SYonghong Song return func_with_ptr_stack_arg(a, b, c, d, e, &data); 62*79e7ec00SYonghong Song } 63*79e7ec00SYonghong Song 64*79e7ec00SYonghong Song SEC("tc") 65*79e7ec00SYonghong Song int test_bpf2bpf_ptr_stack_arg(void) 66*79e7ec00SYonghong Song { 67*79e7ec00SYonghong Song return global_ptr_stack_arg(1, 2, 3, 4, 5); 68*79e7ec00SYonghong Song } 69*79e7ec00SYonghong Song 70*79e7ec00SYonghong Song /* 1 + 2 + 3 + 4 + 5 + 10 + 6 + 20 = 51 */ 71*79e7ec00SYonghong Song __noinline static long func_with_mix_stack_args(long a, long b, long c, long d, 72*79e7ec00SYonghong Song long e, struct test_data *p, 73*79e7ec00SYonghong Song long f, struct test_data *q) 74*79e7ec00SYonghong Song { 75*79e7ec00SYonghong Song return a + b + c + d + e + p->x + f + q->y; 76*79e7ec00SYonghong Song } 77*79e7ec00SYonghong Song 78*79e7ec00SYonghong Song __noinline long global_mix_stack_args(long a, long b, long c, long d, long e) 79*79e7ec00SYonghong Song { 80*79e7ec00SYonghong Song struct test_data p = { .x = 10 }; 81*79e7ec00SYonghong Song struct test_data q = { .y = 20 }; 82*79e7ec00SYonghong Song 83*79e7ec00SYonghong Song return func_with_mix_stack_args(a, b, c, d, e, &p, e + 1, &q); 84*79e7ec00SYonghong Song } 85*79e7ec00SYonghong Song 86*79e7ec00SYonghong Song SEC("tc") 87*79e7ec00SYonghong Song int test_bpf2bpf_mix_stack_args(void) 88*79e7ec00SYonghong Song { 89*79e7ec00SYonghong Song return global_mix_stack_args(1, 2, 3, 4, 5); 90*79e7ec00SYonghong Song } 91*79e7ec00SYonghong Song 92*79e7ec00SYonghong Song /* 93*79e7ec00SYonghong Song * Nesting test: func_outer calls func_inner, both with struct pointer 94*79e7ec00SYonghong Song * as stack arg. 95*79e7ec00SYonghong Song * 96*79e7ec00SYonghong Song * func_inner: (a+1) + (b+1) + (c+1) + (d+1) + (e+1) + p->x + p->y 97*79e7ec00SYonghong Song * = 2 + 3 + 4 + 5 + 6 + 10 + 20 = 50 98*79e7ec00SYonghong Song */ 99*79e7ec00SYonghong Song __noinline static long func_inner_ptr(long a, long b, long c, long d, 100*79e7ec00SYonghong Song long e, struct test_data *p) 101*79e7ec00SYonghong Song { 102*79e7ec00SYonghong Song return a + b + c + d + e + p->x + p->y; 103*79e7ec00SYonghong Song } 104*79e7ec00SYonghong Song 105*79e7ec00SYonghong Song __noinline static long func_outer_ptr(long a, long b, long c, long d, 106*79e7ec00SYonghong Song long e, struct test_data *p) 107*79e7ec00SYonghong Song { 108*79e7ec00SYonghong Song return func_inner_ptr(a + 1, b + 1, c + 1, d + 1, e + 1, p); 109*79e7ec00SYonghong Song } 110*79e7ec00SYonghong Song 111*79e7ec00SYonghong Song __noinline long global_nesting_ptr(long a, long b, long c, long d, long e) 112*79e7ec00SYonghong Song { 113*79e7ec00SYonghong Song struct test_data data = { .x = 10, .y = 20 }; 114*79e7ec00SYonghong Song 115*79e7ec00SYonghong Song return func_outer_ptr(a, b, c, d, e, &data); 116*79e7ec00SYonghong Song } 117*79e7ec00SYonghong Song 118*79e7ec00SYonghong Song SEC("tc") 119*79e7ec00SYonghong Song int test_bpf2bpf_nesting_stack_arg(void) 120*79e7ec00SYonghong Song { 121*79e7ec00SYonghong Song return global_nesting_ptr(1, 2, 3, 4, 5); 122*79e7ec00SYonghong Song } 123*79e7ec00SYonghong Song 124*79e7ec00SYonghong Song /* 1 + 2 + 3 + 4 + 5 + sizeof(pkt_v4) = 15 + 54 = 69 */ 125*79e7ec00SYonghong Song __noinline static long func_with_dynptr(long a, long b, long c, long d, 126*79e7ec00SYonghong Song long e, struct bpf_dynptr *ptr) 127*79e7ec00SYonghong Song { 128*79e7ec00SYonghong Song return a + b + c + d + e + bpf_dynptr_size(ptr); 129*79e7ec00SYonghong Song } 130*79e7ec00SYonghong Song 131*79e7ec00SYonghong Song __noinline long global_dynptr_stack_arg(void *ctx __arg_ctx, long a, long b, 132*79e7ec00SYonghong Song long c, long d) 133*79e7ec00SYonghong Song { 134*79e7ec00SYonghong Song struct bpf_dynptr ptr; 135*79e7ec00SYonghong Song 136*79e7ec00SYonghong Song bpf_dynptr_from_skb(ctx, 0, &ptr); 137*79e7ec00SYonghong Song return func_with_dynptr(a, b, c, d, d + 1, &ptr); 138*79e7ec00SYonghong Song } 139*79e7ec00SYonghong Song 140*79e7ec00SYonghong Song SEC("tc") 141*79e7ec00SYonghong Song int test_bpf2bpf_dynptr_stack_arg(struct __sk_buff *skb) 142*79e7ec00SYonghong Song { 143*79e7ec00SYonghong Song return global_dynptr_stack_arg(skb, 1, 2, 3, 4); 144*79e7ec00SYonghong Song } 145*79e7ec00SYonghong Song 146*79e7ec00SYonghong Song /* foo1: a+b+c+d+e+f+g+h */ 147*79e7ec00SYonghong Song __noinline static int foo1(int a, int b, int c, int d, 148*79e7ec00SYonghong Song int e, int f, int g, int h) 149*79e7ec00SYonghong Song { 150*79e7ec00SYonghong Song return a + b + c + d + e + f + g + h; 151*79e7ec00SYonghong Song } 152*79e7ec00SYonghong Song 153*79e7ec00SYonghong Song /* foo2: a+b+c+d+e+f+g+h+i+j */ 154*79e7ec00SYonghong Song __noinline static int foo2(int a, int b, int c, int d, int e, 155*79e7ec00SYonghong Song int f, int g, int h, int i, int j) 156*79e7ec00SYonghong Song { 157*79e7ec00SYonghong Song return a + b + c + d + e + f + g + h + i + j; 158*79e7ec00SYonghong Song } 159*79e7ec00SYonghong Song 160*79e7ec00SYonghong Song /* global_two_callees calls foo1 (3 stack args) and foo2 (5 stack args). 161*79e7ec00SYonghong Song * The outgoing stack arg area is sized for foo2 (the larger callee). 162*79e7ec00SYonghong Song * Stores for foo1 are a subset of the area used by foo2. 163*79e7ec00SYonghong 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 164*79e7ec00SYonghong Song * 165*79e7ec00SYonghong Song * Pass a-e through so the compiler can't constant-fold the stack args away. 166*79e7ec00SYonghong Song */ 167*79e7ec00SYonghong Song __noinline int global_two_callees(int a, int b, int c, int d, int e) 168*79e7ec00SYonghong Song { 169*79e7ec00SYonghong Song int ret; 170*79e7ec00SYonghong Song 171*79e7ec00SYonghong Song ret = foo1(a, b, c, d, e, a + 5, a + 6, a + 7); 172*79e7ec00SYonghong Song ret += foo2(a, b, c, d, e, a + 5, a + 6, a + 7, a + 8, a + 9); 173*79e7ec00SYonghong Song return ret; 174*79e7ec00SYonghong Song } 175*79e7ec00SYonghong Song 176*79e7ec00SYonghong Song SEC("tc") 177*79e7ec00SYonghong Song int test_two_callees(void) 178*79e7ec00SYonghong Song { 179*79e7ec00SYonghong Song return global_two_callees(1, 2, 3, 4, 5); 180*79e7ec00SYonghong Song } 181*79e7ec00SYonghong Song 182*79e7ec00SYonghong Song static int timer_cb_many_args(void *map, int *key, struct bpf_timer *timer) 183*79e7ec00SYonghong Song { 184*79e7ec00SYonghong Song timer_result = static_func_many_args(10, 20, 30, 40, 50, 60, 70, 80); 185*79e7ec00SYonghong Song return 0; 186*79e7ec00SYonghong Song } 187*79e7ec00SYonghong Song 188*79e7ec00SYonghong Song SEC("tc") 189*79e7ec00SYonghong Song int test_async_cb_many_args(void) 190*79e7ec00SYonghong Song { 191*79e7ec00SYonghong Song struct timer_elem *elem; 192*79e7ec00SYonghong Song int key = 0; 193*79e7ec00SYonghong Song 194*79e7ec00SYonghong Song elem = bpf_map_lookup_elem(&timer_map, &key); 195*79e7ec00SYonghong Song if (!elem) 196*79e7ec00SYonghong Song return -1; 197*79e7ec00SYonghong Song 198*79e7ec00SYonghong Song bpf_timer_init(&elem->timer, &timer_map, CLOCK_MONOTONIC); 199*79e7ec00SYonghong Song bpf_timer_set_callback(&elem->timer, timer_cb_many_args); 200*79e7ec00SYonghong Song bpf_timer_start(&elem->timer, 1, 0); 201*79e7ec00SYonghong Song return 0; 202*79e7ec00SYonghong Song } 203*79e7ec00SYonghong Song 204*79e7ec00SYonghong Song #else 205*79e7ec00SYonghong Song 206*79e7ec00SYonghong Song const volatile bool has_stack_arg = false; 207*79e7ec00SYonghong Song 208*79e7ec00SYonghong Song SEC("tc") 209*79e7ec00SYonghong Song int test_global_many_args(void) 210*79e7ec00SYonghong Song { 211*79e7ec00SYonghong Song return 0; 212*79e7ec00SYonghong Song } 213*79e7ec00SYonghong Song 214*79e7ec00SYonghong Song SEC("tc") 215*79e7ec00SYonghong Song int test_bpf2bpf_ptr_stack_arg(void) 216*79e7ec00SYonghong Song { 217*79e7ec00SYonghong Song return 0; 218*79e7ec00SYonghong Song } 219*79e7ec00SYonghong Song 220*79e7ec00SYonghong Song SEC("tc") 221*79e7ec00SYonghong Song int test_bpf2bpf_mix_stack_args(void) 222*79e7ec00SYonghong Song { 223*79e7ec00SYonghong Song return 0; 224*79e7ec00SYonghong Song } 225*79e7ec00SYonghong Song 226*79e7ec00SYonghong Song SEC("tc") 227*79e7ec00SYonghong Song int test_bpf2bpf_nesting_stack_arg(void) 228*79e7ec00SYonghong Song { 229*79e7ec00SYonghong Song return 0; 230*79e7ec00SYonghong Song } 231*79e7ec00SYonghong Song 232*79e7ec00SYonghong Song SEC("tc") 233*79e7ec00SYonghong Song int test_bpf2bpf_dynptr_stack_arg(struct __sk_buff *skb) 234*79e7ec00SYonghong Song { 235*79e7ec00SYonghong Song return 0; 236*79e7ec00SYonghong Song } 237*79e7ec00SYonghong Song 238*79e7ec00SYonghong Song SEC("tc") 239*79e7ec00SYonghong Song int test_two_callees(void) 240*79e7ec00SYonghong Song { 241*79e7ec00SYonghong Song return 0; 242*79e7ec00SYonghong Song } 243*79e7ec00SYonghong Song 244*79e7ec00SYonghong Song SEC("tc") 245*79e7ec00SYonghong Song int test_async_cb_many_args(void) 246*79e7ec00SYonghong Song { 247*79e7ec00SYonghong Song return 0; 248*79e7ec00SYonghong Song } 249*79e7ec00SYonghong Song 250*79e7ec00SYonghong Song #endif 251*79e7ec00SYonghong Song 252*79e7ec00SYonghong Song char _license[] SEC("license") = "GPL"; 253