1 // SPDX-License-Identifier: GPL-2.0 2 #include <stdbool.h> 3 #include <errno.h> 4 5 #include <linux/perf_event.h> 6 7 #include "../../perf.h" 8 #include <linux/types.h> 9 #include "../../util/debug.h" 10 #include "../../util/tsc.h" 11 12 int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, 13 struct perf_tsc_conversion *tc) 14 { 15 bool cap_user_time_zero; 16 u32 seq; 17 int i = 0; 18 19 while (1) { 20 seq = pc->lock; 21 rmb(); 22 tc->time_mult = pc->time_mult; 23 tc->time_shift = pc->time_shift; 24 tc->time_zero = pc->time_zero; 25 cap_user_time_zero = pc->cap_user_time_zero; 26 rmb(); 27 if (pc->lock == seq && !(seq & 1)) 28 break; 29 if (++i > 10000) { 30 pr_debug("failed to get perf_event_mmap_page lock\n"); 31 return -EINVAL; 32 } 33 } 34 35 if (!cap_user_time_zero) 36 return -EOPNOTSUPP; 37 38 return 0; 39 } 40 41 u64 rdtsc(void) 42 { 43 unsigned int low, high; 44 45 asm volatile("rdtsc" : "=a" (low), "=d" (high)); 46 47 return low | ((u64)high) << 32; 48 } 49 50 int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc, 51 struct perf_tool *tool, 52 perf_event__handler_t process, 53 struct machine *machine) 54 { 55 union perf_event event = { 56 .time_conv = { 57 .header = { 58 .type = PERF_RECORD_TIME_CONV, 59 .size = sizeof(struct time_conv_event), 60 }, 61 }, 62 }; 63 struct perf_tsc_conversion tc; 64 int err; 65 66 if (!pc) 67 return 0; 68 err = perf_read_tsc_conversion(pc, &tc); 69 if (err == -EOPNOTSUPP) 70 return 0; 71 if (err) 72 return err; 73 74 pr_debug2("Synthesizing TSC conversion information\n"); 75 76 event.time_conv.time_mult = tc.time_mult; 77 event.time_conv.time_shift = tc.time_shift; 78 event.time_conv.time_zero = tc.time_zero; 79 80 return process(tool, &event, NULL, machine); 81 } 82