2 * Strictly speaking, this is not a test. But it can report during test
3 * runs so relative performace can be measured.
16 #include <linux/filter.h>
17 #include <linux/seccomp.h>
18 #include <sys/param.h>
19 #include <sys/prctl.h>
20 #include <sys/syscall.h>
21 #include <sys/types.h>
23 #include "../kselftest.h"
25 unsigned long long timing(clockid_t clk_id
, unsigned long long samples
)
27 struct timespec start
, finish
;
32 assert(clock_gettime(clk_id
, &start
) == 0);
33 for (i
= 0; i
< samples
; i
++) {
34 ret
= syscall(__NR_getpid
);
37 assert(clock_gettime(clk_id
, &finish
) == 0);
39 i
= finish
.tv_sec
- start
.tv_sec
;
41 i
+= finish
.tv_nsec
- start
.tv_nsec
;
43 ksft_print_msg("%lu.%09lu - %lu.%09lu = %llu (%.1fs)\n",
44 finish
.tv_sec
, finish
.tv_nsec
,
45 start
.tv_sec
, start
.tv_nsec
,
46 i
, (double)i
/ 1000000000.0);
51 unsigned long long calibrate(void)
53 struct timespec start
, finish
;
54 unsigned long long i
, samples
, step
= 9973;
58 ksft_print_msg("Calibrating sample size for %d seconds worth of syscalls ...\n", seconds
);
62 assert(clock_gettime(CLOCK_MONOTONIC
, &start
) == 0);
64 for (i
= 0; i
< step
; i
++) {
65 ret
= syscall(__NR_getpid
);
68 assert(clock_gettime(CLOCK_MONOTONIC
, &finish
) == 0);
71 i
= finish
.tv_sec
- start
.tv_sec
;
73 i
+= finish
.tv_nsec
- start
.tv_nsec
;
74 } while (i
< 1000000000ULL);
76 return samples
* seconds
;
79 bool approx(int i_one
, int i_two
)
82 * This continues to be a noisy test. Instead of a 1% comparison
85 double one
= i_one
, one_bump
= one
* 0.1;
86 double two
= i_two
, two_bump
= two
* 0.1;
88 one_bump
= one
+ MAX(one_bump
, 2.0);
89 two_bump
= two
+ MAX(two_bump
, 2.0);
91 /* Equal to, or within 1% or 2 digits */
93 (one
> two
&& one
<= two_bump
) ||
94 (two
> one
&& two
<= one_bump
))
99 bool le(int i_one
, int i_two
)
106 long compare(const char *name_one
, const char *name_eval
, const char *name_two
,
107 unsigned long long one
, bool (*eval
)(int, int), unsigned long long two
,
113 ksft_test_result_skip("%s %s %s\n", name_one
, name_eval
,
118 ksft_print_msg("\t%s %s %s (%lld %s %lld): ", name_one
, name_eval
, name_two
,
119 (long long)one
, name_eval
, (long long)two
);
121 ksft_print_msg("Miscalculation! Measurement went negative: %lld\n", (long long)one
);
126 ksft_print_msg("Miscalculation! Measurement went negative: %lld\n", (long long)two
);
131 good
= eval(one
, two
);
132 printf("%s\n", good
? "✔️" : "❌");
135 ksft_test_result(good
, "%s %s %s\n", name_one
, name_eval
, name_two
);
140 /* Pin to a single CPU so the benchmark won't bounce around the system. */
144 ulong ncores
= sysconf(_SC_NPROCESSORS_CONF
);
145 cpu_set_t
*setp
= CPU_ALLOC(ncores
);
146 ulong setsz
= CPU_ALLOC_SIZE(ncores
);
149 * Totally unscientific way to avoid CPUs that might be busier:
150 * choose the highest CPU instead of the lowest.
152 for (cpu
= ncores
- 1; cpu
>= 0; cpu
--) {
153 CPU_ZERO_S(setsz
, setp
);
154 CPU_SET_S(cpu
, setsz
, setp
);
155 if (sched_setaffinity(getpid(), setsz
, setp
) == -1)
157 printf("Pinned to CPU %lu of %lu\n", cpu
+ 1, ncores
);
160 fprintf(stderr
, "Could not set CPU affinity -- calibration may not work well");
166 int main(int argc
, char *argv
[])
168 struct sock_filter bitmap_filter
[] = {
169 BPF_STMT(BPF_LD
|BPF_W
|BPF_ABS
, offsetof(struct seccomp_data
, nr
)),
170 BPF_STMT(BPF_RET
|BPF_K
, SECCOMP_RET_ALLOW
),
172 struct sock_fprog bitmap_prog
= {
173 .len
= (unsigned short)ARRAY_SIZE(bitmap_filter
),
174 .filter
= bitmap_filter
,
176 struct sock_filter filter
[] = {
177 BPF_STMT(BPF_LD
|BPF_W
|BPF_ABS
, offsetof(struct seccomp_data
, args
[0])),
178 BPF_STMT(BPF_RET
|BPF_K
, SECCOMP_RET_ALLOW
),
180 struct sock_fprog prog
= {
181 .len
= (unsigned short)ARRAY_SIZE(filter
),
186 unsigned long long samples
, calc
;
187 unsigned long long native
, filter1
, filter2
, bitmap1
, bitmap2
;
188 unsigned long long entry
, per_filter1
, per_filter2
;
191 setbuf(stdout
, NULL
);
196 ksft_print_msg("Running on:\n");
197 ksft_print_msg("%s", "");
200 ksft_print_msg("Current BPF sysctl settings:\n");
201 /* Avoid using "sysctl" which may not be installed. */
202 ksft_print_msg("%s", "");
203 system("grep -H . /proc/sys/net/core/bpf_jit_enable");
204 ksft_print_msg("%s", "");
205 system("grep -H . /proc/sys/net/core/bpf_jit_harden");
210 samples
= strtoull(argv
[1], NULL
, 0);
212 samples
= calibrate();
214 ksft_print_msg("Benchmarking %llu syscalls...\n", samples
);
217 native
= timing(CLOCK_PROCESS_CPUTIME_ID
, samples
) / samples
;
218 ksft_print_msg("getpid native: %llu ns\n", native
);
220 ret
= prctl(PR_SET_NO_NEW_PRIVS
, 1, 0, 0, 0);
223 /* One filter resulting in a bitmap */
224 ret
= prctl(PR_SET_SECCOMP
, SECCOMP_MODE_FILTER
, &bitmap_prog
);
227 bitmap1
= timing(CLOCK_PROCESS_CPUTIME_ID
, samples
) / samples
;
228 ksft_print_msg("getpid RET_ALLOW 1 filter (bitmap): %llu ns\n", bitmap1
);
230 /* Second filter resulting in a bitmap */
231 ret
= prctl(PR_SET_SECCOMP
, SECCOMP_MODE_FILTER
, &bitmap_prog
);
234 bitmap2
= timing(CLOCK_PROCESS_CPUTIME_ID
, samples
) / samples
;
235 ksft_print_msg("getpid RET_ALLOW 2 filters (bitmap): %llu ns\n", bitmap2
);
237 /* Third filter, can no longer be converted to bitmap */
238 ret
= prctl(PR_SET_SECCOMP
, SECCOMP_MODE_FILTER
, &prog
);
241 filter1
= timing(CLOCK_PROCESS_CPUTIME_ID
, samples
) / samples
;
242 ksft_print_msg("getpid RET_ALLOW 3 filters (full): %llu ns\n", filter1
);
244 /* Fourth filter, can not be converted to bitmap because of filter 3 */
245 ret
= prctl(PR_SET_SECCOMP
, SECCOMP_MODE_FILTER
, &bitmap_prog
);
248 filter2
= timing(CLOCK_PROCESS_CPUTIME_ID
, samples
) / samples
;
249 ksft_print_msg("getpid RET_ALLOW 4 filters (full): %llu ns\n", filter2
);
252 #define ESTIMATE(fmt, var, what) do { \
254 ksft_print_msg("Estimated " fmt ": %llu ns\n", var); \
255 if (var > INT_MAX) { \
261 ESTIMATE("total seccomp overhead for 1 bitmapped filter", calc
,
263 ESTIMATE("total seccomp overhead for 2 bitmapped filters", calc
,
265 ESTIMATE("total seccomp overhead for 3 full filters", calc
,
267 ESTIMATE("total seccomp overhead for 4 full filters", calc
,
269 ESTIMATE("seccomp entry overhead", entry
,
270 bitmap1
- native
- (bitmap2
- bitmap1
));
271 ESTIMATE("seccomp per-filter overhead (last 2 diff)", per_filter1
,
273 ESTIMATE("seccomp per-filter overhead (filters / 4)", per_filter2
,
274 (filter2
- native
- entry
) / 4);
276 ksft_print_msg("Expectations:\n");
277 ret
|= compare("native", "≤", "1 bitmap", native
, le
, bitmap1
,
279 bits
= compare("native", "≤", "1 filter", native
, le
, filter1
,
284 ret
|= compare("per-filter (last 2 diff)", "≈", "per-filter (filters / 4)",
285 per_filter1
, approx
, per_filter2
, skip
);
287 bits
= compare("1 bitmapped", "≈", "2 bitmapped",
288 bitmap1
- native
, approx
, bitmap2
- native
, skip
);
290 ksft_print_msg("Skipping constant action bitmap expectations: they appear unsupported.\n");
294 ret
|= compare("entry", "≈", "1 bitmapped", entry
, approx
,
295 bitmap1
- native
, skip
);
296 ret
|= compare("entry", "≈", "2 bitmapped", entry
, approx
,
297 bitmap2
- native
, skip
);
298 ret
|= compare("native + entry + (per filter * 4)", "≈", "4 filters total",
299 entry
+ (per_filter1
* 4) + native
, approx
, filter2
,
303 ksft_print_msg("Saw unexpected benchmark result. Try running again with more samples?\n");