1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2016 Facebook |
3 | */ |
4 | #define _GNU_SOURCE |
5 | #include <sched.h> |
6 | #include <errno.h> |
7 | #include <stdio.h> |
8 | #include <sys/types.h> |
9 | #include <asm/unistd.h> |
10 | #include <fcntl.h> |
11 | #include <unistd.h> |
12 | #include <assert.h> |
13 | #include <sys/wait.h> |
14 | #include <sys/socket.h> |
15 | #include <arpa/inet.h> |
16 | #include <stdlib.h> |
17 | #include <signal.h> |
18 | #include <linux/bpf.h> |
19 | #include <string.h> |
20 | #include <time.h> |
21 | #include <bpf/bpf.h> |
22 | #include <bpf/libbpf.h> |
23 | |
24 | #define MAX_CNT 1000000 |
25 | #define DUMMY_IP "127.0.0.1" |
26 | #define DUMMY_PORT 80 |
27 | |
28 | static struct bpf_link *links[2]; |
29 | static struct bpf_object *obj; |
30 | static int cnt; |
31 | |
32 | static __u64 time_get_ns(void) |
33 | { |
34 | struct timespec ts; |
35 | |
36 | clock_gettime(CLOCK_MONOTONIC, tp: &ts); |
37 | return ts.tv_sec * 1000000000ull + ts.tv_nsec; |
38 | } |
39 | |
40 | static void test_task_rename(int cpu) |
41 | { |
42 | char buf[] = "test\n" ; |
43 | __u64 start_time; |
44 | int i, fd; |
45 | |
46 | fd = open(file: "/proc/self/comm" , O_WRONLY|O_TRUNC); |
47 | if (fd < 0) { |
48 | printf(format: "couldn't open /proc\n" ); |
49 | exit(status: 1); |
50 | } |
51 | start_time = time_get_ns(); |
52 | for (i = 0; i < MAX_CNT; i++) { |
53 | if (write(fd: fd, buf: buf, n: sizeof(buf)) < 0) { |
54 | printf(format: "task rename failed: %s\n" , strerror(errno)); |
55 | close(fd: fd); |
56 | return; |
57 | } |
58 | } |
59 | printf(format: "task_rename:%d: %lld events per sec\n" , |
60 | cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); |
61 | close(fd: fd); |
62 | } |
63 | |
64 | static void test_fib_table_lookup(int cpu) |
65 | { |
66 | struct sockaddr_in addr; |
67 | char buf[] = "test\n" ; |
68 | __u64 start_time; |
69 | int i, fd; |
70 | |
71 | fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
72 | if (fd < 0) { |
73 | printf(format: "couldn't open socket\n" ); |
74 | exit(status: 1); |
75 | } |
76 | memset(s: (char *)&addr, c: 0, n: sizeof(addr)); |
77 | addr.sin_addr.s_addr = inet_addr(DUMMY_IP); |
78 | addr.sin_port = htons(DUMMY_PORT); |
79 | addr.sin_family = AF_INET; |
80 | start_time = time_get_ns(); |
81 | for (i = 0; i < MAX_CNT; i++) { |
82 | if (sendto(fd: fd, buf: buf, n: strlen(s: buf), flags: 0, |
83 | addr: (struct sockaddr *)&addr, addr_len: sizeof(addr)) < 0) { |
84 | printf(format: "failed to start ping: %s\n" , strerror(errno)); |
85 | close(fd: fd); |
86 | return; |
87 | } |
88 | } |
89 | printf(format: "fib_table_lookup:%d: %lld events per sec\n" , |
90 | cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); |
91 | close(fd: fd); |
92 | } |
93 | |
94 | static void loop(int cpu, int flags) |
95 | { |
96 | cpu_set_t cpuset; |
97 | |
98 | CPU_ZERO(&cpuset); |
99 | CPU_SET(cpu, &cpuset); |
100 | sched_setaffinity(pid: 0, cpusetsize: sizeof(cpuset), cpuset: &cpuset); |
101 | |
102 | if (flags & 1) |
103 | test_task_rename(cpu); |
104 | if (flags & 2) |
105 | test_fib_table_lookup(cpu); |
106 | } |
107 | |
108 | static void run_perf_test(int tasks, int flags) |
109 | { |
110 | pid_t pid[tasks]; |
111 | int i; |
112 | |
113 | for (i = 0; i < tasks; i++) { |
114 | pid[i] = fork(); |
115 | if (pid[i] == 0) { |
116 | loop(cpu: i, flags); |
117 | exit(status: 0); |
118 | } else if (pid[i] == -1) { |
119 | printf(format: "couldn't spawn #%d process\n" , i); |
120 | exit(status: 1); |
121 | } |
122 | } |
123 | for (i = 0; i < tasks; i++) { |
124 | int status; |
125 | |
126 | assert(waitpid(pid[i], &status, 0) == pid[i]); |
127 | assert(status == 0); |
128 | } |
129 | } |
130 | |
131 | static int load_progs(char *filename) |
132 | { |
133 | struct bpf_program *prog; |
134 | int err = 0; |
135 | |
136 | obj = bpf_object__open_file(filename, NULL); |
137 | err = libbpf_get_error(obj); |
138 | if (err < 0) { |
139 | fprintf(stderr, format: "ERROR: opening BPF object file failed\n" ); |
140 | return err; |
141 | } |
142 | |
143 | /* load BPF program */ |
144 | err = bpf_object__load(obj); |
145 | if (err < 0) { |
146 | fprintf(stderr, format: "ERROR: loading BPF object file failed\n" ); |
147 | return err; |
148 | } |
149 | |
150 | bpf_object__for_each_program(prog, obj) { |
151 | links[cnt] = bpf_program__attach(prog); |
152 | err = libbpf_get_error(links[cnt]); |
153 | if (err < 0) { |
154 | fprintf(stderr, format: "ERROR: bpf_program__attach failed\n" ); |
155 | links[cnt] = NULL; |
156 | return err; |
157 | } |
158 | cnt++; |
159 | } |
160 | |
161 | return err; |
162 | } |
163 | |
164 | static void unload_progs(void) |
165 | { |
166 | while (cnt) |
167 | bpf_link__destroy(links[--cnt]); |
168 | |
169 | bpf_object__close(obj); |
170 | } |
171 | |
172 | int main(int argc, char **argv) |
173 | { |
174 | int num_cpu = sysconf(_SC_NPROCESSORS_ONLN); |
175 | int test_flags = ~0; |
176 | char filename[256]; |
177 | int err = 0; |
178 | |
179 | |
180 | if (argc > 1) |
181 | test_flags = atoi(nptr: argv[1]) ? : test_flags; |
182 | if (argc > 2) |
183 | num_cpu = atoi(nptr: argv[2]) ? : num_cpu; |
184 | |
185 | if (test_flags & 0x3) { |
186 | printf(format: "BASE\n" ); |
187 | run_perf_test(tasks: num_cpu, flags: test_flags); |
188 | } |
189 | |
190 | if (test_flags & 0xC) { |
191 | snprintf(s: filename, maxlen: sizeof(filename), |
192 | format: "%s_kprobe.bpf.o" , argv[0]); |
193 | |
194 | printf(format: "w/KPROBE\n" ); |
195 | err = load_progs(filename); |
196 | if (!err) |
197 | run_perf_test(tasks: num_cpu, flags: test_flags >> 2); |
198 | |
199 | unload_progs(); |
200 | } |
201 | |
202 | if (test_flags & 0x30) { |
203 | snprintf(s: filename, maxlen: sizeof(filename), |
204 | format: "%s_tp.bpf.o" , argv[0]); |
205 | printf(format: "w/TRACEPOINT\n" ); |
206 | err = load_progs(filename); |
207 | if (!err) |
208 | run_perf_test(tasks: num_cpu, flags: test_flags >> 4); |
209 | |
210 | unload_progs(); |
211 | } |
212 | |
213 | if (test_flags & 0xC0) { |
214 | snprintf(s: filename, maxlen: sizeof(filename), |
215 | format: "%s_raw_tp.bpf.o" , argv[0]); |
216 | printf(format: "w/RAW_TRACEPOINT\n" ); |
217 | err = load_progs(filename); |
218 | if (!err) |
219 | run_perf_test(tasks: num_cpu, flags: test_flags >> 6); |
220 | |
221 | unload_progs(); |
222 | } |
223 | |
224 | return err; |
225 | } |
226 | |