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, &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