SUPDrvGip.cpp revision dc45a8f3e936581748c248e00ce572cfe3ea331e
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * VBoxDrv - The VirtualBox Support Driver - Common code for GIP.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Copyright (C) 2006-2015 Oracle Corporation
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * available from http://www.virtualbox.org. This file is free software;
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * you can redistribute it and/or modify it under the terms of the GNU
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * General Public License (GPL) as published by the Free Software
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * The contents of this file may alternatively be used under the terms
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * of the Common Development and Distribution License Version 1.0
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * VirtualBox OSE distribution, in which case the provisions of the
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * CDDL are applicable instead of those of the GPL.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * You may elect to license modified versions of this file under the
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * terms and conditions of either the GPL or the CDDL or both.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/*******************************************************************************
cc66247640b520463f925a5533fc9e5de06aa982vboxsync* Header Files *
cc66247640b520463f925a5533fc9e5de06aa982vboxsync*******************************************************************************/
cc66247640b520463f925a5533fc9e5de06aa982vboxsync#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/*******************************************************************************
cc66247640b520463f925a5533fc9e5de06aa982vboxsync* Defined Constants And Macros *
cc66247640b520463f925a5533fc9e5de06aa982vboxsync*******************************************************************************/
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** The frequency by which we recalculate the u32UpdateHz and
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * u32UpdateIntervalNS GIP members. The value must be a power of 2.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Warning: Bumping this too high might overflow u32UpdateIntervalNS.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** A reserved TSC value used for synchronization as well as measurement of
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * TSC deltas. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** The number of TSC delta measurement loops in total (includes primer and
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * read-time loops). */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** The number of cache primer loops. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** The number of loops until we keep computing the minumum read time. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** Stop measurement of TSC delta. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** Start measurement of TSC delta. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** Worker thread is ready for reading the TSC. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** Worker thread is done updating TSC delta info. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** When IPRT is isn't concurrent safe: Master is ready and will wait for worker
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * with a timeout. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** When IPRT is isn't concurrent safe: Worker is ready after waiting for
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * master with a timeout. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** The TSC-refinement interval in seconds. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_PRACTICALLY_ZERO rating */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_ROUGHLY_ZERO rating */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** The TSC delta value for the initial GIP master - 0 in regular builds.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * To test the delta code this can be set to a non-zero value. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(170139095182512) /* 0x00009abd9854acb0 */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(0)
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS < GIP_TSC_DELTA_READ_TIME_LOOPS);
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS + GIP_TSC_DELTA_READ_TIME_LOOPS < GIP_TSC_DELTA_LOOPS);
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/** @def VBOX_SVN_REV
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * The makefile should define this if it can. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync#if 0 /* Don't start the GIP timers. Useful when debugging the IPRT timer code. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/*******************************************************************************
cc66247640b520463f925a5533fc9e5de06aa982vboxsync* Internal Functions *
cc66247640b520463f925a5533fc9e5de06aa982vboxsync*******************************************************************************/
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncstatic DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncstatic DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncstatic void supdrvGipInitCpu(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS);
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncstatic int supdrvTscDeltaThreadInit(PSUPDRVDEVEXT pDevExt);
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncstatic void supdrvTscDeltaTerm(PSUPDRVDEVEXT pDevExt);
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncstatic int supdrvTscDeltaThreadWaitForOnlineCpus(PSUPDRVDEVEXT pDevExt);
cc66247640b520463f925a5533fc9e5de06aa982vboxsync/*******************************************************************************
cc66247640b520463f925a5533fc9e5de06aa982vboxsync* Global Variables *
cc66247640b520463f925a5533fc9e5de06aa982vboxsync*******************************************************************************/
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncDECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage = NULL;
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Misc Common GIP Code
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Misc Common GIP Code
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Misc Common GIP Code
8429669b672301e12d6ddab8bc9ce0618d930d2evboxsync * Finds the GIP CPU index corresponding to @a idCpu.
8429669b672301e12d6ddab8bc9ce0618d930d2evboxsync * @returns GIP CPU array index, UINT32_MAX if not found.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param pGip The GIP.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param idCpu The CPU ID.
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncstatic uint32_t supdrvGipFindCpuIndexForCpuId(PSUPGLOBALINFOPAGE pGip, RTCPUID idCpu)
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Applies the TSC delta to the supplied raw TSC value.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @returns VBox status code. (Ignored by all users, just FYI.)
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param pGip Pointer to the GIP.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param puTsc Pointer to a valid TSC value before the TSC delta has been applied.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param idApic The APIC ID of the CPU @c puTsc corresponds to.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param fDeltaApplied Where to store whether the TSC delta was succesfully
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * applied or not (optional, can be NULL).
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @remarks Maybe called with interrupts disabled in ring-0!
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @note Don't you dare change the delta calculation. If you really do, make
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * sure you update all places where it's used (IPRT, SUPLibAll.cpp,
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * SUPDrv.c, supdrvGipMpEvent, and more).
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncDECLINLINE(int) supdrvTscDeltaApply(PSUPGLOBALINFOPAGE pGip, uint64_t *puTsc, uint16_t idApic, bool *pfDeltaApplied)
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Validate input.
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync Assert(pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED);
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Carefully convert the idApic into a GIPCPU entry.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync if (RT_LIKELY(idApic < RT_ELEMENTS(pGip->aiCpuFromApicId)))
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Apply the delta if valid.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync AssertMsgFailed(("iCpu=%u cCpus=%u\n", iCpu, pGip->cCpus));
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * GIP Mapping and Unmapping Related Code.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * GIP Mapping and Unmapping Related Code.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * GIP Mapping and Unmapping Related Code.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * (Re-)initializes the per-cpu structure prior to starting or resuming the GIP
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * updating.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param pGip Pointer to the GIP.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param pGipCpu The per CPU structure for this CPU.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param u64NanoTS The current time.
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncstatic void supdrvGipReInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS)
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Here we don't really care about applying the TSC delta. The re-initialization of this
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * value is not relevant especially while (re)starting the GIP as the first few ones will
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * be ignored anyway, see supdrvGipDoUpdateCpu().
cc66247640b520463f925a5533fc9e5de06aa982vboxsync pGipCpu->u64TSC = ASMReadTSC() - pGipCpu->u32UpdateIntervalTSC;
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Set the current TSC and NanoTS value for the CPU.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param pvUser1 Pointer to the ring-0 GIP mapping.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * @param pvUser2 Pointer to the variable holding the current time.
cc66247640b520463f925a5533fc9e5de06aa982vboxsyncstatic DECLCALLBACK(void) supdrvGipReInitCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
cc66247640b520463f925a5533fc9e5de06aa982vboxsync PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser1;
cc66247640b520463f925a5533fc9e5de06aa982vboxsync unsigned iCpu = pGip->aiCpuFromApicId[ASMGetApicId()];
cc66247640b520463f925a5533fc9e5de06aa982vboxsync if (RT_LIKELY(iCpu < pGip->cCpus && pGip->aCPUs[iCpu].idCpu == idCpu))
cc66247640b520463f925a5533fc9e5de06aa982vboxsync supdrvGipReInitCpu(pGip, &pGip->aCPUs[iCpu], *(uint64_t *)pvUser2);
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * State structure for supdrvGipDetectGetGipCpuCallback.
cc66247640b520463f925a5533fc9e5de06aa982vboxsync /** Bitmap of APIC IDs that has been seen (initialized to zero).
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * Used to detect duplicate APIC IDs (paranoia). */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync /** Mask of supported GIP CPU getter methods (SUPGIPGETCPU_XXX) (all bits set
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * initially). The callback clears the methods not detected. */
cc66247640b520463f925a5533fc9e5de06aa982vboxsync /** The first callback detecting any kind of range issues (initialized to
cc66247640b520463f925a5533fc9e5de06aa982vboxsync * NIL_RTCPUID). */
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 DECLCALLBACK(void) supdrvInitAsyncRefineTscTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
bool fDeltaApplied = false;
#if !defined(RT_OS_OS2) /* PORTME: Disable if timers are called from clock interrupt handler or with interrupts disabled. */
ASMNopPause();
&& !fDeltaApplied))
int rc;
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
ASMNopPause();
while (cTriesLeft-- > 0)
ASMNopPause();
ASMNopPause();
ASMNopPause();
int rc;
bool fAppliedBefore;
bool fAppliedAfter;
rc = supdrvTscDeltaApply(pGip, &u64TscBefore, idApicBefore, &fAppliedBefore); AssertRCReturn(rc, rc);
if ( !fAppliedBefore
|| !fAppliedAfter)
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
SUPR0Printf("vboxdrv: supdrvGipInitMeasureTscFreq: timedout waiting for TSC-delta measurements.\n");
pGip->u64CpuHz = ((u64TscAfter - u64TscBefore) * RT_NS_1SEC_64) / (u64NanoTsAfter - u64NanoTsBefore);
return VINF_SUCCESS;
cTries = 0;
bool fRc;
if (fRc)
int iCpuSet = 0;
uint32_t i = 0;
if (iCpuSet >= 0)
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
int iCpuSet;
/* If we are the initiator going offline while measuring the TSC delta, unspin other waiting CPUs! */
if (pGip)
switch (enmEvent)
case RTMPEVENT_ONLINE:
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)
rc = RTMpOnSpecific(RTMpCpuIdFromSetIndex(iCpu), supdrvGipInitDetermineAsyncTscWorker, &CurTsc, NULL);
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(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS)
#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;
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;
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
#ifndef 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)
* timers fire on the CPU they were registered/started on. Darwin timers doesn't
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
# define GIP_TSC_DELTA_METHOD_1
# define GIP_TSC_DELTA_METHOD_2
typedef struct SUPDRVTSCDELTAMETHOD2ENTRY
typedef struct SUPDRVTSCDELTAMETHOD2
typedef struct SUPDRVGIPTSCDELTARGS
} M1;
#ifdef GIP_TSC_DELTA_METHOD_2
bool fLagMaster;
bool fLagWorker;
bool volatile fQuitEarly;
} M2;
ASMNopPause(); \
#ifdef GIP_TSC_DELTA_METHOD_1
static void supdrvTscDeltaMethod1Loop(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC pSync, bool fIsMaster, uint32_t iTry)
unsigned iLoop;
if (fIsMaster)
pGipCpuMaster->u64TSCSample, pGipCpuMaster->idCpu, pGipCpuWorker->idCpu, pArgs->pDevExt->idGipMaster));
if (!fIsMaster)
return VINF_SUCCESS;
#ifdef GIP_TSC_DELTA_METHOD_2
cHits++;
/*SUPR0Printf("quitting early #1: hits=%#x iLoop=%d iBestDelta=%lld\n", cHits, iLoop, iBestDelta);*/
uint32_t const cHitsNeeded = GIP_TSC_DELTA_M2_LOOPS * RT_ELEMENTS(pArgs->M2.pMasterData->aResults) / 4; /* 25% */
pArgs->M2.cHits, cHits, cHitsNeeded, iLoop, iBestDelta);*/
static void supdrvTscDeltaMethod2CollectData(PSUPDRVTSCDELTAMETHOD2 pMyData, uint32_t volatile *piOtherSeqNo, bool fLag)
while (cLeft-- > 0)
pEntry++;
if (fLag)
ASMNopPause();
static void supdrvTscDeltaMethod2Loop(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC pSync, bool fIsMaster, uint32_t iTry)
unsigned iLoop;
if (fIsMaster)
if (fIsMaster)
# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
supdrvTscDeltaMethod2CollectData(pArgs->M2.pMasterData, &pArgs->M2.pWorkerData->iCurSeqNo, pArgs->M2.fLagMaster);
# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
supdrvTscDeltaMethod2CollectData(pArgs->M2.pWorkerData, &pArgs->M2.pMasterData->iCurSeqNo, pArgs->M2.fLagWorker);
return rc;
SUPR0Printf("cHits=%d m=%d w=%d\n", pArgs->M2.cHits, pArgs->pMaster->idApic, pArgs->pWorker->idApic);
static DECLCALLBACK(void) supdrvMeasureTscDeltaCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
if (!RTMpOnAllIsConcurrentSafe())
ASMNopPause();
ASMNopPause();
#ifdef GIP_TSC_DELTA_METHOD_1
unsigned iCpu;
if (fClearDeltas)
int rc;
return VINF_SUCCESS;
&& ASMHasCpuId()
uint32_t i;
if ( i != iGipCpuMaster
&& i != idxWorker
iGipCpuMaster = i;
#ifdef GIP_TSC_DELTA_METHOD_1
#ifdef GIP_TSC_DELTA_METHOD_1
return rc;
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 cMsTotalWait;
int cMsWaited = 0;
return VERR_THREAD_NOT_WAITABLE;
while (cTriesLeft-- > 0)
return VINF_SUCCESS;
return VERR_TIMEOUT;
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;
if (cTries == 0)
return VINF_SUCCESS;
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
return VINF_SUCCESS;
return VINF_SUCCESS;
if (cMsWaitThread == 0)
return rc;
cTries--;
if (cMsWaitRetry)
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;