vppinfra: preserve total_cpu_time across clock freq adjustment 09/23709/4
authorDave Barach <dave@barachs.net>
Sun, 1 Dec 2019 13:59:03 +0000 (08:59 -0500)
committerFlorin Coras <florin.coras@gmail.com>
Mon, 2 Dec 2019 23:00:21 +0000 (23:00 +0000)
If clib_time_verify_frequency() adjusts the clock frequency, transform
total_cpu_time to the new time coordinate space. Otherwise, we break
comparisons with previous clib_time_now() value.

Without this correction, time jumps in one direction or the other
depending on the sign of the frequency change. Reasonably harmless in
most cases, but under perfect storm conditions the wheels fall off.

Type: fix

Signed-off-by: Dave Barach <dave@barachs.net>
Change-Id: I21802c2630e2c87ff817cd732b7d78bc022cd2d7

src/vppinfra/time.c
src/vppinfra/time.h

index dee5002..bfc48ce 100644 (file)
@@ -198,6 +198,7 @@ clib_time_verify_frequency (clib_time_t * c)
   f64 dtr_max;
   u64 dtc = c->last_cpu_time - c->last_verify_cpu_time;
   f64 new_clocks_per_second, delta;
+  f64 save_total_cpu_time_in_seconds;
 
   c->last_verify_cpu_time = c->last_cpu_time;
   c->last_verify_reference_time = now_reference;
@@ -239,10 +240,12 @@ clib_time_verify_frequency (clib_time_t * c)
     flt_round_nearest ((f64) dtc / (dtr * c->round_to_units))
     * c->round_to_units;
 
+  /* Compute abs(rate change) */
   delta = new_clocks_per_second - c->clocks_per_second;
   if (delta < 0.0)
     delta = -delta;
 
+  /* If rate change > 1%, reject it and try again */
   if (PREDICT_FALSE ((delta / c->clocks_per_second) > .01))
     {
       clib_warning ("Rejecting large frequency change of %.2f%%",
@@ -251,11 +254,19 @@ clib_time_verify_frequency (clib_time_t * c)
       return;
     }
 
-  c->clocks_per_second =
-    flt_round_nearest ((f64) dtc / (dtr * c->round_to_units))
-    * c->round_to_units;
+  /* Save total cpu time in seconds */
+  save_total_cpu_time_in_seconds = c->total_cpu_time * c->seconds_per_clock;
+
+  /* Recalculate clock rate */
+  c->clocks_per_second = new_clocks_per_second;
   c->seconds_per_clock = 1 / c->clocks_per_second;
 
+  /*
+   * Restore total cpu time in seconds. Otherwise, if c->clocks_per_second
+   * has decreased, time may appear to flow backwards.
+   */
+  c->total_cpu_time = save_total_cpu_time_in_seconds * c->clocks_per_second;
+
   /* Double time between verifies; max at 64 secs ~ 1 minute. */
   if (c->log2_clocks_per_frequency_verify < c->log2_clocks_per_second + 6)
     c->log2_clocks_per_frequency_verify += 1;
index ae738db..b2d7492 100644 (file)
@@ -205,14 +205,16 @@ clib_time_now_internal (clib_time_t * c, u64 n)
 {
   u64 l = c->last_cpu_time;
   u64 t = c->total_cpu_time;
+  f64 rv;
   t += n - l;
   c->total_cpu_time = t;
   c->last_cpu_time = n;
+  rv = t * c->seconds_per_clock;
   if (PREDICT_FALSE
       ((c->last_cpu_time -
        c->last_verify_cpu_time) >> c->log2_clocks_per_frequency_verify))
     clib_time_verify_frequency (c);
-  return t * c->seconds_per_clock;
+  return rv;
 }
 
 /* Maximum f64 value as max clib_time */