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
perf_read_tsc_conversion(const struct perf_event_mmap_page * pc,struct perf_tsc_conversion * tc)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
rdtsc(void)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
perf_event__synth_time_conv(const struct perf_event_mmap_page * pc,struct perf_tool * tool,perf_event__handler_t process,struct machine * machine)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