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