SUPDrvGip.cpp revision 5cb0813f7ea960345b7bc247add053f4aa5841aa
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * VBoxDrv - The VirtualBox Support Driver - Common code for GIP.
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * Copyright (C) 2006-2015 Oracle Corporation
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * available from http://www.virtualbox.org. This file is free software;
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * you can redistribute it and/or modify it under the terms of the GNU
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * General Public License (GPL) as published by the Free Software
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * The contents of this file may alternatively be used under the terms
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * of the Common Development and Distribution License Version 1.0
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * VirtualBox OSE distribution, in which case the provisions of the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * CDDL are applicable instead of those of the GPL.
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * You may elect to license modified versions of this file under the
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * terms and conditions of either the GPL or the CDDL or both.
eb3a3435bb6f3ad6a38085912929372669c498favboxsync/*******************************************************************************
eb3a3435bb6f3ad6a38085912929372669c498favboxsync* Header Files *
eb3a3435bb6f3ad6a38085912929372669c498favboxsync*******************************************************************************/
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync/*******************************************************************************
4b783d68e6d569ab798901917ace422a4810edf0vboxsync* Defined Constants And Macros *
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync*******************************************************************************/
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync/** The frequency by which we recalculate the u32UpdateHz and
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync * u32UpdateIntervalNS GIP members. The value must be a power of 2.
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync * Warning: Bumping this too high might overflow u32UpdateIntervalNS.
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync/** A reserved TSC value used for synchronization as well as measurement of
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync * TSC deltas. */
4b783d68e6d569ab798901917ace422a4810edf0vboxsync/** The number of TSC delta measurement loops in total (includes primer and
4b783d68e6d569ab798901917ace422a4810edf0vboxsync * read-time loops). */
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync/** The number of cache primer loops. */
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync/** The number of loops until we keep computing the minumum read time. */
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync/** @name Master / worker synchronization values.
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** Stop measurement of TSC delta. */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** Start measurement of TSC delta. */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** Worker thread is ready for reading the TSC. */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** Worker thread is done updating TSC delta info. */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** When IPRT is isn't concurrent safe: Master is ready and will wait for worker
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync * with a timeout. */
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync#define GIP_TSC_DELTA_SYNC_PRESTART_MASTER UINT32_C(4)
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync/** When IPRT is isn't concurrent safe: Worker is ready after waiting for
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync * master with a timeout. */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** The TSC-refinement interval in seconds. */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_PRACTICALLY_ZERO rating */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_ROUGHLY_ZERO rating */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** The TSC delta value for the initial GIP master - 0 in regular builds.
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync * To test the delta code this can be set to a non-zero value. */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(170139095182512) /* 0x00009abd9854acb0 */
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(0)
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS < GIP_TSC_DELTA_READ_TIME_LOOPS);
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS + GIP_TSC_DELTA_READ_TIME_LOOPS < GIP_TSC_DELTA_LOOPS);
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync/** @def VBOX_SVN_REV
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync * The makefile should define this if it can. */
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync#if 0 /* Don't start the GIP timers. Useful when debugging the IPRT timer code. */
4b783d68e6d569ab798901917ace422a4810edf0vboxsync/*******************************************************************************
eb3a3435bb6f3ad6a38085912929372669c498favboxsync* Internal Functions *
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync*******************************************************************************/
eb3a3435bb6f3ad6a38085912929372669c498favboxsyncstatic DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
4b783d68e6d569ab798901917ace422a4810edf0vboxsyncstatic DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
4b783d68e6d569ab798901917ace422a4810edf0vboxsyncstatic void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsyncstatic int supdrvTscDeltaThreadInit(PSUPDRVDEVEXT pDevExt);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsyncstatic void supdrvTscDeltaTerm(PSUPDRVDEVEXT pDevExt);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsyncstatic void supdrvTscDeltaThreadStartMeasurement(PSUPDRVDEVEXT pDevExt);
eb3a3435bb6f3ad6a38085912929372669c498favboxsyncstatic int supdrvMeasureInitialTscDeltas(PSUPDRVDEVEXT pDevExt);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsyncstatic int supdrvMeasureTscDeltaOne(PSUPDRVDEVEXT pDevExt, uint32_t idxWorker);
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync/*******************************************************************************
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync* Global Variables *
29e50fce8a16b6c8955f48b7d1e19cb7aa3694ddvboxsync*******************************************************************************/
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsyncDECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage = NULL;
c72f00ff9f31a65845a8722126d37708b61bb6aevboxsync * Misc Common GIP Code
eb3a3435bb6f3ad6a38085912929372669c498favboxsync * Misc Common GIP Code
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync * Misc Common GIP Code
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync * Finds the GIP CPU index corresponding to @a idCpu.
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync * @returns GIP CPU array index, UINT32_MAX if not found.
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync * @param pGip The GIP.
f13fb2df3059d9304d27e4a594a0401ba9de4c91vboxsync * @param idCpu The CPU ID.
eb3a3435bb6f3ad6a38085912929372669c498favboxsyncstatic uint32_t supdrvGipFindCpuIndexForCpuId(PSUPGLOBALINFOPAGE pGip, RTCPUID idCpu)
typedef struct SUPDRVGIPDETECTGETCPU
static DECLCALLBACK(void) supdrvGipDetectGetGipCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
int iCpuSet;
AssertMsg(idCpu == RTMpCpuId(), ("idCpu=%#x RTMpCpuId()=%#x\n", idCpu, RTMpCpuId())); /* paranoia^3 */
if ( iCpuSet >= 0
#ifdef RT_ARCH_X86
if (ASMHasCpuId())
ASMNopPause();
LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU set index is out of range.\n",
LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU id/index roundtrip problem: %#x\n",
int rc;
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
if (ppGipR3)
#ifndef DO_NOT_START_GIP
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
if (pHCPhysGip)
if (ppGipR3)
#ifdef DEBUG_DARWIN_GIP
OSDBGPRINT(("SUPR0GipMap: returns %d *pHCPhysGip=%lx pGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)pGipR3));
LogFlow(( "SUPR0GipMap: returns %d *pHCPhysGip=%lx pGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)pGipR3));
return rc;
#ifdef DEBUG_DARWIN_GIP
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
#ifndef DO_NOT_START_GIP
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
return rc;
return g_pSUPGlobalInfoPage;
static void supdrvGipInitSetCpuFreq(PSUPGLOBALINFOPAGE pGip, uint64_t nsElapsed, uint64_t cElapsedTscTicks, uint32_t iTick)
static DECLCALLBACK(void) supdrvInitRefineInvariantTscFreqTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
#if !defined(RT_OS_OS2)
ASMNopPause();
int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
/* cTscTicksElapsed = (uTsc - iStopTscDelta) - (pDevExt->uTscStartInvarTscRefine - iStartTscDelta); */
SUPR0Printf("vboxdrv: Failed to refine invariant TSC frequency because deltas are unavailable after %u (%u) seconds\n",
static void supdrvGipInitStartTimerForRefiningInvariantTscFreq(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip)
int rc;
ASMNopPause();
while (cTriesLeft-- > 0)
ASMNopPause();
uint64_t msDelay = ( ((fRough ? 16 : 200) * RT_NS_1MS + nsTimerIncr - 1) / nsTimerIncr * nsTimerIncr - RT_NS_100US )
/ RT_NS_1MS;
ASMNopPause();
ASMNopPause();
bool fDoXCall = false;
else if (fRough)
fDoXCall = true;
int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
return VERR_INVALID_CPU_INDEX;
else if (cTriesLeft > 0)
fDoXCall = true;
if (cTriesLeft > 0)
fDoXCall = true;
if (fDoXCall)
return VINF_SUCCESS;
cTries = 0;
bool fRc;
if (fRc)
int iCpuSet = 0;
uint32_t i = 0;
if (iCpuSet >= 0)
static DECLCALLBACK(void) supdrvGipMpEventOnlineCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
int iCpuSet;
if (pGip)
switch (enmEvent)
case RTMPEVENT_ONLINE:
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
case RTMPEVENT_OFFLINE:
bool fIgnored;
for (i = 0; i < RTCPUSET_MAX_CPUS; i++)
static DECLCALLBACK(void) supdrvGipInitDetermineAsyncTscWorker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
int iCpu;
bool fAsync = false;
while (cLoops-- > 0)
fAsync = true;
if (poffMin)
return fAsync;
bool fInvariantTsc = false;
if (ASMHasCpuId())
fInvariantTsc = true;
return SUPGIPMODE_ASYNC_TSC;
if (fInvariantTsc)
return SUPGIPMODE_INVARIANT_TSC;
return SUPGIPMODE_ASYNC_TSC;
return SUPGIPMODE_ASYNC_TSC;
return SUPGIPMODE_SYNC_TSC;
static void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz)
if (!uCpuHz)
#ifdef DEBUG_DARWIN_GIP
OSDBGPRINT(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d cCpus=%u\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz, cCpus));
LogFlow(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d cCpus=%u\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz, cCpus));
for (i = 0; i < cCpus; i++)
unsigned cCpus;
int rc;
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
SUPR0Printf("VBoxDrv: Too many CPUs (%u) for the GIP (max %u)\n", cCpus, RT_MIN(RTCPUSET_MAX_CPUS, 256));
return VERR_TOO_MANY_CPUS;
rc = RTR0MemObjAllocCont(&pDevExt->GipMemObj, RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[cCpus]), false /*fExecutable*/);
return rc;
if (uMod)
supdrvGipInit(pDevExt, pGip, HCPhysGip, RTTimeSystemNanoTS(), RT_NS_1SEC / u32Interval /*=Hz*/, u32Interval, cCpus);
/* Basically, invariant Windows boxes, should never be detected as async (i.e. TSC-deltas should be 0). */
OSDBGPRINT(("supdrvGipCreate: The TSC-deltas should be normalized by the host OS, but verifying shows it's not!\n"));
return VERR_INTERNAL_ERROR_2;
* RTMpOnAll/supdrvGipInitOnCpu call below).
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
} while (--cTries > 0);
AssertMsg(!pGip->aCPUs[iCpu].i64TSCDelta, ("iCpu=%u %lld mode=%d\n", iCpu, pGip->aCPUs[iCpu].i64TSCDelta, pGip->u32Mode));
return VINF_SUCCESS;
OSDBGPRINT(("supdrvGipCreate: failed create GIP timer at %u ns interval. rc=%Rrc\n", u32Interval, rc));
return rc;
int rc;
#ifdef DEBUG_DARWIN_GIP
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
static void supdrvGipDoUpdateCpu(PSUPDRVDEVEXT pDevExt, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS, uint64_t u64TSC, uint64_t iTick)
unsigned iTSCHistoryHead;
ASMAtomicWriteU32(&pGipCpu->u32UpdateIntervalTSC, u32UpdateIntervalTSC + u32UpdateIntervalTSCSlack);
static void supdrvGipUpdate(PSUPDRVDEVEXT pDevExt, uint64_t u64NanoTS, uint64_t u64TSC, RTCPUID idCpu, uint64_t iTick)
/* this can happen on win32 if we're taking to long and there are more CPUs around. shouldn't happen though. */
if ( pGip->u32Mode != SUPGIPMODE_INVARIANT_TSC /* cuz we're not recalculating the frequency on invariants hosts. */
static DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
* Solaris timers fire on the CPU they were registered/started on.
# define GIP_TSC_DELTA_METHOD_1
# define GIP_TSC_DELTA_METHOD_2
typedef struct SUPDRVTSCDELTAMETHOD2ENTRY
typedef struct SUPDRVTSCDELTAMETHOD2
typedef struct SUPTSCDELTASYNC2
typedef struct SUPDRVGIPTSCDELTARGS
bool volatile fAbortSetup;
bool volatile fTimedOut;
} Verify;
bool fLag;
} M2;
} uMaster;
#ifdef TSCDELTA_VERIFY_WITH_STATS
} Verify;
bool fLag;
} M2;
} uWorker;
# define TSCDELTA_DBG_CHECK_LOOP() \
# define TSCDELTA_DBG_VARS() ((void)0)
# define TSCDELTA_DBG_START_LOOP() ((void)0)
# define TSCDELTA_DBG_CHECK_LOOP() ((void)0)
if (fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_STEADY, GIP_TSC_DELTA_SYNC2_READY)))
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #1 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
ASMNopPause();
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #2 u32Tmp=%#x\n", fIsMaster ? "master" : "worker", u32Tmp));
#if 0 /* This is crazy, I know, but enable this code and the results are markedly better when enabled on the 1.4GHz AMD (debug). */
if (ASMAtomicCmpXchgU32(&pMySync->uSyncVar, GIP_TSC_DELTA_SYNC2_TIMEOUT, GIP_TSC_DELTA_SYNC2_READY))
ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_TIMEOUT, GIP_TSC_DELTA_SYNC2_STEADY);
iSync2Loops++;
if (!fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_STEADY, GIP_TSC_DELTA_SYNC2_READY)))
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #3 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
if (fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO, GIP_TSC_DELTA_SYNC2_STEADY)))
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #4 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #5 u32Tmp=%#x\n", fIsMaster ? "master" : "worker", u32Tmp));
ASMNopPause();
if (!fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO, GIP_TSC_DELTA_SYNC2_STEADY)))
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #6 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
ASMNopPause();
ASMNopPause();
ASMNopPause();
if (RT_LIKELY(supdrvTscDeltaSync2_Before(a_pMySync, a_pOtherSync, true /*fIsMaster*/, a_pfEFlags, a_pArgs))) \
if (RT_LIKELY(supdrvTscDeltaSync2_Before(a_pMySync, a_pOtherSync, false /*fIsMaster*/, a_pfEFlags, a_pArgs))) \
ASMNopPause();
ASMNopPause();
if (RT_LIKELY(ASMAtomicCmpXchgU32(&(a_pOtherSync)->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_GO))) \
if (RT_LIKELY(ASMAtomicCmpXchgU32(&(a_pOtherSync)->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_GO))) \
if (RT_LIKELY(supdrvTscDeltaSync2_After(a_pMySync, a_pOtherSync, false /*fIsMaster*/, a_fEFlags))) \
#ifdef GIP_TSC_DELTA_METHOD_1
static void supdrvTscDeltaMethod1Loop(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync, PSUPTSCDELTASYNC2 pOtherSync,
unsigned iLoop;
if (fIsMaster)
pGipCpuMaster->u64TSCSample, pGipCpuMaster->idCpu, pGipCpuWorker->idCpu, pArgs->pDevExt->idGipMaster));
TSCDELTA_DBG_SYNC_MSG9(("sync/method1loop/%s: #92 iLoop=%u MyState=%#x\n", fIsMaster ? "master" : "worker", iLoop,
if (!fIsMaster)
#ifdef GIP_TSC_DELTA_METHOD_2
# define GIP_TSC_DELTA_M2_PRIMER_LOOPS 0
if (pArgs->uWorker.M2.Data.aResults[idxOther].iSeqOther == pArgs->uMaster.M2.Data.aResults[idxResult].iSeqMine)
cHits++;
static void supdrvTscDeltaMethod2CollectData(PSUPDRVTSCDELTAMETHOD2 pMyData, uint32_t volatile *piOtherSeqNo, bool fLag)
while (cLeft-- > 0)
pEntry++;
if (fLag)
ASMNopPause();
static void supdrvTscDeltaMethod2Loop(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync, PSUPTSCDELTASYNC2 pOtherSync,
unsigned iLoop;
if (fIsMaster)
# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
supdrvTscDeltaMethod2CollectData(&pArgs->uMaster.M2.Data, &pArgs->uWorker.M2.Data.iCurSeqNo, pArgs->uMaster.M2.fLag);
# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
supdrvTscDeltaMethod2CollectData(&pArgs->uWorker.M2.Data, &pArgs->uMaster.M2.Data.iCurSeqNo, pArgs->uWorker.M2.fLag);
uint32_t i;
AssertCompile(RT_ELEMENTS(pArgs->uMaster.Verify.auTscs) == RT_ELEMENTS(pArgs->uWorker.Verify.auTscs));
if (fIsMaster)
ASMNopPause();
ASMNopPause();
#ifdef TSCDELTA_VERIFY_WITH_STATS
uTscWorker = 0;
#ifdef TSCDELTA_VERIFY_WITH_STATS
if (iDiff < 0)
#ifdef TSCDELTA_VERIFY_WITH_STATS
#ifdef TSCDELTA_VERIFY_WITH_STATS
if (iDiff < 0)
#ifdef TSCDELTA_VERIFY_WITH_STATS
ASMNopPause();
ASMNopPause();
return VERR_TIMEOUT;
DECL_NO_INLINE(static, int)
supdrvMeasureTscDeltaCallbackAbortSyncSetup(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync, bool fIsMaster, bool fTimeout)
if (fTimeout)
ASMNopPause();
ASMNopPause();
ASMNopPause();
int rc;
if ( !fIsMaster
iTry = 0;
ASMNopPause();
ASMNopPause();
if (fIsMaster)
if (!ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT)) /* parnaoia */
iTry = 0;
ASMNopPause();
if ( fIsMaster
&& !ASMAtomicCmpXchgU32(&MySync.uSyncVar, GIP_TSC_DELTA_SYNC2_PRESTART_ABORT, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT))
if (!fIsMaster)
if (!ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT)) /* race #1 */
rc = supdrvTscDeltaVerify(pArgs, &MySync, pOtherSync, fIsMaster, GIP_TSC_DELTA_INITIAL_MASTER_VALUE);
if (fIsMaster)
&& (fIsMaster || u32Tmp != GIP_TSC_DELTA_SYNC2_STEADY) /* worker may be late prepping for the next round */ )
TSCDELTA_DBG_SYNC_MSG(("sync/loop/%s: #0 iTry=%u MyState=%#x\n", fIsMaster ? "master" : "worker", iTry, u32Tmp));
#ifdef GIP_TSC_DELTA_METHOD_1
&& (fIsMaster || u32Tmp != GIP_TSC_DELTA_SYNC2_STEADY) /* worker may be late prepping for the next round */ )
if (fIsMaster)
if (fIsMaster)
if (fIsMaster)
iTry = 0;
iTry++;
if ( iTry == 0
ASMNopPause();
if (fIsMaster)
static DECLCALLBACK(void) supdrvMeasureTscDeltaCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
int rc;
int rc2;
return VINF_SUCCESS;
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
return rc;
&& ASMHasCpuId()
&& !ASMIsAmdCpu()
uint32_t i;
if ( i != iGipCpuMaster
&& i != idxWorker
iGipCpuMaster = i;
if (pArgs)
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
return rc;
unsigned iCpu;
if (fClearDeltas)
unsigned iCpu;
unsigned iOddEven;
return VINF_SUCCESS;
SUPR0Printf("supdrvMeasureTscDeltaOne failed. rc=%d CPU[%u].idCpu=%u Master[%u].idCpu=%u\n", rc, iCpu,
SUPR0Printf("One or more CPUs transitioned between online & offline states. I'm confused, retry...\n");
return rc;
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
static int supdrvTscDeltaThreadButchered(PSUPDRVDEVEXT pDevExt, bool fSpinlockHeld, const char *pszFailed, int rcFailed)
if (!fSpinlockHeld)
return rcFailed;
bool fInitialMeasurement = true;
switch (enmState)
cConsecutiveTimeouts = 0;
rc = RTSemEventSignal(pDevExt->hTscDeltaEvent); /* (Safe on windows as long as spinlock isn't IRQ safe.) */
cConsecutiveTimeouts = 0;
if (fInitialMeasurement)
fInitialMeasurement = false;
} while (cTries-- > 0);
unsigned iCpu;
return VINF_SUCCESS;
return supdrvTscDeltaThreadButchered(pDevExt, true /* fSpinlockHeld */, "Invalid state", VERR_INVALID_STATE);
return rc;
int rc;
OSDBGPRINT(("supdrvTscDeltaThreadWait: timed out state transition. enmState=%d enmNewState=%d\n", enmState,
enmNewState));
OSDBGPRINT(("supdrvTscDeltaThreadWait: invalid state transition from %d to %d, expected %d\n", enmCurState,
OSDBGPRINT(("supdrvTscDeltaThreadWait: invalid state transition from %d to %d\n", enmCurState, enmNewState));
return rc;
int rc;
while (--cTriesLeft > 0)
int rc;
rc = RTSpinlockCreate(&pDevExt->hTscDeltaSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "VBoxTscSpnLck");
rc = supdrvTscDeltaThreadWait(pDevExt, kTscDeltaThreadState_Creating, kTscDeltaThreadState_Listening);
return rc;
return rc;
SUPR0DECL(int) SUPR0TscDeltaMeasureBySetIndex(PSUPDRVSESSION pSession, uint32_t iCpuSet, uint32_t fFlags,
int rc;
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
return VERR_WRONG_ORDER;
return VERR_INVALID_FLAGS;
return VINF_SUCCESS;
if (cTries == 0)
if (cMsWaitRetry == 0)
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
return VINF_SUCCESS;
return VINF_SUCCESS;
if (cMsWaitThread == 0)
return rc;
cTries--;
return rc;
int VBOXCALL supdrvIOCtl_TscDeltaMeasure(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPTSCDELTAMEASURE pReq)
return VERR_INVALID_CPU_ID;
return VERR_INVALID_CPU_ID;
fFlags = 0;
cTries);
int rc;
return VERR_WRONG_ORDER;
int iGipCpu;
int iGipCpu;
return rc;