17d5cb68aSAndrei Vagin // SPDX-License-Identifier: GPL-2.0
27d5cb68aSAndrei Vagin #include <subcmd/parse-options.h>
37d5cb68aSAndrei Vagin #include "bench.h"
47d5cb68aSAndrei Vagin
57d5cb68aSAndrei Vagin #include <uapi/linux/filter.h>
67d5cb68aSAndrei Vagin #include <sys/types.h>
77d5cb68aSAndrei Vagin #include <sys/time.h>
87d5cb68aSAndrei Vagin #include <linux/unistd.h>
97d5cb68aSAndrei Vagin #include <sys/syscall.h>
107d5cb68aSAndrei Vagin #include <sys/ioctl.h>
117d5cb68aSAndrei Vagin #include <linux/time64.h>
12*678ddf73SArnaldo Carvalho de Melo #include <uapi/linux/seccomp.h>
137d5cb68aSAndrei Vagin #include <sys/prctl.h>
147d5cb68aSAndrei Vagin
157d5cb68aSAndrei Vagin #include <unistd.h>
167d5cb68aSAndrei Vagin #include <limits.h>
177d5cb68aSAndrei Vagin #include <stddef.h>
187d5cb68aSAndrei Vagin #include <stdint.h>
197d5cb68aSAndrei Vagin #include <stdio.h>
207d5cb68aSAndrei Vagin #include <stdlib.h>
217d5cb68aSAndrei Vagin #include <signal.h>
227d5cb68aSAndrei Vagin #include <sys/wait.h>
237d5cb68aSAndrei Vagin #include <string.h>
247d5cb68aSAndrei Vagin #include <errno.h>
257d5cb68aSAndrei Vagin #include <err.h>
267d5cb68aSAndrei Vagin #include <inttypes.h>
277d5cb68aSAndrei Vagin
287d5cb68aSAndrei Vagin #define LOOPS_DEFAULT 1000000UL
297d5cb68aSAndrei Vagin static uint64_t loops = LOOPS_DEFAULT;
307d5cb68aSAndrei Vagin static bool sync_mode;
317d5cb68aSAndrei Vagin
327d5cb68aSAndrei Vagin static const struct option options[] = {
337d5cb68aSAndrei Vagin OPT_U64('l', "loop", &loops, "Specify number of loops"),
347d5cb68aSAndrei Vagin OPT_BOOLEAN('s', "sync-mode", &sync_mode,
357d5cb68aSAndrei Vagin "Enable the synchronious mode for seccomp notifications"),
367d5cb68aSAndrei Vagin OPT_END()
377d5cb68aSAndrei Vagin };
387d5cb68aSAndrei Vagin
397d5cb68aSAndrei Vagin static const char * const bench_seccomp_usage[] = {
407d5cb68aSAndrei Vagin "perf bench sched secccomp-notify <options>",
417d5cb68aSAndrei Vagin NULL
427d5cb68aSAndrei Vagin };
437d5cb68aSAndrei Vagin
seccomp(unsigned int op,unsigned int flags,void * args)447d5cb68aSAndrei Vagin static int seccomp(unsigned int op, unsigned int flags, void *args)
457d5cb68aSAndrei Vagin {
467d5cb68aSAndrei Vagin return syscall(__NR_seccomp, op, flags, args);
477d5cb68aSAndrei Vagin }
487d5cb68aSAndrei Vagin
user_notif_syscall(int nr,unsigned int flags)497d5cb68aSAndrei Vagin static int user_notif_syscall(int nr, unsigned int flags)
507d5cb68aSAndrei Vagin {
517d5cb68aSAndrei Vagin struct sock_filter filter[] = {
527d5cb68aSAndrei Vagin BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
537d5cb68aSAndrei Vagin offsetof(struct seccomp_data, nr)),
547d5cb68aSAndrei Vagin BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, nr, 0, 1),
557d5cb68aSAndrei Vagin BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_USER_NOTIF),
567d5cb68aSAndrei Vagin BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
577d5cb68aSAndrei Vagin };
587d5cb68aSAndrei Vagin
597d5cb68aSAndrei Vagin struct sock_fprog prog = {
607d5cb68aSAndrei Vagin .len = (unsigned short)ARRAY_SIZE(filter),
617d5cb68aSAndrei Vagin .filter = filter,
627d5cb68aSAndrei Vagin };
637d5cb68aSAndrei Vagin
647d5cb68aSAndrei Vagin return seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog);
657d5cb68aSAndrei Vagin }
667d5cb68aSAndrei Vagin
677d5cb68aSAndrei Vagin #define USER_NOTIF_MAGIC INT_MAX
user_notification_sync_loop(int listener)687d5cb68aSAndrei Vagin static void user_notification_sync_loop(int listener)
697d5cb68aSAndrei Vagin {
707d5cb68aSAndrei Vagin struct seccomp_notif_resp resp;
717d5cb68aSAndrei Vagin struct seccomp_notif req;
727d5cb68aSAndrei Vagin uint64_t nr;
737d5cb68aSAndrei Vagin
747d5cb68aSAndrei Vagin for (nr = 0; nr < loops; nr++) {
757d5cb68aSAndrei Vagin memset(&req, 0, sizeof(req));
767d5cb68aSAndrei Vagin if (ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req))
777d5cb68aSAndrei Vagin err(EXIT_FAILURE, "SECCOMP_IOCTL_NOTIF_RECV failed");
787d5cb68aSAndrei Vagin
797d5cb68aSAndrei Vagin if (req.data.nr != __NR_gettid)
807d5cb68aSAndrei Vagin errx(EXIT_FAILURE, "unexpected syscall: %d", req.data.nr);
817d5cb68aSAndrei Vagin
827d5cb68aSAndrei Vagin resp.id = req.id;
837d5cb68aSAndrei Vagin resp.error = 0;
847d5cb68aSAndrei Vagin resp.val = USER_NOTIF_MAGIC;
857d5cb68aSAndrei Vagin resp.flags = 0;
867d5cb68aSAndrei Vagin if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp))
877d5cb68aSAndrei Vagin err(EXIT_FAILURE, "SECCOMP_IOCTL_NOTIF_SEND failed");
887d5cb68aSAndrei Vagin }
897d5cb68aSAndrei Vagin }
907d5cb68aSAndrei Vagin
917d5cb68aSAndrei Vagin #ifndef SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP
927d5cb68aSAndrei Vagin #define SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP (1UL << 0)
937d5cb68aSAndrei Vagin #define SECCOMP_IOCTL_NOTIF_SET_FLAGS SECCOMP_IOW(4, __u64)
947d5cb68aSAndrei Vagin #endif
bench_sched_seccomp_notify(int argc,const char ** argv)957d5cb68aSAndrei Vagin int bench_sched_seccomp_notify(int argc, const char **argv)
967d5cb68aSAndrei Vagin {
977d5cb68aSAndrei Vagin struct timeval start, stop, diff;
987d5cb68aSAndrei Vagin unsigned long long result_usec = 0;
997d5cb68aSAndrei Vagin int status, listener;
1007d5cb68aSAndrei Vagin pid_t pid;
1017d5cb68aSAndrei Vagin long ret;
1027d5cb68aSAndrei Vagin
1037d5cb68aSAndrei Vagin argc = parse_options(argc, argv, options, bench_seccomp_usage, 0);
1047d5cb68aSAndrei Vagin
1057d5cb68aSAndrei Vagin gettimeofday(&start, NULL);
1067d5cb68aSAndrei Vagin
1077d5cb68aSAndrei Vagin prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
1087d5cb68aSAndrei Vagin listener = user_notif_syscall(__NR_gettid,
1097d5cb68aSAndrei Vagin SECCOMP_FILTER_FLAG_NEW_LISTENER);
1107d5cb68aSAndrei Vagin if (listener < 0)
1117d5cb68aSAndrei Vagin err(EXIT_FAILURE, "can't create a notification descriptor");
1127d5cb68aSAndrei Vagin
1137d5cb68aSAndrei Vagin pid = fork();
1147d5cb68aSAndrei Vagin if (pid < 0)
1157d5cb68aSAndrei Vagin err(EXIT_FAILURE, "fork");
1167d5cb68aSAndrei Vagin if (pid == 0) {
1177d5cb68aSAndrei Vagin if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0))
1187d5cb68aSAndrei Vagin err(EXIT_FAILURE, "can't set the parent death signal");
1197d5cb68aSAndrei Vagin while (1) {
1207d5cb68aSAndrei Vagin ret = syscall(__NR_gettid);
1217d5cb68aSAndrei Vagin if (ret == USER_NOTIF_MAGIC)
1227d5cb68aSAndrei Vagin continue;
1237d5cb68aSAndrei Vagin break;
1247d5cb68aSAndrei Vagin }
1257d5cb68aSAndrei Vagin _exit(1);
1267d5cb68aSAndrei Vagin }
1277d5cb68aSAndrei Vagin
1287d5cb68aSAndrei Vagin if (sync_mode) {
1297d5cb68aSAndrei Vagin if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SET_FLAGS,
1307d5cb68aSAndrei Vagin SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP, 0))
1317d5cb68aSAndrei Vagin err(EXIT_FAILURE,
1327d5cb68aSAndrei Vagin "can't set SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP");
1337d5cb68aSAndrei Vagin }
1347d5cb68aSAndrei Vagin user_notification_sync_loop(listener);
1357d5cb68aSAndrei Vagin
1367d5cb68aSAndrei Vagin kill(pid, SIGKILL);
1377d5cb68aSAndrei Vagin if (waitpid(pid, &status, 0) != pid)
1387d5cb68aSAndrei Vagin err(EXIT_FAILURE, "waitpid(%d) failed", pid);
1397d5cb68aSAndrei Vagin if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL)
1407d5cb68aSAndrei Vagin errx(EXIT_FAILURE, "unexpected exit code: %d", status);
1417d5cb68aSAndrei Vagin
1427d5cb68aSAndrei Vagin gettimeofday(&stop, NULL);
1437d5cb68aSAndrei Vagin timersub(&stop, &start, &diff);
1447d5cb68aSAndrei Vagin
1457d5cb68aSAndrei Vagin switch (bench_format) {
1467d5cb68aSAndrei Vagin case BENCH_FORMAT_DEFAULT:
1477d5cb68aSAndrei Vagin printf("# Executed %" PRIu64 " system calls\n\n",
1487d5cb68aSAndrei Vagin loops);
1497d5cb68aSAndrei Vagin
1507d5cb68aSAndrei Vagin result_usec = diff.tv_sec * USEC_PER_SEC;
1517d5cb68aSAndrei Vagin result_usec += diff.tv_usec;
1527d5cb68aSAndrei Vagin
1537d5cb68aSAndrei Vagin printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
1547d5cb68aSAndrei Vagin (unsigned long) diff.tv_sec,
1557d5cb68aSAndrei Vagin (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
1567d5cb68aSAndrei Vagin
1577d5cb68aSAndrei Vagin printf(" %14lf usecs/op\n",
1587d5cb68aSAndrei Vagin (double)result_usec / (double)loops);
1597d5cb68aSAndrei Vagin printf(" %14d ops/sec\n",
1607d5cb68aSAndrei Vagin (int)((double)loops /
1617d5cb68aSAndrei Vagin ((double)result_usec / (double)USEC_PER_SEC)));
1627d5cb68aSAndrei Vagin break;
1637d5cb68aSAndrei Vagin
1647d5cb68aSAndrei Vagin case BENCH_FORMAT_SIMPLE:
1657d5cb68aSAndrei Vagin printf("%lu.%03lu\n",
1667d5cb68aSAndrei Vagin (unsigned long) diff.tv_sec,
1677d5cb68aSAndrei Vagin (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
1687d5cb68aSAndrei Vagin break;
1697d5cb68aSAndrei Vagin
1707d5cb68aSAndrei Vagin default:
1717d5cb68aSAndrei Vagin /* reaching here is something disaster */
1727d5cb68aSAndrei Vagin fprintf(stderr, "Unknown format:%d\n", bench_format);
1737d5cb68aSAndrei Vagin exit(1);
1747d5cb68aSAndrei Vagin break;
1757d5cb68aSAndrei Vagin }
1767d5cb68aSAndrei Vagin
1777d5cb68aSAndrei Vagin return 0;
1787d5cb68aSAndrei Vagin }
179