mirror of
https://github.com/opnsense/src.git
synced 2026-02-03 20:49:35 -05:00
dtrace/arm64: Fix dtrace_gethrtime()
This routine returns a monotonic count of the number of nanoseconds elapsed since the previous call. On arm64 it uses the generic system timer. The implementation multiplies the counter value by 10**9 then divides by the counter frequency, but this multiplication can overflow. This can result in trace records with non-monotonic timestamps, which breaks libdtrace's temporal ordering algorithm. An easy fix is to reverse the order of operations, since the counter frequency will in general be smaller than 10**9. (In fact, it's mandated to be 1Ghz in ARMv9, which makes life simple.) However, this can give a fair bit of error. Adopt the calculation used on amd64, with tweaks to handle frequencies as low as 1MHz: the ARM generic timer documentation suggests that ARMv8 timers are typically in the 1MHz-50MHz range, which is true on arm64 systems that I have access to. MFC after: 2 weeks Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D49244 (cherry picked from commit 36ae5ce2f2fda35763c2655a19bf1b0ee22fdf3c)
This commit is contained in:
parent
1cbe878164
commit
294cda7e4e
1 changed files with 31 additions and 2 deletions
|
|
@ -151,6 +151,32 @@ dtrace_sync(void)
|
|||
dtrace_xcall(DTRACE_CPUALL, (dtrace_xcall_t)dtrace_sync_func, NULL);
|
||||
}
|
||||
|
||||
static uint64_t nsec_scale;
|
||||
|
||||
#define SCALE_SHIFT 25
|
||||
|
||||
/*
|
||||
* Choose scaling factors which let us convert a cntvct_el0 value to nanoseconds
|
||||
* without overflow, as in the amd64 implementation.
|
||||
*
|
||||
* Documentation for the ARM generic timer states that typical counter
|
||||
* frequencies are in the range 1Mhz-50Mhz; in ARMv9 the frequency is fixed at
|
||||
* 1GHz. The lower bound of 1MHz forces the shift to be at most 25 bits. At
|
||||
* that frequency, the calculation (hi * scale) << (32 - shift) will not
|
||||
* overflow for over 100 years, assuming that the counter value starts at 0 upon
|
||||
* boot.
|
||||
*/
|
||||
static void
|
||||
dtrace_gethrtime_init(void *arg __unused)
|
||||
{
|
||||
uint64_t freq;
|
||||
|
||||
freq = READ_SPECIALREG(cntfrq_el0);
|
||||
nsec_scale = ((uint64_t)NANOSEC << SCALE_SHIFT) / freq;
|
||||
}
|
||||
SYSINIT(dtrace_gethrtime_init, SI_SUB_DTRACE, SI_ORDER_ANY,
|
||||
dtrace_gethrtime_init, NULL);
|
||||
|
||||
/*
|
||||
* DTrace needs a high resolution time function which can be called from a
|
||||
* probe context and guaranteed not to have instrumented with probes itself.
|
||||
|
|
@ -161,10 +187,13 @@ uint64_t
|
|||
dtrace_gethrtime(void)
|
||||
{
|
||||
uint64_t count, freq;
|
||||
uint32_t lo, hi;
|
||||
|
||||
count = READ_SPECIALREG(cntvct_el0);
|
||||
freq = READ_SPECIALREG(cntfrq_el0);
|
||||
return ((1000000000UL * count) / freq);
|
||||
lo = count;
|
||||
hi = count >> 32;
|
||||
return (((lo * nsec_scale) >> SCALE_SHIFT) +
|
||||
((hi * nsec_scale) << (32 - SCALE_SHIFT)));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue