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