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