162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <subcmd/parse-options.h>
362306a36Sopenharmony_ci#include "bench.h"
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <uapi/linux/filter.h>
662306a36Sopenharmony_ci#include <sys/types.h>
762306a36Sopenharmony_ci#include <sys/time.h>
862306a36Sopenharmony_ci#include <linux/unistd.h>
962306a36Sopenharmony_ci#include <sys/syscall.h>
1062306a36Sopenharmony_ci#include <sys/ioctl.h>
1162306a36Sopenharmony_ci#include <linux/time64.h>
1262306a36Sopenharmony_ci#include <uapi/linux/seccomp.h>
1362306a36Sopenharmony_ci#include <sys/prctl.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <unistd.h>
1662306a36Sopenharmony_ci#include <limits.h>
1762306a36Sopenharmony_ci#include <stddef.h>
1862306a36Sopenharmony_ci#include <stdint.h>
1962306a36Sopenharmony_ci#include <stdio.h>
2062306a36Sopenharmony_ci#include <stdlib.h>
2162306a36Sopenharmony_ci#include <signal.h>
2262306a36Sopenharmony_ci#include <sys/wait.h>
2362306a36Sopenharmony_ci#include <string.h>
2462306a36Sopenharmony_ci#include <errno.h>
2562306a36Sopenharmony_ci#include <err.h>
2662306a36Sopenharmony_ci#include <inttypes.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define LOOPS_DEFAULT 1000000UL
2962306a36Sopenharmony_cistatic uint64_t loops = LOOPS_DEFAULT;
3062306a36Sopenharmony_cistatic bool sync_mode;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic const struct option options[] = {
3362306a36Sopenharmony_ci	OPT_U64('l', "loop",	&loops,		"Specify number of loops"),
3462306a36Sopenharmony_ci	OPT_BOOLEAN('s', "sync-mode", &sync_mode,
3562306a36Sopenharmony_ci		    "Enable the synchronious mode for seccomp notifications"),
3662306a36Sopenharmony_ci	OPT_END()
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic const char * const bench_seccomp_usage[] = {
4062306a36Sopenharmony_ci	"perf bench sched secccomp-notify <options>",
4162306a36Sopenharmony_ci	NULL
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int seccomp(unsigned int op, unsigned int flags, void *args)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	return syscall(__NR_seccomp, op, flags, args);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int user_notif_syscall(int nr, unsigned int flags)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct sock_filter filter[] = {
5262306a36Sopenharmony_ci		BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
5362306a36Sopenharmony_ci			offsetof(struct seccomp_data, nr)),
5462306a36Sopenharmony_ci		BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, nr, 0, 1),
5562306a36Sopenharmony_ci		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_USER_NOTIF),
5662306a36Sopenharmony_ci		BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
5762306a36Sopenharmony_ci	};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	struct sock_fprog prog = {
6062306a36Sopenharmony_ci		.len = (unsigned short)ARRAY_SIZE(filter),
6162306a36Sopenharmony_ci		.filter = filter,
6262306a36Sopenharmony_ci	};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define USER_NOTIF_MAGIC INT_MAX
6862306a36Sopenharmony_cistatic void user_notification_sync_loop(int listener)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct seccomp_notif_resp resp;
7162306a36Sopenharmony_ci	struct seccomp_notif req;
7262306a36Sopenharmony_ci	uint64_t nr;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	for (nr = 0; nr < loops; nr++) {
7562306a36Sopenharmony_ci		memset(&req, 0, sizeof(req));
7662306a36Sopenharmony_ci		if (ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req))
7762306a36Sopenharmony_ci			err(EXIT_FAILURE, "SECCOMP_IOCTL_NOTIF_RECV failed");
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		if (req.data.nr != __NR_gettid)
8062306a36Sopenharmony_ci			errx(EXIT_FAILURE, "unexpected syscall: %d", req.data.nr);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		resp.id = req.id;
8362306a36Sopenharmony_ci		resp.error = 0;
8462306a36Sopenharmony_ci		resp.val = USER_NOTIF_MAGIC;
8562306a36Sopenharmony_ci		resp.flags = 0;
8662306a36Sopenharmony_ci		if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp))
8762306a36Sopenharmony_ci			err(EXIT_FAILURE, "SECCOMP_IOCTL_NOTIF_SEND failed");
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#ifndef SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP
9262306a36Sopenharmony_ci#define SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP (1UL << 0)
9362306a36Sopenharmony_ci#define SECCOMP_IOCTL_NOTIF_SET_FLAGS  SECCOMP_IOW(4, __u64)
9462306a36Sopenharmony_ci#endif
9562306a36Sopenharmony_ciint bench_sched_seccomp_notify(int argc, const char **argv)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct timeval start, stop, diff;
9862306a36Sopenharmony_ci	unsigned long long result_usec = 0;
9962306a36Sopenharmony_ci	int status, listener;
10062306a36Sopenharmony_ci	pid_t pid;
10162306a36Sopenharmony_ci	long ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	argc = parse_options(argc, argv, options, bench_seccomp_usage, 0);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	gettimeofday(&start, NULL);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
10862306a36Sopenharmony_ci	listener = user_notif_syscall(__NR_gettid,
10962306a36Sopenharmony_ci				      SECCOMP_FILTER_FLAG_NEW_LISTENER);
11062306a36Sopenharmony_ci	if (listener < 0)
11162306a36Sopenharmony_ci		err(EXIT_FAILURE, "can't create a notification descriptor");
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	pid = fork();
11462306a36Sopenharmony_ci	if (pid < 0)
11562306a36Sopenharmony_ci		err(EXIT_FAILURE, "fork");
11662306a36Sopenharmony_ci	if (pid == 0) {
11762306a36Sopenharmony_ci		if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0))
11862306a36Sopenharmony_ci			err(EXIT_FAILURE, "can't set the parent death signal");
11962306a36Sopenharmony_ci		while (1) {
12062306a36Sopenharmony_ci			ret = syscall(__NR_gettid);
12162306a36Sopenharmony_ci			if (ret == USER_NOTIF_MAGIC)
12262306a36Sopenharmony_ci				continue;
12362306a36Sopenharmony_ci			break;
12462306a36Sopenharmony_ci		}
12562306a36Sopenharmony_ci		_exit(1);
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (sync_mode) {
12962306a36Sopenharmony_ci		if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SET_FLAGS,
13062306a36Sopenharmony_ci			     SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP, 0))
13162306a36Sopenharmony_ci			err(EXIT_FAILURE,
13262306a36Sopenharmony_ci			    "can't set SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP");
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	user_notification_sync_loop(listener);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	kill(pid, SIGKILL);
13762306a36Sopenharmony_ci	if (waitpid(pid, &status, 0) != pid)
13862306a36Sopenharmony_ci		err(EXIT_FAILURE, "waitpid(%d) failed", pid);
13962306a36Sopenharmony_ci	if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL)
14062306a36Sopenharmony_ci		errx(EXIT_FAILURE, "unexpected exit code: %d", status);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	gettimeofday(&stop, NULL);
14362306a36Sopenharmony_ci	timersub(&stop, &start, &diff);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	switch (bench_format) {
14662306a36Sopenharmony_ci	case BENCH_FORMAT_DEFAULT:
14762306a36Sopenharmony_ci		printf("# Executed %" PRIu64 " system calls\n\n",
14862306a36Sopenharmony_ci			loops);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		result_usec = diff.tv_sec * USEC_PER_SEC;
15162306a36Sopenharmony_ci		result_usec += diff.tv_usec;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
15462306a36Sopenharmony_ci		       (unsigned long) diff.tv_sec,
15562306a36Sopenharmony_ci		       (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		printf(" %14lf usecs/op\n",
15862306a36Sopenharmony_ci		       (double)result_usec / (double)loops);
15962306a36Sopenharmony_ci		printf(" %14d ops/sec\n",
16062306a36Sopenharmony_ci		       (int)((double)loops /
16162306a36Sopenharmony_ci			     ((double)result_usec / (double)USEC_PER_SEC)));
16262306a36Sopenharmony_ci		break;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	case BENCH_FORMAT_SIMPLE:
16562306a36Sopenharmony_ci		printf("%lu.%03lu\n",
16662306a36Sopenharmony_ci		       (unsigned long) diff.tv_sec,
16762306a36Sopenharmony_ci		       (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
16862306a36Sopenharmony_ci		break;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	default:
17162306a36Sopenharmony_ci		/* reaching here is something disaster */
17262306a36Sopenharmony_ci		fprintf(stderr, "Unknown format:%d\n", bench_format);
17362306a36Sopenharmony_ci		exit(1);
17462306a36Sopenharmony_ci		break;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
179