summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/progs/async_stack_depth.c
blob: 9cd874a90b39abd9bf077411b86d5190d896a786 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// SPDX-License-Identifier: GPL-2.0
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>

#include "bpf_misc.h"

struct hmap_elem {
	struct bpf_timer timer;
};

struct {
	__uint(type, BPF_MAP_TYPE_HASH);
	__uint(max_entries, 64);
	__type(key, int);
	__type(value, struct hmap_elem);
} hmap SEC(".maps");

__attribute__((noinline))
static int timer_cb(void *map, int *key, struct bpf_timer *timer)
{
	volatile char buf[256] = {};
	return buf[69];
}

__attribute__((noinline))
static int bad_timer_cb(void *map, int *key, struct bpf_timer *timer)
{
	volatile char buf[300] = {};
	return buf[255] + timer_cb(NULL, NULL, NULL);
}

/*
 * The same shapes scaled to the 2 KiB budget of JITs with large stacks. The
 * compiler caps a single function at 512 bytes, so the depth comes from a
 * chain of 480-byte frames.
 */
__attribute__((noinline))
static int timer_cb_large_0(void *map, int *key, struct bpf_timer *timer)
{
	volatile char buf[480] = {};
	return buf[69];
}

__attribute__((noinline))
static int timer_cb_large_1(void *map, int *key, struct bpf_timer *timer)
{
	volatile char buf[480] = {};
	return buf[69] + timer_cb_large_0(map, key, timer);
}

__attribute__((noinline))
static int timer_cb_large_2(void *map, int *key, struct bpf_timer *timer)
{
	volatile char buf[480] = {};
	return buf[69] + timer_cb_large_1(map, key, timer);
}

__attribute__((noinline))
static int timer_cb_large_3(void *map, int *key, struct bpf_timer *timer)
{
	volatile char buf[480] = {};
	return buf[69] + timer_cb_large_2(map, key, timer);
}

/* 5 * 480 = 2400 bytes on its own */
__attribute__((noinline))
static int bad_timer_cb_large(void *map, int *key, struct bpf_timer *timer)
{
	volatile char buf[480] = {};
	return buf[255] + timer_cb_large_3(map, key, timer);
}

SEC("tc")
__load_if_no_large_stack()
__failure __msg("combined stack size of 2 calls is")
int pseudo_call_check(struct __sk_buff *ctx)
{
	struct hmap_elem *elem;
	volatile char buf[256] = {};

	elem = bpf_map_lookup_elem(&hmap, &(int){0});
	if (!elem)
		return 0;

	timer_cb(NULL, NULL, NULL);
	return bpf_timer_set_callback(&elem->timer, timer_cb) + buf[0];
}

/* main plus the four frames under timer_cb_large_3: 2400 bytes */
SEC("tc")
__load_if_large_stack()
__failure __msg("combined stack size of 5 calls is")
int pseudo_call_check_large(struct __sk_buff *ctx)
{
	struct hmap_elem *elem;
	volatile char buf[480] = {};

	elem = bpf_map_lookup_elem(&hmap, &(int){0});
	if (!elem)
		return 0;

	timer_cb_large_3(NULL, NULL, NULL);
	return bpf_timer_set_callback(&elem->timer, timer_cb_large_3) + buf[0];
}

SEC("tc")
__load_if_no_large_stack()
__failure __msg("combined stack size of 2 calls is")
int async_call_root_check(struct __sk_buff *ctx)
{
	struct hmap_elem *elem;
	volatile char buf[256] = {};

	elem = bpf_map_lookup_elem(&hmap, &(int){0});
	if (!elem)
		return 0;

	return bpf_timer_set_callback(&elem->timer, bad_timer_cb) + buf[0];
}

SEC("tc")
__load_if_large_stack()
__failure __msg("combined stack size of 5 calls is")
int async_call_root_check_large(struct __sk_buff *ctx)
{
	struct hmap_elem *elem;
	volatile char buf[480] = {};

	elem = bpf_map_lookup_elem(&hmap, &(int){0});
	if (!elem)
		return 0;

	return bpf_timer_set_callback(&elem->timer, bad_timer_cb_large) + buf[0];
}

char _license[] SEC("license") = "GPL";