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