gettime: fix overflow in cycle to usec conversion
If this multiplication overflows:
usecs = (t * inv_cycles_per_usec) / 16777216UL;
then usecs is 2^64/2^24, which is 1099511627776. Divide that by
10^6 to get seconds, and that is 1099511. Since we cached the
old value previously, we'd get stuck with this amount of seconds.
To avoid turning this into an expensive division, have a check
and only divide if we have to. This avoids the overflow.
Signed-off-by: Jens Axboe <axboe@fb.com>
diff --git a/gettime.c b/gettime.c
index e274671..30513e0 100644
--- a/gettime.c
+++ b/gettime.c
@@ -16,6 +16,7 @@
#if defined(ARCH_HAVE_CPU_CLOCK) && !defined(ARCH_CPU_CLOCK_CYCLES_PER_USEC)
static unsigned long cycles_per_usec;
static unsigned long inv_cycles_per_usec;
+static uint64_t max_cycles_for_mult;
#endif
int tsc_reliable = 0;
@@ -182,7 +183,10 @@
#ifdef ARCH_CPU_CLOCK_CYCLES_PER_USEC
usecs = t / ARCH_CPU_CLOCK_CYCLES_PER_USEC;
#else
- usecs = (t * inv_cycles_per_usec) / 16777216UL;
+ if (t < max_cycles_for_mult)
+ usecs = (t * inv_cycles_per_usec) / 16777216UL;
+ else
+ usecs = t / cycles_per_usec;
#endif
tp->tv_sec = usecs / 1000000;
tp->tv_usec = usecs % 1000000;
@@ -296,6 +300,7 @@
cycles_per_usec = avg;
inv_cycles_per_usec = 16777216UL / cycles_per_usec;
+ max_cycles_for_mult = ~0ULL / inv_cycles_per_usec;
dprint(FD_TIME, "inv_cycles_per_usec=%lu\n", inv_cycles_per_usec);
return 0;
}