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