rdpmc.c (3493B)
1// SPDX-License-Identifier: GPL-2.0 2#include <errno.h> 3#include <unistd.h> 4#include <stdlib.h> 5#include <signal.h> 6#include <sys/mman.h> 7#include <sys/types.h> 8#include <sys/wait.h> 9#include <linux/string.h> 10#include <linux/types.h> 11#include "perf-sys.h" 12#include "debug.h" 13#include "tests/tests.h" 14#include "cloexec.h" 15#include "event.h" 16#include <internal/lib.h> // page_size 17#include "arch-tests.h" 18 19static u64 rdpmc(unsigned int counter) 20{ 21 unsigned int low, high; 22 23 asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); 24 25 return low | ((u64)high) << 32; 26} 27 28static u64 rdtsc(void) 29{ 30 unsigned int low, high; 31 32 asm volatile("rdtsc" : "=a" (low), "=d" (high)); 33 34 return low | ((u64)high) << 32; 35} 36 37static u64 mmap_read_self(void *addr) 38{ 39 struct perf_event_mmap_page *pc = addr; 40 u32 seq, idx, time_mult = 0, time_shift = 0; 41 u64 count, cyc = 0, time_offset = 0, enabled, running, delta; 42 43 do { 44 seq = pc->lock; 45 barrier(); 46 47 enabled = pc->time_enabled; 48 running = pc->time_running; 49 50 if (enabled != running) { 51 cyc = rdtsc(); 52 time_mult = pc->time_mult; 53 time_shift = pc->time_shift; 54 time_offset = pc->time_offset; 55 } 56 57 idx = pc->index; 58 count = pc->offset; 59 if (idx) 60 count += rdpmc(idx - 1); 61 62 barrier(); 63 } while (pc->lock != seq); 64 65 if (enabled != running) { 66 u64 quot, rem; 67 68 quot = (cyc >> time_shift); 69 rem = cyc & (((u64)1 << time_shift) - 1); 70 delta = time_offset + quot * time_mult + 71 ((rem * time_mult) >> time_shift); 72 73 enabled += delta; 74 if (idx) 75 running += delta; 76 77 quot = count / running; 78 rem = count % running; 79 count = quot * enabled + (rem * enabled) / running; 80 } 81 82 return count; 83} 84 85/* 86 * If the RDPMC instruction faults then signal this back to the test parent task: 87 */ 88static void segfault_handler(int sig __maybe_unused, 89 siginfo_t *info __maybe_unused, 90 void *uc __maybe_unused) 91{ 92 exit(-1); 93} 94 95static int __test__rdpmc(void) 96{ 97 volatile int tmp = 0; 98 u64 i, loops = 1000; 99 int n; 100 int fd; 101 void *addr; 102 struct perf_event_attr attr = { 103 .type = PERF_TYPE_HARDWARE, 104 .config = PERF_COUNT_HW_INSTRUCTIONS, 105 .exclude_kernel = 1, 106 }; 107 u64 delta_sum = 0; 108 struct sigaction sa; 109 char sbuf[STRERR_BUFSIZE]; 110 111 sigfillset(&sa.sa_mask); 112 sa.sa_sigaction = segfault_handler; 113 sa.sa_flags = 0; 114 sigaction(SIGSEGV, &sa, NULL); 115 116 fd = sys_perf_event_open(&attr, 0, -1, -1, 117 perf_event_open_cloexec_flag()); 118 if (fd < 0) { 119 pr_err("Error: sys_perf_event_open() syscall returned " 120 "with %d (%s)\n", fd, 121 str_error_r(errno, sbuf, sizeof(sbuf))); 122 return -1; 123 } 124 125 addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); 126 if (addr == (void *)(-1)) { 127 pr_err("Error: mmap() syscall returned with (%s)\n", 128 str_error_r(errno, sbuf, sizeof(sbuf))); 129 goto out_close; 130 } 131 132 for (n = 0; n < 6; n++) { 133 u64 stamp, now, delta; 134 135 stamp = mmap_read_self(addr); 136 137 for (i = 0; i < loops; i++) 138 tmp++; 139 140 now = mmap_read_self(addr); 141 loops *= 10; 142 143 delta = now - stamp; 144 pr_debug("%14d: %14Lu\n", n, (long long)delta); 145 146 delta_sum += delta; 147 } 148 149 munmap(addr, page_size); 150 pr_debug(" "); 151out_close: 152 close(fd); 153 154 if (!delta_sum) 155 return -1; 156 157 return 0; 158} 159 160int test__rdpmc(struct test_suite *test __maybe_unused, int subtest __maybe_unused) 161{ 162 int status = 0; 163 int wret = 0; 164 int ret; 165 int pid; 166 167 pid = fork(); 168 if (pid < 0) 169 return -1; 170 171 if (!pid) { 172 ret = __test__rdpmc(); 173 174 exit(ret); 175 } 176 177 wret = waitpid(pid, &status, 0); 178 if (wret < 0 || status) 179 return -1; 180 181 return 0; 182}