From: VMware, Inc <> Date: Thu, 17 Jun 2010 22:17:31 +0000 (-0700) Subject: Reshuffle Hostinfo_SystemTimerNS X-Git-Tag: 2010.06.16-268169~32 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8db28864a57bb2fa0d8f3613f7412fdfe63be0c1;p=thirdparty%2Fopen-vm-tools.git Reshuffle Hostinfo_SystemTimerNS 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 --- diff --git a/open-vm-tools/lib/misc/hostinfoPosix.c b/open-vm-tools/lib/misc/hostinfoPosix.c index 41ba947c7..de1bc543a 100644 --- a/open-vm-tools/lib/misc/hostinfoPosix.c +++ b/open-vm-tools/lib/misc/hostinfoPosix.c @@ -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 /*