1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
203fca3afSLeo Yan #include <errno.h>
3*81e70d7eSLeo Yan #include <inttypes.h>
4*81e70d7eSLeo Yan #include <string.h>
503fca3afSLeo Yan
60b437860SAdrian Hunter #include <linux/compiler.h>
703fca3afSLeo Yan #include <linux/perf_event.h>
803fca3afSLeo Yan #include <linux/stddef.h>
90b437860SAdrian Hunter #include <linux/types.h>
100b437860SAdrian Hunter
1103fca3afSLeo Yan #include <asm/barrier.h>
1203fca3afSLeo Yan
1303fca3afSLeo Yan #include "event.h"
1403fca3afSLeo Yan #include "synthetic-events.h"
1503fca3afSLeo Yan #include "debug.h"
160b437860SAdrian Hunter #include "tsc.h"
170b437860SAdrian Hunter
perf_time_to_tsc(u64 ns,struct perf_tsc_conversion * tc)180b437860SAdrian Hunter u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc)
190b437860SAdrian Hunter {
200b437860SAdrian Hunter u64 t, quot, rem;
210b437860SAdrian Hunter
220b437860SAdrian Hunter t = ns - tc->time_zero;
230b437860SAdrian Hunter quot = t / tc->time_mult;
240b437860SAdrian Hunter rem = t % tc->time_mult;
250b437860SAdrian Hunter return (quot << tc->time_shift) +
260b437860SAdrian Hunter (rem << tc->time_shift) / tc->time_mult;
270b437860SAdrian Hunter }
280b437860SAdrian Hunter
tsc_to_perf_time(u64 cyc,struct perf_tsc_conversion * tc)290b437860SAdrian Hunter u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc)
300b437860SAdrian Hunter {
310b437860SAdrian Hunter u64 quot, rem;
320b437860SAdrian Hunter
3378a93d4cSLeo Yan if (tc->cap_user_time_short)
3478a93d4cSLeo Yan cyc = tc->time_cycles +
3578a93d4cSLeo Yan ((cyc - tc->time_cycles) & tc->time_mask);
3678a93d4cSLeo Yan
370b437860SAdrian Hunter quot = cyc >> tc->time_shift;
38a23f96eeSAdrian Hunter rem = cyc & (((u64)1 << tc->time_shift) - 1);
390b437860SAdrian Hunter return tc->time_zero + quot * tc->time_mult +
400b437860SAdrian Hunter ((rem * tc->time_mult) >> tc->time_shift);
410b437860SAdrian Hunter }
42a6a69db4SAdrian Hunter
perf_read_tsc_conversion(const struct perf_event_mmap_page * pc,struct perf_tsc_conversion * tc)4303fca3afSLeo Yan int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
4403fca3afSLeo Yan struct perf_tsc_conversion *tc)
4503fca3afSLeo Yan {
4603fca3afSLeo Yan u32 seq;
4703fca3afSLeo Yan int i = 0;
4803fca3afSLeo Yan
4903fca3afSLeo Yan while (1) {
5003fca3afSLeo Yan seq = pc->lock;
5103fca3afSLeo Yan rmb();
5203fca3afSLeo Yan tc->time_mult = pc->time_mult;
5303fca3afSLeo Yan tc->time_shift = pc->time_shift;
5403fca3afSLeo Yan tc->time_zero = pc->time_zero;
5578a93d4cSLeo Yan tc->time_cycles = pc->time_cycles;
5678a93d4cSLeo Yan tc->time_mask = pc->time_mask;
5778a93d4cSLeo Yan tc->cap_user_time_zero = pc->cap_user_time_zero;
5878a93d4cSLeo Yan tc->cap_user_time_short = pc->cap_user_time_short;
5903fca3afSLeo Yan rmb();
6003fca3afSLeo Yan if (pc->lock == seq && !(seq & 1))
6103fca3afSLeo Yan break;
6203fca3afSLeo Yan if (++i > 10000) {
6303fca3afSLeo Yan pr_debug("failed to get perf_event_mmap_page lock\n");
6403fca3afSLeo Yan return -EINVAL;
6503fca3afSLeo Yan }
6603fca3afSLeo Yan }
6703fca3afSLeo Yan
6878a93d4cSLeo Yan if (!tc->cap_user_time_zero)
6903fca3afSLeo Yan return -EOPNOTSUPP;
7003fca3afSLeo Yan
7103fca3afSLeo Yan return 0;
7203fca3afSLeo Yan }
7303fca3afSLeo Yan
perf_event__synth_time_conv(const struct perf_event_mmap_page * pc,struct perf_tool * tool,perf_event__handler_t process,struct machine * machine)7403fca3afSLeo Yan int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc,
7503fca3afSLeo Yan struct perf_tool *tool,
7603fca3afSLeo Yan perf_event__handler_t process,
7703fca3afSLeo Yan struct machine *machine)
7803fca3afSLeo Yan {
7903fca3afSLeo Yan union perf_event event = {
8003fca3afSLeo Yan .time_conv = {
8103fca3afSLeo Yan .header = {
8203fca3afSLeo Yan .type = PERF_RECORD_TIME_CONV,
8303fca3afSLeo Yan .size = sizeof(struct perf_record_time_conv),
8403fca3afSLeo Yan },
8503fca3afSLeo Yan },
8603fca3afSLeo Yan };
8703fca3afSLeo Yan struct perf_tsc_conversion tc;
8803fca3afSLeo Yan int err;
8903fca3afSLeo Yan
9003fca3afSLeo Yan if (!pc)
9103fca3afSLeo Yan return 0;
9203fca3afSLeo Yan err = perf_read_tsc_conversion(pc, &tc);
9303fca3afSLeo Yan if (err == -EOPNOTSUPP)
9403fca3afSLeo Yan return 0;
9503fca3afSLeo Yan if (err)
9603fca3afSLeo Yan return err;
9703fca3afSLeo Yan
9803fca3afSLeo Yan pr_debug2("Synthesizing TSC conversion information\n");
9903fca3afSLeo Yan
10003fca3afSLeo Yan event.time_conv.time_mult = tc.time_mult;
10103fca3afSLeo Yan event.time_conv.time_shift = tc.time_shift;
10203fca3afSLeo Yan event.time_conv.time_zero = tc.time_zero;
103d110162cSLeo Yan event.time_conv.time_cycles = tc.time_cycles;
104d110162cSLeo Yan event.time_conv.time_mask = tc.time_mask;
105d110162cSLeo Yan event.time_conv.cap_user_time_zero = tc.cap_user_time_zero;
106d110162cSLeo Yan event.time_conv.cap_user_time_short = tc.cap_user_time_short;
10703fca3afSLeo Yan
10803fca3afSLeo Yan return process(tool, &event, NULL, machine);
10903fca3afSLeo Yan }
11003fca3afSLeo Yan
rdtsc(void)111a6a69db4SAdrian Hunter u64 __weak rdtsc(void)
112a6a69db4SAdrian Hunter {
113a6a69db4SAdrian Hunter return 0;
114a6a69db4SAdrian Hunter }
115*81e70d7eSLeo Yan
perf_event__fprintf_time_conv(union perf_event * event,FILE * fp)116*81e70d7eSLeo Yan size_t perf_event__fprintf_time_conv(union perf_event *event, FILE *fp)
117*81e70d7eSLeo Yan {
118*81e70d7eSLeo Yan struct perf_record_time_conv *tc = (struct perf_record_time_conv *)event;
119*81e70d7eSLeo Yan size_t ret;
120*81e70d7eSLeo Yan
121*81e70d7eSLeo Yan ret = fprintf(fp, "\n... Time Shift %" PRI_lu64 "\n", tc->time_shift);
122*81e70d7eSLeo Yan ret += fprintf(fp, "... Time Muliplier %" PRI_lu64 "\n", tc->time_mult);
123*81e70d7eSLeo Yan ret += fprintf(fp, "... Time Zero %" PRI_lu64 "\n", tc->time_zero);
124*81e70d7eSLeo Yan
125*81e70d7eSLeo Yan /*
126*81e70d7eSLeo Yan * The event TIME_CONV was extended for the fields from "time_cycles"
127*81e70d7eSLeo Yan * when supported cap_user_time_short, for backward compatibility,
128*81e70d7eSLeo Yan * prints the extended fields only if they are contained in the event.
129*81e70d7eSLeo Yan */
130*81e70d7eSLeo Yan if (event_contains(*tc, time_cycles)) {
131*81e70d7eSLeo Yan ret += fprintf(fp, "... Time Cycles %" PRI_lu64 "\n",
132*81e70d7eSLeo Yan tc->time_cycles);
133*81e70d7eSLeo Yan ret += fprintf(fp, "... Time Mask %#" PRI_lx64 "\n",
134*81e70d7eSLeo Yan tc->time_mask);
135*81e70d7eSLeo Yan ret += fprintf(fp, "... Cap Time Zero %" PRId32 "\n",
136*81e70d7eSLeo Yan tc->cap_user_time_zero);
137*81e70d7eSLeo Yan ret += fprintf(fp, "... Cap Time Short %" PRId32 "\n",
138*81e70d7eSLeo Yan tc->cap_user_time_short);
139*81e70d7eSLeo Yan }
140*81e70d7eSLeo Yan
141*81e70d7eSLeo Yan return ret;
142*81e70d7eSLeo Yan }
143