kvm: Fix a case where the run timers weren't armed properly
authorAndreas Sandberg <andreas@sandberg.pp.se>
Thu, 19 Sep 2013 15:55:03 +0000 (17:55 +0200)
committerAndreas Sandberg <andreas@sandberg.pp.se>
Thu, 19 Sep 2013 15:55:03 +0000 (17:55 +0200)
There is a possibility that the timespec used to arm a timer becomes
zero if the number of ticks used when arming a timer is close to the
resolution of the timer. Due to the semantics of POSIX timers, this
actually disarms the timer. This changeset fixes this issue by
eliminating the rounding error (we always round away from zero
now). It also reuses the minimum number of cycles, which were
previously only used for cycle-based timers, to calculate a more
useful resolution.

src/cpu/kvm/timer.cc

index 03cdea6fb7f8984fd9bfb6ed57fe5085b419618a..6882063e4c9a4df524356fb5bd2bfa43eb1e058a 100644 (file)
@@ -37,6 +37,7 @@
  * Authors: Andreas Sandberg
  */
 
+#include <algorithm>
 #include <csignal>
 #include <ctime>
 
 #include "cpu/kvm/timer.hh"
 #include "debug/KvmTimer.hh"
 
+/**
+ * Minimum number of cycles that a host can spend in a KVM call (used
+ * to calculate the resolution of some timers).
+ *
+ * The value of this constant is a bit arbitrary, but in practice, we
+ * can't really do anything useful in less than ~1000 cycles.
+ */
+static const uint64_t MIN_HOST_CYCLES = 1000;
 
 PosixKvmTimer::PosixKvmTimer(int signo, clockid_t clockID,
                              float hostFactor, Tick hostFreq)
@@ -82,6 +91,8 @@ PosixKvmTimer::arm(Tick ticks)
     ts.it_value.tv_sec = hostNs(ticks) / 1000000000ULL;
     ts.it_value.tv_nsec = hostNs(ticks) % 1000000000ULL;
 
+    assert(ts.it_value.tv_nsec > 0 || ts.it_value.tv_sec > 0);
+
     DPRINTF(KvmTimer, "Arming POSIX timer: %i ticks (%is%ins)\n",
             ticks, ts.it_value.tv_sec, ts.it_value.tv_nsec);
 
@@ -109,9 +120,23 @@ PosixKvmTimer::calcResolution()
     if (clock_getres(clockID, &ts) == -1)
         panic("PosixKvmTimer: Failed to get timer resolution\n");
 
-    Tick resolution(ticksFromHostNs(ts.tv_sec * 1000000000ULL + ts.tv_nsec));
-
-    return resolution;
+    const uint64_t res_ns(ts.tv_sec * 1000000000ULL + ts.tv_nsec);
+    // We preferrably want ticksFromHostNs() to calculate the the
+    // ceiling rather than truncating the value. However, there are
+    // other cases where truncating is fine, so we just add 1 here to
+    // make sure that the actual resolution is strictly less than what
+    // we return. We could get all kinds of nasty behavior if
+    // arm(resolution) is called and the resulting time is 0 (which
+    // could happen if we truncate the results and the resolution is
+    // 1ns).
+    const Tick resolution(ticksFromHostNs(res_ns) + 1);
+    // It might not make sense to enter into KVM for less than a
+    // certain number of host cycles. In some systems (e.g., Linux)
+    // the resolution of the timer we use is 1ns (a few cycles on most
+    // CPUs), which isn't very useful.
+    const Tick min_cycles(ticksFromHostCycles(MIN_HOST_CYCLES));
+
+    return std::max(resolution, min_cycles);
 }
 
 
@@ -143,7 +168,5 @@ PerfKvmTimer::disarm()
 Tick
 PerfKvmTimer::calcResolution()
 {
-    // This is a bit arbitrary, but in practice, we can't really do
-    // anything useful in less than ~1000 anyway.
-    return ticksFromHostCycles(1000);
+    return ticksFromHostCycles(MIN_HOST_CYCLES);
 }