VirtualBox

Changeset 53768 in vbox for trunk/src/VBox/Runtime


Ignore:
Timestamp:
Jan 10, 2015 12:07:47 AM (10 years ago)
Author:
vboxsync
Message:

RTMpOnSpecific/r0drv-nt: Sketched out an alternative implementation that doesn't end up flushing all DPCs in the system. Needs testing to figure out whether this is more efficient.

File:
1 edited

Legend:

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

    r53765 r53768  
    3535#include <iprt/err.h>
    3636#include <iprt/asm.h>
     37#include <iprt/log.h>
     38#include <iprt/time.h>
    3739#include "r0drv/mp-r0drv.h"
    3840#include "internal-r0drv-nt.h"
     
    4850    RT_NT_CPUID_ALL
    4951} RT_NT_CPUID;
     52
     53
     54/**
     55 * Used by the RTMpOnSpecific.
     56 */
     57typedef struct RTMPNTONSPECIFICARGS
     58{
     59    /** Set if we're executing. */
     60    bool volatile       fExecuting;
     61    /** Set when done executing. */
     62    bool volatile       fDone;
     63    /** Number of references to this heap block. */
     64    uint32_t volatile   cRefs;
     65    /** Event that the calling thread is waiting on. */
     66    KEVENT              DoneEvt;
     67    /** The deferred procedure call object. */
     68    KDPC                Dpc;
     69    /** The callback argument package. */
     70    RTMPARGS            CallbackArgs;
     71} RTMPNTONSPECIFICARGS;
     72/** Pointer to an argument/state structure for RTMpOnSpecific on NT. */
     73typedef RTMPNTONSPECIFICARGS *PRTMPNTONSPECIFICARGS;
     74
    5075
    5176
     
    428453}
    429454
     455/**
     456 * Releases a reference to a RTMPNTONSPECIFICARGS heap allocation, freeing it
     457 * when the last reference is released.
     458 */
     459DECLINLINE(void) rtMpNtOnSpecificRelease(PRTMPNTONSPECIFICARGS pArgs)
     460{
     461    uint32_t cRefs = ASMAtomicDecU32(&pArgs->cRefs);
     462    AssertMsg(cRefs <= 1, ("cRefs=%#x\n", cRefs));
     463    if (cRefs == 0)
     464        ExFreePool(pArgs);
     465}
     466
     467
     468/**
     469 * Wrapper between the native nt per-cpu callbacks and PFNRTWORKER
     470 *
     471 * @param   Dpc                 DPC object
     472 * @param   DeferredContext     Context argument specified by KeInitializeDpc
     473 * @param   SystemArgument1     Argument specified by KeInsertQueueDpc
     474 * @param   SystemArgument2     Argument specified by KeInsertQueueDpc
     475 */
     476static VOID __stdcall rtMpNtOnSpecificDpcWrapper(IN PKDPC Dpc, IN PVOID DeferredContext,
     477                                                 IN PVOID SystemArgument1, IN PVOID SystemArgument2)
     478{
     479    PRTMPNTONSPECIFICARGS pArgs = (PRTMPNTONSPECIFICARGS)DeferredContext;
     480    ASMAtomicWriteBool(&pArgs->fExecuting, true);
     481
     482    pArgs->CallbackArgs.pfnWorker(KeGetCurrentProcessorNumber(), pArgs->CallbackArgs.pvUser1, pArgs->CallbackArgs.pvUser2);
     483
     484    ASMAtomicWriteBool(&pArgs->fDone, true);
     485    KeSetEvent(&pArgs->DoneEvt, 1 /*PriorityIncrement*/, FALSE /*Wait*/);
     486
     487    rtMpNtOnSpecificRelease(pArgs);
     488}
     489
    430490
    431491RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
    432492{
     493    /*
     494     * Don't try mess with an offline CPU.
     495     */
    433496    if (!RTMpIsCpuOnline(idCpu))
    434497        return !RTMpIsCpuPossible(idCpu)
     
    436499              : VERR_CPU_OFFLINE;
    437500
     501    /*
     502     * Use the broadcast IPI routine if there are no more than two CPUs online,
     503     * or if the current IRQL is unsuitable for KeWaitForSingleObject.
     504     */
    438505    if (   g_pfnrtKeIpiGenericCall
    439         && RTMpGetOnlineCount() <= 2)
     506        && (   RTMpGetOnlineCount() <= 2
     507            || KeGetCurrentIrql()   > APC_LEVEL) )
    440508        return rtMpCallUsingBroadcastIpi(pfnWorker, pvUser1, pvUser2, rtmpNtOnSpecificBroadcastIpiWrapper, 0);
     509
     510#if 0 /** @todo untested code. needs some tuning. */
     511    /*
     512     * Initialize the argument package and the objects within it.
     513     * The package is referenced counted to avoid unnecessary spinning to
     514     * synchronize cleanup and prevent stack corruption.
     515     */
     516    PRTMPNTONSPECIFICARGS pArgs = (PRTMPNTONSPECIFICARGS)ExAllocatePoolWithTag(NonPagedPool, sizeof(*pArgs), (ULONG)'RTMp');
     517    if (!pArgs)
     518        return VERR_NO_MEMORY;
     519    pArgs->cRefs                  = 2;
     520    pArgs->fExecuting             = false;
     521    pArgs->fDone                  = false;
     522    pArgs->CallbackArgs.pfnWorker = pfnWorker;
     523    pArgs->CallbackArgs.pvUser1   = pvUser1;
     524    pArgs->CallbackArgs.pvUser2   = pvUser2;
     525    pArgs->CallbackArgs.idCpu     = idCpu;
     526    pArgs->CallbackArgs.cHits     = 0;
     527    pArgs->CallbackArgs.cRefs     = 2;
     528    KeInitializeEvent(&pArgs->DoneEvt, SynchronizationEvent, FALSE /* not signalled */);
     529    KeInitializeDpc(&pArgs->Dpc, rtMpNtOnSpecificDpcWrapper, pArgs);
     530    KeSetImportanceDpc(&pArgs->Dpc, HighImportance);
     531    KeSetTargetProcessorDpc(&pArgs->Dpc, (int)idCpu);
     532
     533    /*
     534     * Disable preemption while we check the current processor and inserts the DPC.
     535     */
     536    KIRQL bOldIrql;
     537    KeRaiseIrql(DISPATCH_LEVEL, &bOldIrql);
     538    ASMCompilerBarrier(); /* paranoia */
     539
     540    if (RTMpCpuId() == idCpu)
     541    {
     542        /* Just execute the callback on the current CPU. */
     543        pfnWorker(idCpu, pvUser1, pvUser2);
     544        KeLowerIrql(bOldIrql);
     545
     546        ExFreePool(pArgs);
     547        return VINF_SUCCESS;
     548    }
     549
     550    /* Different CPU, so queue it if the CPU is still online. */
     551    int rc;
     552    if (RTMpIsCpuOnline(idCpu))
     553    {
     554        BOOLEAN fRc = KeInsertQueueDpc(&pArgs->Dpc, 0, 0);
     555        Assert(fRc);
     556        KeLowerIrql(bOldIrql);
     557
     558        uint64_t const nsRealWaitTS = RTTimeNanoTS();
     559
     560        /*
     561         * Wait actively for a while in case the CPU/thread responds quickly.
     562         */
     563        uint32_t cLoopsLeft = 0x20000;
     564        while (cLoopsLeft-- > 0)
     565        {
     566            if (pArgs->fDone)
     567            {
     568                rtMpNtOnSpecificRelease(pArgs);
     569                return VINF_SUCCESS;
     570            }
     571            ASMNopPause();
     572        }
     573
     574        /*
     575         * It didn't respond, so wait on the event object, poking the CPU if it's slow.
     576         */
     577        LARGE_INTEGER Timeout;
     578        Timeout.QuadPart = -10000; /* 1ms */
     579        NTSTATUS rcNt = KeWaitForSingleObject(&pArgs->DoneEvt, Executive, KernelMode, FALSE /* Alertable */, &Timeout);
     580        if (rcNt == STATUS_SUCCESS)
     581        {
     582            rtMpNtOnSpecificRelease(pArgs);
     583            return VINF_SUCCESS;
     584        }
     585
     586        /* If it hasn't respondend yet, maybe poke it and wait some more. */
     587        if (rcNt == STATUS_TIMEOUT)
     588        {
     589            if (   !pArgs->fExecuting
     590                && (   g_pfnrtMpPokeCpuWorker == rtMpPokeCpuUsingHalSendSoftwareInterrupt
     591                    || g_pfnrtMpPokeCpuWorker == rtMpPokeCpuUsingHalReqestIpiW7Plus
     592                    || g_pfnrtMpPokeCpuWorker == rtMpPokeCpuUsingHalReqestIpiPreW7))
     593                RTMpPokeCpu(idCpu);
     594
     595            Timeout.QuadPart = -1280000; /* 128ms */
     596            rcNt = KeWaitForSingleObject(&pArgs->DoneEvt, Executive, KernelMode, FALSE /* Alertable */, &Timeout);
     597            if (rcNt == STATUS_SUCCESS)
     598            {
     599                rtMpNtOnSpecificRelease(pArgs);
     600                return VINF_SUCCESS;
     601            }
     602        }
     603
     604        /*
     605         * Something weird is happening, try bail out.
     606         */
     607        if (KeRemoveQueueDpc(&pArgs->Dpc))
     608        {
     609            ExFreePool(pArgs); /* DPC was still queued, so we can return without further ado. */
     610            LogRel(("RTMpOnSpecific(%#x): Not processed after %llu ns: rcNt=%#x\n", idCpu, RTTimeNanoTS() - nsRealWaitTS, rcNt));
     611        }
     612        else
     613        {
     614            /* DPC is running, wait a good while for it to complete. */
     615            LogRel(("RTMpOnSpecific(%#x): Still running after %llu ns: rcNt=%#x\n", idCpu, RTTimeNanoTS() - nsRealWaitTS, rcNt));
     616
     617            Timeout.QuadPart = -30*1000*1000*10; /* 30 seconds */
     618            rcNt = KeWaitForSingleObject(&pArgs->DoneEvt, Executive, KernelMode, FALSE /* Alertable */, &Timeout);
     619            if (rcNt != STATUS_SUCCESS)
     620                LogRel(("RTMpOnSpecific(%#x): Giving up on running worker after %llu ns: rcNt=%#x\n", idCpu, RTTimeNanoTS() - nsRealWaitTS, rcNt));
     621        }
     622        rc = RTErrConvertFromNtStatus(rcNt);
     623    }
     624    else
     625    {
     626        /* CPU is offline.*/
     627        KeLowerIrql(bOldIrql);
     628        rc = !RTMpIsCpuPossible(idCpu) ? VERR_CPU_NOT_FOUND : VERR_CPU_OFFLINE;
     629    }
     630
     631    rtMpNtOnSpecificRelease(pArgs);
     632    return rc;
     633
     634#else
    441635    return rtMpCallUsingDpcs(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_SPECIFIC, idCpu);
     636#endif
    442637}
    443638
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