VirtualBox

Changeset 87816 in vbox


Ignore:
Timestamp:
Feb 20, 2021 12:54:46 AM (4 years ago)
Author:
vboxsync
Message:

VMM/TM: Gearing up to spreading out the timer work a little. bugref:9943

Location:
trunk/src/VBox/VMM
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/VMM/VMMR3/TM.cpp

    r87814 r87816  
    180180#endif
    181181static DECLCALLBACK(void)   tmR3TimerCallback(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
    182 static void                 tmR3TimerQueueRun(PVM pVM, PTMTIMERQUEUE pQueue);
    183 static void                 tmR3TimerQueueRunVirtualSync(PVM pVM);
    184182static DECLCALLBACK(int)    tmR3SetWarpDrive(PUVM pUVM, uint32_t u32Percent);
    185183#ifndef VBOX_WITHOUT_NS_ACCOUNTING
     
    228226    {
    229227        Assert(pVM->tm.s.aTimerQueues[i].szName[0] != '\0');
    230         pVM->tm.s.aTimerQueues[i].enmClock    = (TMCLOCK)i;
    231         pVM->tm.s.aTimerQueues[i].u64Expire   = INT64_MAX;
    232         pVM->tm.s.aTimerQueues[i].idxActive   = UINT32_MAX;
    233         pVM->tm.s.aTimerQueues[i].idxSchedule = UINT32_MAX;
    234         pVM->tm.s.aTimerQueues[i].idxFreeHint = 1;
     228        pVM->tm.s.aTimerQueues[i].enmClock          = (TMCLOCK)i;
     229        pVM->tm.s.aTimerQueues[i].u64Expire         = INT64_MAX;
     230        pVM->tm.s.aTimerQueues[i].idxActive         = UINT32_MAX;
     231        pVM->tm.s.aTimerQueues[i].idxSchedule       = UINT32_MAX;
     232        pVM->tm.s.aTimerQueues[i].idxFreeHint       = 1;
     233        pVM->tm.s.aTimerQueues[i].fBeingProcessed   = false;
     234        pVM->tm.s.aTimerQueues[i].fCannotGrow       = false;
     235        pVM->tm.s.aTimerQueues[i].hThread           = NIL_RTTHREAD;
     236        pVM->tm.s.aTimerQueues[i].hWorkerEvt        = NIL_SUPSEMEVENT;
     237
    235238        rc = PDMR3CritSectInit(pVM, &pVM->tm.s.aTimerQueues[i].TimerLock, RT_SRC_POS,
    236239                               "TM %s queue timer lock", pVM->tm.s.aTimerQueues[i].szName);
     
    22822285
    22832286/**
    2284  * Schedules and runs any pending timers.
    2285  *
    2286  * This is normally called from a forced action handler in EMT.
    2287  *
    2288  * @param   pVM             The cross context VM structure.
    2289  *
    2290  * @thread  EMT (actually EMT0, but we fend off the others)
    2291  */
    2292 VMMR3DECL(void) TMR3TimerQueuesDo(PVM pVM)
    2293 {
    2294     /*
    2295      * Only the dedicated timer EMT should do stuff here.
    2296      * (fRunningQueues is only used as an indicator.)
    2297      */
    2298     Assert(pVM->tm.s.idTimerCpu < pVM->cCpus);
    2299     PVMCPU pVCpuDst = pVM->apCpusR3[pVM->tm.s.idTimerCpu];
    2300     if (VMMGetCpu(pVM) != pVCpuDst)
    2301     {
    2302         Assert(pVM->cCpus > 1);
    2303         return;
    2304     }
    2305     STAM_PROFILE_START(&pVM->tm.s.StatDoQueues, a);
    2306     Log2(("TMR3TimerQueuesDo:\n"));
    2307     Assert(!pVM->tm.s.fRunningQueues);
    2308     ASMAtomicWriteBool(&pVM->tm.s.fRunningQueues, true);
    2309 
    2310     /*
    2311      * Process the queues.
    2312      */
    2313     AssertCompile(TMCLOCK_MAX == 4);
    2314 
    2315     /*
    2316      * TMCLOCK_VIRTUAL_SYNC (see also TMR3VirtualSyncFF)
    2317      */
    2318     PTMTIMERQUEUE pQueue = &pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC];
    2319     STAM_PROFILE_START(&pQueue->StatDo, s1);
    2320     PDMCritSectEnter(&pQueue->TimerLock, VERR_IGNORED);
    2321     PDMCritSectEnter(&pVM->tm.s.VirtualSyncLock, VERR_IGNORED);
    2322     ASMAtomicWriteBool(&pVM->tm.s.fRunningVirtualSyncQueue, true);
    2323     VMCPU_FF_CLEAR(pVCpuDst, VMCPU_FF_TIMER);   /* Clear the FF once we started working for real. */
    2324 
    2325     Assert(pQueue->idxSchedule == UINT32_MAX);
    2326     tmR3TimerQueueRunVirtualSync(pVM);
    2327     if (pVM->tm.s.fVirtualSyncTicking) /** @todo move into tmR3TimerQueueRunVirtualSync - FIXME */
    2328         VM_FF_CLEAR(pVM, VM_FF_TM_VIRTUAL_SYNC);
    2329 
    2330     ASMAtomicWriteBool(&pVM->tm.s.fRunningVirtualSyncQueue, false);
    2331     PDMCritSectLeave(&pVM->tm.s.VirtualSyncLock);
    2332     PDMCritSectLeave(&pQueue->TimerLock);
    2333     STAM_PROFILE_STOP(&pQueue->StatDo, s1);
    2334 
    2335     /*
    2336      * TMCLOCK_VIRTUAL
    2337      */
    2338     pQueue = &pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL];
    2339     STAM_PROFILE_START(&pQueue->StatDo, s2);
    2340     PDMCritSectEnter(&pQueue->TimerLock, VERR_IGNORED);
    2341     if (pQueue->idxSchedule != UINT32_MAX)
    2342         tmTimerQueueSchedule(pVM, pQueue, pQueue);
    2343     tmR3TimerQueueRun(pVM, pQueue);
    2344     PDMCritSectLeave(&pQueue->TimerLock);
    2345     STAM_PROFILE_STOP(&pQueue->StatDo, s2);
    2346 
    2347     /*
    2348      * TMCLOCK_TSC
    2349      */
    2350     Assert(pVM->tm.s.aTimerQueues[TMCLOCK_TSC].idxActive == UINT32_MAX); /* not used */
    2351 
    2352     /*
    2353      * TMCLOCK_REAL
    2354      */
    2355     pQueue = &pVM->tm.s.aTimerQueues[TMCLOCK_REAL];
    2356     STAM_PROFILE_START(&pQueue->StatDo, s3);
    2357     PDMCritSectEnter(&pQueue->TimerLock, VERR_IGNORED);
    2358     if (pQueue->idxSchedule != UINT32_MAX)
    2359         tmTimerQueueSchedule(pVM, pQueue, pQueue);
    2360     tmR3TimerQueueRun(pVM, pQueue);
    2361     PDMCritSectLeave(&pQueue->TimerLock);
    2362     STAM_PROFILE_STOP(&pQueue->StatDo, s3);
    2363 
    2364 #ifdef VBOX_STRICT
    2365     /* check that we didn't screw up. */
    2366     tmTimerQueuesSanityChecks(pVM, "TMR3TimerQueuesDo");
    2367 #endif
    2368 
    2369     /* done */
    2370     Log2(("TMR3TimerQueuesDo: returns void\n"));
    2371     ASMAtomicWriteBool(&pVM->tm.s.fRunningQueues, false);
    2372     STAM_PROFILE_STOP(&pVM->tm.s.StatDoQueues, a);
    2373 }
    2374 
    2375 //RT_C_DECLS_BEGIN
    2376 //int     iomLock(PVM pVM);
    2377 //void    iomUnlock(PVM pVM);
    2378 //RT_C_DECLS_END
    2379 
    2380 
    2381 /**
    2382  * Schedules and runs any pending times in the specified queue.
    2383  *
    2384  * This is normally called from a forced action handler in EMT.
     2287 * Worker for tmR3TimerQueueDoOne that runs pending timers on the specified
     2288 * non-empty timer queue.
    23852289 *
    23862290 * @param   pVM             The cross context VM structure.
    23872291 * @param   pQueue          The queue to run.
    2388  */
    2389 static void tmR3TimerQueueRun(PVM pVM, PTMTIMERQUEUE pQueue)
    2390 {
    2391     VM_ASSERT_EMT(pVM);
     2292 * @param   pNext           The head timer.  Caller already check that this is
     2293 *                          not NULL.
     2294 */
     2295static void tmR3TimerQueueRun(PVM pVM, PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
     2296{
     2297    VM_ASSERT_EMT(pVM); /** @todo relax this */
    23922298
    23932299    /*
     
    23992305     * N.B. A generic unlink must be applied since other threads
    24002306     *      are allowed to mess with any active timer at any time.
     2307     *
    24012308     *      However, we only allow EMT to handle EXPIRED_PENDING
    24022309     *      timers, thus enabling the timer handler function to
    24032310     *      arm the timer again.
    24042311     */
    2405     PTMTIMER pNext = tmTimerQueueGetHead(pQueue, pQueue);
    2406     if (!pNext)
    2407         return;
     2312/** @todo the above 'however' is outdated.   */
    24082313    const uint64_t u64Now = tmClock(pVM, pQueue->enmClock);
    2409     while (pNext && pNext->u64Expire <= u64Now)
    2410     {
    2411         PTMTIMER        pTimer    = pNext;
    2412         pNext = tmTimerGetNext(pQueue, pTimer);
     2314    while (pTimer->u64Expire <= u64Now)
     2315    {
     2316        PTMTIMER const  pNext = tmTimerGetNext(pQueue, pTimer);
    24132317        PPDMCRITSECT    pCritSect = pTimer->pCritSect;
    24142318        if (pCritSect)
     
    24612365        if (pCritSect)
    24622366            PDMCritSectLeave(pCritSect);
     2367
     2368        /* Advance? */
     2369        pTimer = pNext;
     2370        if (!pTimer)
     2371            break;
    24632372    } /* run loop */
     2373}
     2374
     2375
     2376/**
     2377 * Service one regular timer queue.
     2378 *
     2379 * @param   pVM     The cross context VM structure.
     2380 * @param   pQueue  The queue.
     2381 */
     2382static void tmR3TimerQueueDoOne(PVM pVM, PTMTIMERQUEUE pQueue)
     2383{
     2384    Assert(pQueue->enmClock != TMCLOCK_VIRTUAL_SYNC);
     2385
     2386    /*
     2387     * Only one thread should be "doing" the queue.
     2388     */
     2389    if (ASMAtomicCmpXchgBool(&pQueue->fBeingProcessed, true, false))
     2390    {
     2391        STAM_PROFILE_START(&pQueue->StatDo, s);
     2392        PDMCritSectEnter(&pQueue->TimerLock, VERR_IGNORED);
     2393
     2394        if (pQueue->idxSchedule != UINT32_MAX)
     2395            tmTimerQueueSchedule(pVM, pQueue, pQueue);
     2396
     2397        PTMTIMER pHead = tmTimerQueueGetHead(pQueue, pQueue);
     2398        if (pHead)
     2399            tmR3TimerQueueRun(pVM, pQueue, pHead);
     2400
     2401        PDMCritSectLeave(&pQueue->TimerLock);
     2402        STAM_PROFILE_STOP(&pQueue->StatDo, s);
     2403        ASMAtomicWriteBool(&pQueue->fBeingProcessed, false);
     2404    }
    24642405}
    24652406
     
    26112552        }
    26122553
    2613         Log2(("tmR3TimerQueueRun: %p:{.enmState=%s, .enmClock=%d, .enmType=%d, u64Expire=%llx (now=%llx) .szName='%s'}\n",
     2554        Log2(("tmR3TimerQueueRunVirtualSync: %p:{.enmState=%s, .enmClock=%d, .enmType=%d, u64Expire=%llx (now=%llx) .szName='%s'}\n",
    26142555              pTimer, tmTimerState(pTimer->enmState), pQueue->enmClock, pTimer->enmType, pTimer->u64Expire, u64Now, pTimer->szName));
    26152556
     
    26492590            pTimer->uHzHint = 0;
    26502591        }
    2651         Log2(("tmR3TimerQueueRun: new state %s\n", tmTimerState(pTimer->enmState)));
     2592        Log2(("tmR3TimerQueueRunVirtualSync: new state %s\n", tmTimerState(pTimer->enmState)));
    26522593
    26532594        /* Leave the associated lock. */
     
    28082749    else
    28092750    {
     2751/** @todo Optimize for SMP   */
    28102752        STAM_PROFILE_START(&pVM->tm.s.StatVirtualSyncFF, a);
    28112753        PDMCritSectEnter(&pVM->tm.s.VirtualSyncLock, VERR_IGNORED);
     
    28432785    }
    28442786}
     2787
     2788
     2789/**
     2790 * Service the special virtual sync timer queue.
     2791 *
     2792 * @param   pVM     The cross context VM structure.
     2793 * @param   pQueue  The queue.
     2794 */
     2795static void tmR3TimerQueueDoVirtualSync(PVM pVM, PVMCPU pVCpuDst)
     2796{
     2797    PTMTIMERQUEUE pQueue = &pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC];
     2798    if (ASMAtomicCmpXchgBool(&pQueue->fBeingProcessed, true, false))
     2799    {
     2800        STAM_PROFILE_START(&pQueue->StatDo, s1);
     2801        PDMCritSectEnter(&pQueue->TimerLock, VERR_IGNORED);
     2802        PDMCritSectEnter(&pVM->tm.s.VirtualSyncLock, VERR_IGNORED);
     2803        ASMAtomicWriteBool(&pVM->tm.s.fRunningVirtualSyncQueue, true);
     2804        VMCPU_FF_CLEAR(pVCpuDst, VMCPU_FF_TIMER);   /* Clear the FF once we started working for real. */
     2805
     2806        Assert(pQueue->idxSchedule == UINT32_MAX);
     2807        tmR3TimerQueueRunVirtualSync(pVM);
     2808        if (pVM->tm.s.fVirtualSyncTicking) /** @todo move into tmR3TimerQueueRunVirtualSync - FIXME */
     2809            VM_FF_CLEAR(pVM, VM_FF_TM_VIRTUAL_SYNC);
     2810
     2811        ASMAtomicWriteBool(&pVM->tm.s.fRunningVirtualSyncQueue, false);
     2812        PDMCritSectLeave(&pVM->tm.s.VirtualSyncLock);
     2813        PDMCritSectLeave(&pQueue->TimerLock);
     2814        STAM_PROFILE_STOP(&pQueue->StatDo, s1);
     2815        ASMAtomicWriteBool(&pQueue->fBeingProcessed, false);
     2816    }
     2817}
     2818
     2819
     2820/**
     2821 * Schedules and runs any pending timers.
     2822 *
     2823 * This is normally called from a forced action handler in EMT.
     2824 *
     2825 * @param   pVM     The cross context VM structure.
     2826 *
     2827 * @thread  EMT (actually EMT0, but we fend off the others)
     2828 */
     2829VMMR3DECL(void) TMR3TimerQueuesDo(PVM pVM)
     2830{
     2831    /*
     2832     * Only the dedicated timer EMT should do stuff here.
     2833     * (fRunningQueues is only used as an indicator.)
     2834     */
     2835    Assert(pVM->tm.s.idTimerCpu < pVM->cCpus);
     2836    PVMCPU pVCpuDst = pVM->apCpusR3[pVM->tm.s.idTimerCpu];
     2837    if (VMMGetCpu(pVM) != pVCpuDst)
     2838    {
     2839        Assert(pVM->cCpus > 1);
     2840        return;
     2841    }
     2842    STAM_PROFILE_START(&pVM->tm.s.StatDoQueues, a);
     2843    Log2(("TMR3TimerQueuesDo:\n"));
     2844    Assert(!pVM->tm.s.fRunningQueues);
     2845    ASMAtomicWriteBool(&pVM->tm.s.fRunningQueues, true);
     2846
     2847    /*
     2848     * Process the queues.
     2849     */
     2850    AssertCompile(TMCLOCK_MAX == 4);
     2851
     2852    /*
     2853     * TMCLOCK_VIRTUAL_SYNC (see also TMR3VirtualSyncFF)
     2854     */
     2855    tmR3TimerQueueDoVirtualSync(pVM, pVCpuDst);
     2856
     2857    /*
     2858     * TMCLOCK_VIRTUAL
     2859     */
     2860    tmR3TimerQueueDoOne(pVM, &pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL]);
     2861
     2862    /*
     2863     * TMCLOCK_TSC
     2864     */
     2865    Assert(pVM->tm.s.aTimerQueues[TMCLOCK_TSC].idxActive == UINT32_MAX); /* not used */
     2866
     2867    /*
     2868     * TMCLOCK_REAL
     2869     */
     2870    tmR3TimerQueueDoOne(pVM, &pVM->tm.s.aTimerQueues[TMCLOCK_REAL]);
     2871
     2872#ifdef VBOX_STRICT
     2873    /* check that we didn't screw up. */
     2874    tmTimerQueuesSanityChecks(pVM, "TMR3TimerQueuesDo");
     2875#endif
     2876
     2877    /* done */
     2878    Log2(("TMR3TimerQueuesDo: returns void\n"));
     2879    ASMAtomicWriteBool(&pVM->tm.s.fRunningQueues, false);
     2880    STAM_PROFILE_STOP(&pVM->tm.s.StatDoQueues, a);
     2881}
     2882
    28452883
    28462884
  • trunk/src/VBox/VMM/include/TMInternal.h

    r87815 r87816  
    292292    /** The queue name. */
    293293    char                    szName[16];
     294    /** Set when a thread is doing scheduling and callback. */
     295    bool volatile           fBeingProcessed;
    294296    /** Set if we've disabled growing. */
    295297    bool                    fCannotGrow;
    296298    /** Align on 64-byte boundrary. */
    297     bool                    afAlignment1[3];
     299    bool                    afAlignment1[2];
    298300    /** The current max timer Hz hint. */
    299301    uint32_t volatile       uMaxHzHint;
     
    303305    /** Time spent doing scheduling and timer callbacks. */
    304306    STAMPROFILE             StatDo;
    305     uint64_t                u64Alignment2[4];
     307    /** The thread servicing this queue, NIL if none. */
     308    R3PTRTYPE(RTTHREAD)     hThread;
     309    /** The handle to the event semaphore the worker thread sleeps on. */
     310    SUPSEMEVENT             hWorkerEvt;
     311    /** Absolute sleep deadline for the worker (enmClock time). */
     312    uint64_t volatile       tsWorkerWakeup;
     313    uint64_t                u64Alignment2;
    306314
    307315    /** Lock serializing the active timer list and associated work. */
     
    593601
    594602    /** Lock serializing access to the VirtualSync clock and the associated
    595      * timer queue. */
     603     * timer queue.
     604     * @todo Consider merging this with the TMTIMERQUEUE::TimerLock for the
     605     *       virtual sync queue. */
    596606    PDMCRITSECT                 VirtualSyncLock;
    597607
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