Index: trunk/src/VBox/VMM/VMMAll/TMAllCpu.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/TMAllCpu.cpp	(revision 87626)
+++ trunk/src/VBox/VMM/VMMAll/TMAllCpu.cpp	(revision 87633)
@@ -272,4 +272,5 @@
 
 
+#ifdef IN_RING0 /* Only used in ring-0 at present (AMD-V and VT-x). */
 /**
  * Checks if AMD-V / VT-x can use an offsetted hardware TSC or not.
@@ -308,9 +309,9 @@
         /** @todo We should negate both deltas!  It's soo weird that we do the
          *        exact opposite of what the hardware implements. */
-#ifdef IN_RING3
+# ifdef IN_RING3
         *poffRealTsc = (uint64_t)0 - pVCpu->tm.s.offTSCRawSrc - (uint64_t)SUPGetTscDelta();
-#else
+# else
         *poffRealTsc = (uint64_t)0 - pVCpu->tm.s.offTSCRawSrc - (uint64_t)SUPGetTscDeltaByCpuSetIndex(pVCpu->iHostCpuSet);
-#endif
+# endif
         return true;
     }
@@ -331,5 +332,6 @@
     {
         /* The source is the timer synchronous virtual clock. */
-        uint64_t u64Now = tmCpuTickGetRawVirtual(pVM, false /* don't check for pending timers */)
+        uint64_t uTscNow;
+        uint64_t u64Now = tmCpuTickCalcFromVirtual(pVM, TMVirtualSyncGetNoCheckWithTsc(pVM, &uTscNow))
                         - pVCpu->tm.s.offTSCRawSrc;
         /** @todo When we start collecting statistics on how much time we spend executing
@@ -339,15 +341,19 @@
         if (u64Now >= pVCpu->tm.s.u64TSCLastSeen)
         {
-            *poffRealTsc = u64Now - ASMReadTSC();
+# ifdef IN_RING3
+            *poffRealTsc = u64Now - (uTscNow + (uint64_t)SUPGetTscDelta();
+# else
+            *poffRealTsc = u64Now - (uTscNow + (uint64_t)SUPGetTscDeltaByCpuSetIndex(pVCpu->iHostCpuSet));
+# endif
             return true;    /** @todo count this? */
         }
     }
 
-#ifdef VBOX_WITH_STATISTICS
+# ifdef VBOX_WITH_STATISTICS
     tmCpuTickRecordOffsettedTscRefusal(pVM, pVCpu);
-#endif
+# endif
     return false;
 }
-
+#endif /* IN_RING0 - at the moment */
 
 /**
@@ -383,4 +389,5 @@
 
 
+#ifdef IN_RING0 /* Only used in ring-0 from VT-x code at the moment. */
 /**
  * Gets the next deadline in host CPU clock ticks and the TSC offset if we can
@@ -388,10 +395,17 @@
  *
  * @returns The number of host CPU clock ticks to the next timer deadline.
- * @param   pVM             The cross context VM structure.
- * @param   pVCpu           The cross context virtual CPU structure of the calling EMT.
- * @param   poffRealTsc     The offset against the TSC of the current host CPU,
- *                          if pfOffsettedTsc is set to true.
- * @param   pfOffsettedTsc  Where to return whether TSC offsetting can be used.
- * @param   pfParavirtTsc   Where to return whether paravirt TSC is enabled.
+ * @param   pVM                 The cross context VM structure.
+ * @param   pVCpu               The cross context virtual CPU structure of the calling EMT.
+ * @param   poffRealTsc         The offset against the TSC of the current host CPU,
+ *                              if pfOffsettedTsc is set to true.
+ * @param   pfOffsettedTsc      Where to return whether TSC offsetting can be used.
+ * @param   pfParavirtTsc       Where to return whether paravirt TSC is enabled.
+ * @param   puTscNow            Where to return the TSC value that the return
+ *                              value is relative to.   This is delta adjusted.
+ * @param   puDeadlineVersion   Where to return the deadline "version" number.
+ *                              Use with TMVirtualSyncIsCurrentDeadlineVersion()
+ *                              to check if the absolute deadline is still up to
+ *                              date and the caller can skip calling this
+ *                              function.
  *
  * @thread  EMT(pVCpu).
@@ -399,5 +413,6 @@
  */
 VMM_INT_DECL(uint64_t) TMCpuTickGetDeadlineAndTscOffset(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *poffRealTsc,
-                                                        bool *pfOffsettedTsc, bool *pfParavirtTsc)
+                                                        bool *pfOffsettedTsc, bool *pfParavirtTsc,
+                                                        uint64_t *puTscNow, uint64_t *puDeadlineVersion)
 {
     Assert(pVCpu->tm.s.fTSCTicking || DBGFIsStepping(pVCpu));
@@ -412,11 +427,11 @@
         /** @todo We should negate both deltas!  It's soo weird that we do the
          *        exact opposite of what the hardware implements. */
-#ifdef IN_RING3
+# ifdef IN_RING3
         *poffRealTsc     = (uint64_t)0 - pVCpu->tm.s.offTSCRawSrc - (uint64_t)SUPGetTscDelta();
-#else
+# else
         *poffRealTsc     = (uint64_t)0 - pVCpu->tm.s.offTSCRawSrc - (uint64_t)SUPGetTscDeltaByCpuSetIndex(pVCpu->iHostCpuSet);
-#endif
+# endif
         *pfOffsettedTsc  = true;
-        return tmCpuCalcTicksToDeadline(pVCpu, TMVirtualSyncGetNsToDeadline(pVM));
+        return tmCpuCalcTicksToDeadline(pVCpu, TMVirtualSyncGetNsToDeadline(pVM, puDeadlineVersion, puTscNow));
     }
 
@@ -431,19 +446,25 @@
         /* The source is the timer synchronous virtual clock. */
         uint64_t cNsToDeadline;
-        uint64_t u64NowVirtSync = TMVirtualSyncGetWithDeadlineNoCheck(pVM, &cNsToDeadline);
+        uint64_t u64NowVirtSync = TMVirtualSyncGetWithDeadlineNoCheck(pVM, &cNsToDeadline, puDeadlineVersion, puTscNow);
         uint64_t u64Now = tmCpuTickCalcFromVirtual(pVM, u64NowVirtSync);
         u64Now -= pVCpu->tm.s.offTSCRawSrc;
-        *poffRealTsc     = u64Now - ASMReadTSC();
+
+# ifdef IN_RING3
+        *poffRealTsc     = u64Now - (*puTscNow + (uint64_t)SUPGetTscDelta()); /* undoing delta */
+# else
+        *poffRealTsc     = u64Now - (*puTscNow + (uint64_t)SUPGetTscDeltaByCpuSetIndex(pVCpu->iHostCpuSet)); /* undoing delta */
+# endif
         *pfOffsettedTsc  = u64Now >= pVCpu->tm.s.u64TSCLastSeen;
         return tmCpuCalcTicksToDeadline(pVCpu, cNsToDeadline);
     }
 
-#ifdef VBOX_WITH_STATISTICS
+# ifdef VBOX_WITH_STATISTICS
     tmCpuTickRecordOffsettedTscRefusal(pVM, pVCpu);
-#endif
+# endif
     *pfOffsettedTsc  = false;
     *poffRealTsc     = 0;
-    return tmCpuCalcTicksToDeadline(pVCpu, TMVirtualSyncGetNsToDeadline(pVM));
-}
+    return tmCpuCalcTicksToDeadline(pVCpu, TMVirtualSyncGetNsToDeadline(pVM, puDeadlineVersion, puTscNow));
+}
+#endif /* IN_RING0 - at the moment */
 
 
Index: trunk/src/VBox/VMM/VMMAll/TMAllVirtual.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/TMAllVirtual.cpp	(revision 87626)
+++ trunk/src/VBox/VMM/VMMAll/TMAllVirtual.cpp	(revision 87633)
@@ -180,10 +180,33 @@
 
 /**
+ * Wrapper around the IPRT GIP time methods, extended version.
+ */
+DECLINLINE(uint64_t) tmVirtualGetRawNanoTSEx(PVMCC pVM, uint64_t *puTscNow)
+{
+    RTITMENANOTSEXTRA Extra;
+# ifdef IN_RING3
+    uint64_t u64 = CTXALLSUFF(pVM->tm.s.pfnVirtualGetRaw)(&CTXALLSUFF(pVM->tm.s.VirtualGetRawData), &Extra);
+# else  /* !IN_RING3 */
+    uint32_t cPrevSteps = pVM->tm.s.CTX_SUFF(VirtualGetRawData).c1nsSteps;
+    uint64_t u64 = pVM->tm.s.CTX_SUFF(pfnVirtualGetRaw)(&pVM->tm.s.CTX_SUFF(VirtualGetRawData), &Extra);
+    if (cPrevSteps != pVM->tm.s.CTX_SUFF(VirtualGetRawData).c1nsSteps)
+        VMCPU_FF_SET(VMMGetCpu(pVM), VMCPU_FF_TO_R3);
+# endif /* !IN_RING3 */
+    if (puTscNow)
+        *puTscNow = Extra.uTSCValue;
+    /*DBGFTRACE_POS_U64(pVM, u64);*/
+    return u64;
+}
+
+
+/**
  * Get the time when we're not running at 100%
  *
  * @returns The timestamp.
- * @param   pVM     The cross context VM structure.
- */
-static uint64_t tmVirtualGetRawNonNormal(PVMCC pVM)
+ * @param   pVM         The cross context VM structure.
+ * @param   puTscNow    Where to return the TSC corresponding to the returned
+ *                      timestamp (delta adjusted). Optional.
+ */
+static uint64_t tmVirtualGetRawNonNormal(PVMCC pVM, uint64_t *puTscNow)
 {
     /*
@@ -191,5 +214,5 @@
      * warp drive has been enabled.
      */
-    uint64_t u64 = tmVirtualGetRawNanoTS(pVM);
+    uint64_t u64 = tmVirtualGetRawNanoTSEx(pVM, puTscNow);
     u64 -= pVM->tm.s.u64VirtualWarpDriveStart;
     u64 *= pVM->tm.s.u32VirtualWarpDrivePercentage;
@@ -211,5 +234,5 @@
  *
  * @returns The current time stamp.
- * @param   pVM     The cross context VM structure.
+ * @param   pVM         The cross context VM structure.
  */
 DECLINLINE(uint64_t) tmVirtualGetRaw(PVMCC pVM)
@@ -217,5 +240,21 @@
     if (RT_LIKELY(!pVM->tm.s.fVirtualWarpDrive))
         return tmVirtualGetRawNanoTS(pVM) - pVM->tm.s.u64VirtualOffset;
-    return tmVirtualGetRawNonNormal(pVM);
+    return tmVirtualGetRawNonNormal(pVM, NULL /*puTscNow*/);
+}
+
+
+/**
+ * Get the raw virtual time, extended version.
+ *
+ * @returns The current time stamp.
+ * @param   pVM         The cross context VM structure.
+ * @param   puTscNow    Where to return the TSC corresponding to the returned
+ *                      timestamp (delta adjusted). Optional.
+ */
+DECLINLINE(uint64_t) tmVirtualGetRawEx(PVMCC pVM, uint64_t *puTscNow)
+{
+    if (RT_LIKELY(!pVM->tm.s.fVirtualWarpDrive))
+        return tmVirtualGetRawNanoTSEx(pVM, puTscNow) - pVM->tm.s.u64VirtualOffset;
+    return tmVirtualGetRawNonNormal(pVM, puTscNow);
 }
 
@@ -322,6 +361,9 @@
  *                              the next virtual sync timer deadline. Can be
  *                              NULL.
- */
-DECLINLINE(uint64_t) tmVirtualSyncGetHandleCatchUpLocked(PVMCC pVM, uint64_t u64, uint64_t off, uint64_t *pcNsToDeadline)
+ * @param   pnsAbsDeadline      Where to return the absolute deadline.
+ *                              Optional.
+ */
+DECLINLINE(uint64_t) tmVirtualSyncGetHandleCatchUpLocked(PVMCC pVM, uint64_t u64, uint64_t off,
+                                                         uint64_t *pcNsToDeadline, uint64_t *pnsAbsDeadline)
 {
     STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGetLocked);
@@ -375,4 +417,7 @@
 
     uint64_t u64Expire = ASMAtomicReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire);
+    if (pnsAbsDeadline)
+        *pnsAbsDeadline = u64Expire; /* Always return the unadjusted absolute deadline, or HM will waste time going
+                                        thru this code over an over again even if there aren't any timer changes. */
     if (u64 < u64Expire)
     {
@@ -432,6 +477,8 @@
  *                              the next virtual sync timer deadline.  Can be
  *                              NULL.
- */
-DECLINLINE(uint64_t) tmVirtualSyncGetLocked(PVMCC pVM, uint64_t u64, uint64_t *pcNsToDeadline)
+ * @param   pnsAbsDeadline      Where to return the absolute deadline.
+ *                              Optional.
+ */
+DECLINLINE(uint64_t) tmVirtualSyncGetLocked(PVMCC pVM, uint64_t u64, uint64_t *pcNsToDeadline, uint64_t *pnsAbsDeadline)
 {
     /*
@@ -444,4 +491,6 @@
         if (pcNsToDeadline)
             *pcNsToDeadline = 0;
+        if (pnsAbsDeadline)
+            *pnsAbsDeadline = u64;
         STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGetLocked);
         Log6(("tmVirtualSyncGetLocked -> %'RU64 [stopped]\n", u64));
@@ -455,5 +504,5 @@
     uint64_t off = ASMAtomicUoReadU64(&pVM->tm.s.offVirtualSync);
     if (ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
-        return tmVirtualSyncGetHandleCatchUpLocked(pVM, u64, off, pcNsToDeadline);
+        return tmVirtualSyncGetHandleCatchUpLocked(pVM, u64, off, pcNsToDeadline, pnsAbsDeadline);
 
     /*
@@ -472,4 +521,6 @@
 
     uint64_t u64Expire = ASMAtomicReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire);
+    if (pnsAbsDeadline)
+        *pnsAbsDeadline = u64Expire;
     if (u64 < u64Expire)
     {
@@ -516,7 +567,12 @@
  *                              the next virtual sync timer deadline.  Can be
  *                              NULL.
+ * @param   pnsAbsDeadline      Where to return the absolute deadline.
+ *                              Optional.
+ * @param   puTscNow            Where to return the TSC corresponding to the
+ *                              returned timestamp (delta adjusted). Optional.
  * @thread  EMT.
  */
-DECLINLINE(uint64_t) tmVirtualSyncGetEx(PVMCC pVM, bool fCheckTimers, uint64_t *pcNsToDeadline)
+DECLINLINE(uint64_t) tmVirtualSyncGetEx(PVMCC pVM, bool fCheckTimers, uint64_t *pcNsToDeadline,
+                                        uint64_t *pnsAbsDeadline, uint64_t *puTscNow)
 {
     STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGet);
@@ -536,5 +592,5 @@
      */
     Assert(pVM->tm.s.cVirtualTicking);
-    u64 = tmVirtualGetRaw(pVM);
+    u64 = tmVirtualGetRawEx(pVM, puTscNow);
     if (fCheckTimers)
     {
@@ -559,6 +615,8 @@
      *       which is less picky or hasn't been adjusted yet
      */
+    /** @todo switch this around, have the tmVirtualSyncGetLocked code inlined
+     *        here and the remainder of this function in a static worker. */
     if (PDMCritSectTryEnter(&pVM->tm.s.VirtualSyncLock) == VINF_SUCCESS)
-        return tmVirtualSyncGetLocked(pVM, u64, pcNsToDeadline);
+        return tmVirtualSyncGetLocked(pVM, u64, pcNsToDeadline, pnsAbsDeadline);
 
     /*
@@ -580,4 +638,6 @@
                 if (off < u64Expire)
                 {
+                    if (pnsAbsDeadline)
+                        *pnsAbsDeadline = u64Expire;
                     if (pcNsToDeadline)
                         *pcNsToDeadline = tmVirtualVirtToNsDeadline(pVM, u64Expire - off);
@@ -597,4 +657,6 @@
             if (pcNsToDeadline)
                 *pcNsToDeadline = 0;
+            if (pnsAbsDeadline)
+                *pnsAbsDeadline = off;
             STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGetLockless);
             Log6(("tmVirtualSyncGetEx -> %'RU64 [lockless/stopped]\n", off));
@@ -629,5 +691,5 @@
         int rcLock = PDMCritSectTryEnter(&pVM->tm.s.VirtualSyncLock);
         if (RT_SUCCESS_NP(rcLock))
-            return tmVirtualSyncGetLocked(pVM, u64, pcNsToDeadline);
+            return tmVirtualSyncGetLocked(pVM, u64, pcNsToDeadline, pnsAbsDeadline);
 
         /* Re-check the ticking flag. */
@@ -640,4 +702,6 @@
             if (pcNsToDeadline)
                 *pcNsToDeadline = 0;
+            if (pnsAbsDeadline)
+                *pnsAbsDeadline = off;
             Log6(("tmVirtualSyncGetEx -> %'RU64 [stopped]\n", off));
             DBGFTRACE_U64_TAG(pVM, off, "tmVirtualSyncGetEx-stopped3");
@@ -704,4 +768,6 @@
 /** @todo u64VirtualSyncLast */
     uint64_t u64Expire = ASMAtomicReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire);
+    if (pnsAbsDeadline)
+        *pnsAbsDeadline = u64Expire;
     if (u64 >= u64Expire)
     {
@@ -749,5 +815,5 @@
 VMM_INT_DECL(uint64_t) TMVirtualSyncGet(PVMCC pVM)
 {
-    return tmVirtualSyncGetEx(pVM, true /*fCheckTimers*/, NULL /*pcNsToDeadline*/);
+    return tmVirtualSyncGetEx(pVM, true /*fCheckTimers*/, NULL /*pcNsToDeadline*/, NULL /*pnsAbsDeadline*/, NULL /*puTscNow*/);
 }
 
@@ -764,5 +830,22 @@
 VMM_INT_DECL(uint64_t) TMVirtualSyncGetNoCheck(PVMCC pVM)
 {
-    return tmVirtualSyncGetEx(pVM, false /*fCheckTimers*/, NULL /*pcNsToDeadline*/);
+    return tmVirtualSyncGetEx(pVM, false /*fCheckTimers*/, NULL /*pcNsToDeadline*/, NULL /*pnsAbsDeadline*/, NULL /*puTscNow*/);
+}
+
+
+/**
+ * Gets the current TMCLOCK_VIRTUAL_SYNC time without checking timers running on
+ * TMCLOCK_VIRTUAL, also returning corresponding TSC value.
+ *
+ * @returns The timestamp.
+ * @param   pVM             The cross context VM structure.
+ * @param   puTscNow        Where to return the TSC value that the return
+ *                          value is relative to.   This is delta adjusted.
+ * @thread  EMT.
+ * @remarks May set the timer and virtual sync FFs.
+ */
+VMM_INT_DECL(uint64_t) TMVirtualSyncGetNoCheckWithTsc(PVMCC pVM, uint64_t *puTscNow)
+{
+    return tmVirtualSyncGetEx(pVM, false /*fCheckTimers*/, NULL /*pcNsToDeadline*/, NULL /*pnsAbsDeadline*/, puTscNow);
 }
 
@@ -779,5 +862,5 @@
 VMM_INT_DECL(uint64_t) TMVirtualSyncGetEx(PVMCC pVM, bool fCheckTimers)
 {
-    return tmVirtualSyncGetEx(pVM, fCheckTimers, NULL /*pcNsToDeadline*/);
+    return tmVirtualSyncGetEx(pVM, fCheckTimers, NULL /*pcNsToDeadline*/, NULL /*pnsAbsDeadline*/, NULL /*puTscNow*/);
 }
 
@@ -791,11 +874,19 @@
  * @param   pcNsToDeadline      Where to return the number of nano seconds to
  *                              the next virtual sync timer deadline.
+ * @param   puTscNow            Where to return the TSC value that the return
+ *                              value is relative to.   This is delta adjusted.
+ * @param   puDeadlineVersion   Where to return the deadline "version" number.
+ *                              Use with TMVirtualSyncIsCurrentDeadlineVersion()
+ *                              to check if the absolute deadline is still up to
+ *                              date and the caller can skip calling this
+ *                              function.
  * @thread  EMT.
  * @remarks May set the timer and virtual sync FFs.
  */
-VMM_INT_DECL(uint64_t) TMVirtualSyncGetWithDeadlineNoCheck(PVMCC pVM, uint64_t *pcNsToDeadline)
+VMM_INT_DECL(uint64_t) TMVirtualSyncGetWithDeadlineNoCheck(PVMCC pVM, uint64_t *pcNsToDeadline,
+                                                           uint64_t *puDeadlineVersion, uint64_t *puTscNow)
 {
     uint64_t cNsToDeadlineTmp;       /* try convince the compiler to skip the if tests. */
-    uint64_t u64Now = tmVirtualSyncGetEx(pVM, false /*fCheckTimers*/, &cNsToDeadlineTmp);
+    uint64_t u64Now = tmVirtualSyncGetEx(pVM, false /*fCheckTimers*/, &cNsToDeadlineTmp, puDeadlineVersion, puTscNow);
     *pcNsToDeadline = cNsToDeadlineTmp;
     return u64Now;
@@ -808,12 +899,35 @@
  * @returns The number of TMCLOCK_VIRTUAL ticks.
  * @param   pVM                 The cross context VM structure.
+ * @param   puTscNow            Where to return the TSC value that the return
+ *                              value is relative to.   This is delta adjusted.
+ * @param   puDeadlineVersion   Where to return the deadline "version" number.
+ *                              Use with TMVirtualSyncIsCurrentDeadlineVersion()
+ *                              to check if the absolute deadline is still up to
+ *                              date and the caller can skip calling this
+ *                              function.
  * @thread  EMT.
  * @remarks May set the timer and virtual sync FFs.
  */
-VMMDECL(uint64_t) TMVirtualSyncGetNsToDeadline(PVMCC pVM)
+VMMDECL(uint64_t) TMVirtualSyncGetNsToDeadline(PVMCC pVM, uint64_t *puDeadlineVersion, uint64_t *puTscNow)
 {
     uint64_t cNsToDeadline;
-    tmVirtualSyncGetEx(pVM, false /*fCheckTimers*/, &cNsToDeadline);
+    tmVirtualSyncGetEx(pVM, false /*fCheckTimers*/, &cNsToDeadline, puDeadlineVersion, puTscNow);
     return cNsToDeadline;
+}
+
+
+/**
+ * Checks if the given deadline is still current.
+ *
+ * @retval  true if the deadline is still current.
+ * @retval  false if the deadline is outdated.
+ * @param   pVM                 The cross context VM structure.
+ * @param   uDeadlineVersion    The deadline version to check.
+ */
+VMM_INT_DECL(bool) TMVirtualSyncIsCurrentDeadlineVersion(PVMCC pVM, uint64_t uDeadlineVersion)
+{
+    /** @todo Try use ASMAtomicUoReadU64 instead. */
+    uint64_t u64Expire = ASMAtomicReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire);
+    return u64Expire == uDeadlineVersion;
 }
 
Index: trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp	(revision 87626)
+++ trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp	(revision 87633)
@@ -2284,5 +2284,7 @@
     Assert(pGuestMsrLoad);
 
+#ifndef DEBUG_bird
     LogFlowFunc(("pVCpu=%p idMsr=%#RX32 uGuestMsrValue=%#RX64\n", pVCpu, idMsr, uGuestMsrValue));
+#endif
 
     /* Check if the MSR already exists in the VM-entry MSR-load area. */
@@ -2369,5 +2371,7 @@
     uint32_t        cMsrs         = pVmcsInfo->cEntryMsrLoad;
 
+#ifndef DEBUG_bird
     LogFlowFunc(("pVCpu=%p idMsr=%#RX32\n", pVCpu, idMsr));
+#endif
 
     for (uint32_t i = 0; i < cMsrs; i++)
@@ -7114,21 +7118,51 @@
  * @param   pVCpu           The cross context virtual CPU structure.
  * @param   pVmxTransient   The VMX-transient structure.
+ * @param   idCurrentCpu    The current CPU number.
  *
  * @remarks No-long-jump zone!!!
  */
-static void hmR0VmxUpdateTscOffsettingAndPreemptTimer(PVMCPUCC pVCpu, PVMXTRANSIENT pVmxTransient)
+static void hmR0VmxUpdateTscOffsettingAndPreemptTimer(PVMCPUCC pVCpu, PVMXTRANSIENT pVmxTransient, RTCPUID idCurrentCpu)
 {
     bool         fOffsettedTsc;
     bool         fParavirtTsc;
     uint64_t     uTscOffset;
-    PVMCC pVM = pVCpu->CTX_SUFF(pVM);
+    PVMCC        pVM       = pVCpu->CTX_SUFF(pVM);
     PVMXVMCSINFO pVmcsInfo = hmGetVmxActiveVmcsInfo(pVCpu);
 
     if (pVM->hmr0.s.vmx.fUsePreemptTimer)
     {
-        uint64_t cTicksToDeadline = TMCpuTickGetDeadlineAndTscOffset(pVM, pVCpu, &uTscOffset, &fOffsettedTsc, &fParavirtTsc);
+
+        /* The TMCpuTickGetDeadlineAndTscOffset function is expensive (calling it on
+           every entry slowed down the bs2-test1 CPUID testcase by ~33% (on an 10980xe). */
+        uint64_t cTicksToDeadline;
+        if (   idCurrentCpu == pVCpu->hmr0.s.idLastCpu
+            && TMVirtualSyncIsCurrentDeadlineVersion(pVM, pVCpu->hmr0.s.vmx.uTscDeadlineVersion))
+        {
+            STAM_REL_COUNTER_INC(&pVCpu->hm.s.StatVmxPreemptionReusingDeadline);
+            fOffsettedTsc = TMCpuTickCanUseRealTSC(pVM, pVCpu, &uTscOffset, &fParavirtTsc);
+            cTicksToDeadline = pVCpu->hmr0.s.vmx.uTscDeadline - SUPReadTsc();
+            if ((int64_t)cTicksToDeadline > 0)
+            { /* hopefully */ }
+            else
+            {
+                STAM_REL_COUNTER_INC(&pVCpu->hm.s.StatVmxPreemptionReusingDeadlineExpired);
+                cTicksToDeadline = 0;
+            }
+        }
+        else
+        {
+            STAM_REL_COUNTER_INC(&pVCpu->hm.s.StatVmxPreemptionRecalcingDeadline);
+            cTicksToDeadline = TMCpuTickGetDeadlineAndTscOffset(pVM, pVCpu, &uTscOffset, &fOffsettedTsc, &fParavirtTsc,
+                                                                &pVCpu->hmr0.s.vmx.uTscDeadline,
+                                                                &pVCpu->hmr0.s.vmx.uTscDeadlineVersion);
+            pVCpu->hmr0.s.vmx.uTscDeadline += cTicksToDeadline;
+            if (cTicksToDeadline >= 128)
+            { /* hopefully */ }
+            else
+                STAM_REL_COUNTER_INC(&pVCpu->hm.s.StatVmxPreemptionRecalcingDeadlineExpired);
+        }
 
         /* Make sure the returned values have sane upper and lower boundaries. */
-        uint64_t u64CpuHz  = SUPGetCpuHzFromGipBySetIndex(g_pSUPGlobalInfoPage, pVCpu->iHostCpuSet);
+        uint64_t const u64CpuHz = SUPGetCpuHzFromGipBySetIndex(g_pSUPGlobalInfoPage, pVCpu->iHostCpuSet);
         cTicksToDeadline   = RT_MIN(cTicksToDeadline, u64CpuHz / 64);      /* 1/64th of a second */ /** @todo r=bird: Once real+virtual timers move to separate thread, we can raise the upper limit (16ms isn't much). ASSUMES working poke cpu function. */
         cTicksToDeadline   = RT_MAX(cTicksToDeadline, u64CpuHz / 2048);    /* 1/2048th of a second */
@@ -11042,5 +11076,5 @@
         || idCurrentCpu != pVCpu->hmr0.s.idLastCpu)
     {
-        hmR0VmxUpdateTscOffsettingAndPreemptTimer(pVCpu, pVmxTransient);
+        hmR0VmxUpdateTscOffsettingAndPreemptTimer(pVCpu, pVmxTransient, idCurrentCpu);
         pVmxTransient->fUpdatedTscOffsettingAndPreemptTimer = true;
     }
@@ -15001,9 +15035,10 @@
     /* If the VMX-preemption timer has expired, reinitialize the preemption timer on next VM-entry. */
     pVmxTransient->fUpdatedTscOffsettingAndPreemptTimer = false;
+Log12(("hmR0VmxExitPreemptTimer:\n"));
 
     /* If there are any timer events pending, fall back to ring-3, otherwise resume guest execution. */
     PVMCC pVM = pVCpu->CTX_SUFF(pVM);
     bool fTimersPending = TMTimerPollBool(pVM, pVCpu);
-    STAM_COUNTER_INC(&pVCpu->hm.s.StatExitPreemptTimer);
+    STAM_REL_COUNTER_INC(&pVCpu->hm.s.StatExitPreemptTimer);
     return fTimersPending ? VINF_EM_RAW_TIMER_PENDING : VINF_SUCCESS;
 }
Index: trunk/src/VBox/VMM/VMMR3/HM.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/HM.cpp	(revision 87626)
+++ trunk/src/VBox/VMM/VMMR3/HM.cpp	(revision 87633)
@@ -704,7 +704,5 @@
 #endif
 
-#ifdef VBOX_WITH_STATISTICS
     bool const fCpuSupportsVmx = ASMIsIntelCpu() || ASMIsViaCentaurCpu() || ASMIsShanghaiCpu();
-#endif
     for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
     {
@@ -792,6 +790,6 @@
         HM_REG_COUNTER(&pHmCpu->StatExitHostNmiInGC,        "/HM/CPU%u/Exit/HostNmiInGC", "Host NMI received while in guest context.");
         HM_REG_COUNTER(&pHmCpu->StatExitHostNmiInGCIpi,     "/HM/CPU%u/Exit/HostNmiInGCIpi", "Host NMI received while in guest context dispatched using IPIs.");
+        HM_REG_COUNTER(&pHmCpu->StatExitPreemptTimer,       "/HM/CPU%u/Exit/PreemptTimer", "VMX-preemption timer expired.");
 #ifdef VBOX_WITH_STATISTICS
-        HM_REG_COUNTER(&pHmCpu->StatExitPreemptTimer,       "/HM/CPU%u/Exit/PreemptTimer", "VMX-preemption timer expired.");
         HM_REG_COUNTER(&pHmCpu->StatExitTprBelowThreshold,  "/HM/CPU%u/Exit/TprBelowThreshold", "TPR lowered below threshold by the guest.");
         HM_REG_COUNTER(&pHmCpu->StatExitTaskSwitch,         "/HM/CPU%u/Exit/TaskSwitch", "Task switch caused through task gate in IDT.");
@@ -871,5 +869,14 @@
         HM_REG_COUNTER(&pHmCpu->StatVmxCheckBadRpl,         "/HM/CPU%u/VMXCheck/RPL", "Could not use VMX due to unsuitable RPL.");
         HM_REG_COUNTER(&pHmCpu->StatVmxCheckPmOk,           "/HM/CPU%u/VMXCheck/VMX_PM", "VMX execution in protected mode OK.");
-
+#endif
+        if (fCpuSupportsVmx)
+        {
+            HM_REG_COUNTER(&pHmCpu->StatExitPreemptTimer,                      "/HM/CPU%u/PreemptTimer",                          "VMX-preemption timer fired.");
+            HM_REG_COUNTER(&pHmCpu->StatVmxPreemptionReusingDeadline,          "/HM/CPU%u/PreemptTimer/ReusingDeadline",          "VMX-preemption timer arming logic using previously calculated deadline");
+            HM_REG_COUNTER(&pHmCpu->StatVmxPreemptionReusingDeadlineExpired,   "/HM/CPU%u/PreemptTimer/ReusingDeadlineExpired",   "VMX-preemption timer arming logic found previous deadline already expired (ignored)");
+            HM_REG_COUNTER(&pHmCpu->StatVmxPreemptionRecalcingDeadline,        "/HM/CPU%u/PreemptTimer/RecalcingDeadline",        "VMX-preemption timer arming logic recalculating the deadline (slighly expensive)");
+            HM_REG_COUNTER(&pHmCpu->StatVmxPreemptionRecalcingDeadlineExpired, "/HM/CPU%u/PreemptTimer/RecalcingDeadlineExpired", "VMX-preemption timer arming logic found recalculated deadline expired (ignored)");
+        }
+#ifdef VBOX_WITH_STATISTICS
         /*
          * Guest Exit reason stats.
Index: trunk/src/VBox/VMM/include/HMInternal.h
===================================================================
--- trunk/src/VBox/VMM/include/HMInternal.h	(revision 87626)
+++ trunk/src/VBox/VMM/include/HMInternal.h	(revision 87633)
@@ -1321,4 +1321,9 @@
     STAMCOUNTER             StatVmxCheckPmOk;
 
+    STAMCOUNTER             StatVmxPreemptionRecalcingDeadline;
+    STAMCOUNTER             StatVmxPreemptionRecalcingDeadlineExpired;
+    STAMCOUNTER             StatVmxPreemptionReusingDeadline;
+    STAMCOUNTER             StatVmxPreemptionReusingDeadlineExpired;
+
 #ifdef VBOX_WITH_STATISTICS
     R3PTRTYPE(PSTAMCOUNTER) paStatExitReason;
@@ -1389,4 +1394,8 @@
         /** Ring-0 pointer to the hardware-assisted VMX execution function. */
         PFNHMVMXSTARTVM             pfnStartVm;
+        /** Absolute TSC deadline. */
+        uint64_t                    uTscDeadline;
+        /** The deadline version number. */
+        uint64_t                    uTscDeadlineVersion;
 
         /** @name Guest information.
Index: trunk/src/VBox/VMM/include/HMInternal.mac
===================================================================
--- trunk/src/VBox/VMM/include/HMInternal.mac	(revision 87626)
+++ trunk/src/VBox/VMM/include/HMInternal.mac	(revision 87633)
@@ -182,4 +182,7 @@
 struc HMR0CPUVMX
     .pfnStartVm                     RTR0PTR_RES  1
+    .uTscDeadline                   resq    1
+    .uTscDeadlineVersion            resq    1
+
 
     .VmcsInfo                       resb    VMXVMCSINFO_size
