brintos

brintos / linux-shallow public Read only

0
0
Text · 4.6 KiB · 073a037 Raw
203 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2020 Collabora Ltd.4 *5 * Benchmark and test syscall user dispatch6 */7 8#define _GNU_SOURCE9#include <stdio.h>10#include <string.h>11#include <stdlib.h>12#include <signal.h>13#include <errno.h>14#include <time.h>15#include <sys/time.h>16#include <unistd.h>17#include <sys/sysinfo.h>18#include <sys/prctl.h>19#include <sys/syscall.h>20 21#ifndef PR_SET_SYSCALL_USER_DISPATCH22# define PR_SET_SYSCALL_USER_DISPATCH	5923# define PR_SYS_DISPATCH_OFF	024# define PR_SYS_DISPATCH_ON	125# define SYSCALL_DISPATCH_FILTER_ALLOW	026# define SYSCALL_DISPATCH_FILTER_BLOCK	127#endif28 29#ifdef __NR_syscalls30# define MAGIC_SYSCALL_1 (__NR_syscalls + 1) /* Bad Linux syscall number */31#else32# define MAGIC_SYSCALL_1 (0xff00)  /* Bad Linux syscall number */33#endif34 35/*36 * To test returning from a sigsys with selector blocked, the test37 * requires some per-architecture support (i.e. knowledge about the38 * signal trampoline address).  On i386, we know it is on the vdso, and39 * a small trampoline is open-coded for x86_64.  Other architectures40 * that have a trampoline in the vdso will support TEST_BLOCKED_RETURN41 * out of the box, but don't enable them until they support syscall user42 * dispatch.43 */44#if defined(__x86_64__) || defined(__i386__)45#define TEST_BLOCKED_RETURN46#endif47 48#ifdef __x86_64__49void* (syscall_dispatcher_start)(void);50void* (syscall_dispatcher_end)(void);51#else52unsigned long syscall_dispatcher_start = 0;53unsigned long syscall_dispatcher_end = 0;54#endif55 56unsigned long trapped_call_count = 0;57unsigned long native_call_count = 0;58 59char selector;60#define SYSCALL_BLOCK   (selector = SYSCALL_DISPATCH_FILTER_BLOCK)61#define SYSCALL_UNBLOCK (selector = SYSCALL_DISPATCH_FILTER_ALLOW)62 63#define CALIBRATION_STEP 10000064#define CALIBRATE_TO_SECS 565int factor;66 67static double one_sysinfo_step(void)68{69	struct timespec t1, t2;70	int i;71	struct sysinfo info;72 73	clock_gettime(CLOCK_MONOTONIC, &t1);74	for (i = 0; i < CALIBRATION_STEP; i++)75		sysinfo(&info);76	clock_gettime(CLOCK_MONOTONIC, &t2);77	return (t2.tv_sec - t1.tv_sec) + 1.0e-9 * (t2.tv_nsec - t1.tv_nsec);78}79 80static void calibrate_set(void)81{82	double elapsed = 0;83 84	printf("Calibrating test set to last ~%d seconds...\n", CALIBRATE_TO_SECS);85 86	while (elapsed < 1) {87		elapsed += one_sysinfo_step();88		factor += CALIBRATE_TO_SECS;89	}90 91	printf("test iterations = %d\n", CALIBRATION_STEP * factor);92}93 94static double perf_syscall(void)95{96	unsigned int i;97	double partial = 0;98 99	for (i = 0; i < factor; ++i)100		partial += one_sysinfo_step()/(CALIBRATION_STEP*factor);101	return partial;102}103 104static void handle_sigsys(int sig, siginfo_t *info, void *ucontext)105{106	char buf[1024];107	int len;108 109	SYSCALL_UNBLOCK;110 111	/* printf and friends are not signal-safe. */112	len = snprintf(buf, 1024, "Caught sys_%x\n", info->si_syscall);113	write(1, buf, len);114 115	if (info->si_syscall == MAGIC_SYSCALL_1)116		trapped_call_count++;117	else118		native_call_count++;119 120#ifdef TEST_BLOCKED_RETURN121	SYSCALL_BLOCK;122#endif123 124#ifdef __x86_64__125	__asm__ volatile("movq $0xf, %rax");126	__asm__ volatile("leaveq");127	__asm__ volatile("add $0x8, %rsp");128	__asm__ volatile("syscall_dispatcher_start:");129	__asm__ volatile("syscall");130	__asm__ volatile("nop"); /* Landing pad within dispatcher area */131	__asm__ volatile("syscall_dispatcher_end:");132#endif133 134}135 136int main(void)137{138	struct sigaction act;139	double time1, time2;140	int ret;141	sigset_t mask;142 143	memset(&act, 0, sizeof(act));144	sigemptyset(&mask);145 146	act.sa_sigaction = handle_sigsys;147	act.sa_flags = SA_SIGINFO;148	act.sa_mask = mask;149 150	calibrate_set();151 152	time1 = perf_syscall();153	printf("Avg syscall time %.0lfns.\n", time1 * 1.0e9);154 155	ret = sigaction(SIGSYS, &act, NULL);156	if (ret) {157		perror("Error sigaction:");158		exit(-1);159	}160 161	fprintf(stderr, "Enabling syscall trapping.\n");162 163	if (prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON,164		  syscall_dispatcher_start,165		  (syscall_dispatcher_end - syscall_dispatcher_start + 1),166		  &selector)) {167		perror("prctl failed\n");168		exit(-1);169	}170 171	SYSCALL_BLOCK;172	syscall(MAGIC_SYSCALL_1);173 174#ifdef TEST_BLOCKED_RETURN175	if (selector == SYSCALL_DISPATCH_FILTER_ALLOW) {176		fprintf(stderr, "Failed to return with selector blocked.\n");177		exit(-1);178	}179#endif180 181	SYSCALL_UNBLOCK;182 183	if (!trapped_call_count) {184		fprintf(stderr, "syscall trapping does not work.\n");185		exit(-1);186	}187 188	time2 = perf_syscall();189 190	if (native_call_count) {191		perror("syscall trapping intercepted more syscalls than expected\n");192		exit(-1);193	}194 195	printf("trapped_call_count %lu, native_call_count %lu.\n",196	       trapped_call_count, native_call_count);197	printf("Avg syscall time %.0lfns.\n", time2 * 1.0e9);198	printf("Interception overhead: %.1lf%% (+%.0lfns).\n",199	       100.0 * (time2 / time1 - 1.0), 1.0e9 * (time2 - time1));200	return 0;201 202}203