diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index a6fb347e27afcfb85d5ceaf78b39e0d8e05396c1..da62116a0b26b4d6ef511d0d9cdde9910f9f296a 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -29,6 +29,7 @@ #include #include #include +#include /* * ARMv8 PMUv3 Performance Events handling code. @@ -1417,11 +1418,22 @@ static int __init armv8_pmu_driver_init(void) } device_initcall(armv8_pmu_driver_init) +static u64 cyc_to_ns(u64 cyc, u16 time_shift, u32 time_mult) +{ + u64 quot, rem; + + quot = cyc >> time_shift; + rem = cyc & (((u64)1 << time_shift) - 1); + return quot * time_mult + + ((rem * time_mult) >> time_shift); +} + void arch_perf_update_userpage(struct perf_event *event, struct perf_event_mmap_page *userpg, u64 now) { u32 freq; u32 shift; + u64 offset; /* * Internal timekeeping for enabled/running/stopped times @@ -1444,4 +1456,16 @@ void arch_perf_update_userpage(struct perf_event *event, } userpg->time_shift = (u16)shift; userpg->time_offset = -now; + + offset = local_clock() - cyc_to_ns(arch_timer_read_counter(), + userpg->time_shift, userpg->time_mult); + + /* + * cap_user_time_zero doesn't make sense when we're using a different + * time base for the records. + */ + if (!event->attr.use_clockid) { + userpg->cap_user_time_zero = 1; + userpg->time_zero = offset; + } }