]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Reshuffle Hostinfo_SystemTimerNS
authorVMware, Inc <>
Thu, 17 Jun 2010 22:17:31 +0000 (15:17 -0700)
committerMarcelo Vanzin <mvanzin@vmware.com>
Thu, 17 Jun 2010 22:17:31 +0000 (15:17 -0700)
Core observation: the fallback is always gettimeofday(), but
each Posix-y OS has a (possible) monotonic timer.
So, restructure the code so we optionally try OS-specific
timers (which the optimizer can prune out cheaply) then fall
back to the slower microsecond timer.

With two OS-specific timers already present (Visor and Apple),
I'll do the work of adding the third (Posix's clock_gettime).
Note that I use a weak symbol trick to avoid either librt or
libdl dependencies; an app that links librt will get
ns-resolution and everything else falls back to ms-resolution
gettimeofday().

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/lib/misc/hostinfoPosix.c

index 41ba947c7d251eedf3f51e576cef4c6f314a2afd..de1bc543aee0e100b8d4094789849f8cee8b1e89 100644 (file)
@@ -1491,31 +1491,83 @@ Hostinfo_LogLoadAverage(void)
 /*
  *-----------------------------------------------------------------------------
  *
- * Hostinfo_RawSystemTimerNS --
+ * HostinfoGetTimeOfDayMonotonic --
  *
- *      Return the raw time.
- *         - Do this as fast as is practical; no locks are used
- *         - These timers may go backwards or make no forward progress
+ *      Return the system time as indicated by Hostinfo_GetTimeOfDay(), with
+ *      locking to ensure monotonicity.
  *
- * Hostinfo_SystemTimerNS --
+ *      Uses OS native locks as lib/lock is not available in lib/misc.  This
+ *      is safe because nothing occurs while locked that can be reentrant.
  *
- *      Return the time.
- *         - These timers are documented to never go backwards.
- *         - These timers may take locks
+ * Results:
+ *      The time in microseconds is returned. Zero upon error.
  *
- * NOTES:
- *      These are the routines to use when performing timing measurements.
+ * Side effects:
+ *     None.
  *
- *      The value returned is valid (finish-time - start-time) only within a
- *      single process. Don't send a time measurement obtained with these
- *      routines to another process and expect a relative time measurement
- *      to be correct.
+ *-----------------------------------------------------------------------------
+ */
+
+static VmTimeType
+HostinfoGetTimeOfDayMonotonic(void)
+{
+   VmTimeType newTime;
+   VmTimeType curTime;
+
+   static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+   static VmTimeType lastTimeBase;
+   static VmTimeType lastTimeRead;
+   static VmTimeType lastTimeReset;
+
+   pthread_mutex_lock(&mutex);  // Use native mechanism, just like Windows
+
+   Hostinfo_GetTimeOfDay(&curTime);
+
+   if (curTime == 0) {
+      newTime = 0;
+      goto exit;
+   }
+
+   /*
+    * Don't let time be negative or go backward.  We do this by tracking a
+    * base and moving forward from there.
+    */
+
+   newTime = lastTimeBase + (curTime - lastTimeReset);
+
+   if (newTime < lastTimeRead) {
+      lastTimeReset = curTime;
+      lastTimeBase = lastTimeRead + 1;
+      newTime = lastTimeBase + (curTime - lastTimeReset);
+   }
+
+   lastTimeRead = newTime;
+
+exit:
+   pthread_mutex_unlock(&mutex);
+
+   return newTime;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
  *
- *      The actual resolution of these "clocks" are undefined - it varies
- *      depending on hardware, OSen and OS versions.
+ * HostinfoSystemTimerVmkernel --
+ * HostinfoSystemTimerMach --
+ * HostinfoSystemTimerPosix --
+ *
+ *      Returns system time based on a monotonic, nanosecond-resolution,
+ *      fast timer provided by the (relevant) operating system.
+ *
+ *      Caller should check return value, as some variants may not be known
+ *      to be absent until runtime.  Where possible, these functions collapse
+ *      into constants at compile-time.
  *
  * Results:
- *      The time in nanoseconds is returned. Zero upon error.
+ *      TRUE if timer is available; FALSE to indicate unavailability.
+ *      If available, the current time is returned via 'result'.
  *
  * Side effects:
  *     None.
@@ -1523,10 +1575,37 @@ Hostinfo_LogLoadAverage(void)
  *-----------------------------------------------------------------------------
  */
 
-#if defined(__APPLE__)
-VmTimeType
-Hostinfo_RawSystemTimerNS(void)
+static Bool
+HostinfoSystemTimerVmkernel(VmTimeType *result)  // OUT
 {
+   if (HostType_OSIsPureVMK()) {
+      uint64 uptime = 0;
+      /*
+       * The uptime is ensured to never go backwards and is valid across
+       * processes.
+       *
+       * TODO: get a vmkernel uptime that reports in ns (for real?)
+       */
+#ifdef VMX86_SERVER
+      if (UNLIKELY(VMKernel_GetUptimeUS(&uptime) != VMK_OK)) {
+         Log("%s: failure!\n", __FUNCTION__);
+
+         uptime = 0;  // A timer read failure - this is really bad!
+      }
+#endif
+
+      *result = 1000 * uptime;  // Convert to nanoseconds
+      return TRUE;
+   } else {
+      return FALSE;
+   }
+}
+
+static Bool
+HostinfoSystemTimerMach(VmTimeType *result)  // OUT
+{
+#if __APPLE__
+#  define vmx86_apple 1
    VmTimeType raw;
    mach_timebase_info_data_t *ptr;
    static Atomic_Ptr atomic; /* Implicitly initialized to NULL. --mbellon */
@@ -1557,117 +1636,140 @@ Hostinfo_RawSystemTimerNS(void)
 
    if ((ptr->numer == 1) && (ptr->denom == 1)) {
       /* The scaling values are unity, save some time/arithmetic */
-      return raw;
+      *result = raw;
    } else {
       /* The scaling values are not unity. Prevent overflow when scaling */
-      return ((double) raw) * (((double) ptr->numer) / ((double) ptr->denom));
+      *result = ((double) raw) * (((double) ptr->numer) / ((double) ptr->denom));
    }
+   return TRUE;
+#else
+#  define vmx86_apple 0
+   return FALSE;
+#endif
 }
 
-
-VmTimeType
-Hostinfo_SystemTimerNS(void)
-{
-   return Hostinfo_RawSystemTimerNS();
-}
-#elif defined(VMX86_SERVER)
-VmTimeType
-Hostinfo_RawSystemTimerNS(void)
+static Bool
+HostinfoSystemTimerPosix(VmTimeType *result)  // OUT
 {
-   VmTimeType uptime;
+#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
+#  define vmx86_posix 1
+   /* Weak declaration to avoid librt.so dependency */
+   extern int clock_gettime(clockid_t clk_id, struct timespec *tp) __attribute__ ((weak));
 
-   /*
-    * The uptime is ensured to never go backwards and is valid across
-    * processes.
-    *
-    * TODO: get a vmkernel uptime that reports in ns (for real?)
-    */
+   /* Assignment is idempotent (expected to always be same answer). */
+   static volatile enum { UNKNOWN, PRESENT, FAILED } hasGetTime = UNKNOWN;
 
-   if (UNLIKELY(VMKernel_GetUptimeUS(&uptime) != VMK_OK)) {
-      Log("%s: failure!\n", __FUNCTION__);
+   struct timespec ts;
+   int ret;
 
-      uptime = 0;  // A timer read failure - this is really bad!
-   }
+   switch (hasGetTime) {
+   case FAILED:
+      break;
+   case UNKNOWN:
+      if (clock_gettime == NULL) {
+         /* librt.so is not present.  No clock_gettime() */
+         hasGetTime = FAILED;
+         break;
+      }
+      ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+      if (ret != 0) {
+         hasGetTime = FAILED;
+         /*
+          * Well-understood error codes:
+          * ENOSYS, OS does not implement syscall
+          * EINVAL, OS implements syscall but not CLOCK_MONOTONIC
+          */
+         if (errno != ENOSYS && errno != EINVAL) {
+            Log("%s: failure, err %d!\n", __FUNCTION__, errno);
+         }
+         break;
+      }
+      hasGetTime = PRESENT;
+      /* Fall through to 'case PRESENT' */
 
-   return 1000 * uptime;  // Convert to nanoseconds
+   case PRESENT:
+      ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+      ASSERT(ret == 0);
+      *result = (VmTimeType)ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+      return TRUE;
+   }
+   return FALSE;
+#else
+#  define vmx86_posix 0
+   /* No Posix support for clock_gettime() */
+   return FALSE;
+#endif
 }
 
 
-VmTimeType
-Hostinfo_SystemTimerNS(void)
-{
-   return Hostinfo_RawSystemTimerNS();
-}
-#else
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Hostinfo_RawSystemTimerNS --
+ *
+ *      Return the raw time.
+ *         - Do this as fast as is practical; no locks are used
+ *         - These timers may go backwards or make no forward progress
+ *
+ * Hostinfo_SystemTimerNS --
+ *
+ *      Return the time.
+ *         - These timers are documented to never go backwards.
+ *         - These timers may take locks
+ *
+ * NOTES:
+ *      These are the routines to use when performing timing measurements.
+ *
+ *      The value returned is valid (finish-time - start-time) only within a
+ *      single process. Don't send a time measurement obtained with these
+ *      routines to another process and expect a relative time measurement
+ *      to be correct.
+ *
+ *      The actual resolution of these "clocks" are undefined - it varies
+ *      depending on hardware, OSen and OS versions.
+ *
+ * Results:
+ *      The time in nanoseconds is returned. Zero upon error.
+ *
+ * Side effects:
+ *     None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
 VmTimeType
 Hostinfo_RawSystemTimerNS(void)
 {
    VmTimeType result;
-   struct timeval tval;
 
-   /* Read the time from the operating system - may go backwards. */
-   if (LIKELY(gettimeofday(&tval, NULL) == 0)) {
-      /* Convert into nanoseconds. */
-      result = 1000ULL * (((VmTimeType)tval.tv_sec) * 1000000 + tval.tv_usec);
+   if ((vmx86_server && HostinfoSystemTimerVmkernel(&result)) ||
+       (vmx86_apple && HostinfoSystemTimerMach(&result)) ||
+       (vmx86_posix && HostinfoSystemTimerPosix(&result))) {
+      /* Host provides monotonic clock source. */
    } else {
-      Log("%s: failure!\n", __FUNCTION__);
-
-      result = 0;  // A timer read failure - this is really bad!
+      Hostinfo_GetTimeOfDay(&result);
+      result *= 1000;  /* GetTimeOfDay is microseconds. */
    }
-
    return result;
 }
 
-
 VmTimeType
 Hostinfo_SystemTimerNS(void)
 {
-   VmTimeType newTime;
-   VmTimeType curTime;
-
-   static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
-   static VmTimeType lastTimeBase;
-   static VmTimeType lastTimeRead;
-   static VmTimeType lastTimeReset;
-
-   /*
-    * TODO: research using -lrt and clock_gettime.
-    *
-    * These routines live in lib/misc and cannot use routines from other
-    * libraries, including lib/lock.
-    */
-
-   pthread_mutex_lock(&mutex);  // Use native mechanism, just like Windows
-
-   curTime = Hostinfo_RawSystemTimerNS();
-
-   if (curTime == 0) {
-      newTime = 0;
-      goto exit;
-   }
-
-   /*
-    * Don't let time be negative or go backward.  We do this by tracking a
-    * base and moving forward from there.
-    */
-
-   newTime = lastTimeBase + (curTime - lastTimeReset);
+   VmTimeType result;
 
-   if (newTime < lastTimeRead) {
-      lastTimeReset = curTime;
-      lastTimeBase = lastTimeRead + 1;
-      newTime = lastTimeBase + (curTime - lastTimeReset);
+   if ((vmx86_server && HostinfoSystemTimerVmkernel(&result)) ||
+       (vmx86_apple && HostinfoSystemTimerMach(&result)) ||
+       (vmx86_posix && HostinfoSystemTimerPosix(&result))) {
+      /* Host provides monotonic clock source. */
+      return result;
+   } else {
+      /* GetTimeOfDay is microseconds. */
+      return HostinfoGetTimeOfDayMonotonic() * 1000;
    }
-
-   lastTimeRead = newTime;
-
-exit:
-   pthread_mutex_unlock(&mutex);
-
-   return newTime;
 }
-#endif
+#undef vmx86_apple
+#undef vmx86_posix
 
 
 /*