162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Test null syscall performance
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2009-2015 Anton Blanchard, IBM
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define NR_LOOPS 10000000
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <string.h>
1162306a36Sopenharmony_ci#include <stdio.h>
1262306a36Sopenharmony_ci#include <stdlib.h>
1362306a36Sopenharmony_ci#include <unistd.h>
1462306a36Sopenharmony_ci#include <time.h>
1562306a36Sopenharmony_ci#include <sys/types.h>
1662306a36Sopenharmony_ci#include <sys/time.h>
1762306a36Sopenharmony_ci#include <sys/syscall.h>
1862306a36Sopenharmony_ci#include <signal.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic volatile int soak_done;
2162306a36Sopenharmony_ciunsigned long long clock_frequency;
2262306a36Sopenharmony_ciunsigned long long timebase_frequency;
2362306a36Sopenharmony_cidouble timebase_multiplier;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic inline unsigned long mftb(void)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	unsigned long low;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	asm volatile("mftb %0" : "=r" (low));
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	return low;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic void sigalrm_handler(int unused)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	soak_done = 1;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * Use a timer instead of busy looping on clock_gettime() so we don't
4162306a36Sopenharmony_ci * pollute profiles with glibc and VDSO hits.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_cistatic void cpu_soak_usecs(unsigned long usecs)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct itimerval val;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	memset(&val, 0, sizeof(val));
4862306a36Sopenharmony_ci	val.it_value.tv_usec = usecs;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	signal(SIGALRM, sigalrm_handler);
5162306a36Sopenharmony_ci	setitimer(ITIMER_REAL, &val, NULL);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	while (1) {
5462306a36Sopenharmony_ci		if (soak_done)
5562306a36Sopenharmony_ci			break;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	signal(SIGALRM, SIG_DFL);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * This only works with recent kernels where cpufreq modifies
6362306a36Sopenharmony_ci * /proc/cpuinfo dynamically.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_cistatic void get_proc_frequency(void)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	FILE *f;
6862306a36Sopenharmony_ci	char line[128];
6962306a36Sopenharmony_ci	char *p, *end;
7062306a36Sopenharmony_ci	unsigned long v;
7162306a36Sopenharmony_ci	double d;
7262306a36Sopenharmony_ci	char *override;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Try to get out of low power/low frequency mode */
7562306a36Sopenharmony_ci	cpu_soak_usecs(0.25 * 1000000);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	f = fopen("/proc/cpuinfo", "r");
7862306a36Sopenharmony_ci	if (f == NULL)
7962306a36Sopenharmony_ci		return;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	timebase_frequency = 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	while (fgets(line, sizeof(line), f) != NULL) {
8462306a36Sopenharmony_ci		if (strncmp(line, "timebase", 8) == 0) {
8562306a36Sopenharmony_ci			p = strchr(line, ':');
8662306a36Sopenharmony_ci			if (p != NULL) {
8762306a36Sopenharmony_ci				v = strtoull(p + 1, &end, 0);
8862306a36Sopenharmony_ci				if (end != p + 1)
8962306a36Sopenharmony_ci					timebase_frequency = v;
9062306a36Sopenharmony_ci			}
9162306a36Sopenharmony_ci		}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		if (((strncmp(line, "clock", 5) == 0) ||
9462306a36Sopenharmony_ci		     (strncmp(line, "cpu MHz", 7) == 0))) {
9562306a36Sopenharmony_ci			p = strchr(line, ':');
9662306a36Sopenharmony_ci			if (p != NULL) {
9762306a36Sopenharmony_ci				d = strtod(p + 1, &end);
9862306a36Sopenharmony_ci				if (end != p + 1) {
9962306a36Sopenharmony_ci					/* Find fastest clock frequency */
10062306a36Sopenharmony_ci					if ((d * 1000000ULL) > clock_frequency)
10162306a36Sopenharmony_ci						clock_frequency = d * 1000000ULL;
10262306a36Sopenharmony_ci				}
10362306a36Sopenharmony_ci			}
10462306a36Sopenharmony_ci		}
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	fclose(f);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	override = getenv("FREQUENCY");
11062306a36Sopenharmony_ci	if (override)
11162306a36Sopenharmony_ci		clock_frequency = strtoull(override, NULL, 10);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (timebase_frequency)
11462306a36Sopenharmony_ci		timebase_multiplier = (double)clock_frequency
11562306a36Sopenharmony_ci					/ timebase_frequency;
11662306a36Sopenharmony_ci	else
11762306a36Sopenharmony_ci		timebase_multiplier = 1;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void do_null_syscall(unsigned long nr)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	unsigned long i;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	for (i = 0; i < nr; i++)
12562306a36Sopenharmony_ci		syscall(__NR_gettid);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci#define TIME(A, STR) \
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciint main(void)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	unsigned long tb_start, tb_now;
13362306a36Sopenharmony_ci	struct timespec tv_start, tv_now;
13462306a36Sopenharmony_ci	unsigned long long elapsed_ns, elapsed_tb;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	get_proc_frequency();
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC, &tv_start);
13962306a36Sopenharmony_ci	tb_start = mftb();
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	do_null_syscall(NR_LOOPS);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC, &tv_now);
14462306a36Sopenharmony_ci	tb_now = mftb();
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	elapsed_ns = (tv_now.tv_sec - tv_start.tv_sec) * 1000000000ULL +
14762306a36Sopenharmony_ci			(tv_now.tv_nsec - tv_start.tv_nsec);
14862306a36Sopenharmony_ci	elapsed_tb = tb_now - tb_start;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	printf("%10.2f ns %10.2f cycles\n", (float)elapsed_ns / NR_LOOPS,
15162306a36Sopenharmony_ci			(float)elapsed_tb * timebase_multiplier / NR_LOOPS);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
155