xref: /openbmc/linux/tools/perf/arch/x86/util/tsc.c (revision 161f4089)
1 #include <stdbool.h>
2 #include <errno.h>
3 
4 #include <linux/perf_event.h>
5 
6 #include "../../perf.h"
7 #include "../../util/types.h"
8 #include "../../util/debug.h"
9 #include "tsc.h"
10 
11 u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc)
12 {
13 	u64 t, quot, rem;
14 
15 	t = ns - tc->time_zero;
16 	quot = t / tc->time_mult;
17 	rem  = t % tc->time_mult;
18 	return (quot << tc->time_shift) +
19 	       (rem << tc->time_shift) / tc->time_mult;
20 }
21 
22 u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc)
23 {
24 	u64 quot, rem;
25 
26 	quot = cyc >> tc->time_shift;
27 	rem  = cyc & ((1 << tc->time_shift) - 1);
28 	return tc->time_zero + quot * tc->time_mult +
29 	       ((rem * tc->time_mult) >> tc->time_shift);
30 }
31 
32 int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
33 			     struct perf_tsc_conversion *tc)
34 {
35 	bool cap_user_time_zero;
36 	u32 seq;
37 	int i = 0;
38 
39 	while (1) {
40 		seq = pc->lock;
41 		rmb();
42 		tc->time_mult = pc->time_mult;
43 		tc->time_shift = pc->time_shift;
44 		tc->time_zero = pc->time_zero;
45 		cap_user_time_zero = pc->cap_user_time_zero;
46 		rmb();
47 		if (pc->lock == seq && !(seq & 1))
48 			break;
49 		if (++i > 10000) {
50 			pr_debug("failed to get perf_event_mmap_page lock\n");
51 			return -EINVAL;
52 		}
53 	}
54 
55 	if (!cap_user_time_zero)
56 		return -EOPNOTSUPP;
57 
58 	return 0;
59 }
60