VirtualBox

Changeset 53457 in vbox for trunk/src/VBox/Runtime/r0drv


Ignore:
Timestamp:
Dec 5, 2014 12:54:16 PM (10 years ago)
Author:
vboxsync
Message:

RTimer/r0drv/nt: Rearm the interval timers ourselves to avoid rounding errors caused by millsecond interval resolution vs. sub-millisecond clock tick (e.g. 0.9766ms). Also fixed single-shot omni timers.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp

    r47637 r53457  
    4040#include "internal-r0drv-nt.h"
    4141#include "internal/magics.h"
     42
     43/** This seems to provide better accuracy. */
     44#define RTR0TIMER_NT_MANUAL_RE_ARM 1
    4245
    4346
     
    7174     * is destroyed to indicate clearly that thread should exit. */
    7275    uint32_t volatile       u32Magic;
     76    /** Suspend count down for single shot omnit timers. */
     77    int32_t volatile        cOmniSuspendCountDown;
    7378    /** Flag indicating the timer is suspended. */
    7479    bool volatile           fSuspended;
     
    8691    /** The timer interval. 0 if one-shot. */
    8792    uint64_t                u64NanoInterval;
     93#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
     94    /** The NT start time . */
     95    uint64_t                uNtStartTime;
     96#endif
    8897    /** The Nt timer object. */
    8998    KTIMER                  NtTimer;
     
    98107
    99108
     109#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
     110/**
     111 * Get current NT interrupt time.
     112 * @return NT interrupt time
     113 */
     114static uint64_t rtTimerNtQueryInterruptTime(void)
     115{
     116# ifdef RT_ARCH_AMD64
     117    return KeQueryInterruptTime(); /* macro */
     118# else
     119    if (g_pfnrtKeQueryInterruptTime)
     120        return g_pfnrtKeQueryInterruptTime();
     121
     122    /* NT4 */
     123    ULARGE_INTEGER InterruptTime;
     124    do
     125    {
     126        InterruptTime.HighPart = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High1Time;
     127        InterruptTime.LowPart  = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.LowPart;
     128    } while (((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High2Time != InterruptTime.HighPart);
     129    return InterruptTime.QuadPart;
     130# endif
     131}
     132#endif /* RTR0TIMER_NT_MANUAL_RE_ARM */
     133
     134
     135/**
     136 * Manually re-arms an internval timer.
     137 *
     138 * Turns out NT doesn't necessarily do a very good job at re-arming timers
     139 * accurately.
     140 *
     141 * @param   pTimer              The timer.
     142 * @param   iTick               The current timer tick.
     143 * @param   pMasterDpc          The master DPC.
     144 */
     145DECLINLINE(void) rtTimerNtRearmInternval(PRTTIMER pTimer, uint64_t iTick, PKDPC pMasterDpc)
     146{
     147#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
     148    Assert(pTimer->u64NanoInterval);
     149
     150    uint64_t uNtNext = (iTick * pTimer->u64NanoInterval) / 100 - 10; /* 1us fudge */
     151    LARGE_INTEGER DueTime;
     152    DueTime.QuadPart = rtTimerNtQueryInterruptTime() - pTimer->uNtStartTime;
     153    if (DueTime.QuadPart < 0)
     154        DueTime.QuadPart = 0;
     155    if ((uint64_t)DueTime.QuadPart < uNtNext)
     156        DueTime.QuadPart -= uNtNext;
     157    else
     158        DueTime.QuadPart = -2500; /* 0.25ms */
     159
     160    KeSetTimerEx(&pTimer->NtTimer, DueTime, 0, &pTimer->aSubTimers[0].NtDpc);
     161#endif
     162}
     163
    100164
    101165/**
     
    125189        if (!pTimer->u64NanoInterval)
    126190            ASMAtomicWriteBool(&pTimer->fSuspended, true);
    127         pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->aSubTimers[0].iTick);
     191        uint64_t iTick = ++pTimer->aSubTimers[0].iTick;
     192        if (pTimer->u64NanoInterval)
     193            rtTimerNtRearmInternval(pTimer, iTick, &pTimer->aSubTimers[0].NtDpc);
     194        pTimer->pfnTimer(pTimer, pTimer->pvUser, iTick);
    128195    }
    129196
     
    161228    {
    162229        if (!pTimer->u64NanoInterval)
    163             ASMAtomicWriteBool(&pTimer->fSuspended, true);
     230            if (ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown) <= 0)
     231                ASMAtomicWriteBool(&pTimer->fSuspended, true);
     232
    164233        pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
    165234    }
     
    203272        RTCPUSET    OnlineSet;
    204273        RTMpGetOnlineSet(&OnlineSet);
    205         for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
    206             if (    RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)
    207                 &&  iCpuSelf != iCpu)
    208                 KeInsertQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc, 0, 0);
    209 
    210         if (!pTimer->u64NanoInterval)
    211             ASMAtomicWriteBool(&pTimer->fSuspended, true);
    212         pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
     274
     275        if (pTimer->u64NanoInterval)
     276        {
     277            /*
     278             * Recurring timer.
     279             */
     280            for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
     281                if (    RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)
     282                    &&  iCpuSelf != iCpu)
     283                    KeInsertQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc, 0, 0);
     284
     285            uint64_t iTick = ++pSubTimer->iTick;
     286            rtTimerNtRearmInternval(pTimer, iTick, &pTimer->aSubTimers[RTMpCpuIdToSetIndex(pTimer->idCpu)].NtDpc);
     287            pTimer->pfnTimer(pTimer, pTimer->pvUser, iTick);
     288        }
     289        else
     290        {
     291            /*
     292             * Single shot timers gets complicated wrt to fSuspended maintance.
     293             */
     294            uint32_t cCpus = 0;
     295            for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
     296                if (RTCpuSetIsMemberByIndex(&OnlineSet, iCpu))
     297                    cCpus++;
     298            ASMAtomicAddS32(&pTimer->cOmniSuspendCountDown, cCpus);
     299
     300            for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
     301                if (    RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)
     302                    &&  iCpuSelf != iCpu)
     303                    if (!KeInsertQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc, 0, 0))
     304                        ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown); /* already queued and counted. */
     305
     306            if (ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown) <= 0)
     307                ASMAtomicWriteBool(&pTimer->fSuspended, true);
     308
     309            pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
     310        }
    213311    }
    214312
     
    239337                     : &pTimer->aSubTimers[0].NtDpc;
    240338
     339#ifndef RTR0TIMER_NT_MANUAL_RE_ARM
    241340    uint64_t u64Interval = pTimer->u64NanoInterval / 1000000; /* This is ms, believe it or not. */
    242341    ULONG ulInterval = (ULONG)u64Interval;
     
    245344    else if (!ulInterval && pTimer->u64NanoInterval)
    246345        ulInterval = 1;
     346#endif
    247347
    248348    LARGE_INTEGER DueTime;
     
    254354    for (unsigned iCpu = 0; iCpu < cSubTimers; iCpu++)
    255355        pTimer->aSubTimers[iCpu].iTick = 0;
     356    ASMAtomicWriteS32(&pTimer->cOmniSuspendCountDown, 0);
    256357    ASMAtomicWriteBool(&pTimer->fSuspended, false);
     358#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
     359    pTimer->uNtStartTime = rtTimerNtQueryInterruptTime();
     360    KeSetTimerEx(&pTimer->NtTimer, DueTime, 0, pMasterDpc);
     361#else
    257362    KeSetTimerEx(&pTimer->NtTimer, DueTime, ulInterval, pMasterDpc);
     363#endif
    258364    return VINF_SUCCESS;
    259365}
     
    369475     */
    370476    pTimer->u32Magic = RTTIMER_MAGIC;
     477    pTimer->cOmniSuspendCountDown = 0;
    371478    pTimer->fSuspended = true;
    372479    pTimer->fSpecificCpu = (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL;
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette