1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <subcmd/parse-options.h> |
3 | #include "bench.h" |
4 | |
5 | #include <uapi/linux/filter.h> |
6 | #include <sys/types.h> |
7 | #include <sys/time.h> |
8 | #include <linux/unistd.h> |
9 | #include <sys/syscall.h> |
10 | #include <sys/ioctl.h> |
11 | #include <linux/time64.h> |
12 | #include <uapi/linux/seccomp.h> |
13 | #include <sys/prctl.h> |
14 | |
15 | #include <unistd.h> |
16 | #include <limits.h> |
17 | #include <stddef.h> |
18 | #include <stdint.h> |
19 | #include <stdio.h> |
20 | #include <stdlib.h> |
21 | #include <signal.h> |
22 | #include <sys/wait.h> |
23 | #include <string.h> |
24 | #include <errno.h> |
25 | #include <err.h> |
26 | #include <inttypes.h> |
27 | |
28 | #define LOOPS_DEFAULT 1000000UL |
29 | static uint64_t loops = LOOPS_DEFAULT; |
30 | static bool sync_mode; |
31 | |
32 | static const struct option options[] = { |
33 | OPT_U64('l', "loop" , &loops, "Specify number of loops" ), |
34 | OPT_BOOLEAN('s', "sync-mode" , &sync_mode, |
35 | "Enable the synchronous mode for seccomp notifications" ), |
36 | OPT_END() |
37 | }; |
38 | |
39 | static const char * const bench_seccomp_usage[] = { |
40 | "perf bench sched secccomp-notify <options>" , |
41 | NULL |
42 | }; |
43 | |
44 | static int seccomp(unsigned int op, unsigned int flags, void *args) |
45 | { |
46 | return syscall(__NR_seccomp, op, flags, args); |
47 | } |
48 | |
49 | static int user_notif_syscall(int nr, unsigned int flags) |
50 | { |
51 | struct sock_filter filter[] = { |
52 | BPF_STMT(BPF_LD|BPF_W|BPF_ABS, |
53 | offsetof(struct seccomp_data, nr)), |
54 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, nr, 0, 1), |
55 | BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_USER_NOTIF), |
56 | BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), |
57 | }; |
58 | |
59 | struct sock_fprog prog = { |
60 | .len = (unsigned short)ARRAY_SIZE(filter), |
61 | .filter = filter, |
62 | }; |
63 | |
64 | return seccomp(SECCOMP_SET_MODE_FILTER, flags, args: &prog); |
65 | } |
66 | |
67 | #define USER_NOTIF_MAGIC INT_MAX |
68 | static void user_notification_sync_loop(int listener) |
69 | { |
70 | struct seccomp_notif_resp resp; |
71 | struct seccomp_notif req; |
72 | uint64_t nr; |
73 | |
74 | for (nr = 0; nr < loops; nr++) { |
75 | memset(&req, 0, sizeof(req)); |
76 | if (ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req)) |
77 | err(EXIT_FAILURE, "SECCOMP_IOCTL_NOTIF_RECV failed" ); |
78 | |
79 | if (req.data.nr != __NR_gettid) |
80 | errx(EXIT_FAILURE, "unexpected syscall: %d" , req.data.nr); |
81 | |
82 | resp.id = req.id; |
83 | resp.error = 0; |
84 | resp.val = USER_NOTIF_MAGIC; |
85 | resp.flags = 0; |
86 | if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp)) |
87 | err(EXIT_FAILURE, "SECCOMP_IOCTL_NOTIF_SEND failed" ); |
88 | } |
89 | } |
90 | |
91 | #ifndef SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP |
92 | #define SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP (1UL << 0) |
93 | #define SECCOMP_IOCTL_NOTIF_SET_FLAGS SECCOMP_IOW(4, __u64) |
94 | #endif |
95 | int bench_sched_seccomp_notify(int argc, const char **argv) |
96 | { |
97 | struct timeval start, stop, diff; |
98 | unsigned long long result_usec = 0; |
99 | int status, listener; |
100 | pid_t pid; |
101 | long ret; |
102 | |
103 | argc = parse_options(argc, argv, options, bench_seccomp_usage, 0); |
104 | |
105 | gettimeofday(&start, NULL); |
106 | |
107 | prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); |
108 | listener = user_notif_syscall(__NR_gettid, |
109 | SECCOMP_FILTER_FLAG_NEW_LISTENER); |
110 | if (listener < 0) |
111 | err(EXIT_FAILURE, "can't create a notification descriptor" ); |
112 | |
113 | pid = fork(); |
114 | if (pid < 0) |
115 | err(EXIT_FAILURE, "fork" ); |
116 | if (pid == 0) { |
117 | if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0)) |
118 | err(EXIT_FAILURE, "can't set the parent death signal" ); |
119 | while (1) { |
120 | ret = syscall(__NR_gettid); |
121 | if (ret == USER_NOTIF_MAGIC) |
122 | continue; |
123 | break; |
124 | } |
125 | _exit(1); |
126 | } |
127 | |
128 | if (sync_mode) { |
129 | if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SET_FLAGS, |
130 | SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP, 0)) |
131 | err(EXIT_FAILURE, |
132 | "can't set SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP" ); |
133 | } |
134 | user_notification_sync_loop(listener); |
135 | |
136 | kill(pid, SIGKILL); |
137 | if (waitpid(pid, &status, 0) != pid) |
138 | err(EXIT_FAILURE, "waitpid(%d) failed" , pid); |
139 | if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL) |
140 | errx(EXIT_FAILURE, "unexpected exit code: %d" , status); |
141 | |
142 | gettimeofday(&stop, NULL); |
143 | timersub(&stop, &start, &diff); |
144 | |
145 | switch (bench_format) { |
146 | case BENCH_FORMAT_DEFAULT: |
147 | printf("# Executed %" PRIu64 " system calls\n\n" , |
148 | loops); |
149 | |
150 | result_usec = diff.tv_sec * USEC_PER_SEC; |
151 | result_usec += diff.tv_usec; |
152 | |
153 | printf(" %14s: %lu.%03lu [sec]\n\n" , "Total time" , |
154 | (unsigned long) diff.tv_sec, |
155 | (unsigned long) (diff.tv_usec / USEC_PER_MSEC)); |
156 | |
157 | printf(" %14lf usecs/op\n" , |
158 | (double)result_usec / (double)loops); |
159 | printf(" %14d ops/sec\n" , |
160 | (int)((double)loops / |
161 | ((double)result_usec / (double)USEC_PER_SEC))); |
162 | break; |
163 | |
164 | case BENCH_FORMAT_SIMPLE: |
165 | printf("%lu.%03lu\n" , |
166 | (unsigned long) diff.tv_sec, |
167 | (unsigned long) (diff.tv_usec / USEC_PER_MSEC)); |
168 | break; |
169 | |
170 | default: |
171 | /* reaching here is something disaster */ |
172 | fprintf(stderr, "Unknown format:%d\n" , bench_format); |
173 | exit(1); |
174 | break; |
175 | } |
176 | |
177 | return 0; |
178 | } |
179 | |