1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Test null syscall performance
4  *
5  * Copyright (C) 2009-2015 Anton Blanchard, IBM
6  */
7 
8 #define NR_LOOPS 10000000
9 
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <time.h>
15 #include <sys/types.h>
16 #include <sys/time.h>
17 #include <signal.h>
18 
19 static volatile int soak_done;
20 unsigned long long clock_frequency;
21 unsigned long long timebase_frequency;
22 double timebase_multiplier;
23 
24 static inline unsigned long mftb(void)
25 {
26 	unsigned long low;
27 
28 	asm volatile("mftb %0" : "=r" (low));
29 
30 	return low;
31 }
32 
33 static void sigalrm_handler(int unused)
34 {
35 	soak_done = 1;
36 }
37 
38 /*
39  * Use a timer instead of busy looping on clock_gettime() so we don't
40  * pollute profiles with glibc and VDSO hits.
41  */
42 static void cpu_soak_usecs(unsigned long usecs)
43 {
44 	struct itimerval val;
45 
46 	memset(&val, 0, sizeof(val));
47 	val.it_value.tv_usec = usecs;
48 
49 	signal(SIGALRM, sigalrm_handler);
50 	setitimer(ITIMER_REAL, &val, NULL);
51 
52 	while (1) {
53 		if (soak_done)
54 			break;
55 	}
56 
57 	signal(SIGALRM, SIG_DFL);
58 }
59 
60 /*
61  * This only works with recent kernels where cpufreq modifies
62  * /proc/cpuinfo dynamically.
63  */
64 static void get_proc_frequency(void)
65 {
66 	FILE *f;
67 	char line[128];
68 	char *p, *end;
69 	unsigned long v;
70 	double d;
71 	char *override;
72 
73 	/* Try to get out of low power/low frequency mode */
74 	cpu_soak_usecs(0.25 * 1000000);
75 
76 	f = fopen("/proc/cpuinfo", "r");
77 	if (f == NULL)
78 		return;
79 
80 	timebase_frequency = 0;
81 
82 	while (fgets(line, sizeof(line), f) != NULL) {
83 		if (strncmp(line, "timebase", 8) == 0) {
84 			p = strchr(line, ':');
85 			if (p != NULL) {
86 				v = strtoull(p + 1, &end, 0);
87 				if (end != p + 1)
88 					timebase_frequency = v;
89 			}
90 		}
91 
92 		if (((strncmp(line, "clock", 5) == 0) ||
93 		     (strncmp(line, "cpu MHz", 7) == 0))) {
94 			p = strchr(line, ':');
95 			if (p != NULL) {
96 				d = strtod(p + 1, &end);
97 				if (end != p + 1) {
98 					/* Find fastest clock frequency */
99 					if ((d * 1000000ULL) > clock_frequency)
100 						clock_frequency = d * 1000000ULL;
101 				}
102 			}
103 		}
104 	}
105 
106 	fclose(f);
107 
108 	override = getenv("FREQUENCY");
109 	if (override)
110 		clock_frequency = strtoull(override, NULL, 10);
111 
112 	if (timebase_frequency)
113 		timebase_multiplier = (double)clock_frequency
114 					/ timebase_frequency;
115 	else
116 		timebase_multiplier = 1;
117 }
118 
119 static void do_null_syscall(unsigned long nr)
120 {
121 	unsigned long i;
122 
123 	for (i = 0; i < nr; i++)
124 		getppid();
125 }
126 
127 #define TIME(A, STR) \
128 
129 int main(void)
130 {
131 	unsigned long tb_start, tb_now;
132 	struct timespec tv_start, tv_now;
133 	unsigned long long elapsed_ns, elapsed_tb;
134 
135 	get_proc_frequency();
136 
137 	clock_gettime(CLOCK_MONOTONIC, &tv_start);
138 	tb_start = mftb();
139 
140 	do_null_syscall(NR_LOOPS);
141 
142 	clock_gettime(CLOCK_MONOTONIC, &tv_now);
143 	tb_now = mftb();
144 
145 	elapsed_ns = (tv_now.tv_sec - tv_start.tv_sec) * 1000000000ULL +
146 			(tv_now.tv_nsec - tv_start.tv_nsec);
147 	elapsed_tb = tb_now - tb_start;
148 
149 	printf("%10.2f ns %10.2f cycles\n", (float)elapsed_ns / NR_LOOPS,
150 			(float)elapsed_tb * timebase_multiplier / NR_LOOPS);
151 
152 	return 0;
153 }
154