xref: /openbmc/linux/tools/perf/arch/x86/util/tsc.c (revision 3dc4b6fb)
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