xref: /openbmc/linux/tools/perf/util/tsc.c (revision 81e70d7e)
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