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