VirtualBox

Ignore:
Timestamp:
Feb 23, 2015 5:34:01 PM (10 years ago)
Author:
vboxsync
Message:

IPRT, HostDriver, VMMR0: MP notifications fixes for TSC-delta measurements, scheduling of notification callback taken care by the API consumer instead of IPRT.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/HostDrivers/Support/SUPDrvGip.cpp

    r54392 r54395  
    109109#define GIP_TSC_DELTA_SYNC_PRESTART_WORKER  5
    110110/** The TSC-refinement interval in seconds. */
    111 #define GIP_TSC_REFINE_PREIOD_IN_SECS       5
     111#define GIP_TSC_REFINE_PERIOD_IN_SECS       5
    112112/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_PRACTICALLY_ZERO rating */
    113113#define GIP_TSC_DELTA_THRESHOLD_PRACTICALLY_ZERO    32
     
    142142static DECLCALLBACK(void)   supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
    143143static void                 supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz);
     144static int                  supdrvMeasureInitialTscDeltas(PSUPDRVDEVEXT pDevExt);
     145static int                  supdrvMeasureTscDeltaOne(PSUPDRVDEVEXT pDevExt, uint32_t idxWorker);
    144146#ifdef SUPDRV_USE_TSC_DELTA_THREAD
    145147static int                  supdrvTscDeltaThreadInit(PSUPDRVDEVEXT pDevExt);
    146148static void                 supdrvTscDeltaTerm(PSUPDRVDEVEXT pDevExt);
    147 static int                  supdrvTscDeltaThreadWaitForOnlineCpus(PSUPDRVDEVEXT pDevExt);
     149static void                 supdrvTscDeltaThreadStartMeasurement(PSUPDRVDEVEXT pDevExt);
    148150#endif
    149151
     
    197199 * @note    Don't you dare change the delta calculation.  If you really do, make
    198200 *          sure you update all places where it's used (IPRT, SUPLibAll.cpp,
    199  *          SUPDrv.c, supdrvGipMpEvent, and more).
     201 *          SUPDrv.c, supdrvGipMpEvent(), and more).
    200202 */
    201203DECLINLINE(int) supdrvTscDeltaApply(PSUPGLOBALINFOPAGE pGip, uint64_t *puTsc, uint16_t idApic, bool *pfDeltaApplied)
     
    769771
    770772/**
    771  * Used by supdrvInitRefineInvariantTscFreqTimer and supdrvGipInitMeasureTscFreq
    772  * to update the TSC frequency related GIP variables.
     773 * Used by supdrvInitRefineInvariantTscFreqTimer() and
     774 * supdrvGipInitMeasureTscFreq() to update the TSC frequency related GIP
     775 * variables.
    773776 *
    774777 * @param   pGip                The GIP.
     
    807810 *
    808811 * This is started during driver init and fires once
    809  * GIP_TSC_REFINE_PREIOD_IN_SECS seconds later.
     812 * GIP_TSC_REFINE_PERIOD_IN_SECS seconds later.
    810813 *
    811814 * @param   pTimer      The timer.
     
    834837     *         an interrupt handler with higher priority than the clock
    835838     *         interrupt, or spinning for ages in timer handlers is frowned
    836      *         upon, this look must be disabled!
     839     *         upon, this code must be disabled!
    837840     *
    838841     * Darwin, FreeBSD, Linux, Solaris, Windows 8.1+:
     
    863866    /*
    864867     * If the above measurement was taken on a different CPU than the one we
    865      * started the rprocess on, cTscTicksElapsed will need to be adjusted with
     868     * started the process on, cTscTicksElapsed will need to be adjusted with
    866869     * the TSC deltas of both the CPUs.
    867870     *
     
    896899         * calculations.
    897900         */
    898         else if (cNsElapsed <= GIP_TSC_REFINE_PREIOD_IN_SECS * 5 * RT_NS_1SEC_64)
     901        else if (cNsElapsed <= GIP_TSC_REFINE_PERIOD_IN_SECS * 5 * RT_NS_1SEC_64)
    899902        {
    900903            int rc = RTTimerStart(pTimer, RT_NS_1SEC);
     
    905908        {
    906909            SUPR0Printf("vboxdrv: Failed to refine invariant TSC frequency because deltas are unavailable after %u (%u) seconds\n",
    907                         (uint32_t)(cNsElapsed / RT_NS_1SEC), GIP_TSC_REFINE_PREIOD_IN_SECS);
     910                        (uint32_t)(cNsElapsed / RT_NS_1SEC), GIP_TSC_REFINE_PERIOD_IN_SECS);
    908911            SUPR0Printf("vboxdrv: start: %u, %u, %#llx  stop: %u, %u, %#llx\n",
    909912                        iStartCpuSet, iStartGipCpu, iStartTscDelta, iStopCpuSet, iStopGipCpu, iStopTscDelta);
     
    927930         * Reschedule the timer if we haven't yet reached the defined refinement period.
    928931         */
    929         if (cNsElapsed < GIP_TSC_REFINE_PREIOD_IN_SECS * RT_NS_1SEC_64)
     932        if (cNsElapsed < GIP_TSC_REFINE_PERIOD_IN_SECS * RT_NS_1SEC_64)
    930933        {
    931934            int rc = RTTimerStart(pTimer, RT_NS_1SEC);
     
    980983     * first VMs before we're done.  On most systems we will be loading the
    981984     * support driver during boot and VMs won't be started for a while yet,
    982      * it is really only a problem during development (especiall with
     985     * it is really only a problem during development (especially with
    983986     * on-demand driver starting on windows).
    984987     *
    985      * To avoid wasting time doing a long supdrvGipInitMeasureTscFreq call
    986      * to calculate the frequencey during driver loading, the timer is set
     988     * To avoid wasting time doing a long supdrvGipInitMeasureTscFreq() call
     989     * to calculate the frequency during driver loading, the timer is set
    987990     * to fire after 200 ms the first time. It will then reschedule itself
    988      * to fire every second until GIP_TSC_REFINE_PREIOD_IN_SECS has been
     991     * to fire every second until GIP_TSC_REFINE_PERIOD_IN_SECS has been
    989992     * reached or it notices that there is a user land client with GIP
    990993     * mapped (we want a stable frequency for all VMs).
     
    12921295
    12931296    AssertPtrReturnVoid(pGip);
     1297    Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
    12941298    AssertRelease(idCpu == RTMpCpuId());
    12951299    Assert(pGip->cPossibleCpus == RTMpGetCount());
     
    13331337    ASMAtomicWriteU16(&pGip->aiCpuFromCpuSetIdx[iCpuSet], i);
    13341338
     1339    /* Add this CPU to this set of CPUs we need to calculate the TSC-delta for. */
     1340    RTCpuSetAddByIndex(&pDevExt->TscDeltaCpuSet, RTMpCpuIdToSetIndex(idCpu));
     1341
    13351342    /* Update the Mp online/offline counter. */
    13361343    ASMAtomicIncU32(&pDevExt->cMpOnOffEvents);
    13371344
    1338     /* Add this CPU to the set of CPUs for which we need to calculate their TSC-deltas. */
    1339     if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
    1340     {
    1341         RTCpuSetAddByIndex(&pDevExt->TscDeltaCpuSet, iCpuSet);
    1342 #ifdef SUPDRV_USE_TSC_DELTA_THREAD
    1343         RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
    1344         if (   pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Listening
    1345             || pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Measuring)
    1346         {
    1347             pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_WaitAndMeasure;
    1348         }
    1349         RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
    1350 #endif
    1351     }
    1352 
    1353     /* commit it */
     1345    /* Commit it. */
    13541346    ASMAtomicWriteSize(&pGip->aCPUs[i].enmState, SUPGIPCPUSTATE_ONLINE);
    13551347
    13561348    RTSpinlockRelease(pDevExt->hGipSpinlock);
     1349}
     1350
     1351
     1352/**
     1353 * RTMpOnSpecific callback wrapper for supdrvGipMpEventOnlineOrInitOnCpu().
     1354 *
     1355 * @param   idCpu     The CPU ID we are running on.
     1356 * @param   pvUser1    Opaque pointer to the device instance data.
     1357 * @param   pvUser2    Not used.
     1358 */
     1359static DECLCALLBACK(void) supdrvGipMpEventOnlineCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
     1360{
     1361    PSUPDRVDEVEXT pDevExt = (PSUPDRVDEVEXT)pvUser1;
     1362    NOREF(pvUser2);
     1363    supdrvGipMpEventOnlineOrInitOnCpu(pDevExt, idCpu);
    13571364}
    13581365
     
    13961403    }
    13971404
    1398     /* commit it */
     1405    /* Commit it. */
    13991406    ASMAtomicWriteSize(&pGip->aCPUs[i].enmState, SUPGIPCPUSTATE_OFFLINE);
    14001407
     
    14121419 * @param   idCpu       The cpu it applies to.
    14131420 * @param   pvUser      Pointer to the device extension.
    1414  *
    1415  * @remarks This function -must- fire on the newly online'd CPU for the
    1416  *          RTMPEVENT_ONLINE case and can fire on any CPU for the
    1417  *          RTMPEVENT_OFFLINE case.
    14181421 */
    14191422static DECLCALLBACK(void) supdrvGipMpEvent(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser)
     
    14221425    PSUPGLOBALINFOPAGE  pGip    = pDevExt->pGip;
    14231426
    1424     AssertRelease(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
    1425 
    1426     /*
    1427      * Update the GIP CPU data.
    1428      */
    14291427    if (pGip)
    14301428    {
     1429        RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
    14311430        switch (enmEvent)
    14321431        {
    14331432            case RTMPEVENT_ONLINE:
    1434                 AssertRelease(idCpu == RTMpCpuId());
    1435                 supdrvGipMpEventOnlineOrInitOnCpu(pDevExt, idCpu);
     1433            {
     1434                RTThreadPreemptDisable(&PreemptState);
     1435                if (idCpu == RTMpCpuId())
     1436                {
     1437                    supdrvGipMpEventOnlineOrInitOnCpu(pDevExt, idCpu);
     1438                    RTThreadPreemptRestore(&PreemptState);
     1439                }
     1440                else
     1441                {
     1442                    RTThreadPreemptRestore(&PreemptState);
     1443                    RTMpOnSpecific(idCpu, supdrvGipMpEventOnlineCallback, pDevExt, NULL /* pvUser2 */);
     1444                }
     1445
     1446                /*
     1447                 * Recompute TSC-delta for the newly online'd CPU.
     1448                 */
     1449                if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
     1450                {
     1451#ifdef SUPDRV_USE_TSC_DELTA_THREAD
     1452                    supdrvTscDeltaThreadStartMeasurement(pDevExt);
     1453#else
     1454                    uint32_t iCpu = supdrvGipFindOrAllocCpuIndexForCpuId(pGip, idCpu);
     1455                    supdrvMeasureTscDeltaOne(pDevExt, iCpu);
     1456#endif
     1457                }
    14361458                break;
     1459            }
     1460
    14371461            case RTMPEVENT_OFFLINE:
    14381462                supdrvGipMpEventOffline(pDevExt, idCpu);
     
    16891713    ASMAtomicWriteU16(&pCpu->idApic,    UINT16_MAX);
    16901714
    1691     /* 
     1715    /*
    16921716     * The first time we're called, we don't have a CPU frequency handy,
    16931717     * so pretend it's a 4 GHz CPU.  On CPUs that are online, we'll get
     
    18731897     * If we're in any of the other two modes, neither which require MP init,
    18741898     * notifications or deltas for the job, do the full measurement now so
    1875      * that supdrvGipInitOnCpu can populate the TSC interval and history
     1899     * that supdrvGipInitOnCpu() can populate the TSC interval and history
    18761900     * array with more reasonable values.
    18771901     */
    18781902    if (pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC)
    18791903    {
    1880         rc = supdrvGipInitMeasureTscFreq(pDevExt, pGip, true /*fRough*/); /* cannot fail */
     1904        rc = supdrvGipInitMeasureTscFreq(pDevExt, pGip, true /* fRough */); /* cannot fail */
    18811905        supdrvGipInitStartTimerForRefiningInvariantTscFreq(pDevExt, pGip);
    18821906    }
    18831907    else
    1884         rc = supdrvGipInitMeasureTscFreq(pDevExt, pGip, false /*fRough*/);
     1908        rc = supdrvGipInitMeasureTscFreq(pDevExt, pGip, false /* fRough */);
    18851909    if (RT_SUCCESS(rc))
    18861910    {
     
    19101934                {
    19111935#ifdef SUPDRV_USE_TSC_DELTA_THREAD
    1912                     if (pDevExt->hTscDeltaThread != NIL_RTTHREAD)
    1913                         RTThreadUserSignal(pDevExt->hTscDeltaThread);
     1936                    supdrvTscDeltaThreadStartMeasurement(pDevExt);
    19141937#else
    19151938                    uint16_t iCpu;
     
    24322455
    24332456    ASMSetFlags(uFlags);
    2434 
    2435 #ifdef SUPDRV_USE_TSC_DELTA_THREAD
    2436     if (   pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED
    2437         && !RTCpuSetIsEmpty(&pDevExt->TscDeltaCpuSet))
    2438     {
    2439         RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
    2440         if (   pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Listening
    2441             || pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Measuring)
    2442             pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_WaitAndMeasure;
    2443         RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
    2444         /** @todo Do the actual poking using -- RTThreadUserSignal() */
    2445     }
    2446 #endif
    24472457}
    24482458
     
    25772587
    25782588/**
    2579  * Argument package/state passed by supdrvMeasureTscDeltaOne to the RTMpOn
     2589 * Argument package/state passed by supdrvMeasureTscDeltaOne() to the RTMpOn
    25802590 * callback worker.
    25812591 */
     
    31573167 * The idea here is that we have the two CPUs execute the exact same code
    31583168 * collecting a largish set of TSC samples.  The code has one data dependency on
    3159  * the other CPU which intention it is to synchronize the execution as well as
    3160  * help cross references the two sets of TSC samples (the sequence numbers).
     3169 * the other CPU with the intention to synchronize the execution as well
     3170 * as help cross references the two sets of TSC samples (the sequence numbers).
    31613171 *
    31623172 * The @a fLag parameter is used to modify the execution a tiny bit on one or
     
    38113821         */
    38123822        PSUPDRVGIPTSCDELTARGS pArgs = (PSUPDRVGIPTSCDELTARGS)RTMemAllocZ(sizeof(*pArgs));
    3813         if (pArgs)
     3823        if (RT_LIKELY(pArgs))
    38143824        {
    38153825            pArgs->pWorker      = pGipCpuWorker;
     
    41064116                RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
    41074117                pDevExt->cMsTscDeltaTimeout = 1;
    4108                 RTThreadSleep(10);
     4118                RTThreadSleep(1);
    41094119                /* fall thru */
    41104120            }
     
    42564266
    42574267/**
    4258  * Waits for TSC-delta measurements to be completed for all online CPUs.
    4259  *
    4260  * @returns VBox status code.
    4261  * @param   pDevExt         Pointer to the device instance data.
    4262  */
    4263 static int supdrvTscDeltaThreadWaitForOnlineCpus(PSUPDRVDEVEXT pDevExt)
    4264 {
    4265     int cTriesLeft = 5;
    4266     int cMsTotalWait;
    4267     int cMsWaited = 0;
    4268     int cMsWaitGranularity = 1;
    4269 
    4270     PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
    4271     AssertReturn(pGip, VERR_INVALID_POINTER);
    4272 
    4273     if (RT_UNLIKELY(pDevExt->hTscDeltaThread == NIL_RTTHREAD))
    4274         return VERR_THREAD_NOT_WAITABLE;
    4275 
    4276     cMsTotalWait = RT_MIN(pGip->cPresentCpus + 10, 200);
    4277     while (cTriesLeft-- > 0)
    4278     {
    4279         if (RTCpuSetIsEqual(&pDevExt->TscDeltaObtainedCpuSet, &pGip->OnlineCpuSet))
    4280             return VINF_SUCCESS;
    4281         RTThreadSleep(cMsWaitGranularity);
    4282         cMsWaited += cMsWaitGranularity;
    4283         if (cMsWaited >= cMsTotalWait)
    4284             break;
    4285     }
    4286 
    4287     return VERR_TIMEOUT;
     4268 * Signals the TSC-delta thread to start measuring TSC-deltas.
     4269 *
     4270 * @param   pDevExt     Pointer to the device instance data.
     4271 */
     4272static void supdrvTscDeltaThreadStartMeasurement(PSUPDRVDEVEXT pDevExt)
     4273{
     4274    if (RT_LIKELY(pDevExt->hTscDeltaThread != NIL_RTTHREAD))
     4275    {
     4276        RTSpinlockAcquire(pDevExt->hTscDeltaSpinlock);
     4277        if (   pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Listening
     4278            || pDevExt->enmTscDeltaThreadState == kTscDeltaThreadState_Measuring)
     4279        {
     4280            pDevExt->enmTscDeltaThreadState = kTscDeltaThreadState_WaitAndMeasure;
     4281        }
     4282        RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
     4283        RTThreadUserSignal(pDevExt->hTscDeltaThread);
     4284    }
    42884285}
    42894286
     
    44574454        return VERR_INVALID_FLAGS;
    44584455
     4456    /*
     4457     * The request is a noop if the TSC delta isn't being used.
     4458     */
     4459    if (pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ZERO_CLAIMED)
     4460        return VINF_SUCCESS;
     4461
    44594462    if (cTries == 0)
    44604463        cTries = 12;
     
    44664469    else if (cMsWaitRetry > 1000)
    44674470        cMsWaitRetry = 1000;
    4468 
    4469     /*
    4470      * The request is a noop if the TSC delta isn't being used.
    4471      */
    4472     if (pGip->enmUseTscDelta <= SUPGIPUSETSCDELTA_ZERO_CLAIMED)
    4473         return VINF_SUCCESS;
    44744471
    44754472#ifdef SUPDRV_USE_TSC_DELTA_THREAD
     
    44974494            rc = VINF_SUCCESS;
    44984495        }
    4499         else
     4496        else if (pDevExt->enmTscDeltaThreadState != kTscDeltaThreadState_WaitAndMeasure)
    45004497            rc = VERR_THREAD_IS_DEAD;
    45014498        RTSpinlockRelease(pDevExt->hTscDeltaSpinlock);
     
    46134610
    46144611    return SUPR0TscDeltaMeasureBySetIndex(pSession, iCpuSet, fFlags, cMsWaitRetry,
    4615                                           cTries == 0 ? 5*RT_MS_1SEC : cMsWaitRetry * cTries /*cMsWaitThread*/,
     4612                                          cTries == 0 ? 5 * RT_MS_1SEC : cMsWaitRetry * cTries /*cMsWaitThread*/,
    46164613                                          cTries);
    46174614}
     
    46914688                rc = supdrvMeasureTscDeltaOne(pDevExt, iGipCpu);
    46924689                Assert(pGip->aCPUs[iGipCpu].i64TSCDelta != INT64_MAX || RT_FAILURE_NP(rc));
    4693                 /** @todo should probably delay on failure... dpc watchdogs   */
     4690                /** @todo should probably delay on failure... dpc watchdogs */
    46944691            }
    46954692            else
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