VirtualBox

Changeset 2248

Show
Ignore:
Timestamp:
04/19/07 23:43:29 (2 years ago)
Author:
vboxsync
Message:

Implementing timer syncrhonous virtual clock.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/include/VBox/tm.h

    r2074 r2248  
    114114 
    115115/** 
     116 * Gets the current lag of the synchronous virtual clock (relative to the virtual clock). 
     117 *  
     118 * @return  The current lag. 
     119 * @param   pVM     VM handle. 
     120 */ 
     121TMDECL(uint64_t) TMVirtualSyncGetLag(PVM pVM); 
     122 
     123/** 
     124 * Get the current catch-up percent. 
     125 *  
     126 * @return  The current catch0up percent. 0 means running at the same speed as the virtual clock. 
     127 * @param   pVM     VM handle. 
     128 */ 
     129TMDECL(uint32_t) TMVirtualSyncGetCatchUpPct(PVM pVM); 
     130 
     131/** 
     132 * Gets the current TMCLOCK_VIRTUAL frequency. 
     133 * 
     134 * @returns The freqency. 
     135 * @param   pVM     VM handle. 
     136 */ 
     137TMDECL(uint64_t) TMVirtualGetFreq(PVM pVM); 
     138 
     139/** 
    116140 * Gets the current TMCLOCK_VIRTUAL_SYNC time. 
    117141 * 
    118142 * @returns The timestamp. 
    119143 * @param   pVM     VM handle. 
    120  * 
    121  */ 
    122 TMDECL(uint64_t) TMVirtualGetSync(PVM pVM); 
    123  
    124 /** 
    125  * Gets the current TMCLOCK_VIRTUAL frequency. 
    126  * 
    127  * @returns The freqency. 
    128  * @param   pVM     VM handle. 
    129  */ 
    130 TMDECL(uint64_t) TMVirtualGetFreq(PVM pVM); 
     144 * @thread  EMT. 
     145 */ 
     146TMDECL(uint64_t) TMVirtualSyncGet(PVM pVM); 
    131147 
    132148/** 
  • trunk/include/VBox/vm.h

    r1832 r2248  
    477477        struct TM   s; 
    478478#endif 
    479         char        padding[768];       /* multiple of 32 */ 
     479        char        padding[1056];      /* multiple of 32 */ 
    480480    } tm; 
    481481 
  • trunk/src/VBox/VMM/TM.cpp

    r2105 r2248  
    131131static DECLCALLBACK(void)   tmR3TimerCallback(PRTTIMER pTimer, void *pvUser); 
    132132static void                 tmR3TimerQueueRun(PVM pVM, PTMTIMERQUEUE pQueue); 
     133static void                 tmR3TimerQueueRunVirtualSync(PVM pVM); 
     134static uint64_t             tmR3TimerQueueRunVirtualSyncGiveup(PVM pVM, uint64_t offNew); 
    133135static DECLCALLBACK(void)   tmR3TimerInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs); 
    134136static DECLCALLBACK(void)   tmR3TimerInfoActive(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs); 
     
    148150    { 
    149151        case TMCLOCK_VIRTUAL:       return TMVirtualGet(pVM); 
    150         case TMCLOCK_VIRTUAL_SYNC:  return TMVirtualGetSync(pVM); 
     152        case TMCLOCK_VIRTUAL_SYNC:  return TMVirtualSyncGet(pVM); 
    151153        case TMCLOCK_REAL:          return TMRealGet(pVM); 
    152154        case TMCLOCK_TSC:           return TMCpuTickGet(pVM); 
     
    215217 
    216218    /* 
     219     * Get our CFGM node, create it if necessary. 
     220     */ 
     221    PCFGMNODE pCfgHandle = CFGMR3GetChild(CFGMR3GetRoot(pVM), "TM"); 
     222    if (!pCfgHandle) 
     223    { 
     224        rc = CFGMR3InsertNode(CFGMR3GetRoot(pVM), "TM", &pCfgHandle); 
     225        AssertRCReturn(rc, rc); 
     226    } 
     227 
     228    /* 
    217229     * Determin the TSC configuration and frequency. 
    218230     */ 
    219231    /* mode */ 
    220     rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "TSCVirtualized", &pVM->tm.s.fTSCVirtualized); 
     232    rc = CFGMR3QueryBool(pCfgHandle, "TSCVirtualized", &pVM->tm.s.fTSCVirtualized); 
    221233    if (rc == VERR_CFGM_VALUE_NOT_FOUND) 
    222234        pVM->tm.s.fTSCVirtualized = true; /* trap rdtsc */ 
     
    226238 
    227239    /* source */ 
    228     rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "UseRealTSC", &pVM->tm.s.fTSCTicking); 
     240    rc = CFGMR3QueryBool(pCfgHandle, "UseRealTSC", &pVM->tm.s.fTSCTicking); 
    229241    if (rc == VERR_CFGM_VALUE_NOT_FOUND) 
    230242        pVM->tm.s.fTSCUseRealTSC = false; /* use virtual time */ 
     
    236248 
    237249    /* frequency */ 
    238     rc = CFGMR3QueryU64(CFGMR3GetRoot(pVM), "TSCTicksPerSecond", &pVM->tm.s.cTSCTicksPerSecond); 
     250    rc = CFGMR3QueryU64(pCfgHandle, "TSCTicksPerSecond", &pVM->tm.s.cTSCTicksPerSecond); 
    239251    if (rc == VERR_CFGM_VALUE_NOT_FOUND) 
    240252    { 
     
    267279 
    268280    /* 
    269      * Register saved state. 
    270      */ 
    271     rc = SSMR3RegisterInternal(pVM, "tm", 1, TM_SAVED_STATE_VERSION, sizeof(uint64_t) * 8, 
    272                                NULL, tmR3Save, NULL, 
    273                                NULL, tmR3Load, NULL); 
    274     if (VBOX_FAILURE(rc)) 
    275         return rc; 
     281     * Configure the timer synchronous virtual time. 
     282     */ 
     283    rc = CFGMR3QueryU32(pCfgHandle, "ScheduleSlack", &pVM->tm.s.u32VirtualSyncScheduleSlack); 
     284    if (rc == VERR_CFGM_VALUE_NOT_FOUND) 
     285        pVM->tm.s.u32VirtualSyncScheduleSlack           =   250000; /* 0.25ms (ASSUMES virtual time is nanoseconds) */ 
     286    else if (VBOX_FAILURE(rc)) 
     287        return VMSetError(pVM, rc, RT_SRC_POS,  
     288                          N_("Configuration error: Failed to querying 32-bit integer value \"ScheduleSlack\". (%Vrc)"), rc);  
     289 
     290    rc = CFGMR3QueryU64(pCfgHandle, "CatchUpStopThreshold", &pVM->tm.s.u64VirtualSyncCatchUpStopThreshold); 
     291    if (rc == VERR_CFGM_VALUE_NOT_FOUND) 
     292        pVM->tm.s.u64VirtualSyncCatchUpStopThreshold    =   500000; /* 0.5ms */ 
     293    else if (VBOX_FAILURE(rc)) 
     294        return VMSetError(pVM, rc, RT_SRC_POS,  
     295                          N_("Configuration error: Failed to querying 64-bit integer value \"CatchUpStopThreshold\". (%Vrc)"), rc);  
     296 
     297    rc = CFGMR3QueryU64(pCfgHandle, "CatchUpGiveUpThreshold", &pVM->tm.s.u64VirtualSyncCatchUpGiveUpThreshold); 
     298    if (rc == VERR_CFGM_VALUE_NOT_FOUND) 
     299        pVM->tm.s.u64VirtualSyncCatchUpGiveUpThreshold  = 1500000000; /* 1.5 sec */ 
     300    else if (VBOX_FAILURE(rc)) 
     301        return VMSetError(pVM, rc, RT_SRC_POS,  
     302                          N_("Configuration error: Failed to querying 64-bit integer value \"CatchUpGiveUpThreshold\". (%Vrc)"), rc);  
     303 
     304 
     305#define TM_CFG_PERIOD(iPeriod, DefStart, DefPct) \ 
     306    do \ 
     307    { \ 
     308        uint64_t u64; \ 
     309        rc = CFGMR3QueryU64(pCfgHandle, "CatchUpStartThreshold" #iPeriod, &u64); \ 
     310        if (rc == VERR_CFGM_VALUE_NOT_FOUND) \ 
     311            u64 = (DefStart); \ 
     312        else if (VBOX_FAILURE(rc)) \ 
     313            return VMSetError(pVM, rc, RT_SRC_POS, N_("Configuration error: Failed to querying 64-bit integer value \"CatchUpThreshold" #iPeriod "\". (%Vrc)"), rc); \ 
     314        if (    (iPeriod > 0 && u64 <= pVM->tm.s.aVirtualSyncCatchUpPeriods[iPeriod - 1].u64Start) \ 
     315            ||  u64 >= pVM->tm.s.u64VirtualSyncCatchUpGiveUpThreshold) \ 
     316            return VMSetError(pVM, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("Configuration error: Invalid start of period #" #iPeriod ": %RU64\n"), u64); \ 
     317        pVM->tm.s.aVirtualSyncCatchUpPeriods[iPeriod].u64Start = u64; \ 
     318        rc = CFGMR3QueryU32(pCfgHandle, "CatchUpPrecentage" #iPeriod, &pVM->tm.s.aVirtualSyncCatchUpPeriods[iPeriod].u32Percentage); \ 
     319        if (rc == VERR_CFGM_VALUE_NOT_FOUND) \ 
     320            pVM->tm.s.aVirtualSyncCatchUpPeriods[iPeriod].u32Percentage = (DefPct); \ 
     321        else if (VBOX_FAILURE(rc)) \ 
     322            return VMSetError(pVM, rc, RT_SRC_POS, N_("Configuration error: Failed to querying 32-bit integer value \"CatchUpPrecentage" #iPeriod "\". (%Vrc)"), rc); \ 
     323    } while (0) 
     324    TM_CFG_PERIOD(0,   25000000,  25); /*   25ms at 1.25x */ 
     325    TM_CFG_PERIOD(1,   75000000,  50); /*   75ms at 1.50x */ 
     326    TM_CFG_PERIOD(2,  100000000,  75); /*   75ms at 1.75x */ 
     327    TM_CFG_PERIOD(3,  150000000, 100); /*  150ms at 2x */ 
     328    TM_CFG_PERIOD(4,  400000000, 200); /*  400ms at 3x */ 
     329    TM_CFG_PERIOD(5,  800000000, 300); /*  800ms at 4x */ 
     330    TM_CFG_PERIOD(6, 1200000000, 400); /* 1200ms at 6x */ 
     331    TM_CFG_PERIOD(7, 1400000000, 500); /* 1400ms at 8x */ 
     332    AssertCompile(RT_ELEMENTS(pVM->tm.s.aVirtualSyncCatchUpPeriods) == 8); 
     333#undef TM_CFG_PERIOD 
    276334 
    277335    /* 
    278336     * Setup the warp drive. 
    279337     */ 
    280     rc = CFGMR3QueryU32(CFGMR3GetRoot(pVM), "WarpDrivePercentage", &pVM->tm.s.u32VirtualWarpDrivePercentage); 
     338    rc = CFGMR3QueryU32(pCfgHandle, "WarpDrivePercentage", &pVM->tm.s.u32VirtualWarpDrivePercentage); 
     339    if (rc == VERR_CFGM_VALUE_NOT_FOUND) 
     340        rc = CFGMR3QueryU32(CFGMR3GetRoot(pVM), "WarpDrivePercentage", &pVM->tm.s.u32VirtualWarpDrivePercentage); /* legacy */ 
    281341    if (rc == VERR_CFGM_VALUE_NOT_FOUND) 
    282342        pVM->tm.s.u32VirtualWarpDrivePercentage = 100; 
     
    297357     */ 
    298358    uint32_t u32Millies; 
    299     rc = CFGMR3QueryU32(CFGMR3GetRoot(pVM), "TimerMillies", &u32Millies); 
     359    rc = CFGMR3QueryU32(pCfgHandle, "TimerMillies", &u32Millies); 
    300360    if (rc == VERR_CFGM_VALUE_NOT_FOUND) 
    301361        u32Millies = 10; 
     
    312372    pVM->tm.s.u32TimerMillies = u32Millies; 
    313373 
     374    /* 
     375     * Register saved state. 
     376     */ 
     377    rc = SSMR3RegisterInternal(pVM, "tm", 1, TM_SAVED_STATE_VERSION, sizeof(uint64_t) * 8, 
     378                               NULL, tmR3Save, NULL, 
     379                               NULL, tmR3Load, NULL); 
     380    if (VBOX_FAILURE(rc)) 
     381        return rc; 
     382 
    314383#ifdef VBOX_WITH_STATISTICS 
    315384    /* 
     
    348417 
    349418    STAM_REG(pVM, &pVM->tm.s.StatTimerCallbackSetFF,STAMTYPE_COUNTER,       "/TM/CallbackSetFF",    STAMUNIT_OCCURENCES,        "The number of times the timer callback set FF."); 
     419 
     420 
     421    STAM_REG(pVM, &pVM->tm.s.StatVirtualSyncRun,                STAMTYPE_COUNTER,   "/TM/VirtualSync/Run",                  STAMUNIT_OCCURENCES,    "Times the virtual sync timer queue was considered."); 
     422    STAM_REG(pVM, &pVM->tm.s.StatVirtualSyncRunRestart,         STAMTYPE_COUNTER,   "/TM/VirtualSync/Run/Restarts",         STAMUNIT_OCCURENCES,    "Times the clock was restarted after a run."); 
     423    STAM_REG(pVM, &pVM->tm.s.StatVirtualSyncRunStop,            STAMTYPE_COUNTER,   "/TM/VirtualSync/Run/Stop",             STAMUNIT_OCCURENCES,    "Times the clock was stopped when calculating the current time before examining the timers."); 
     424    STAM_REG(pVM, &pVM->tm.s.StatVirtualSyncRunStoppedAlready,  STAMTYPE_COUNTER,   "/TM/VirtualSync/Run/StoppedAlready",   STAMUNIT_OCCURENCES,    "Times the clock was already stopped elsewhere (TMVirtualSyncGet)."); 
     425    STAM_REG(pVM, &pVM->tm.s.StatVirtualSyncRunSlack,           STAMTYPE_PROFILE,   "/TM/VirtualSync/Run/Slack",            STAMUNIT_NS_PER_OCCURENCE, "The scheduling slack. (Catch-up handed out when running timers.)"); 
     426    STAM_REG(pVM, &pVM->tm.s.StatVirtualSyncGiveUp,             STAMTYPE_COUNTER,   "/TM/VirtualSync/GiveUp",               STAMUNIT_OCCURENCES,    "Times the catch-up was abandoned."); 
     427    STAM_REG(pVM, &pVM->tm.s.StatVirtualSyncGiveUpBeforeStarting,STAMTYPE_COUNTER,  "/TM/VirtualSync/GiveUpBeforeStarting", STAMUNIT_OCCURENCES,    "Times the catch-up was abandoned before even starting. (Typically debugging++.)"); 
     428    STAM_REG(pVM, &pVM->tm.s.StatVirtualSyncCatchup,        STAMTYPE_PROFILE_ADV,   "/TM/VirtualSync/CatchUp",              STAMUNIT_TICKS_PER_OCCURENCE, "Counting and measuring the times spent catching up."); 
     429    for (unsigned i = 0; i < RT_ELEMENTS(pVM->tm.s.aVirtualSyncCatchUpPeriods); i++) 
     430    { 
     431        STAMR3RegisterF(pVM, &pVM->tm.s.aVirtualSyncCatchUpPeriods[i].u32Percentage,    STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_PCT,          "The catch-up percentage.",         "/TM/VirtualSync/%u", i); 
     432        STAMR3RegisterF(pVM, &pVM->tm.s.aStatVirtualSyncCatchupAdjust[i],           STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,   "Times adjusted to this period.",   "/TM/VirtualSync/%u/Adjust", i); 
     433        STAMR3RegisterF(pVM, &pVM->tm.s.aStatVirtualSyncCatchupInitial[i],          STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,   "Times started in this period.",    "/TM/VirtualSync/%u/Initial", i); 
     434        STAMR3RegisterF(pVM, &pVM->tm.s.aVirtualSyncCatchUpPeriods[i].u64Start,         STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_NS,           "Start of this period (lag).",      "/TM/VirtualSync/%u/Start", i); 
     435    } 
     436 
    350437#endif /* VBOX_WITH_STATISTICS */ 
    351438 
     
    624711    /* the virtual timer synchronous clock. */ 
    625712    pVM->tm.s.fVirtualSyncTicking = false; 
    626     SSMR3GetU64(pSSM, &pVM->tm.s.u64VirtualSync); 
    627713    uint64_t u64; 
     714    SSMR3GetU64(pSSM, &u64); 
     715    pVM->tm.s.u64VirtualSync = u64; 
    628716    SSMR3GetU64(pSSM, &u64); 
    629717    pVM->tm.s.u64VirtualSyncOffset = u64; 
     
    667755 
    668756 
    669 /** @todo doc */ 
    670 static int tmr3TimerCreate(PVM pVM, TMCLOCK enmClock, const char *pszDesc, PPTMTIMERHC ppTimer) 
     757/** 
     758 * Internal TMR3TimerCreate worker. 
     759 *  
     760 * @returns VBox status code. 
     761 * @param   pVM         The VM handle. 
     762 * @param   enmClock    The timer clock. 
     763 * @param   pszDesc     The timer description. 
     764 * @param   ppTimer     Where to store the timer pointer on success. 
     765 */ 
     766static int tmr3TimerCreate(PVM pVM, TMCLOCK enmClock, const char *pszDesc, PPTMTIMERR3 ppTimer) 
    671767{ 
    672768    VM_ASSERT_EMT(pVM); 
     
    697793    pTimer->enmClock        = enmClock; 
    698794    pTimer->pVMR3           = pVM; 
    699     pTimer->pVMR0           = (PVMR0)pVM->pVMHC; /// @todo pTimer->pVMR0 = pVM->pVMR0; 
     795    pTimer->pVMR0           = pVM->pVMR0; 
    700796    pTimer->pVMGC           = pVM->pVMGC; 
    701797    pTimer->enmState        = TMTIMERSTATE_STOPPED; 
     
    9041000 
    9051001/** 
    906  * Checks if a queue has a pending timer. 
    907  * 
    908  * @returns true if it has a pending timer. 
    909  * @returns false is no pending timer. 
     1002 * Checks if the sync queue has one or more expired timers. 
     1003 * 
     1004 * @returns true / false. 
    9101005 * 
    9111006 * @param   pVM         The VM handle. 
    9121007 * @param   enmClock    The queue. 
    9131008 */ 
    914 DECLINLINE(bool) tmR3HasPending(PVM pVM, TMCLOCK enmClock) 
     1009DECLINLINE(bool) tmR3HasExpiredTimer(PVM pVM, TMCLOCK enmClock) 
    9151010{ 
    9161011    const uint64_t u64Expire = pVM->tm.s.CTXALLSUFF(paTimerQueues)[enmClock].u64Expire; 
     
    9201015 
    9211016/** 
     1017 * Checks for expired timers in all the queues. 
     1018 * 
     1019 * @returns true / false. 
     1020 * @param   pVM         The VM handle. 
     1021 */ 
     1022DECLINLINE(bool) tmR3AnyExpiredTimers(PVM pVM) 
     1023{ 
     1024    /*  
     1025     * Combine the time calculation for the first two since we're not on EMT 
     1026     * TMVirtualSyncGet only permits EMT. 
     1027     */ 
     1028    uint64_t u64Now = TMVirtualGet(pVM); 
     1029    if (pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64Now) 
     1030        return true; 
     1031    u64Now = pVM->tm.s.fVirtualSyncTicking  
     1032           ? u64Now - pVM->tm.s.u64VirtualSyncOffset  
     1033           : pVM->tm.s.u64VirtualSync; 
     1034    if (pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire <= u64Now) 
     1035        return true; 
     1036 
     1037    /* 
     1038     * The remaining timers. 
     1039     */ 
     1040    if (tmR3HasExpiredTimer(pVM, TMCLOCK_REAL)) 
     1041        return true; 
     1042    if (tmR3HasExpiredTimer(pVM, TMCLOCK_TSC)) 
     1043        return true; 
     1044    return false; 
     1045} 
     1046 
     1047 
     1048/** 
    9221049 * Schedulation timer callback. 
    9231050 * 
    9241051 * @param   pTimer      Timer handle. 
    9251052 * @param   pvUser      VM handle. 
     1053 * @thread  Timer thread. 
     1054 *  
    9261055 * @remark  We cannot do the scheduling and queues running from a timer handler 
    9271056 *          since it's not executing in EMT, and even if it was it would be async 
     
    9421071            ||  pVM->tm.s.paTimerQueuesR3[TMCLOCK_REAL].offSchedule 
    9431072            ||  pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC].offSchedule 
    944             ||  tmR3HasPending(pVM, TMCLOCK_VIRTUAL_SYNC) 
    945             ||  tmR3HasPending(pVM, TMCLOCK_VIRTUAL) 
    946             ||  tmR3HasPending(pVM, TMCLOCK_REAL) 
    947             ||  tmR3HasPending(pVM, TMCLOCK_TSC) 
     1073            ||  tmR3AnyExpiredTimers(pVM) 
    9481074            ) 
    9491075        && !VM_FF_ISSET(pVM, VM_FF_TIMER) 
     
    9751101    AssertCompile(TMCLOCK_MAX == 4); 
    9761102 
     1103    /* TMCLOCK_VIRTUAL_SYNC */ 
     1104    STAM_PROFILE_ADV_START(&pVM->tm.s.StatDoQueuesSchedule, s1); 
     1105    tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL_SYNC]); 
     1106    STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesSchedule, s1); 
     1107    STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatDoQueuesRun, r1); 
     1108    tmR3TimerQueueRunVirtualSync(pVM); 
     1109    STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesRun, r1); 
     1110 
    9771111    /* TMCLOCK_VIRTUAL */ 
    978     STAM_PROFILE_ADV_START(&pVM->tm.s.StatDoQueuesSchedule, s1); 
     1112    STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesSchedule, s1); 
    9791113    tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL]); 
    980     STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesSchedule, s1); 
    981     STAM_PROFILE_ADV_START(&pVM->tm.s.StatDoQueuesRun, r1); 
    982     tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL]); 
    983     STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesRun, r1); 
    984  
    985     /* TMCLOCK_VIRTUAL_SYNC */ 
    986     STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesSchedule, s1); 
    987     tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL_SYNC]); 
    9881114    STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesSchedule, s2); 
    9891115    STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesRun, r1); 
    990     tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL_SYNC]); 
     1116    tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL]); 
    9911117    STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesRun, r2); 
     1118 
     1119#if 0 /** @todo if ever used, remove this and fix the stam prefixes on TMCLOCK_REAL below. */ 
     1120    /* TMCLOCK_TSC */ 
     1121    STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesSchedule, s2); 
     1122    tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC]); 
     1123    STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesSchedule, s3); 
     1124    STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesRun, r2); 
     1125    tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC]); 
     1126    STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesRun, r3); 
     1127#endif  
    9921128 
    9931129    /* TMCLOCK_REAL */ 
    9941130    STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesSchedule, s2); 
    9951131    tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_REAL]); 
    996     STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesSchedule, s3); 
     1132    STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatDoQueuesSchedule, s3); 
    9971133    STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesRun, r2); 
    9981134    tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_REAL]); 
    999     STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesRun, r3); 
    1000  
    1001     /* TMCLOCK_TSC */ 
    1002     STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesSchedule, s3); 
    1003     tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC]); 
    1004     STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatDoQueuesSchedule, s3); 
    1005     STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesRun, r3); 
    1006     tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC]); 
    10071135    STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatDoQueuesRun, r3); 
    10081136 
     
    10471175    if (!pNext) 
    10481176        return; 
    1049     /** @todo deal with the VIRTUAL_SYNC pausing and catch calcs ++ */ 
    1050     uint64_t u64Now = tmClock(pVM, pQueue->enmClock); 
     1177    const uint64_t u64Now = tmClock(pVM, pQueue->enmClock); 
    10511178    while (pNext && pNext->u64Expire <= u64Now) 
    10521179    { 
     
    10931220        } 
    10941221    } /* run loop */ 
     1222} 
     1223 
     1224 
     1225/** 
     1226 * Schedules and runs any pending times in the timer queue for the  
     1227 * synchronous virtual clock. 
     1228 *  
     1229 * This scheduling is a bit different from the other queues as it need 
     1230 * to implement the special requirements of the timer synchronous virtual  
     1231 * clock, thus this 2nd queue run funcion. 
     1232 * 
     1233 * @param   pVM             The VM to run the timers for. 
     1234 */ 
     1235static void tmR3TimerQueueRunVirtualSync(PVM pVM) 
     1236{ 
     1237    PTMTIMERQUEUE const pQueue = &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL_SYNC]; 
     1238    VM_ASSERT_EMT(pVM); 
     1239 
     1240    /* 
     1241     * Any timers? 
     1242     */ 
     1243    PTMTIMER pNext = TMTIMER_GET_HEAD(pQueue); 
     1244    if (RT_UNLIKELY(!pNext)) 
     1245    { 
     1246        Assert(pVM->tm.s.fVirtualSyncTicking || !pVM->tm.s.fVirtualTicking); 
     1247        return; 
     1248    } 
     1249    STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncRun); 
     1250 
     1251    /* 
     1252     * Calculate the time frame for which we will dispatch timers. 
     1253     * 
     1254     * We use a time frame ranging from the current sync time (which is most likely the 
     1255     * same as the head timer) and some configurable period (250000ns) up towards the 
     1256     * current virtual time. This period might also need to be restricted by the catch-up 
     1257     * rate so frequent calls to this function won't accelerate the time too much, however 
     1258     * this will be implemented at a later point. 
     1259     * 
     1260     * Without this frame we would 1) having to run timers much more frequently  
     1261     * and 2) lag behind at a steady rate. 
     1262     */ 
     1263    const uint64_t u64VirtualNow = TMVirtualGetEx(pVM, false /* don't check timers */); 
     1264    uint64_t u64Now; 
     1265    uint64_t u64Max; 
     1266    if (!pVM->tm.s.fVirtualSyncTicking) 
     1267    { 
     1268        STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncRunStoppedAlready); 
     1269        u64Now = pVM->tm.s.u64VirtualSync; 
     1270        Assert(u64Now >= pNext->u64Expire); 
     1271 
     1272        u64Max = u64Now + pVM->tm.s.u32VirtualSyncScheduleSlack; 
     1273        if (u64Max > u64VirtualNow) 
     1274            u64Max = u64VirtualNow; 
     1275    } 
     1276    else 
     1277    { 
     1278        /* Calc now. */ 
     1279        uint64_t off = pVM->tm.s.u64VirtualSyncOffset; 
     1280        if (pVM->tm.s.fVirtualSyncCatchUp) 
     1281        { 
     1282            const uint64_t u64Prev = pVM->tm.s.u64VirtualSyncCatchUpPrev; 
     1283            uint64_t u64Delta = u64VirtualNow - u64Prev; 
     1284            if (RT_LIKELY(!(u64Delta >> 32))) 
     1285            { 
     1286                uint32_t u32Sub = ASMDivU64ByU32RetU32(ASMMult2xU32RetU64((uint32_t)u64Delta, pVM->tm.s.u32VirtualSyncCatchUpPercentage), 
     1287                                                       100); 
     1288                if (off > u32Sub) 
     1289                    off -= u32Sub; 
     1290                else 
     1291                    off = 0; 
     1292            } 
     1293        } 
     1294        u64Now = u64VirtualNow - off; 
     1295 
     1296        /* Check if stopped by expired timer and calc the frame end. */ 
     1297        if (u64Now <= pNext->u64Expire) 
     1298        { 
     1299            if (pVM->tm.s.u64VirtualSyncOffset <= pVM->tm.s.u32VirtualSyncScheduleSlack) 
     1300                u64Max = pVM->tm.s.u64VirtualSyncOffset; 
     1301            else 
     1302                u64Max = pVM->tm.s.u32VirtualSyncScheduleSlack; 
     1303        } 
     1304        else 
     1305        { 
     1306            STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncRunStop); 
     1307            u64Now = pNext->u64Expire; 
     1308            ASMAtomicXchgU64(&pVM->tm.s.u64VirtualSync, u64Now); 
     1309            ASMAtomicXchgBool(&pVM->tm.s.fVirtualSyncTicking, false); 
     1310 
     1311            u64Max = u64Now + pVM->tm.s.u32VirtualSyncScheduleSlack; 
     1312            if (u64Max > u64VirtualNow) 
     1313                u64Max = u64VirtualNow; 
     1314        } 
     1315    } 
     1316 
     1317    /* 
     1318     * Process the expired timers moving the clock along as we progress. 
     1319     */ 
     1320    while (pNext && pNext->u64Expire <= u64Max) 
     1321    { 
     1322        PTMTIMER pTimer = pNext; 
     1323        pNext = TMTIMER_GET_NEXT(pTimer); 
     1324        Log2(("tmR3TimerQueueRun: pTimer=%p:{.enmState=%s, .enmClock=%d, .enmType=%d, u64Expire=%llx (now=%llx) .pszDesc=%s}\n", 
     1325              pTimer, tmTimerState(pTimer->enmState), pTimer->enmClock, pTimer->enmType, pTimer->u64Expire, u64Now, pTimer->pszDesc)); 
     1326        bool fRc; 
     1327        TM_TRY_SET_STATE(pTimer, TMTIMERSTATE_EXPIRED, TMTIMERSTATE_ACTIVE, fRc); 
     1328        if (fRc) 
     1329        { 
     1330            Assert(!pTimer->offScheduleNext); /* this can trigger falsely */ 
     1331 
     1332            /* unlink */ 
     1333            const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer); 
     1334            if (pPrev) 
     1335                TMTIMER_SET_NEXT(pPrev, pNext); 
     1336            else 
     1337            { 
     1338                TMTIMER_SET_HEAD(pQueue, pNext); 
     1339                pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX; 
     1340            } 
     1341            if (pNext) 
     1342                TMTIMER_SET_PREV(pNext, pPrev); 
     1343            pTimer->offNext = 0; 
     1344            pTimer->offPrev = 0; 
     1345 
     1346            /* advance the clock */ 
     1347            ASMAtomicXchgSize(&pVM->tm.s.fVirtualSyncTicking, false); 
     1348            ASMAtomicXchgU64(&pVM->tm.s.u64Virtual, pTimer->u64Expire); 
     1349 
     1350            /* fire */ 
     1351            switch (pTimer->enmType) 
     1352            { 
     1353                case TMTIMERTYPE_DEV:       pTimer->u.Dev.pfnTimer(pTimer->u.Dev.pDevIns, pTimer); break; 
     1354                case TMTIMERTYPE_DRV:       pTimer->u.Drv.pfnTimer(pTimer->u.Drv.pDrvIns, pTimer); break; 
     1355                case TMTIMERTYPE_INTERNAL:  pTimer->u.Internal.pfnTimer(pVM, pTimer, pTimer->u.Internal.pvUser); break; 
     1356                case TMTIMERTYPE_EXTERNAL:  pTimer->u.External.pfnTimer(pTimer->u.External.pvUser); break; 
     1357                default: 
     1358                    AssertMsgFailed(("Invalid timer type %d (%s)\n", pTimer->enmType, pTimer->pszDesc)); 
     1359                    break; 
     1360            } 
     1361 
     1362            /* change the state if it wasn't changed already in the handler. */ 
     1363            TM_TRY_SET_STATE(pTimer, TMTIMERSTATE_STOPPED, TMTIMERSTATE_EXPIRED, fRc); 
     1364            Log2(("tmR3TimerQueueRun: new state %s\n", tmTimerState(pTimer->enmState))); 
     1365        } 
     1366    } /* run loop */ 
     1367 
     1368    /* 
     1369     * Restart the clock if it was stopped to serve any timers,  
     1370     * and start/adjust catch-up if necessary. 
     1371     */ 
     1372    if (    !pVM->tm.s.fVirtualSyncTicking 
     1373        &&  pVM->tm.s.fVirtualTicking) 
     1374    { 
     1375        STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncRunRestart); 
     1376 
     1377        const uint64_t u64VirtualNow2 = TMVirtualGetEx(pVM, false /* don't check timers */); 
     1378        Assert(u64VirtualNow2 >= u64VirtualNow); 
     1379        const uint64_t offSlack = pVM->tm.s.u64VirtualSync - u64Now; 
     1380        STAM_STATS( {  
     1381            if (offSlack) {  
     1382                PSTAMPROFILE p = &pVM->tm.s.StatVirtualSyncRunSlack; 
     1383                p->cPeriods++;  
     1384                p->cTicks += offSlack; 
     1385                if (p->cTicksMax < offSlack) p->cTicksMax = offSlack; 
     1386                if (p->cTicksMin > offSlack) p->cTicksMax = offSlack; 
     1387            }  
     1388        }); 
     1389 
     1390        /* Let the time run a little bit while we were busy running timers(?). */ 
     1391        uint64_t u64Elapsed; 
     1392#define MAX_ELAPSED 15000 /*ns*/ 
     1393        if (offSlack > MAX_ELAPSED) 
     1394            u64Elapsed = 0; 
     1395        else 
     1396        { 
     1397            u64Elapsed = u64VirtualNow2 - u64VirtualNow; 
     1398            if (u64Elapsed > MAX_ELAPSED) 
     1399                u64Elapsed = MAX_ELAPSED; 
     1400            u64Elapsed = u64Elapsed > offSlack ? u64Elapsed - offSlack : 0; 
     1401        } 
     1402#undef MAX_ELAPSED 
     1403 
     1404        /* Calc the current offset. */ 
     1405        uint64_t offNew = u64VirtualNow2 - pVM->tm.s.u64VirtualSync - u64Elapsed; 
     1406 
     1407        /* Deal with starting, adjusting and stopping catchup. */ 
     1408        if (pVM->tm.s.fVirtualSyncCatchUp) 
     1409        { 
     1410            if (offNew <= pVM->tm.s.u64VirtualSyncCatchUpStopThreshold) 
     1411            { 
     1412                /* stop */ 
     1413                STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatVirtualSyncCatchup, c); 
     1414                ASMAtomicXchgBool(&pVM->tm.s.fVirtualSyncCatchUp, false); 
     1415            } 
     1416            else if (offNew <= pVM->tm.s.u64VirtualSyncCatchUpGiveUpThreshold) 
     1417            { 
     1418                /* adjust */ 
     1419                unsigned i = 0; 
     1420                while (     i + 1 < RT_ELEMENTS(pVM->tm.s.aVirtualSyncCatchUpPeriods) 
     1421                       &&   offNew >= pVM->tm.s.aVirtualSyncCatchUpPeriods[i + 1].u64Start) 
     1422                    i++; 
     1423                if (pVM->tm.s.u32VirtualSyncCatchUpPercentage < pVM->tm.s.aVirtualSyncCatchUpPeriods[i].u32Percentage) 
     1424                { 
     1425                    STAM_COUNTER_INC(&pVM->tm.s.aStatVirtualSyncCatchupAdjust[i]); 
     1426                    ASMAtomicXchgU32(&pVM->tm.s.u32VirtualSyncCatchUpPercentage, pVM->tm.s.aVirtualSyncCatchUpPeriods[i].u32Percentage); 
     1427                } 
     1428                pVM->tm.s.u64VirtualSyncCatchUpPrev = u64VirtualNow2; 
     1429            } 
     1430            else 
     1431            { 
     1432                /* give up */ 
     1433                STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGiveUp); 
     1434                offNew = tmR3TimerQueueRunVirtualSyncGiveup(pVM, offNew); 
     1435            } 
     1436        } 
     1437        else if (offNew >= pVM->tm.s.aVirtualSyncCatchUpPeriods[0].u64Start) 
     1438        { 
     1439            if (offNew <= pVM->tm.s.u64VirtualSyncCatchUpGiveUpThreshold) 
     1440            { 
     1441                /* start */ 
     1442                STAM_PROFILE_ADV_START(&pVM->tm.s.StatVirtualSyncCatchup, c); 
     1443                unsigned i = 0; 
     1444                while (     i + 1 < RT_ELEMENTS(pVM->tm.s.aVirtualSyncCatchUpPeriods) 
     1445                       &&   offNew >= pVM->tm.s.aVirtualSyncCatchUpPeriods[i + 1].u64Start) 
     1446                    i++; 
     1447                STAM_COUNTER_INC(&pVM->tm.s.aStatVirtualSyncCatchupInitial[i]); 
     1448                ASMAtomicXchgU32(&pVM->tm.s.u32VirtualSyncCatchUpPercentage, pVM->tm.s.aVirtualSyncCatchUpPeriods[i].u32Percentage); 
     1449                ASMAtomicXchgBool(&pVM->tm.s.fVirtualSyncCatchUp, true); 
     1450            } 
     1451            else 
     1452            { 
     1453                /* not bother */ 
     1454                STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGiveUpBeforeStarting); 
     1455                offNew = tmR3TimerQueueRunVirtualSyncGiveup(pVM, offNew); 
     1456            } 
     1457        } 
     1458 
     1459        /* Update the offset and start the clock. */ 
     1460        ASMAtomicXchgU64(&pVM->tm.s.u64VirtualSyncOffset, offNew); 
     1461        ASMAtomicXchgBool(&pVM->tm.s.fVirtualSyncTicking, true); 
     1462    } 
     1463} 
     1464 
     1465 
     1466/** 
     1467 * Give up the chase. 
     1468 *  
     1469 * Not quite sure how to let the devices know about this, but somehow they will have  
     1470 * to (quietly) drop interrupts en masse and not cause any interrupt storms... 
     1471 *  
     1472 * @returns New offset. 
     1473 * 
     1474 * @param   pVM         The VM handle. 
     1475 * @param   offNew      The current offset. 
     1476 */ 
     1477static uint64_t tmR3TimerQueueRunVirtualSyncGiveup(PVM pVM, uint64_t offNew) 
     1478{ 
     1479    /** @todo deal with this. */ 
     1480    ASMAtomicXchgBool(&pVM->tm.s.fVirtualSyncCatchUp, false); 
     1481    return 0; 
    10951482} 
    10961483 
     
    13201707 
    13211708    /* virtual sync */ 
    1322     u64 = TMVirtualGetSync(pVM); 
     1709    u64 = TMVirtualSyncGet(pVM); 
    13231710    pHlp->pfnPrintf(pHlp, 
    13241711                    "VirtSync: %#RX64 (%RU64) %s%s", 
  • trunk/src/VBox/VMM/TMInternal.h

    r1057 r2248  
    311311    bool                        fVirtualWarpDrive; 
    312312    /** Virtual timer synchronous time ticking enabled indicator (bool). (TMCLOCK_VIRTUAL_SYNC) */ 
    313     bool                        fVirtualSyncTicking; 
     313    bool volatile               fVirtualSyncTicking; 
    314314    /** Virtual timer synchronous time catch-up active. */ 
    315315    bool volatile               fVirtualSyncCatchUp; 
     
    335335    uint64_t volatile           u64VirtualSyncCatchUpPrev; 
    336336    /** The guest virtual timer synchronous time when fVirtualSyncTicking is cleared. */ 
    337     uint64_t                    u64VirtualSync; 
    338     /** How many percent faster the clock should advance when catch-up is active. */ 
    339     uint32_t                    u32VirtualSyncCatchupPercentage; 
     337    uint64_t volatile           u64VirtualSync; 
     338    /** The current catch-up percentage. */ 
     339    uint32_t volatile           u32VirtualSyncCatchUpPercentage; 
     340    /** How much slack when processing timers. */ 
     341    uint32_t                    u32VirtualSyncScheduleSlack; 
    340342    /** When to stop catch-up. */ 
    341     uint32_t                    u32VirtualSyncCatchupStopThreashold; 
    342     /** When to start catch-up. */ 
    343     uint64_t                    u64VirtualSyncCatchupStartTreashold; 
     343    uint64_t                    u64VirtualSyncCatchUpStopThreshold; 
    344344    /** When to give up catch-up. */ 
    345     uint64_t                    u64VirtualSyncCatchupGiveUpTreashold; 
     345    uint64_t                    u64VirtualSyncCatchUpGiveUpThreshold; 
     346/** @def TM_MAX_CATCHUP_PERIODS  
     347 * The number of catchup rates. */ 
     348#define TM_MAX_CATCHUP_PERIODS  8 
     349    /** The agressivness of the catch-up relative to how far we've lagged behind.  
     350     * The idea is to have increasing catch-up percentage as the lag increases. */ 
     351    struct TMCATCHUPPERIOD 
     352    { 
     353        uint64_t                u64Start;       /**< When this period starts. (u64VirtualSyncOffset). */ 
     354        uint32_t                u32Percentage;  /**< The catch-up percent to apply. */ 
     355        uint32_t                u32Alignment;   /**< Structure alignment */ 
     356    }                           aVirtualSyncCatchUpPeriods[TM_MAX_CATCHUP_PERIODS]; 
    346357 
    347358    /** Timer queues for the different clock types - R3 Ptr */ 
     
    390401    STAMPROFILE                 StatScheduleOneR3; 
    391402    STAMCOUNTER                 StatScheduleSetFF; 
    392     /** @} */ 
     403    STAMCOUNTER                 StatPostponedR3; 
     404    STAMCOUNTER                 StatPostponedR0; 
     405    STAMCOUNTER                 StatPostponedGC; 
     406    /** @} */ 
     407    /** Read the time  
     408     * @{ */ 
    393409    STAMCOUNTER                 StatVirtualGet; 
    394410    STAMCOUNTER                 StatVirtualGetSync; 
    395411    STAMCOUNTER                 StatVirtualPause; 
    396412    STAMCOUNTER                 StatVirtualResume; 
     413    /* @} */ 
    397414    /** TMTimerPoll 
    398415     * @{ */ 
     
    414431    STAMPROFILE                 StatTimerStopR3; 
    415432    /** @} */ 
    416     /** 
    417      * @{ */ 
    418     STAMCOUNTER                 StatPostponedR3; 
    419     STAMCOUNTER                 StatPostponedR0; 
    420     STAMCOUNTER                 StatPostponedGC; 
     433    /** VirtualSync - Running and Catching Up 
     434     * @{ */ 
     435    STAMCOUNTER                 StatVirtualSyncRun; 
     436    STAMCOUNTER                 StatVirtualSyncRunRestart; 
     437    STAMPROFILE                 StatVirtualSyncRunSlack; 
     438    STAMCOUNTER                 StatVirtualSyncRunStop; 
     439    STAMCOUNTER                 StatVirtualSyncRunStoppedAlready; 
     440    STAMCOUNTER                 StatVirtualSyncGiveUp; 
     441    STAMCOUNTER                 StatVirtualSyncGiveUpBeforeStarting; 
     442    STAMPROFILEADV              StatVirtualSyncCatchup; 
     443    STAMCOUNTER                 aStatVirtualSyncCatchupInitial[TM_MAX_CATCHUP_PERIODS]; 
     444    STAMCOUNTER                 aStatVirtualSyncCatchupAdjust[TM_MAX_CATCHUP_PERIODS]; 
    421445    /** @} */ 
    422446    /** The timer callback. */ 
  • trunk/src/VBox/VMM/VMMAll/TMAll.cpp

    r1057 r2248  
    451451            return TMTimerSet(pTimer, cMilliesToNext * (uint64_t)TMCLOCK_FREQ_VIRTUAL / 1000 + TMVirtualGet(pVM)); 
    452452        case TMCLOCK_VIRTUAL_SYNC: 
    453             return TMTimerSet(pTimer, cMilliesToNext * (uint64_t)TMCLOCK_FREQ_VIRTUAL / 1000 + TMVirtualGetSync(pVM)); 
     453            return TMTimerSet(pTimer, cMilliesToNext * (uint64_t)TMCLOCK_FREQ_VIRTUAL / 1000 + TMVirtualSyncGet(pVM)); 
    454454        case TMCLOCK_REAL: 
    455455            AssertCompile(TMCLOCK_FREQ_REAL == 1000); 
     
    572572            break; 
    573573        case TMCLOCK_VIRTUAL_SYNC: 
    574             u64 = TMVirtualGetSync(pVM); 
     574            u64 = TMVirtualSyncGet(pVM); 
    575575            break; 
    576576        case TMCLOCK_REAL: 
  • trunk/src/VBox/VMM/VMMAll/TMAllVirtual.cpp

    r2082 r2248  
    2828#ifdef IN_RING3 
    2929# include <VBox/rem.h> 
     30# include <iprt/thread.h> 
    3031#endif 
    3132#include "TMInternal.h" 
     
    5354 * @param   pVM     The VM handle. 
    5455 */ 
    55 uint64_t tmVirtualGetRawNonNormal(PVM pVM) 
     56static uint64_t tmVirtualGetRawNonNormal(PVM pVM) 
    5657{ 
    5758    /*  
     
    9091 
    9192/** 
    92  * Gets the current TMCLOCK_VIRTUAL time 
    93  * 
    94  * @returns The timestamp. 
    95  * @param   pVM     VM handle. 
    96  * 
    97  * @remark  While the flow of time will never go backwards, the speed of the 
    98  *          progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be 
    99  *          influenced by power saving (SpeedStep, PowerNow!), while the former 
    100  *          makes use of TSC and kernel timers. 
    101  */ 
    102 TMDECL(uint64_t) TMVirtualGet(PVM pVM) 
    103 
    104     return TMVirtualGetEx(pVM, true /* check timers */); 
    105 
    106  
    107  
    108 /** 
    109  * Gets the current TMCLOCK_VIRTUAL time 
    110  * 
    111  * @returns The timestamp. 
    112  * @param   pVM             VM handle. 
    113  * @param   fCheckTimers    Check timers or not 
    114  * 
    115  * @remark  While the flow of time will never go backwards, the speed of the 
    116  *          progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be 
    117  *          influenced by power saving (SpeedStep, PowerNow!), while the former 
    118  *          makes use of TSC and kernel timers. 
    119  */ 
    120 TMDECL(uint64_t) TMVirtualGetEx(PVM pVM, bool fCheckTimers) 
     93 * Inlined version of tmVirtualGetEx. 
     94 */ 
     95DECLINLINE(uint64_t) tmVirtualGet(PVM pVM, bool fCheckTimers) 
    12196{ 
    12297    uint64_t u64; 
     
    152127 
    153128/** 
     129 * Gets the current TMCLOCK_VIRTUAL time 
     130 * 
     131 * @returns The timestamp. 
     132 * @param   pVM     VM handle. 
     133 * 
     134 * @remark  While the flow of time will never go backwards, the speed of the 
     135 *          progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be 
     136 *          influenced by power saving (SpeedStep, PowerNow!), while the former 
     137 *          makes use of TSC and kernel timers. 
     138 */ 
     139TMDECL(uint64_t) TMVirtualGet(PVM pVM) 
     140{ 
     141    return TMVirtualGetEx(pVM, true /* check timers */); 
     142} 
     143 
     144 
     145/** 
     146 * Gets the current TMCLOCK_VIRTUAL time 
     147 * 
     148 * @returns The timestamp. 
     149 * @param   pVM             VM handle. 
     150 * @param   fCheckTimers    Check timers or not 
     151 * 
     152 * @remark  While the flow of time will never go backwards, the speed of the 
     153 *          progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be 
     154 *          influenced by power saving (SpeedStep, PowerNow!), while the former 
     155 *          makes use of TSC and kernel timers. 
     156 */ 
     157TMDECL(uint64_t) TMVirtualGetEx(PVM pVM, bool fCheckTimers) 
     158{ 
     159    return tmVirtualGet(pVM, fCheckTimers); 
     160} 
     161 
     162 
     163/** 
    154164 * Gets the current TMCLOCK_VIRTUAL_SYNC time. 
    155165 * 
    156166 * @returns The timestamp. 
    157167 * @param   pVM     VM handle. 
    158  */ 
    159 TMDECL(uint64_t) TMVirtualGetSync(PVM pVM) 
    160 
     168 * @thread  EMT. 
     169 */ 
     170TMDECL(uint64_t) TMVirtualSyncGet(PVM pVM) 
     171
     172    VM_ASSERT_EMT(pVM); 
     173 
    161174    uint64_t u64; 
    162175    if (pVM->tm.s.fVirtualSyncTicking) 
     
    165178 
    166179        /* 
    167          * Do TMVirtualGet() to get the current TMCLOCK_VIRTUAL time
     180         * Query the virtual clock and do the usual expired timer check
    168181         */ 
    169182        Assert(pVM->tm.s.fVirtualTicking); 
     
    183196         * 
    184197         * The catch-up adjusting work by us decrementing the offset by a percentage of 
    185          * the time elapsed since the previous TMVritualGetSync call. We take some simple 
    186          * precautions against racing other threads here, but assume that this isn't going 
    187          * to be much of a problem since calls to this function is unlikely from threads 
    188          * other than the EMT. 
     198         * the time elapsed since the previous TMVirtualGetSync call. 
    189199         * 
    190200         * It's possible to get a very long or even negative interval between two read 
     
    209219            const uint64_t u64Prev = pVM->tm.s.u64VirtualSyncCatchUpPrev; 
    210220            uint64_t u64Delta = u64 - u64Prev; 
    211             if (!(u64Delta >> 32))&n