[23] | 1 | /* $Id: TMAll.cpp 101088 2023-09-12 10:22:20Z vboxsync $ */
|
---|
[1] | 2 | /** @file
|
---|
| 3 | * TM - Timeout Manager, all contexts.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[1] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[1] | 26 | */
|
---|
| 27 |
|
---|
| 28 |
|
---|
[57358] | 29 | /*********************************************************************************************************************************
|
---|
| 30 | * Header Files *
|
---|
| 31 | *********************************************************************************************************************************/
|
---|
[1] | 32 | #define LOG_GROUP LOG_GROUP_TM
|
---|
[72685] | 33 | #ifdef DEBUG_bird
|
---|
| 34 | # define DBGFTRACE_DISABLED /* annoying */
|
---|
| 35 | #endif
|
---|
[35346] | 36 | #include <VBox/vmm/tm.h>
|
---|
| 37 | #include <VBox/vmm/mm.h>
|
---|
[37517] | 38 | #include <VBox/vmm/dbgftrace.h>
|
---|
[1] | 39 | #ifdef IN_RING3
|
---|
| 40 | #endif
|
---|
[80531] | 41 | #include <VBox/vmm/pdmdev.h> /* (for TMTIMER_GET_CRITSECT implementation) */
|
---|
[1] | 42 | #include "TMInternal.h"
|
---|
[80268] | 43 | #include <VBox/vmm/vmcc.h>
|
---|
[1] | 44 |
|
---|
| 45 | #include <VBox/param.h>
|
---|
| 46 | #include <VBox/err.h>
|
---|
| 47 | #include <VBox/log.h>
|
---|
| 48 | #include <VBox/sup.h>
|
---|
| 49 | #include <iprt/time.h>
|
---|
| 50 | #include <iprt/assert.h>
|
---|
| 51 | #include <iprt/asm.h>
|
---|
[29250] | 52 | #include <iprt/asm-math.h>
|
---|
[92712] | 53 | #include <iprt/string.h>
|
---|
[1] | 54 | #ifdef IN_RING3
|
---|
| 55 | # include <iprt/thread.h>
|
---|
| 56 | #endif
|
---|
| 57 |
|
---|
[37517] | 58 | #include "TMInline.h"
|
---|
[1] | 59 |
|
---|
[37517] | 60 |
|
---|
[57358] | 61 | /*********************************************************************************************************************************
|
---|
| 62 | * Defined Constants And Macros *
|
---|
| 63 | *********************************************************************************************************************************/
|
---|
[80531] | 64 | #ifdef VBOX_STRICT
|
---|
| 65 | /** @def TMTIMER_GET_CRITSECT
|
---|
| 66 | * Helper for safely resolving the critical section for a timer belonging to a
|
---|
| 67 | * device instance.
|
---|
| 68 | * @todo needs reworking later as it uses PDMDEVINSR0::pDevInsR0RemoveMe. */
|
---|
| 69 | # ifdef IN_RING3
|
---|
[87771] | 70 | # define TMTIMER_GET_CRITSECT(a_pVM, a_pTimer) ((a_pTimer)->pCritSect)
|
---|
[80531] | 71 | # else
|
---|
[87771] | 72 | # define TMTIMER_GET_CRITSECT(a_pVM, a_pTimer) tmRZTimerGetCritSect(a_pVM, a_pTimer)
|
---|
[80531] | 73 | # endif
|
---|
| 74 | #endif
|
---|
| 75 |
|
---|
[20089] | 76 | /** @def TMTIMER_ASSERT_CRITSECT
|
---|
| 77 | * Checks that the caller owns the critical section if one is associated with
|
---|
| 78 | * the timer. */
|
---|
| 79 | #ifdef VBOX_STRICT
|
---|
[87771] | 80 | # define TMTIMER_ASSERT_CRITSECT(a_pVM, a_pTimer) \
|
---|
[20089] | 81 | do { \
|
---|
[87771] | 82 | if ((a_pTimer)->pCritSect) \
|
---|
[20089] | 83 | { \
|
---|
[37324] | 84 | VMSTATE enmState; \
|
---|
[87771] | 85 | PPDMCRITSECT pCritSect = TMTIMER_GET_CRITSECT(a_pVM, a_pTimer); \
|
---|
[37324] | 86 | AssertMsg( pCritSect \
|
---|
[90346] | 87 | && ( PDMCritSectIsOwner((a_pVM), pCritSect) \
|
---|
[87771] | 88 | || (enmState = (a_pVM)->enmVMState) == VMSTATE_CREATING \
|
---|
[37324] | 89 | || enmState == VMSTATE_RESETTING \
|
---|
| 90 | || enmState == VMSTATE_RESETTING_LS ),\
|
---|
[87773] | 91 | ("pTimer=%p (%s) pCritSect=%p (%s)\n", a_pTimer, (a_pTimer)->szName, \
|
---|
[87771] | 92 | (a_pTimer)->pCritSect, R3STRING(PDMR3CritSectName((a_pTimer)->pCritSect)) )); \
|
---|
[20089] | 93 | } \
|
---|
| 94 | } while (0)
|
---|
| 95 | #else
|
---|
[87771] | 96 | # define TMTIMER_ASSERT_CRITSECT(pVM, pTimer) do { } while (0)
|
---|
[20089] | 97 | #endif
|
---|
| 98 |
|
---|
[50385] | 99 | /** @def TMTIMER_ASSERT_SYNC_CRITSECT_ORDER
|
---|
| 100 | * Checks for lock order trouble between the timer critsect and the critical
|
---|
| 101 | * section critsect. The virtual sync critsect must always be entered before
|
---|
| 102 | * the one associated with the timer (see TMR3TimerQueuesDo). It is OK if there
|
---|
| 103 | * isn't any critical section associated with the timer or if the calling thread
|
---|
| 104 | * doesn't own it, ASSUMING of course that the thread using this macro is going
|
---|
| 105 | * to enter the virtual sync critical section anyway.
|
---|
| 106 | *
|
---|
| 107 | * @remarks This is a sligtly relaxed timer locking attitude compared to
|
---|
| 108 | * TMTIMER_ASSERT_CRITSECT, however, the calling device/whatever code
|
---|
| 109 | * should know what it's doing if it's stopping or starting a timer
|
---|
| 110 | * without taking the device lock.
|
---|
| 111 | */
|
---|
| 112 | #ifdef VBOX_STRICT
|
---|
| 113 | # define TMTIMER_ASSERT_SYNC_CRITSECT_ORDER(pVM, pTimer) \
|
---|
| 114 | do { \
|
---|
| 115 | if ((pTimer)->pCritSect) \
|
---|
| 116 | { \
|
---|
| 117 | VMSTATE enmState; \
|
---|
[87771] | 118 | PPDMCRITSECT pCritSect = TMTIMER_GET_CRITSECT(pVM, pTimer); \
|
---|
[50385] | 119 | AssertMsg( pCritSect \
|
---|
[90346] | 120 | && ( !PDMCritSectIsOwner((pVM), pCritSect) \
|
---|
| 121 | || PDMCritSectIsOwner((pVM), &(pVM)->tm.s.VirtualSyncLock) \
|
---|
[50385] | 122 | || (enmState = (pVM)->enmVMState) == VMSTATE_CREATING \
|
---|
| 123 | || enmState == VMSTATE_RESETTING \
|
---|
| 124 | || enmState == VMSTATE_RESETTING_LS ),\
|
---|
[87773] | 125 | ("pTimer=%p (%s) pCritSect=%p (%s)\n", pTimer, pTimer->szName, \
|
---|
[50385] | 126 | (pTimer)->pCritSect, R3STRING(PDMR3CritSectName((pTimer)->pCritSect)) )); \
|
---|
| 127 | } \
|
---|
| 128 | } while (0)
|
---|
| 129 | #else
|
---|
[50387] | 130 | # define TMTIMER_ASSERT_SYNC_CRITSECT_ORDER(pVM, pTimer) do { } while (0)
|
---|
[50385] | 131 | #endif
|
---|
[20089] | 132 |
|
---|
[80550] | 133 |
|
---|
[80549] | 134 | #if defined(VBOX_STRICT) && defined(IN_RING0)
|
---|
| 135 | /**
|
---|
| 136 | * Helper for TMTIMER_GET_CRITSECT
|
---|
| 137 | * @todo This needs a redo!
|
---|
| 138 | */
|
---|
[87771] | 139 | DECLINLINE(PPDMCRITSECT) tmRZTimerGetCritSect(PVMCC pVM, PTMTIMER pTimer)
|
---|
[80549] | 140 | {
|
---|
| 141 | if (pTimer->enmType == TMTIMERTYPE_DEV)
|
---|
| 142 | {
|
---|
[93620] | 143 | RTCCUINTREG fSavedFlags = ASMAddFlags(X86_EFL_AC); /** @todo fix ring-3 pointer use */
|
---|
| 144 | PPDMDEVINSR0 pDevInsR0 = ((struct PDMDEVINSR3 *)pTimer->u.Dev.pDevIns)->pDevInsR0RemoveMe; /* !ring-3 read! */
|
---|
[82591] | 145 | ASMSetFlags(fSavedFlags);
|
---|
[80549] | 146 | struct PDMDEVINSR3 *pDevInsR3 = pDevInsR0->pDevInsForR3R0;
|
---|
| 147 | if (pTimer->pCritSect == pDevInsR3->pCritSectRoR3)
|
---|
| 148 | return pDevInsR0->pCritSectRoR0;
|
---|
| 149 | uintptr_t offCritSect = (uintptr_t)pTimer->pCritSect - (uintptr_t)pDevInsR3->pvInstanceDataR3;
|
---|
| 150 | if (offCritSect < pDevInsR0->pReg->cbInstanceShared)
|
---|
| 151 | return (PPDMCRITSECT)((uintptr_t)pDevInsR0->pvInstanceDataR0 + offCritSect);
|
---|
| 152 | }
|
---|
[93717] | 153 | RT_NOREF(pVM);
|
---|
| 154 | Assert(pTimer->pCritSect == NULL);
|
---|
| 155 | return NULL;
|
---|
[80549] | 156 | }
|
---|
[80550] | 157 | #endif /* VBOX_STRICT && IN_RING0 */
|
---|
[50385] | 158 |
|
---|
[80549] | 159 |
|
---|
[1] | 160 | /**
|
---|
[12549] | 161 | * Notification that execution is about to start.
|
---|
| 162 | *
|
---|
| 163 | * This call must always be paired with a TMNotifyEndOfExecution call.
|
---|
| 164 | *
|
---|
| 165 | * The function may, depending on the configuration, resume the TSC and future
|
---|
| 166 | * clocks that only ticks when we're executing guest code.
|
---|
| 167 | *
|
---|
[80268] | 168 | * @param pVM The cross context VM structure.
|
---|
[58123] | 169 | * @param pVCpu The cross context virtual CPU structure.
|
---|
[12549] | 170 | */
|
---|
[80268] | 171 | VMMDECL(void) TMNotifyStartOfExecution(PVMCC pVM, PVMCPUCC pVCpu)
|
---|
[12549] | 172 | {
|
---|
[30581] | 173 | #ifndef VBOX_WITHOUT_NS_ACCOUNTING
|
---|
[87748] | 174 | pVCpu->tm.s.uTscStartExecuting = SUPReadTsc();
|
---|
| 175 | pVCpu->tm.s.fExecuting = true;
|
---|
[30581] | 176 | #endif
|
---|
[12549] | 177 | if (pVM->tm.s.fTSCTiedToExecution)
|
---|
[19032] | 178 | tmCpuTickResume(pVM, pVCpu);
|
---|
[12549] | 179 | }
|
---|
| 180 |
|
---|
| 181 |
|
---|
| 182 | /**
|
---|
[48080] | 183 | * Notification that execution has ended.
|
---|
[12549] | 184 | *
|
---|
| 185 | * This call must always be paired with a TMNotifyStartOfExecution call.
|
---|
| 186 | *
|
---|
| 187 | * The function may, depending on the configuration, suspend the TSC and future
|
---|
| 188 | * clocks that only ticks when we're executing guest code.
|
---|
| 189 | *
|
---|
[80268] | 190 | * @param pVM The cross context VM structure.
|
---|
[58123] | 191 | * @param pVCpu The cross context virtual CPU structure.
|
---|
[87751] | 192 | * @param uTsc TSC value when exiting guest context.
|
---|
[12549] | 193 | */
|
---|
[87751] | 194 | VMMDECL(void) TMNotifyEndOfExecution(PVMCC pVM, PVMCPUCC pVCpu, uint64_t uTsc)
|
---|
[12549] | 195 | {
|
---|
| 196 | if (pVM->tm.s.fTSCTiedToExecution)
|
---|
[87751] | 197 | tmCpuTickPause(pVCpu); /** @todo use uTsc here if we can. */
|
---|
[30581] | 198 |
|
---|
| 199 | #ifndef VBOX_WITHOUT_NS_ACCOUNTING
|
---|
[87748] | 200 | /*
|
---|
| 201 | * Calculate the elapsed tick count and convert it to nanoseconds.
|
---|
| 202 | */
|
---|
| 203 | # ifdef IN_RING3
|
---|
[92709] | 204 | PSUPGLOBALINFOPAGE const pGip = g_pSUPGlobalInfoPage;
|
---|
| 205 | uint64_t cTicks = uTsc - pVCpu->tm.s.uTscStartExecuting - SUPGetTscDelta(pGip);
|
---|
| 206 | uint64_t const uCpuHz = pGip ? SUPGetCpuHzFromGip(pGip) : pVM->tm.s.cTSCTicksPerSecondHost;
|
---|
[87748] | 207 | # else
|
---|
[87751] | 208 | uint64_t cTicks = uTsc - pVCpu->tm.s.uTscStartExecuting - SUPGetTscDeltaByCpuSetIndex(pVCpu->iHostCpuSet);
|
---|
[87749] | 209 | uint64_t const uCpuHz = SUPGetCpuHzFromGipBySetIndex(g_pSUPGlobalInfoPage, pVCpu->iHostCpuSet);
|
---|
[87748] | 210 | # endif
|
---|
| 211 | AssertStmt(cTicks <= uCpuHz << 2, cTicks = uCpuHz << 2); /* max 4 sec */
|
---|
[30684] | 212 |
|
---|
[87748] | 213 | uint64_t cNsExecutingDelta;
|
---|
| 214 | if (uCpuHz < _4G)
|
---|
| 215 | cNsExecutingDelta = ASMMultU64ByU32DivByU32(cTicks, RT_NS_1SEC, uCpuHz);
|
---|
| 216 | else if (uCpuHz < 16*_1G64)
|
---|
| 217 | cNsExecutingDelta = ASMMultU64ByU32DivByU32(cTicks >> 2, RT_NS_1SEC, uCpuHz >> 2);
|
---|
| 218 | else
|
---|
| 219 | {
|
---|
| 220 | Assert(uCpuHz < 64 * _1G64);
|
---|
| 221 | cNsExecutingDelta = ASMMultU64ByU32DivByU32(cTicks >> 4, RT_NS_1SEC, uCpuHz >> 4);
|
---|
| 222 | }
|
---|
| 223 |
|
---|
| 224 | /*
|
---|
| 225 | * Update the data.
|
---|
[87749] | 226 | *
|
---|
[87750] | 227 | * Note! We're not using strict memory ordering here to speed things us.
|
---|
| 228 | * The data is in a single cache line and this thread is the only
|
---|
| 229 | * one writing to that line, so I cannot quite imagine why we would
|
---|
| 230 | * need any strict ordering here.
|
---|
[87748] | 231 | */
|
---|
| 232 | uint64_t const cNsExecutingNew = pVCpu->tm.s.cNsExecuting + cNsExecutingDelta;
|
---|
[87749] | 233 | uint32_t uGen = ASMAtomicUoIncU32(&pVCpu->tm.s.uTimesGen); Assert(uGen & 1);
|
---|
[87750] | 234 | ASMCompilerBarrier();
|
---|
[87748] | 235 | pVCpu->tm.s.fExecuting = false;
|
---|
| 236 | pVCpu->tm.s.cNsExecuting = cNsExecutingNew;
|
---|
| 237 | pVCpu->tm.s.cPeriodsExecuting++;
|
---|
[87750] | 238 | ASMCompilerBarrier();
|
---|
| 239 | ASMAtomicUoWriteU32(&pVCpu->tm.s.uTimesGen, (uGen | 1) + 1);
|
---|
[87748] | 240 |
|
---|
| 241 | /*
|
---|
| 242 | * Update stats.
|
---|
| 243 | */
|
---|
[30799] | 244 | # if defined(VBOX_WITH_STATISTICS) || defined(VBOX_WITH_NS_ACCOUNTING_STATS)
|
---|
| 245 | STAM_REL_PROFILE_ADD_PERIOD(&pVCpu->tm.s.StatNsExecuting, cNsExecutingDelta);
|
---|
[32796] | 246 | if (cNsExecutingDelta < 5000)
|
---|
| 247 | STAM_REL_PROFILE_ADD_PERIOD(&pVCpu->tm.s.StatNsExecTiny, cNsExecutingDelta);
|
---|
| 248 | else if (cNsExecutingDelta < 50000)
|
---|
| 249 | STAM_REL_PROFILE_ADD_PERIOD(&pVCpu->tm.s.StatNsExecShort, cNsExecutingDelta);
|
---|
| 250 | else
|
---|
| 251 | STAM_REL_PROFILE_ADD_PERIOD(&pVCpu->tm.s.StatNsExecLong, cNsExecutingDelta);
|
---|
[30684] | 252 | # endif
|
---|
| 253 |
|
---|
[87748] | 254 | /* The timer triggers occational updating of the others and total stats: */
|
---|
| 255 | if (RT_LIKELY(!pVCpu->tm.s.fUpdateStats))
|
---|
| 256 | { /*likely*/ }
|
---|
| 257 | else
|
---|
| 258 | {
|
---|
| 259 | pVCpu->tm.s.fUpdateStats = false;
|
---|
| 260 |
|
---|
| 261 | uint64_t const cNsTotalNew = RTTimeNanoTS() - pVCpu->tm.s.nsStartTotal;
|
---|
| 262 | uint64_t const cNsOtherNew = cNsTotalNew - cNsExecutingNew - pVCpu->tm.s.cNsHalted;
|
---|
| 263 |
|
---|
| 264 | # if defined(VBOX_WITH_STATISTICS) || defined(VBOX_WITH_NS_ACCOUNTING_STATS)
|
---|
| 265 | STAM_REL_COUNTER_ADD(&pVCpu->tm.s.StatNsTotal, cNsTotalNew - pVCpu->tm.s.cNsTotalStat);
|
---|
| 266 | int64_t const cNsOtherNewDelta = cNsOtherNew - pVCpu->tm.s.cNsOtherStat;
|
---|
| 267 | if (cNsOtherNewDelta > 0)
|
---|
| 268 | STAM_REL_COUNTER_ADD(&pVCpu->tm.s.StatNsOther, (uint64_t)cNsOtherNewDelta);
|
---|
| 269 | # endif
|
---|
| 270 |
|
---|
| 271 | pVCpu->tm.s.cNsTotalStat = cNsTotalNew;
|
---|
| 272 | pVCpu->tm.s.cNsOtherStat = cNsOtherNew;
|
---|
| 273 | }
|
---|
| 274 |
|
---|
[30581] | 275 | #endif
|
---|
[12549] | 276 | }
|
---|
| 277 |
|
---|
| 278 |
|
---|
| 279 | /**
|
---|
| 280 | * Notification that the cpu is entering the halt state
|
---|
| 281 | *
|
---|
| 282 | * This call must always be paired with a TMNotifyEndOfExecution call.
|
---|
| 283 | *
|
---|
| 284 | * The function may, depending on the configuration, resume the TSC and future
|
---|
| 285 | * clocks that only ticks when we're halted.
|
---|
| 286 | *
|
---|
[58123] | 287 | * @param pVCpu The cross context virtual CPU structure.
|
---|
[12549] | 288 | */
|
---|
[80268] | 289 | VMM_INT_DECL(void) TMNotifyStartOfHalt(PVMCPUCC pVCpu)
|
---|
[12549] | 290 | {
|
---|
[80281] | 291 | PVMCC pVM = pVCpu->CTX_SUFF(pVM);
|
---|
[19032] | 292 |
|
---|
[30581] | 293 | #ifndef VBOX_WITHOUT_NS_ACCOUNTING
|
---|
[87748] | 294 | pVCpu->tm.s.nsStartHalting = RTTimeNanoTS();
|
---|
| 295 | pVCpu->tm.s.fHalting = true;
|
---|
[30581] | 296 | #endif
|
---|
| 297 |
|
---|
[12549] | 298 | if ( pVM->tm.s.fTSCTiedToExecution
|
---|
| 299 | && !pVM->tm.s.fTSCNotTiedToHalt)
|
---|
[19032] | 300 | tmCpuTickResume(pVM, pVCpu);
|
---|
[12549] | 301 | }
|
---|
| 302 |
|
---|
| 303 |
|
---|
| 304 | /**
|
---|
| 305 | * Notification that the cpu is leaving the halt state
|
---|
| 306 | *
|
---|
| 307 | * This call must always be paired with a TMNotifyStartOfHalt call.
|
---|
| 308 | *
|
---|
| 309 | * The function may, depending on the configuration, suspend the TSC and future
|
---|
| 310 | * clocks that only ticks when we're halted.
|
---|
| 311 | *
|
---|
[58123] | 312 | * @param pVCpu The cross context virtual CPU structure.
|
---|
[12549] | 313 | */
|
---|
[80268] | 314 | VMM_INT_DECL(void) TMNotifyEndOfHalt(PVMCPUCC pVCpu)
|
---|
[12549] | 315 | {
|
---|
[19032] | 316 | PVM pVM = pVCpu->CTX_SUFF(pVM);
|
---|
| 317 |
|
---|
[12549] | 318 | if ( pVM->tm.s.fTSCTiedToExecution
|
---|
| 319 | && !pVM->tm.s.fTSCNotTiedToHalt)
|
---|
[39078] | 320 | tmCpuTickPause(pVCpu);
|
---|
[30581] | 321 |
|
---|
| 322 | #ifndef VBOX_WITHOUT_NS_ACCOUNTING
|
---|
[30684] | 323 | uint64_t const u64NsTs = RTTimeNanoTS();
|
---|
[87748] | 324 | uint64_t const cNsTotalNew = u64NsTs - pVCpu->tm.s.nsStartTotal;
|
---|
| 325 | uint64_t const cNsHaltedDelta = u64NsTs - pVCpu->tm.s.nsStartHalting;
|
---|
[30684] | 326 | uint64_t const cNsHaltedNew = pVCpu->tm.s.cNsHalted + cNsHaltedDelta;
|
---|
| 327 | uint64_t const cNsOtherNew = cNsTotalNew - pVCpu->tm.s.cNsExecuting - cNsHaltedNew;
|
---|
| 328 |
|
---|
[87750] | 329 | uint32_t uGen = ASMAtomicUoIncU32(&pVCpu->tm.s.uTimesGen); Assert(uGen & 1);
|
---|
| 330 | ASMCompilerBarrier();
|
---|
[87748] | 331 | pVCpu->tm.s.fHalting = false;
|
---|
| 332 | pVCpu->tm.s.fUpdateStats = false;
|
---|
| 333 | pVCpu->tm.s.cNsHalted = cNsHaltedNew;
|
---|
| 334 | pVCpu->tm.s.cPeriodsHalted++;
|
---|
[87750] | 335 | ASMCompilerBarrier();
|
---|
| 336 | ASMAtomicUoWriteU32(&pVCpu->tm.s.uTimesGen, (uGen | 1) + 1);
|
---|
[87748] | 337 |
|
---|
[30799] | 338 | # if defined(VBOX_WITH_STATISTICS) || defined(VBOX_WITH_NS_ACCOUNTING_STATS)
|
---|
| 339 | STAM_REL_PROFILE_ADD_PERIOD(&pVCpu->tm.s.StatNsHalted, cNsHaltedDelta);
|
---|
[87748] | 340 | STAM_REL_COUNTER_ADD(&pVCpu->tm.s.StatNsTotal, cNsTotalNew - pVCpu->tm.s.cNsTotalStat);
|
---|
| 341 | int64_t const cNsOtherNewDelta = cNsOtherNew - pVCpu->tm.s.cNsOtherStat;
|
---|
[30684] | 342 | if (cNsOtherNewDelta > 0)
|
---|
[87748] | 343 | STAM_REL_COUNTER_ADD(&pVCpu->tm.s.StatNsOther, (uint64_t)cNsOtherNewDelta);
|
---|
[30684] | 344 | # endif
|
---|
[87748] | 345 | pVCpu->tm.s.cNsTotalStat = cNsTotalNew;
|
---|
| 346 | pVCpu->tm.s.cNsOtherStat = cNsOtherNew;
|
---|
[30581] | 347 | #endif
|
---|
[12549] | 348 | }
|
---|
| 349 |
|
---|
| 350 |
|
---|
| 351 | /**
|
---|
[19709] | 352 | * Raise the timer force action flag and notify the dedicated timer EMT.
|
---|
| 353 | *
|
---|
[58122] | 354 | * @param pVM The cross context VM structure.
|
---|
[19709] | 355 | */
|
---|
[80268] | 356 | DECLINLINE(void) tmScheduleNotify(PVMCC pVM)
|
---|
[19709] | 357 | {
|
---|
[87822] | 358 | VMCPUID idCpu = pVM->tm.s.idTimerCpu;
|
---|
| 359 | AssertReturnVoid(idCpu < pVM->cCpus);
|
---|
| 360 | PVMCPUCC pVCpuDst = VMCC_GET_CPU(pVM, idCpu);
|
---|
| 361 |
|
---|
[46420] | 362 | if (!VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
|
---|
[19709] | 363 | {
|
---|
| 364 | Log5(("TMAll(%u): FF: 0 -> 1\n", __LINE__));
|
---|
| 365 | VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
|
---|
| 366 | #ifdef IN_RING3
|
---|
| 367 | VMR3NotifyCpuFFU(pVCpuDst->pUVCpu, VMNOTIFYFF_FLAGS_DONE_REM);
|
---|
| 368 | #endif
|
---|
| 369 | STAM_COUNTER_INC(&pVM->tm.s.StatScheduleSetFF);
|
---|
| 370 | }
|
---|
| 371 | }
|
---|
| 372 |
|
---|
| 373 |
|
---|
| 374 | /**
|
---|
[1] | 375 | * Schedule the queue which was changed.
|
---|
| 376 | */
|
---|
[87814] | 377 | DECLINLINE(void) tmSchedule(PVMCC pVM, PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
|
---|
[1] | 378 | {
|
---|
[90346] | 379 | int rc = PDMCritSectTryEnter(pVM, &pQueue->TimerLock);
|
---|
[87822] | 380 | if (RT_SUCCESS_NP(rc))
|
---|
[1] | 381 | {
|
---|
[87822] | 382 | STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatScheduleOne), a);
|
---|
| 383 | Log3(("tmSchedule: tmTimerQueueSchedule\n"));
|
---|
| 384 | tmTimerQueueSchedule(pVM, pQueueCC, pQueue);
|
---|
[1] | 385 | #ifdef VBOX_STRICT
|
---|
[87822] | 386 | tmTimerQueuesSanityChecks(pVM, "tmSchedule");
|
---|
[1] | 387 | #endif
|
---|
[87822] | 388 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatScheduleOne), a);
|
---|
[90346] | 389 | PDMCritSectLeave(pVM, &pQueue->TimerLock);
|
---|
[87822] | 390 | return;
|
---|
[1] | 391 | }
|
---|
[87812] | 392 |
|
---|
| 393 | TMTIMERSTATE enmState = pTimer->enmState;
|
---|
| 394 | if (TMTIMERSTATE_IS_PENDING_SCHEDULING(enmState))
|
---|
| 395 | tmScheduleNotify(pVM);
|
---|
[1] | 396 | }
|
---|
| 397 |
|
---|
| 398 |
|
---|
| 399 | /**
|
---|
| 400 | * Try change the state to enmStateNew from enmStateOld
|
---|
| 401 | * and link the timer into the scheduling queue.
|
---|
| 402 | *
|
---|
| 403 | * @returns Success indicator.
|
---|
| 404 | * @param pTimer Timer in question.
|
---|
| 405 | * @param enmStateNew The new timer state.
|
---|
| 406 | * @param enmStateOld The old timer state.
|
---|
| 407 | */
|
---|
| 408 | DECLINLINE(bool) tmTimerTry(PTMTIMER pTimer, TMTIMERSTATE enmStateNew, TMTIMERSTATE enmStateOld)
|
---|
| 409 | {
|
---|
| 410 | /*
|
---|
| 411 | * Attempt state change.
|
---|
| 412 | */
|
---|
| 413 | bool fRc;
|
---|
| 414 | TM_TRY_SET_STATE(pTimer, enmStateNew, enmStateOld, fRc);
|
---|
| 415 | return fRc;
|
---|
| 416 | }
|
---|
| 417 |
|
---|
| 418 |
|
---|
| 419 | /**
|
---|
| 420 | * Links the timer onto the scheduling queue.
|
---|
| 421 | *
|
---|
[87792] | 422 | * @param pQueueCC The current context queue (same as @a pQueue for
|
---|
| 423 | * ring-3).
|
---|
| 424 | * @param pQueue The shared queue data.
|
---|
| 425 | * @param pTimer The timer.
|
---|
[19709] | 426 | *
|
---|
| 427 | * @todo FIXME: Look into potential race with the thread running the queues
|
---|
| 428 | * and stuff.
|
---|
[1] | 429 | */
|
---|
[87792] | 430 | DECLINLINE(void) tmTimerLinkSchedule(PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
|
---|
[1] | 431 | {
|
---|
[87792] | 432 | Assert(pTimer->idxScheduleNext == UINT32_MAX);
|
---|
| 433 | const uint32_t idxHeadNew = pTimer - &pQueueCC->paTimers[0];
|
---|
| 434 | AssertReturnVoid(idxHeadNew < pQueueCC->cTimersAlloc);
|
---|
| 435 |
|
---|
| 436 | uint32_t idxHead;
|
---|
[1] | 437 | do
|
---|
| 438 | {
|
---|
[87792] | 439 | idxHead = pQueue->idxSchedule;
|
---|
| 440 | Assert(idxHead == UINT32_MAX || idxHead < pQueueCC->cTimersAlloc);
|
---|
| 441 | pTimer->idxScheduleNext = idxHead;
|
---|
| 442 | } while (!ASMAtomicCmpXchgU32(&pQueue->idxSchedule, idxHeadNew, idxHead));
|
---|
[1] | 443 | }
|
---|
| 444 |
|
---|
| 445 |
|
---|
| 446 | /**
|
---|
| 447 | * Try change the state to enmStateNew from enmStateOld
|
---|
| 448 | * and link the timer into the scheduling queue.
|
---|
| 449 | *
|
---|
| 450 | * @returns Success indicator.
|
---|
[87814] | 451 | * @param pQueueCC The current context queue (same as @a pQueue for
|
---|
| 452 | * ring-3).
|
---|
| 453 | * @param pQueue The shared queue data.
|
---|
| 454 | * @param pTimer Timer in question.
|
---|
| 455 | * @param enmStateNew The new timer state.
|
---|
| 456 | * @param enmStateOld The old timer state.
|
---|
[1] | 457 | */
|
---|
[87814] | 458 | DECLINLINE(bool) tmTimerTryWithLink(PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue, PTMTIMER pTimer,
|
---|
| 459 | TMTIMERSTATE enmStateNew, TMTIMERSTATE enmStateOld)
|
---|
[1] | 460 | {
|
---|
| 461 | if (tmTimerTry(pTimer, enmStateNew, enmStateOld))
|
---|
| 462 | {
|
---|
[87814] | 463 | tmTimerLinkSchedule(pQueueCC, pQueue, pTimer);
|
---|
[1] | 464 | return true;
|
---|
| 465 | }
|
---|
| 466 | return false;
|
---|
| 467 | }
|
---|
| 468 |
|
---|
| 469 |
|
---|
[37517] | 470 | /**
|
---|
| 471 | * Links a timer into the active list of a timer queue.
|
---|
| 472 | *
|
---|
[87774] | 473 | * @param pVM The cross context VM structure.
|
---|
[87792] | 474 | * @param pQueueCC The current context queue (same as @a pQueue for
|
---|
| 475 | * ring-3).
|
---|
| 476 | * @param pQueue The shared queue data.
|
---|
[37517] | 477 | * @param pTimer The timer.
|
---|
| 478 | * @param u64Expire The timer expiration time.
|
---|
| 479 | *
|
---|
| 480 | * @remarks Called while owning the relevant queue lock.
|
---|
| 481 | */
|
---|
[87792] | 482 | DECL_FORCE_INLINE(void) tmTimerQueueLinkActive(PVMCC pVM, PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue,
|
---|
| 483 | PTMTIMER pTimer, uint64_t u64Expire)
|
---|
[37517] | 484 | {
|
---|
[87792] | 485 | Assert(pTimer->idxNext == UINT32_MAX);
|
---|
| 486 | Assert(pTimer->idxPrev == UINT32_MAX);
|
---|
[87814] | 487 | Assert(pTimer->enmState == TMTIMERSTATE_ACTIVE || pQueue->enmClock != TMCLOCK_VIRTUAL_SYNC); /* (active is not a stable state) */
|
---|
[87774] | 488 | RT_NOREF(pVM);
|
---|
[37517] | 489 |
|
---|
[87792] | 490 | PTMTIMER pCur = tmTimerQueueGetHead(pQueueCC, pQueue);
|
---|
[37517] | 491 | if (pCur)
|
---|
| 492 | {
|
---|
[87792] | 493 | for (;; pCur = tmTimerGetNext(pQueueCC, pCur))
|
---|
[37517] | 494 | {
|
---|
| 495 | if (pCur->u64Expire > u64Expire)
|
---|
| 496 | {
|
---|
[87792] | 497 | const PTMTIMER pPrev = tmTimerGetPrev(pQueueCC, pCur);
|
---|
| 498 | tmTimerSetNext(pQueueCC, pTimer, pCur);
|
---|
| 499 | tmTimerSetPrev(pQueueCC, pTimer, pPrev);
|
---|
[37517] | 500 | if (pPrev)
|
---|
[87792] | 501 | tmTimerSetNext(pQueueCC, pPrev, pTimer);
|
---|
[37517] | 502 | else
|
---|
| 503 | {
|
---|
[87792] | 504 | tmTimerQueueSetHead(pQueueCC, pQueue, pTimer);
|
---|
[37517] | 505 | ASMAtomicWriteU64(&pQueue->u64Expire, u64Expire);
|
---|
[87774] | 506 | DBGFTRACE_U64_TAG2(pVM, u64Expire, "tmTimerQueueLinkActive head", pTimer->szName);
|
---|
[37517] | 507 | }
|
---|
[87792] | 508 | tmTimerSetPrev(pQueueCC, pCur, pTimer);
|
---|
[37517] | 509 | return;
|
---|
| 510 | }
|
---|
[87792] | 511 | if (pCur->idxNext == UINT32_MAX)
|
---|
[37517] | 512 | {
|
---|
[87792] | 513 | tmTimerSetNext(pQueueCC, pCur, pTimer);
|
---|
| 514 | tmTimerSetPrev(pQueueCC, pTimer, pCur);
|
---|
[87774] | 515 | DBGFTRACE_U64_TAG2(pVM, u64Expire, "tmTimerQueueLinkActive tail", pTimer->szName);
|
---|
[37517] | 516 | return;
|
---|
| 517 | }
|
---|
| 518 | }
|
---|
| 519 | }
|
---|
| 520 | else
|
---|
| 521 | {
|
---|
[87792] | 522 | tmTimerQueueSetHead(pQueueCC, pQueue, pTimer);
|
---|
[37517] | 523 | ASMAtomicWriteU64(&pQueue->u64Expire, u64Expire);
|
---|
[87774] | 524 | DBGFTRACE_U64_TAG2(pVM, u64Expire, "tmTimerQueueLinkActive empty", pTimer->szName);
|
---|
[37517] | 525 | }
|
---|
| 526 | }
|
---|
| 527 |
|
---|
| 528 |
|
---|
| 529 |
|
---|
| 530 | /**
|
---|
| 531 | * Schedules the given timer on the given queue.
|
---|
| 532 | *
|
---|
[87771] | 533 | * @param pVM The cross context VM structure.
|
---|
[87792] | 534 | * @param pQueueCC The current context queue (same as @a pQueue for
|
---|
| 535 | * ring-3).
|
---|
| 536 | * @param pQueue The shared queue data.
|
---|
[37517] | 537 | * @param pTimer The timer that needs scheduling.
|
---|
| 538 | *
|
---|
| 539 | * @remarks Called while owning the lock.
|
---|
| 540 | */
|
---|
[87792] | 541 | DECLINLINE(void) tmTimerQueueScheduleOne(PVMCC pVM, PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
|
---|
[37517] | 542 | {
|
---|
| 543 | Assert(pQueue->enmClock != TMCLOCK_VIRTUAL_SYNC);
|
---|
[87771] | 544 | RT_NOREF(pVM);
|
---|
[37517] | 545 |
|
---|
| 546 | /*
|
---|
| 547 | * Processing.
|
---|
| 548 | */
|
---|
| 549 | unsigned cRetries = 2;
|
---|
| 550 | do
|
---|
| 551 | {
|
---|
| 552 | TMTIMERSTATE enmState = pTimer->enmState;
|
---|
| 553 | switch (enmState)
|
---|
| 554 | {
|
---|
| 555 | /*
|
---|
| 556 | * Reschedule timer (in the active list).
|
---|
| 557 | */
|
---|
| 558 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 559 | if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE, TMTIMERSTATE_PENDING_RESCHEDULE)))
|
---|
| 560 | break; /* retry */
|
---|
[87792] | 561 | tmTimerQueueUnlinkActive(pVM, pQueueCC, pQueue, pTimer);
|
---|
[69046] | 562 | RT_FALL_THRU();
|
---|
[37517] | 563 |
|
---|
| 564 | /*
|
---|
| 565 | * Schedule timer (insert into the active list).
|
---|
| 566 | */
|
---|
| 567 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
[87792] | 568 | Assert(pTimer->idxNext == UINT32_MAX); Assert(pTimer->idxPrev == UINT32_MAX);
|
---|
[37517] | 569 | if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, TMTIMERSTATE_PENDING_SCHEDULE)))
|
---|
| 570 | break; /* retry */
|
---|
[87792] | 571 | tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, pTimer->u64Expire);
|
---|
[37517] | 572 | return;
|
---|
| 573 |
|
---|
| 574 | /*
|
---|
| 575 | * Stop the timer in active list.
|
---|
| 576 | */
|
---|
| 577 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 578 | if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, TMTIMERSTATE_PENDING_STOP)))
|
---|
| 579 | break; /* retry */
|
---|
[87792] | 580 | tmTimerQueueUnlinkActive(pVM, pQueueCC, pQueue, pTimer);
|
---|
[69046] | 581 | RT_FALL_THRU();
|
---|
[37517] | 582 |
|
---|
| 583 | /*
|
---|
| 584 | * Stop the timer (not on the active list).
|
---|
| 585 | */
|
---|
| 586 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
[87792] | 587 | Assert(pTimer->idxNext == UINT32_MAX); Assert(pTimer->idxPrev == UINT32_MAX);
|
---|
[37517] | 588 | if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_STOPPED, TMTIMERSTATE_PENDING_STOP_SCHEDULE)))
|
---|
| 589 | break;
|
---|
| 590 | return;
|
---|
| 591 |
|
---|
| 592 | /*
|
---|
| 593 | * The timer is pending destruction by TMR3TimerDestroy, our caller.
|
---|
| 594 | * Nothing to do here.
|
---|
| 595 | */
|
---|
| 596 | case TMTIMERSTATE_DESTROY:
|
---|
| 597 | break;
|
---|
| 598 |
|
---|
| 599 | /*
|
---|
| 600 | * Postpone these until they get into the right state.
|
---|
| 601 | */
|
---|
| 602 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 603 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
[87792] | 604 | tmTimerLinkSchedule(pQueueCC, pQueue, pTimer);
|
---|
[87771] | 605 | STAM_COUNTER_INC(&pVM->tm.s.CTX_SUFF_Z(StatPostponed));
|
---|
[37517] | 606 | return;
|
---|
| 607 |
|
---|
| 608 | /*
|
---|
| 609 | * None of these can be in the schedule.
|
---|
| 610 | */
|
---|
| 611 | case TMTIMERSTATE_FREE:
|
---|
| 612 | case TMTIMERSTATE_STOPPED:
|
---|
| 613 | case TMTIMERSTATE_ACTIVE:
|
---|
| 614 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
| 615 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
| 616 | default:
|
---|
| 617 | AssertMsgFailed(("Timer (%p) in the scheduling list has an invalid state %s (%d)!",
|
---|
| 618 | pTimer, tmTimerState(pTimer->enmState), pTimer->enmState));
|
---|
| 619 | return;
|
---|
| 620 | }
|
---|
| 621 | } while (cRetries-- > 0);
|
---|
| 622 | }
|
---|
| 623 |
|
---|
| 624 |
|
---|
| 625 | /**
|
---|
| 626 | * Schedules the specified timer queue.
|
---|
| 627 | *
|
---|
[58122] | 628 | * @param pVM The cross context VM structure.
|
---|
[87792] | 629 | * @param pQueueCC The current context queue (same as @a pQueue for
|
---|
| 630 | * ring-3) data of the queue to schedule.
|
---|
| 631 | * @param pQueue The shared queue data of the queue to schedule.
|
---|
[37517] | 632 | *
|
---|
| 633 | * @remarks Called while owning the lock.
|
---|
| 634 | */
|
---|
[87792] | 635 | void tmTimerQueueSchedule(PVMCC pVM, PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue)
|
---|
[37517] | 636 | {
|
---|
[90346] | 637 | Assert(PDMCritSectIsOwner(pVM, &pQueue->TimerLock));
|
---|
[37517] | 638 |
|
---|
| 639 | /*
|
---|
| 640 | * Dequeue the scheduling list and iterate it.
|
---|
| 641 | */
|
---|
[87792] | 642 | uint32_t idxNext = ASMAtomicXchgU32(&pQueue->idxSchedule, UINT32_MAX);
|
---|
| 643 | Log2(("tmTimerQueueSchedule: pQueue=%p:{.enmClock=%d, idxNext=%RI32, .u64Expired=%'RU64}\n", pQueue, pQueue->enmClock, idxNext, pQueue->u64Expire));
|
---|
| 644 | while (idxNext != UINT32_MAX)
|
---|
[37517] | 645 | {
|
---|
[87792] | 646 | AssertBreak(idxNext < pQueueCC->cTimersAlloc);
|
---|
| 647 |
|
---|
[37517] | 648 | /*
|
---|
[87792] | 649 | * Unlink the head timer and take down the index of the next one.
|
---|
[37517] | 650 | */
|
---|
[87792] | 651 | PTMTIMER pTimer = &pQueueCC->paTimers[idxNext];
|
---|
| 652 | idxNext = pTimer->idxScheduleNext;
|
---|
| 653 | pTimer->idxScheduleNext = UINT32_MAX;
|
---|
[37517] | 654 |
|
---|
| 655 | /*
|
---|
| 656 | * Do the scheduling.
|
---|
| 657 | */
|
---|
[87773] | 658 | Log2(("tmTimerQueueSchedule: %p:{.enmState=%s, .enmClock=%d, .enmType=%d, .szName=%s}\n",
|
---|
[87814] | 659 | pTimer, tmTimerState(pTimer->enmState), pQueue->enmClock, pTimer->enmType, pTimer->szName));
|
---|
[87792] | 660 | tmTimerQueueScheduleOne(pVM, pQueueCC, pQueue, pTimer);
|
---|
[37517] | 661 | Log2(("tmTimerQueueSchedule: %p: new %s\n", pTimer, tmTimerState(pTimer->enmState)));
|
---|
[87792] | 662 | }
|
---|
[37517] | 663 | Log2(("tmTimerQueueSchedule: u64Expired=%'RU64\n", pQueue->u64Expire));
|
---|
| 664 | }
|
---|
| 665 |
|
---|
| 666 |
|
---|
| 667 | #ifdef VBOX_STRICT
|
---|
| 668 | /**
|
---|
| 669 | * Checks that the timer queues are sane.
|
---|
| 670 | *
|
---|
[58126] | 671 | * @param pVM The cross context VM structure.
|
---|
| 672 | * @param pszWhere Caller location clue.
|
---|
[37517] | 673 | */
|
---|
[87792] | 674 | void tmTimerQueuesSanityChecks(PVMCC pVM, const char *pszWhere)
|
---|
[37517] | 675 | {
|
---|
[87792] | 676 | for (uint32_t idxQueue = 0; idxQueue < RT_ELEMENTS(pVM->tm.s.aTimerQueues); idxQueue++)
|
---|
[37517] | 677 | {
|
---|
[87792] | 678 | PTMTIMERQUEUE const pQueue = &pVM->tm.s.aTimerQueues[idxQueue];
|
---|
| 679 | PTMTIMERQUEUECC const pQueueCC = TM_GET_TIMER_QUEUE_CC(pVM, idxQueue, pQueue);
|
---|
| 680 | Assert(pQueue->enmClock == (TMCLOCK)idxQueue);
|
---|
| 681 |
|
---|
[90346] | 682 | int rc = PDMCritSectTryEnter(pVM, &pQueue->TimerLock);
|
---|
[87792] | 683 | if (RT_SUCCESS(rc))
|
---|
[37517] | 684 | {
|
---|
[87792] | 685 | if ( pQueue->enmClock != TMCLOCK_VIRTUAL_SYNC
|
---|
[90346] | 686 | || PDMCritSectTryEnter(pVM, &pVM->tm.s.VirtualSyncLock) == VINF_SUCCESS)
|
---|
[37517] | 687 | {
|
---|
[87792] | 688 | /* Check the linking of the active lists. */
|
---|
| 689 | PTMTIMER pPrev = NULL;
|
---|
| 690 | for (PTMTIMER pCur = tmTimerQueueGetHead(pQueueCC, pQueue);
|
---|
| 691 | pCur;
|
---|
| 692 | pPrev = pCur, pCur = tmTimerGetNext(pQueueCC, pCur))
|
---|
[37517] | 693 | {
|
---|
[87792] | 694 | AssertMsg(tmTimerGetPrev(pQueueCC, pCur) == pPrev, ("%s: %p != %p\n", pszWhere, tmTimerGetPrev(pQueueCC, pCur), pPrev));
|
---|
| 695 | TMTIMERSTATE enmState = pCur->enmState;
|
---|
| 696 | switch (enmState)
|
---|
| 697 | {
|
---|
| 698 | case TMTIMERSTATE_ACTIVE:
|
---|
| 699 | AssertMsg( pCur->idxScheduleNext == UINT32_MAX
|
---|
| 700 | || pCur->enmState != TMTIMERSTATE_ACTIVE,
|
---|
| 701 | ("%s: %RI32\n", pszWhere, pCur->idxScheduleNext));
|
---|
| 702 | break;
|
---|
| 703 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 704 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 705 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 706 | break;
|
---|
| 707 | default:
|
---|
| 708 | AssertMsgFailed(("%s: Invalid state enmState=%d %s\n", pszWhere, enmState, tmTimerState(enmState)));
|
---|
| 709 | break;
|
---|
| 710 | }
|
---|
[37517] | 711 | }
|
---|
| 712 |
|
---|
[87792] | 713 | # ifdef IN_RING3
|
---|
| 714 | /* Go thru all the timers and check that the active ones all are in the active lists. */
|
---|
| 715 | uint32_t idxTimer = pQueue->cTimersAlloc;
|
---|
| 716 | uint32_t cFree = 0;
|
---|
| 717 | while (idxTimer-- > 0)
|
---|
[37517] | 718 | {
|
---|
[87792] | 719 | PTMTIMER const pTimer = &pQueue->paTimers[idxTimer];
|
---|
| 720 | TMTIMERSTATE const enmState = pTimer->enmState;
|
---|
| 721 | switch (enmState)
|
---|
[37517] | 722 | {
|
---|
[87792] | 723 | case TMTIMERSTATE_FREE:
|
---|
| 724 | cFree++;
|
---|
| 725 | break;
|
---|
| 726 |
|
---|
| 727 | case TMTIMERSTATE_ACTIVE:
|
---|
| 728 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 729 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 730 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 731 | {
|
---|
| 732 | PTMTIMERR3 pCurAct = tmTimerQueueGetHead(pQueueCC, pQueue);
|
---|
| 733 | Assert(pTimer->idxPrev != UINT32_MAX || pTimer == pCurAct);
|
---|
| 734 | while (pCurAct && pCurAct != pTimer)
|
---|
| 735 | pCurAct = tmTimerGetNext(pQueueCC, pCurAct);
|
---|
| 736 | Assert(pCurAct == pTimer);
|
---|
| 737 | break;
|
---|
| 738 | }
|
---|
| 739 |
|
---|
| 740 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
| 741 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
| 742 | case TMTIMERSTATE_STOPPED:
|
---|
| 743 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
| 744 | {
|
---|
| 745 | Assert(pTimer->idxNext == UINT32_MAX);
|
---|
| 746 | Assert(pTimer->idxPrev == UINT32_MAX);
|
---|
| 747 | for (PTMTIMERR3 pCurAct = tmTimerQueueGetHead(pQueueCC, pQueue);
|
---|
| 748 | pCurAct;
|
---|
| 749 | pCurAct = tmTimerGetNext(pQueueCC, pCurAct))
|
---|
| 750 | {
|
---|
| 751 | Assert(pCurAct != pTimer);
|
---|
| 752 | Assert(tmTimerGetNext(pQueueCC, pCurAct) != pTimer);
|
---|
| 753 | Assert(tmTimerGetPrev(pQueueCC, pCurAct) != pTimer);
|
---|
| 754 | }
|
---|
| 755 | break;
|
---|
| 756 | }
|
---|
| 757 |
|
---|
| 758 | /* ignore */
|
---|
| 759 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 760 | break;
|
---|
| 761 |
|
---|
| 762 | case TMTIMERSTATE_INVALID:
|
---|
| 763 | Assert(idxTimer == 0);
|
---|
| 764 | break;
|
---|
| 765 |
|
---|
| 766 | /* shouldn't get here! */
|
---|
| 767 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
| 768 | case TMTIMERSTATE_DESTROY:
|
---|
| 769 | default:
|
---|
| 770 | AssertMsgFailed(("Invalid state enmState=%d %s\n", enmState, tmTimerState(enmState)));
|
---|
| 771 | break;
|
---|
[37517] | 772 | }
|
---|
[87792] | 773 |
|
---|
| 774 | /* Check the handle value. */
|
---|
| 775 | if (enmState > TMTIMERSTATE_INVALID && enmState < TMTIMERSTATE_DESTROY)
|
---|
| 776 | {
|
---|
| 777 | Assert((pTimer->hSelf & TMTIMERHANDLE_TIMER_IDX_MASK) == idxTimer);
|
---|
| 778 | Assert(((pTimer->hSelf >> TMTIMERHANDLE_QUEUE_IDX_SHIFT) & TMTIMERHANDLE_QUEUE_IDX_SMASK) == idxQueue);
|
---|
| 779 | }
|
---|
[37517] | 780 | }
|
---|
[87792] | 781 | Assert(cFree == pQueue->cTimersFree);
|
---|
| 782 | # endif /* IN_RING3 */
|
---|
[37517] | 783 |
|
---|
[87792] | 784 | if (pQueue->enmClock == TMCLOCK_VIRTUAL_SYNC)
|
---|
[90346] | 785 | PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
|
---|
[87792] | 786 | }
|
---|
[90346] | 787 | PDMCritSectLeave(pVM, &pQueue->TimerLock);
|
---|
[37517] | 788 | }
|
---|
| 789 | }
|
---|
| 790 | }
|
---|
| 791 | #endif /* !VBOX_STRICT */
|
---|
| 792 |
|
---|
[1] | 793 | #ifdef VBOX_HIGH_RES_TIMERS_HACK
|
---|
[19820] | 794 |
|
---|
[1] | 795 | /**
|
---|
[33540] | 796 | * Worker for tmTimerPollInternal that handles misses when the dedicated timer
|
---|
[19820] | 797 | * EMT is polling.
|
---|
[1] | 798 | *
|
---|
[19820] | 799 | * @returns See tmTimerPollInternal.
|
---|
[58122] | 800 | * @param pVM The cross context VM structure.
|
---|
[19820] | 801 | * @param u64Now Current virtual clock timestamp.
|
---|
| 802 | * @param u64Delta The delta to the next even in ticks of the
|
---|
| 803 | * virtual clock.
|
---|
| 804 | * @param pu64Delta Where to return the delta.
|
---|
| 805 | */
|
---|
| 806 | DECLINLINE(uint64_t) tmTimerPollReturnMiss(PVM pVM, uint64_t u64Now, uint64_t u64Delta, uint64_t *pu64Delta)
|
---|
| 807 | {
|
---|
| 808 | Assert(!(u64Delta & RT_BIT_64(63)));
|
---|
| 809 |
|
---|
| 810 | if (!pVM->tm.s.fVirtualWarpDrive)
|
---|
| 811 | {
|
---|
| 812 | *pu64Delta = u64Delta;
|
---|
| 813 | return u64Delta + u64Now + pVM->tm.s.u64VirtualOffset;
|
---|
| 814 | }
|
---|
| 815 |
|
---|
| 816 | /*
|
---|
| 817 | * Warp drive adjustments - this is the reverse of what tmVirtualGetRaw is doing.
|
---|
| 818 | */
|
---|
| 819 | uint64_t const u64Start = pVM->tm.s.u64VirtualWarpDriveStart;
|
---|
| 820 | uint32_t const u32Pct = pVM->tm.s.u32VirtualWarpDrivePercentage;
|
---|
| 821 |
|
---|
| 822 | uint64_t u64GipTime = u64Delta + u64Now + pVM->tm.s.u64VirtualOffset;
|
---|
| 823 | u64GipTime -= u64Start; /* the start is GIP time. */
|
---|
| 824 | if (u64GipTime >= u64Delta)
|
---|
| 825 | {
|
---|
| 826 | ASMMultU64ByU32DivByU32(u64GipTime, 100, u32Pct);
|
---|
| 827 | ASMMultU64ByU32DivByU32(u64Delta, 100, u32Pct);
|
---|
| 828 | }
|
---|
| 829 | else
|
---|
| 830 | {
|
---|
| 831 | u64Delta -= u64GipTime;
|
---|
| 832 | ASMMultU64ByU32DivByU32(u64GipTime, 100, u32Pct);
|
---|
| 833 | u64Delta += u64GipTime;
|
---|
| 834 | }
|
---|
| 835 | *pu64Delta = u64Delta;
|
---|
| 836 | u64GipTime += u64Start;
|
---|
| 837 | return u64GipTime;
|
---|
| 838 | }
|
---|
| 839 |
|
---|
| 840 |
|
---|
| 841 | /**
|
---|
| 842 | * Worker for tmTimerPollInternal dealing with returns on virtual CPUs other
|
---|
| 843 | * than the one dedicated to timer work.
|
---|
| 844 | *
|
---|
| 845 | * @returns See tmTimerPollInternal.
|
---|
[58122] | 846 | * @param pVM The cross context VM structure.
|
---|
[19820] | 847 | * @param u64Now Current virtual clock timestamp.
|
---|
| 848 | * @param pu64Delta Where to return the delta.
|
---|
| 849 | */
|
---|
| 850 | DECL_FORCE_INLINE(uint64_t) tmTimerPollReturnOtherCpu(PVM pVM, uint64_t u64Now, uint64_t *pu64Delta)
|
---|
| 851 | {
|
---|
| 852 | static const uint64_t s_u64OtherRet = 500000000; /* 500 ms for non-timer EMTs. */
|
---|
| 853 | *pu64Delta = s_u64OtherRet;
|
---|
| 854 | return u64Now + pVM->tm.s.u64VirtualOffset + s_u64OtherRet;
|
---|
| 855 | }
|
---|
| 856 |
|
---|
| 857 |
|
---|
| 858 | /**
|
---|
| 859 | * Worker for tmTimerPollInternal.
|
---|
| 860 | *
|
---|
| 861 | * @returns See tmTimerPollInternal.
|
---|
[58123] | 862 | * @param pVM The cross context VM structure.
|
---|
| 863 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
| 864 | * @param pVCpuDst The cross context virtual CPU structure of the dedicated
|
---|
| 865 | * timer EMT.
|
---|
| 866 | * @param u64Now Current virtual clock timestamp.
|
---|
| 867 | * @param pu64Delta Where to return the delta.
|
---|
| 868 | * @param pCounter The statistics counter to update.
|
---|
[19820] | 869 | */
|
---|
| 870 | DECL_FORCE_INLINE(uint64_t) tmTimerPollReturnHit(PVM pVM, PVMCPU pVCpu, PVMCPU pVCpuDst, uint64_t u64Now,
|
---|
| 871 | uint64_t *pu64Delta, PSTAMCOUNTER pCounter)
|
---|
| 872 | {
|
---|
[49623] | 873 | STAM_COUNTER_INC(pCounter); NOREF(pCounter);
|
---|
[19820] | 874 | if (pVCpuDst != pVCpu)
|
---|
| 875 | return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
|
---|
| 876 | *pu64Delta = 0;
|
---|
| 877 | return 0;
|
---|
| 878 | }
|
---|
| 879 |
|
---|
[87822] | 880 |
|
---|
[19820] | 881 | /**
|
---|
| 882 | * Common worker for TMTimerPollGIP and TMTimerPoll.
|
---|
| 883 | *
|
---|
[1] | 884 | * This function is called before FFs are checked in the inner execution EM loops.
|
---|
| 885 | *
|
---|
[19820] | 886 | * @returns The GIP timestamp of the next event.
|
---|
| 887 | * 0 if the next event has already expired.
|
---|
| 888 | *
|
---|
[58122] | 889 | * @param pVM The cross context VM structure.
|
---|
[58123] | 890 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
[19820] | 891 | * @param pu64Delta Where to store the delta.
|
---|
[101088] | 892 | * @param pu64Now Where to store the current time. Optional.
|
---|
[19820] | 893 | *
|
---|
[1] | 894 | * @thread The emulation thread.
|
---|
[19820] | 895 | *
|
---|
| 896 | * @remarks GIP uses ns ticks.
|
---|
[1] | 897 | */
|
---|
[101088] | 898 | DECL_FORCE_INLINE(uint64_t) tmTimerPollInternal(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *pu64Delta, uint64_t *pu64Now)
|
---|
[1] | 899 | {
|
---|
[87822] | 900 | VMCPUID idCpu = pVM->tm.s.idTimerCpu;
|
---|
| 901 | AssertReturn(idCpu < pVM->cCpus, 0);
|
---|
| 902 | PVMCPUCC pVCpuDst = VMCC_GET_CPU(pVM, idCpu);
|
---|
| 903 |
|
---|
| 904 | const uint64_t u64Now = TMVirtualGetNoCheck(pVM);
|
---|
[19660] | 905 | STAM_COUNTER_INC(&pVM->tm.s.StatPoll);
|
---|
[101088] | 906 | if (pu64Now)
|
---|
| 907 | *pu64Now = u64Now;
|
---|
[19444] | 908 |
|
---|
[1] | 909 | /*
|
---|
[19660] | 910 | * Return straight away if the timer FF is already set ...
|
---|
[1] | 911 | */
|
---|
[46420] | 912 | if (VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
|
---|
[19820] | 913 | return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollAlreadySet);
|
---|
[1] | 914 |
|
---|
| 915 | /*
|
---|
[19660] | 916 | * ... or if timers are being run.
|
---|
| 917 | */
|
---|
[19820] | 918 | if (ASMAtomicReadBool(&pVM->tm.s.fRunningQueues))
|
---|
[19660] | 919 | {
|
---|
| 920 | STAM_COUNTER_INC(&pVM->tm.s.StatPollRunning);
|
---|
[19820] | 921 | return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
|
---|
[19660] | 922 | }
|
---|
| 923 |
|
---|
| 924 | /*
|
---|
[19820] | 925 | * Check for TMCLOCK_VIRTUAL expiration.
|
---|
[1] | 926 | */
|
---|
[87792] | 927 | const uint64_t u64Expire1 = ASMAtomicReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL].u64Expire);
|
---|
[19660] | 928 | const int64_t i64Delta1 = u64Expire1 - u64Now;
|
---|
[1] | 929 | if (i64Delta1 <= 0)
|
---|
| 930 | {
|
---|
[46420] | 931 | if (!VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
|
---|
[19810] | 932 | {
|
---|
[74785] | 933 | Log5(("TMAll(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER)));
|
---|
[19810] | 934 | VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
|
---|
| 935 | }
|
---|
[20050] | 936 | LogFlow(("TMTimerPoll: expire1=%'RU64 <= now=%'RU64\n", u64Expire1, u64Now));
|
---|
[19820] | 937 | return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtual);
|
---|
[1] | 938 | }
|
---|
| 939 |
|
---|
[4977] | 940 | /*
|
---|
[19820] | 941 | * Check for TMCLOCK_VIRTUAL_SYNC expiration.
|
---|
[33540] | 942 | * This isn't quite as straight forward if in a catch-up, not only do
|
---|
[2283] | 943 | * we have to adjust the 'now' but when have to adjust the delta as well.
|
---|
| 944 | */
|
---|
[19810] | 945 |
|
---|
| 946 | /*
|
---|
| 947 | * Optimistic lockless approach.
|
---|
| 948 | */
|
---|
[2283] | 949 | uint64_t u64VirtualSyncNow;
|
---|
[87792] | 950 | uint64_t u64Expire2 = ASMAtomicUoReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire);
|
---|
[19810] | 951 | if (ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncTicking))
|
---|
| 952 | {
|
---|
| 953 | if (!ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
|
---|
| 954 | {
|
---|
| 955 | u64VirtualSyncNow = ASMAtomicReadU64(&pVM->tm.s.offVirtualSync);
|
---|
| 956 | if (RT_LIKELY( ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncTicking)
|
---|
| 957 | && !ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncCatchUp)
|
---|
| 958 | && u64VirtualSyncNow == ASMAtomicReadU64(&pVM->tm.s.offVirtualSync)
|
---|
[87792] | 959 | && u64Expire2 == ASMAtomicUoReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire)))
|
---|
[19810] | 960 | {
|
---|
| 961 | u64VirtualSyncNow = u64Now - u64VirtualSyncNow;
|
---|
[19820] | 962 | int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
|
---|
| 963 | if (i64Delta2 > 0)
|
---|
[19810] | 964 | {
|
---|
| 965 | STAM_COUNTER_INC(&pVM->tm.s.StatPollSimple);
|
---|
| 966 | STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
|
---|
[19820] | 967 |
|
---|
| 968 | if (pVCpu == pVCpuDst)
|
---|
| 969 | return tmTimerPollReturnMiss(pVM, u64Now, RT_MIN(i64Delta1, i64Delta2), pu64Delta);
|
---|
| 970 | return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
|
---|
[19810] | 971 | }
|
---|
| 972 |
|
---|
| 973 | if ( !pVM->tm.s.fRunningQueues
|
---|
[46420] | 974 | && !VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
|
---|
[19810] | 975 | {
|
---|
[74785] | 976 | Log5(("TMAll(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER)));
|
---|
[19810] | 977 | VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
|
---|
| 978 | }
|
---|
| 979 |
|
---|
| 980 | STAM_COUNTER_INC(&pVM->tm.s.StatPollSimple);
|
---|
[20050] | 981 | LogFlow(("TMTimerPoll: expire2=%'RU64 <= now=%'RU64\n", u64Expire2, u64Now));
|
---|
[19820] | 982 | return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
|
---|
[19810] | 983 | }
|
---|
| 984 | }
|
---|
| 985 | }
|
---|
[2283] | 986 | else
|
---|
| 987 | {
|
---|
[19810] | 988 | STAM_COUNTER_INC(&pVM->tm.s.StatPollSimple);
|
---|
| 989 | LogFlow(("TMTimerPoll: stopped\n"));
|
---|
[19820] | 990 | return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
|
---|
[19810] | 991 | }
|
---|
| 992 |
|
---|
| 993 | /*
|
---|
| 994 | * Complicated lockless approach.
|
---|
| 995 | */
|
---|
| 996 | uint64_t off;
|
---|
| 997 | uint32_t u32Pct = 0;
|
---|
| 998 | bool fCatchUp;
|
---|
| 999 | int cOuterTries = 42;
|
---|
| 1000 | for (;; cOuterTries--)
|
---|
| 1001 | {
|
---|
| 1002 | fCatchUp = ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp);
|
---|
| 1003 | off = ASMAtomicReadU64(&pVM->tm.s.offVirtualSync);
|
---|
[87792] | 1004 | u64Expire2 = ASMAtomicReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire);
|
---|
[19810] | 1005 | if (fCatchUp)
|
---|
[2283] | 1006 | {
|
---|
[19810] | 1007 | /* No changes allowed, try get a consistent set of parameters. */
|
---|
| 1008 | uint64_t const u64Prev = ASMAtomicReadU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev);
|
---|
| 1009 | uint64_t const offGivenUp = ASMAtomicReadU64(&pVM->tm.s.offVirtualSyncGivenUp);
|
---|
| 1010 | u32Pct = ASMAtomicReadU32(&pVM->tm.s.u32VirtualSyncCatchUpPercentage);
|
---|
| 1011 | if ( ( u64Prev == ASMAtomicReadU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev)
|
---|
| 1012 | && offGivenUp == ASMAtomicReadU64(&pVM->tm.s.offVirtualSyncGivenUp)
|
---|
| 1013 | && u32Pct == ASMAtomicReadU32(&pVM->tm.s.u32VirtualSyncCatchUpPercentage)
|
---|
| 1014 | && off == ASMAtomicReadU64(&pVM->tm.s.offVirtualSync)
|
---|
[87792] | 1015 | && u64Expire2 == ASMAtomicReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire)
|
---|
[19810] | 1016 | && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp)
|
---|
| 1017 | && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncTicking))
|
---|
| 1018 | || cOuterTries <= 0)
|
---|
[2283] | 1019 | {
|
---|
[19810] | 1020 | uint64_t u64Delta = u64Now - u64Prev;
|
---|
| 1021 | if (RT_LIKELY(!(u64Delta >> 32)))
|
---|
| 1022 | {
|
---|
| 1023 | uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, u32Pct, 100);
|
---|
| 1024 | if (off > u64Sub + offGivenUp)
|
---|
| 1025 | off -= u64Sub;
|
---|
| 1026 | else /* we've completely caught up. */
|
---|
| 1027 | off = offGivenUp;
|
---|
| 1028 | }
|
---|
[2283] | 1029 | else
|
---|
[19810] | 1030 | /* More than 4 seconds since last time (or negative), ignore it. */
|
---|
| 1031 | Log(("TMVirtualGetSync: u64Delta=%RX64 (NoLock)\n", u64Delta));
|
---|
| 1032 |
|
---|
| 1033 | /* Check that we're still running and in catch up. */
|
---|
| 1034 | if ( ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncTicking)
|
---|
| 1035 | && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
|
---|
| 1036 | break;
|
---|
[2283] | 1037 | }
|
---|
| 1038 | }
|
---|
[19810] | 1039 | else if ( off == ASMAtomicReadU64(&pVM->tm.s.offVirtualSync)
|
---|
[87792] | 1040 | && u64Expire2 == ASMAtomicReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire)
|
---|
[19810] | 1041 | && !ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp)
|
---|
| 1042 | && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncTicking))
|
---|
| 1043 | break; /* Got an consistent offset */
|
---|
| 1044 |
|
---|
| 1045 | /* Repeat the initial checks before iterating. */
|
---|
[46420] | 1046 | if (VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
|
---|
[19820] | 1047 | return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollAlreadySet);
|
---|
[19810] | 1048 | if (ASMAtomicUoReadBool(&pVM->tm.s.fRunningQueues))
|
---|
| 1049 | {
|
---|
| 1050 | STAM_COUNTER_INC(&pVM->tm.s.StatPollRunning);
|
---|
[19820] | 1051 | return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
|
---|
[19810] | 1052 | }
|
---|
| 1053 | if (!ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncTicking))
|
---|
| 1054 | {
|
---|
| 1055 | LogFlow(("TMTimerPoll: stopped\n"));
|
---|
[19820] | 1056 | return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
|
---|
[19810] | 1057 | }
|
---|
| 1058 | if (cOuterTries <= 0)
|
---|
| 1059 | break; /* that's enough */
|
---|
[2283] | 1060 | }
|
---|
[19810] | 1061 | if (cOuterTries <= 0)
|
---|
| 1062 | STAM_COUNTER_INC(&pVM->tm.s.StatPollELoop);
|
---|
| 1063 | u64VirtualSyncNow = u64Now - off;
|
---|
| 1064 |
|
---|
[19820] | 1065 | /* Calc delta and see if we've got a virtual sync hit. */
|
---|
[2283] | 1066 | int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
|
---|
[1] | 1067 | if (i64Delta2 <= 0)
|
---|
| 1068 | {
|
---|
[19660] | 1069 | if ( !pVM->tm.s.fRunningQueues
|
---|
[46420] | 1070 | && !VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
|
---|
[19660] | 1071 | {
|
---|
[74785] | 1072 | Log5(("TMAll(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER)));
|
---|
[19660] | 1073 | VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
|
---|
| 1074 | }
|
---|
[19444] | 1075 | STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtualSync);
|
---|
[20050] | 1076 | LogFlow(("TMTimerPoll: expire2=%'RU64 <= now=%'RU64\n", u64Expire2, u64Now));
|
---|
[19820] | 1077 | return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
|
---|
[1] | 1078 | }
|
---|
| 1079 |
|
---|
| 1080 | /*
|
---|
[2283] | 1081 | * Return the time left to the next event.
|
---|
[1] | 1082 | */
|
---|
| 1083 | STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
|
---|
[19810] | 1084 | if (pVCpu == pVCpuDst)
|
---|
| 1085 | {
|
---|
| 1086 | if (fCatchUp)
|
---|
| 1087 | i64Delta2 = ASMMultU64ByU32DivByU32(i64Delta2, 100, u32Pct + 100);
|
---|
[19820] | 1088 | return tmTimerPollReturnMiss(pVM, u64Now, RT_MIN(i64Delta1, i64Delta2), pu64Delta);
|
---|
[19810] | 1089 | }
|
---|
[19820] | 1090 | return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
|
---|
[1] | 1091 | }
|
---|
[5167] | 1092 |
|
---|
| 1093 |
|
---|
| 1094 | /**
|
---|
| 1095 | * Set FF if we've passed the next virtual event.
|
---|
| 1096 | *
|
---|
| 1097 | * This function is called before FFs are checked in the inner execution EM loops.
|
---|
| 1098 | *
|
---|
[19821] | 1099 | * @returns true if timers are pending, false if not.
|
---|
| 1100 | *
|
---|
[58122] | 1101 | * @param pVM The cross context VM structure.
|
---|
[58123] | 1102 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
[19820] | 1103 | * @thread The emulation thread.
|
---|
| 1104 | */
|
---|
[80268] | 1105 | VMMDECL(bool) TMTimerPollBool(PVMCC pVM, PVMCPUCC pVCpu)
|
---|
[19820] | 1106 | {
|
---|
| 1107 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
| 1108 | uint64_t off = 0;
|
---|
[101088] | 1109 | tmTimerPollInternal(pVM, pVCpu, &off, NULL);
|
---|
[19821] | 1110 | return off == 0;
|
---|
[19820] | 1111 | }
|
---|
| 1112 |
|
---|
| 1113 |
|
---|
| 1114 | /**
|
---|
[101088] | 1115 | * Set FF if we've passed the next virtual event and return virtual time as MS.
|
---|
| 1116 | *
|
---|
| 1117 | * This function is called before FFs are checked in the inner execution EM loops.
|
---|
| 1118 | *
|
---|
| 1119 | * This is used by the IEM recompiler for polling timers while also providing a
|
---|
| 1120 | * free time source for recent use tracking and such.
|
---|
| 1121 | *
|
---|
| 1122 | * @returns true if timers are pending, false if not.
|
---|
| 1123 | *
|
---|
| 1124 | * @param pVM The cross context VM structure.
|
---|
| 1125 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
| 1126 | * @param pmsNow Where to return the current virtual time in
|
---|
| 1127 | * milliseconds.
|
---|
| 1128 | * @thread The emulation thread.
|
---|
| 1129 | */
|
---|
| 1130 | VMMDECL(bool) TMTimerPollBoolWith32BitMilliTS(PVMCC pVM, PVMCPUCC pVCpu, uint32_t *pmsNow)
|
---|
| 1131 | {
|
---|
| 1132 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
| 1133 | uint64_t off = 0;
|
---|
| 1134 | uint64_t u64Now = 0;
|
---|
| 1135 | tmTimerPollInternal(pVM, pVCpu, &off, &u64Now);
|
---|
| 1136 | *pmsNow = (uint32_t)(u64Now / RT_NS_1MS);
|
---|
| 1137 | return off == 0;
|
---|
| 1138 | }
|
---|
| 1139 |
|
---|
| 1140 |
|
---|
| 1141 | /**
|
---|
[19820] | 1142 | * Set FF if we've passed the next virtual event.
|
---|
| 1143 | *
|
---|
| 1144 | * This function is called before FFs are checked in the inner execution EM loops.
|
---|
| 1145 | *
|
---|
[58122] | 1146 | * @param pVM The cross context VM structure.
|
---|
[58123] | 1147 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
[19821] | 1148 | * @thread The emulation thread.
|
---|
| 1149 | */
|
---|
[80268] | 1150 | VMM_INT_DECL(void) TMTimerPollVoid(PVMCC pVM, PVMCPUCC pVCpu)
|
---|
[19821] | 1151 | {
|
---|
| 1152 | uint64_t off;
|
---|
[101088] | 1153 | tmTimerPollInternal(pVM, pVCpu, &off, NULL);
|
---|
[19821] | 1154 | }
|
---|
| 1155 |
|
---|
| 1156 |
|
---|
| 1157 | /**
|
---|
| 1158 | * Set FF if we've passed the next virtual event.
|
---|
| 1159 | *
|
---|
| 1160 | * This function is called before FFs are checked in the inner execution EM loops.
|
---|
| 1161 | *
|
---|
[5167] | 1162 | * @returns The GIP timestamp of the next event.
|
---|
| 1163 | * 0 if the next event has already expired.
|
---|
[58122] | 1164 | * @param pVM The cross context VM structure.
|
---|
[58123] | 1165 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
[5167] | 1166 | * @param pu64Delta Where to store the delta.
|
---|
| 1167 | * @thread The emulation thread.
|
---|
| 1168 | */
|
---|
[80268] | 1169 | VMM_INT_DECL(uint64_t) TMTimerPollGIP(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *pu64Delta)
|
---|
[5167] | 1170 | {
|
---|
[101088] | 1171 | return tmTimerPollInternal(pVM, pVCpu, pu64Delta, NULL);
|
---|
[5167] | 1172 | }
|
---|
| 1173 |
|
---|
[19820] | 1174 | #endif /* VBOX_HIGH_RES_TIMERS_HACK */
|
---|
[5167] | 1175 |
|
---|
[1] | 1176 | /**
|
---|
[37414] | 1177 | * Locks the timer clock.
|
---|
| 1178 | *
|
---|
| 1179 | * @returns VINF_SUCCESS on success, @a rcBusy if busy, and VERR_NOT_SUPPORTED
|
---|
| 1180 | * if the clock does not have a lock.
|
---|
[87766] | 1181 | * @param pVM The cross context VM structure.
|
---|
| 1182 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 1183 | * @param rcBusy What to return in ring-0 and raw-mode context if the
|
---|
| 1184 | * lock is busy. Pass VINF_SUCCESS to acquired the
|
---|
| 1185 | * critical section thru a ring-3 call if necessary.
|
---|
[37414] | 1186 | *
|
---|
| 1187 | * @remarks Currently only supported on timers using the virtual sync clock.
|
---|
| 1188 | */
|
---|
[87766] | 1189 | VMMDECL(int) TMTimerLock(PVMCC pVM, TMTIMERHANDLE hTimer, int rcBusy)
|
---|
[37414] | 1190 | {
|
---|
[87813] | 1191 | TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 1192 | AssertReturn(idxQueue == TMCLOCK_VIRTUAL_SYNC, VERR_NOT_SUPPORTED);
|
---|
[90346] | 1193 | return PDMCritSectEnter(pVM, &pVM->tm.s.VirtualSyncLock, rcBusy);
|
---|
[37414] | 1194 | }
|
---|
| 1195 |
|
---|
| 1196 |
|
---|
| 1197 | /**
|
---|
| 1198 | * Unlocks a timer clock locked by TMTimerLock.
|
---|
| 1199 | *
|
---|
[87766] | 1200 | * @param pVM The cross context VM structure.
|
---|
| 1201 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[37414] | 1202 | */
|
---|
[87766] | 1203 | VMMDECL(void) TMTimerUnlock(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[37414] | 1204 | {
|
---|
[87813] | 1205 | TMTIMER_HANDLE_TO_VARS_RETURN_VOID(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 1206 | AssertReturnVoid(idxQueue == TMCLOCK_VIRTUAL_SYNC);
|
---|
[90346] | 1207 | PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
|
---|
[37414] | 1208 | }
|
---|
| 1209 |
|
---|
| 1210 |
|
---|
| 1211 | /**
|
---|
| 1212 | * Checks if the current thread owns the timer clock lock.
|
---|
| 1213 | *
|
---|
| 1214 | * @returns @c true if its the owner, @c false if not.
|
---|
[87766] | 1215 | * @param pVM The cross context VM structure.
|
---|
| 1216 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[37414] | 1217 | */
|
---|
[87766] | 1218 | VMMDECL(bool) TMTimerIsLockOwner(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[37414] | 1219 | {
|
---|
[87813] | 1220 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, false); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 1221 | AssertReturn(idxQueue == TMCLOCK_VIRTUAL_SYNC, false);
|
---|
[90346] | 1222 | return PDMCritSectIsOwner(pVM, &pVM->tm.s.VirtualSyncLock);
|
---|
[37414] | 1223 | }
|
---|
| 1224 |
|
---|
| 1225 |
|
---|
| 1226 | /**
|
---|
[20752] | 1227 | * Optimized TMTimerSet code path for starting an inactive timer.
|
---|
| 1228 | *
|
---|
| 1229 | * @returns VBox status code.
|
---|
| 1230 | *
|
---|
[58122] | 1231 | * @param pVM The cross context VM structure.
|
---|
[20752] | 1232 | * @param pTimer The timer handle.
|
---|
| 1233 | * @param u64Expire The new expire time.
|
---|
[87812] | 1234 | * @param pQueue Pointer to the shared timer queue data.
|
---|
[87813] | 1235 | * @param idxQueue The queue index.
|
---|
[20752] | 1236 | */
|
---|
[87813] | 1237 | static int tmTimerSetOptimizedStart(PVMCC pVM, PTMTIMER pTimer, uint64_t u64Expire, PTMTIMERQUEUE pQueue, uint32_t idxQueue)
|
---|
[20752] | 1238 | {
|
---|
[87792] | 1239 | Assert(pTimer->idxPrev == UINT32_MAX);
|
---|
| 1240 | Assert(pTimer->idxNext == UINT32_MAX);
|
---|
[20752] | 1241 | Assert(pTimer->enmState == TMTIMERSTATE_ACTIVE);
|
---|
| 1242 |
|
---|
| 1243 | /*
|
---|
| 1244 | * Calculate and set the expiration time.
|
---|
| 1245 | */
|
---|
[87813] | 1246 | if (idxQueue == TMCLOCK_VIRTUAL_SYNC)
|
---|
[37517] | 1247 | {
|
---|
| 1248 | uint64_t u64Last = ASMAtomicReadU64(&pVM->tm.s.u64VirtualSync);
|
---|
| 1249 | AssertMsgStmt(u64Expire >= u64Last,
|
---|
| 1250 | ("exp=%#llx last=%#llx\n", u64Expire, u64Last),
|
---|
| 1251 | u64Expire = u64Last);
|
---|
| 1252 | }
|
---|
| 1253 | ASMAtomicWriteU64(&pTimer->u64Expire, u64Expire);
|
---|
[87773] | 1254 | Log2(("tmTimerSetOptimizedStart: %p:{.pszDesc='%s', .u64Expire=%'RU64}\n", pTimer, pTimer->szName, u64Expire));
|
---|
[20752] | 1255 |
|
---|
| 1256 | /*
|
---|
| 1257 | * Link the timer into the active list.
|
---|
| 1258 | */
|
---|
[87813] | 1259 | tmTimerQueueLinkActive(pVM, TM_GET_TIMER_QUEUE_CC(pVM, idxQueue, pQueue), pQueue, pTimer, u64Expire);
|
---|
[20752] | 1260 |
|
---|
| 1261 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetOpt);
|
---|
| 1262 | return VINF_SUCCESS;
|
---|
| 1263 | }
|
---|
| 1264 |
|
---|
| 1265 |
|
---|
[37517] | 1266 | /**
|
---|
| 1267 | * TMTimerSet for the virtual sync timer queue.
|
---|
| 1268 | *
|
---|
| 1269 | * This employs a greatly simplified state machine by always acquiring the
|
---|
| 1270 | * queue lock and bypassing the scheduling list.
|
---|
| 1271 | *
|
---|
| 1272 | * @returns VBox status code
|
---|
[58122] | 1273 | * @param pVM The cross context VM structure.
|
---|
[37517] | 1274 | * @param pTimer The timer handle.
|
---|
| 1275 | * @param u64Expire The expiration time.
|
---|
| 1276 | */
|
---|
[80281] | 1277 | static int tmTimerVirtualSyncSet(PVMCC pVM, PTMTIMER pTimer, uint64_t u64Expire)
|
---|
[37517] | 1278 | {
|
---|
| 1279 | STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetVs), a);
|
---|
| 1280 | VM_ASSERT_EMT(pVM);
|
---|
[50385] | 1281 | TMTIMER_ASSERT_SYNC_CRITSECT_ORDER(pVM, pTimer);
|
---|
[90346] | 1282 | int rc = PDMCritSectEnter(pVM, &pVM->tm.s.VirtualSyncLock, VINF_SUCCESS);
|
---|
[37517] | 1283 | AssertRCReturn(rc, rc);
|
---|
[20752] | 1284 |
|
---|
[87792] | 1285 | PTMTIMERQUEUE const pQueue = &pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC];
|
---|
| 1286 | PTMTIMERQUEUECC const pQueueCC = TM_GET_TIMER_QUEUE_CC(pVM, TMCLOCK_VIRTUAL_SYNC, pQueue);
|
---|
| 1287 | TMTIMERSTATE const enmState = pTimer->enmState;
|
---|
[37517] | 1288 | switch (enmState)
|
---|
| 1289 | {
|
---|
| 1290 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
| 1291 | case TMTIMERSTATE_STOPPED:
|
---|
| 1292 | if (enmState == TMTIMERSTATE_EXPIRED_DELIVER)
|
---|
| 1293 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetVsStExpDeliver);
|
---|
| 1294 | else
|
---|
| 1295 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetVsStStopped);
|
---|
[20752] | 1296 |
|
---|
[37517] | 1297 | AssertMsg(u64Expire >= pVM->tm.s.u64VirtualSync,
|
---|
[87773] | 1298 | ("%'RU64 < %'RU64 %s\n", u64Expire, pVM->tm.s.u64VirtualSync, pTimer->szName));
|
---|
[37517] | 1299 | pTimer->u64Expire = u64Expire;
|
---|
| 1300 | TM_SET_STATE(pTimer, TMTIMERSTATE_ACTIVE);
|
---|
[87792] | 1301 | tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
|
---|
[37517] | 1302 | rc = VINF_SUCCESS;
|
---|
| 1303 | break;
|
---|
[20752] | 1304 |
|
---|
[37517] | 1305 | case TMTIMERSTATE_ACTIVE:
|
---|
| 1306 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetVsStActive);
|
---|
[87792] | 1307 | tmTimerQueueUnlinkActive(pVM, pQueueCC, pQueue, pTimer);
|
---|
[37517] | 1308 | pTimer->u64Expire = u64Expire;
|
---|
[87792] | 1309 | tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
|
---|
[37517] | 1310 | rc = VINF_SUCCESS;
|
---|
| 1311 | break;
|
---|
| 1312 |
|
---|
| 1313 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 1314 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 1315 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
| 1316 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
| 1317 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
| 1318 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 1319 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 1320 | case TMTIMERSTATE_DESTROY:
|
---|
| 1321 | case TMTIMERSTATE_FREE:
|
---|
[87773] | 1322 | AssertLogRelMsgFailed(("Invalid timer state %s: %s\n", tmTimerState(enmState), pTimer->szName));
|
---|
[37517] | 1323 | rc = VERR_TM_INVALID_STATE;
|
---|
| 1324 | break;
|
---|
| 1325 |
|
---|
| 1326 | default:
|
---|
[87773] | 1327 | AssertMsgFailed(("Unknown timer state %d: %s\n", enmState, pTimer->szName));
|
---|
[37517] | 1328 | rc = VERR_TM_UNKNOWN_STATE;
|
---|
| 1329 | break;
|
---|
| 1330 | }
|
---|
| 1331 |
|
---|
| 1332 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetVs), a);
|
---|
[90346] | 1333 | PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
|
---|
[37517] | 1334 | return rc;
|
---|
| 1335 | }
|
---|
| 1336 |
|
---|
| 1337 |
|
---|
[20752] | 1338 | /**
|
---|
[1] | 1339 | * Arm a timer with a (new) expire time.
|
---|
| 1340 | *
|
---|
[58170] | 1341 | * @returns VBox status code.
|
---|
[87766] | 1342 | * @param pVM The cross context VM structure.
|
---|
| 1343 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 1344 | * @param u64Expire New expire time.
|
---|
[1] | 1345 | */
|
---|
[87766] | 1346 | VMMDECL(int) TMTimerSet(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t u64Expire)
|
---|
[1] | 1347 | {
|
---|
[87813] | 1348 | TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
[82333] | 1349 | STAM_COUNTER_INC(&pTimer->StatSetAbsolute);
|
---|
[37517] | 1350 |
|
---|
| 1351 | /* Treat virtual sync timers specially. */
|
---|
[87813] | 1352 | if (idxQueue == TMCLOCK_VIRTUAL_SYNC)
|
---|
[37517] | 1353 | return tmTimerVirtualSyncSet(pVM, pTimer, u64Expire);
|
---|
| 1354 |
|
---|
[20752] | 1355 | STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
|
---|
[87771] | 1356 | TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
|
---|
[1] | 1357 |
|
---|
[87773] | 1358 | DBGFTRACE_U64_TAG2(pVM, u64Expire, "TMTimerSet", pTimer->szName);
|
---|
[37517] | 1359 |
|
---|
[20752] | 1360 | #ifdef VBOX_WITH_STATISTICS
|
---|
[37517] | 1361 | /*
|
---|
| 1362 | * Gather optimization info.
|
---|
| 1363 | */
|
---|
[20752] | 1364 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSet);
|
---|
| 1365 | TMTIMERSTATE enmOrgState = pTimer->enmState;
|
---|
| 1366 | switch (enmOrgState)
|
---|
| 1367 | {
|
---|
| 1368 | case TMTIMERSTATE_STOPPED: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStStopped); break;
|
---|
| 1369 | case TMTIMERSTATE_EXPIRED_DELIVER: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStExpDeliver); break;
|
---|
| 1370 | case TMTIMERSTATE_ACTIVE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStActive); break;
|
---|
| 1371 | case TMTIMERSTATE_PENDING_STOP: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStPendStop); break;
|
---|
| 1372 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStPendStopSched); break;
|
---|
| 1373 | case TMTIMERSTATE_PENDING_SCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStPendSched); break;
|
---|
| 1374 | case TMTIMERSTATE_PENDING_RESCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStPendResched); break;
|
---|
| 1375 | default: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStOther); break;
|
---|
| 1376 | }
|
---|
| 1377 | #endif
|
---|
| 1378 |
|
---|
[87813] | 1379 | #if 1
|
---|
[20752] | 1380 | /*
|
---|
| 1381 | * The most common case is setting the timer again during the callback.
|
---|
| 1382 | * The second most common case is starting a timer at some other time.
|
---|
| 1383 | */
|
---|
[25247] | 1384 | TMTIMERSTATE enmState1 = pTimer->enmState;
|
---|
| 1385 | if ( enmState1 == TMTIMERSTATE_EXPIRED_DELIVER
|
---|
| 1386 | || ( enmState1 == TMTIMERSTATE_STOPPED
|
---|
[20752] | 1387 | && pTimer->pCritSect))
|
---|
| 1388 | {
|
---|
| 1389 | /* Try take the TM lock and check the state again. */
|
---|
[90346] | 1390 | int rc = PDMCritSectTryEnter(pVM, &pQueue->TimerLock);
|
---|
[87812] | 1391 | if (RT_SUCCESS_NP(rc))
|
---|
[20752] | 1392 | {
|
---|
[25247] | 1393 | if (RT_LIKELY(tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, enmState1)))
|
---|
[20752] | 1394 | {
|
---|
[87813] | 1395 | tmTimerSetOptimizedStart(pVM, pTimer, u64Expire, pQueue, idxQueue);
|
---|
[37517] | 1396 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
|
---|
[90346] | 1397 | PDMCritSectLeave(pVM, &pQueue->TimerLock);
|
---|
[20752] | 1398 | return VINF_SUCCESS;
|
---|
| 1399 | }
|
---|
[90346] | 1400 | PDMCritSectLeave(pVM, &pQueue->TimerLock);
|
---|
[20752] | 1401 | }
|
---|
| 1402 | }
|
---|
| 1403 | #endif
|
---|
| 1404 |
|
---|
| 1405 | /*
|
---|
| 1406 | * Unoptimized code path.
|
---|
| 1407 | */
|
---|
[1] | 1408 | int cRetries = 1000;
|
---|
| 1409 | do
|
---|
| 1410 | {
|
---|
| 1411 | /*
|
---|
| 1412 | * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
|
---|
| 1413 | */
|
---|
[25247] | 1414 | TMTIMERSTATE enmState = pTimer->enmState;
|
---|
[20050] | 1415 | Log2(("TMTimerSet: %p:{.enmState=%s, .pszDesc='%s'} cRetries=%d u64Expire=%'RU64\n",
|
---|
[87773] | 1416 | pTimer, tmTimerState(enmState), pTimer->szName, cRetries, u64Expire));
|
---|
[1] | 1417 | switch (enmState)
|
---|
| 1418 | {
|
---|
[20050] | 1419 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
[1] | 1420 | case TMTIMERSTATE_STOPPED:
|
---|
[87814] | 1421 | if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
|
---|
[1] | 1422 | {
|
---|
[87792] | 1423 | Assert(pTimer->idxPrev == UINT32_MAX);
|
---|
| 1424 | Assert(pTimer->idxNext == UINT32_MAX);
|
---|
[1] | 1425 | pTimer->u64Expire = u64Expire;
|
---|
| 1426 | TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
|
---|
[87814] | 1427 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[20752] | 1428 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
|
---|
[1] | 1429 | return VINF_SUCCESS;
|
---|
| 1430 | }
|
---|
| 1431 | break;
|
---|
| 1432 |
|
---|
| 1433 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
[6409] | 1434 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
[1] | 1435 | if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
|
---|
| 1436 | {
|
---|
| 1437 | pTimer->u64Expire = u64Expire;
|
---|
| 1438 | TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
|
---|
[87814] | 1439 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[20752] | 1440 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
|
---|
[1] | 1441 | return VINF_SUCCESS;
|
---|
| 1442 | }
|
---|
| 1443 | break;
|
---|
| 1444 |
|
---|
| 1445 |
|
---|
| 1446 | case TMTIMERSTATE_ACTIVE:
|
---|
[87814] | 1447 | if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
|
---|
[1] | 1448 | {
|
---|
| 1449 | pTimer->u64Expire = u64Expire;
|
---|
| 1450 | TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
|
---|
[87814] | 1451 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[20752] | 1452 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
|
---|
[1] | 1453 | return VINF_SUCCESS;
|
---|
| 1454 | }
|
---|
| 1455 | break;
|
---|
| 1456 |
|
---|
| 1457 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 1458 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 1459 | if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
|
---|
| 1460 | {
|
---|
| 1461 | pTimer->u64Expire = u64Expire;
|
---|
| 1462 | TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
|
---|
[87814] | 1463 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[20752] | 1464 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
|
---|
[1] | 1465 | return VINF_SUCCESS;
|
---|
| 1466 | }
|
---|
| 1467 | break;
|
---|
| 1468 |
|
---|
| 1469 |
|
---|
[20050] | 1470 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
[1] | 1471 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 1472 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 1473 | #ifdef IN_RING3
|
---|
| 1474 | if (!RTThreadYield())
|
---|
| 1475 | RTThreadSleep(1);
|
---|
| 1476 | #else
|
---|
| 1477 | /** @todo call host context and yield after a couple of iterations */
|
---|
| 1478 | #endif
|
---|
| 1479 | break;
|
---|
| 1480 |
|
---|
| 1481 | /*
|
---|
| 1482 | * Invalid states.
|
---|
| 1483 | */
|
---|
[19537] | 1484 | case TMTIMERSTATE_DESTROY:
|
---|
[1] | 1485 | case TMTIMERSTATE_FREE:
|
---|
[87773] | 1486 | AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, pTimer->szName));
|
---|
[1] | 1487 | return VERR_TM_INVALID_STATE;
|
---|
| 1488 | default:
|
---|
[87773] | 1489 | AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
|
---|
[1] | 1490 | return VERR_TM_UNKNOWN_STATE;
|
---|
| 1491 | }
|
---|
| 1492 | } while (cRetries-- > 0);
|
---|
| 1493 |
|
---|
[87773] | 1494 | AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, pTimer->szName));
|
---|
[20752] | 1495 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
|
---|
[39402] | 1496 | return VERR_TM_TIMER_UNSTABLE_STATE;
|
---|
[1] | 1497 | }
|
---|
| 1498 |
|
---|
| 1499 |
|
---|
| 1500 | /**
|
---|
[20733] | 1501 | * Return the current time for the specified clock, setting pu64Now if not NULL.
|
---|
| 1502 | *
|
---|
| 1503 | * @returns Current time.
|
---|
[58122] | 1504 | * @param pVM The cross context VM structure.
|
---|
[20733] | 1505 | * @param enmClock The clock to query.
|
---|
| 1506 | * @param pu64Now Optional pointer where to store the return time
|
---|
| 1507 | */
|
---|
[80281] | 1508 | DECL_FORCE_INLINE(uint64_t) tmTimerSetRelativeNowWorker(PVMCC pVM, TMCLOCK enmClock, uint64_t *pu64Now)
|
---|
[20733] | 1509 | {
|
---|
| 1510 | uint64_t u64Now;
|
---|
| 1511 | switch (enmClock)
|
---|
| 1512 | {
|
---|
| 1513 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 1514 | u64Now = TMVirtualSyncGet(pVM);
|
---|
| 1515 | break;
|
---|
| 1516 | case TMCLOCK_VIRTUAL:
|
---|
| 1517 | u64Now = TMVirtualGet(pVM);
|
---|
| 1518 | break;
|
---|
| 1519 | case TMCLOCK_REAL:
|
---|
| 1520 | u64Now = TMRealGet(pVM);
|
---|
| 1521 | break;
|
---|
| 1522 | default:
|
---|
| 1523 | AssertFatalMsgFailed(("%d\n", enmClock));
|
---|
| 1524 | }
|
---|
| 1525 |
|
---|
| 1526 | if (pu64Now)
|
---|
| 1527 | *pu64Now = u64Now;
|
---|
| 1528 | return u64Now;
|
---|
| 1529 | }
|
---|
| 1530 |
|
---|
| 1531 |
|
---|
| 1532 | /**
|
---|
[20750] | 1533 | * Optimized TMTimerSetRelative code path.
|
---|
| 1534 | *
|
---|
| 1535 | * @returns VBox status code.
|
---|
| 1536 | *
|
---|
[58122] | 1537 | * @param pVM The cross context VM structure.
|
---|
[20750] | 1538 | * @param pTimer The timer handle.
|
---|
| 1539 | * @param cTicksToNext Clock ticks until the next time expiration.
|
---|
| 1540 | * @param pu64Now Where to return the current time stamp used.
|
---|
| 1541 | * Optional.
|
---|
[87814] | 1542 | * @param pQueueCC The context specific queue data (same as @a pQueue
|
---|
| 1543 | * for ring-3).
|
---|
| 1544 | * @param pQueue The shared queue data.
|
---|
[20750] | 1545 | */
|
---|
[87812] | 1546 | static int tmTimerSetRelativeOptimizedStart(PVMCC pVM, PTMTIMER pTimer, uint64_t cTicksToNext, uint64_t *pu64Now,
|
---|
[87814] | 1547 | PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue)
|
---|
[20750] | 1548 | {
|
---|
[87792] | 1549 | Assert(pTimer->idxPrev == UINT32_MAX);
|
---|
| 1550 | Assert(pTimer->idxNext == UINT32_MAX);
|
---|
[20750] | 1551 | Assert(pTimer->enmState == TMTIMERSTATE_ACTIVE);
|
---|
| 1552 |
|
---|
| 1553 | /*
|
---|
| 1554 | * Calculate and set the expiration time.
|
---|
| 1555 | */
|
---|
[87813] | 1556 | uint64_t const u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
|
---|
[20750] | 1557 | pTimer->u64Expire = u64Expire;
|
---|
[87773] | 1558 | Log2(("tmTimerSetRelativeOptimizedStart: %p:{.pszDesc='%s', .u64Expire=%'RU64} cTicksToNext=%'RU64\n", pTimer, pTimer->szName, u64Expire, cTicksToNext));
|
---|
[20750] | 1559 |
|
---|
| 1560 | /*
|
---|
| 1561 | * Link the timer into the active list.
|
---|
| 1562 | */
|
---|
[87773] | 1563 | DBGFTRACE_U64_TAG2(pVM, u64Expire, "tmTimerSetRelativeOptimizedStart", pTimer->szName);
|
---|
[87814] | 1564 | tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
|
---|
[20750] | 1565 |
|
---|
| 1566 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeOpt);
|
---|
| 1567 | return VINF_SUCCESS;
|
---|
| 1568 | }
|
---|
| 1569 |
|
---|
| 1570 |
|
---|
| 1571 | /**
|
---|
[37517] | 1572 | * TMTimerSetRelative for the virtual sync timer queue.
|
---|
| 1573 | *
|
---|
| 1574 | * This employs a greatly simplified state machine by always acquiring the
|
---|
| 1575 | * queue lock and bypassing the scheduling list.
|
---|
| 1576 | *
|
---|
| 1577 | * @returns VBox status code
|
---|
[58122] | 1578 | * @param pVM The cross context VM structure.
|
---|
[58126] | 1579 | * @param pTimer The timer to (re-)arm.
|
---|
[37517] | 1580 | * @param cTicksToNext Clock ticks until the next time expiration.
|
---|
| 1581 | * @param pu64Now Where to return the current time stamp used.
|
---|
| 1582 | * Optional.
|
---|
| 1583 | */
|
---|
[80281] | 1584 | static int tmTimerVirtualSyncSetRelative(PVMCC pVM, PTMTIMER pTimer, uint64_t cTicksToNext, uint64_t *pu64Now)
|
---|
[37517] | 1585 | {
|
---|
| 1586 | STAM_PROFILE_START(pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelativeVs), a);
|
---|
| 1587 | VM_ASSERT_EMT(pVM);
|
---|
[50385] | 1588 | TMTIMER_ASSERT_SYNC_CRITSECT_ORDER(pVM, pTimer);
|
---|
[90346] | 1589 | int rc = PDMCritSectEnter(pVM, &pVM->tm.s.VirtualSyncLock, VINF_SUCCESS);
|
---|
[37517] | 1590 | AssertRCReturn(rc, rc);
|
---|
| 1591 |
|
---|
| 1592 | /* Calculate the expiration tick. */
|
---|
| 1593 | uint64_t u64Expire = TMVirtualSyncGetNoCheck(pVM);
|
---|
| 1594 | if (pu64Now)
|
---|
| 1595 | *pu64Now = u64Expire;
|
---|
| 1596 | u64Expire += cTicksToNext;
|
---|
| 1597 |
|
---|
| 1598 | /* Update the timer. */
|
---|
[87792] | 1599 | PTMTIMERQUEUE const pQueue = &pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC];
|
---|
| 1600 | PTMTIMERQUEUECC const pQueueCC = TM_GET_TIMER_QUEUE_CC(pVM, TMCLOCK_VIRTUAL_SYNC, pQueue);
|
---|
| 1601 | TMTIMERSTATE const enmState = pTimer->enmState;
|
---|
[37517] | 1602 | switch (enmState)
|
---|
| 1603 | {
|
---|
| 1604 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
| 1605 | case TMTIMERSTATE_STOPPED:
|
---|
| 1606 | if (enmState == TMTIMERSTATE_EXPIRED_DELIVER)
|
---|
| 1607 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeVsStExpDeliver);
|
---|
| 1608 | else
|
---|
| 1609 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeVsStStopped);
|
---|
| 1610 | pTimer->u64Expire = u64Expire;
|
---|
| 1611 | TM_SET_STATE(pTimer, TMTIMERSTATE_ACTIVE);
|
---|
[87792] | 1612 | tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
|
---|
[37517] | 1613 | rc = VINF_SUCCESS;
|
---|
| 1614 | break;
|
---|
| 1615 |
|
---|
| 1616 | case TMTIMERSTATE_ACTIVE:
|
---|
| 1617 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeVsStActive);
|
---|
[87792] | 1618 | tmTimerQueueUnlinkActive(pVM, pQueueCC, pQueue, pTimer);
|
---|
[37517] | 1619 | pTimer->u64Expire = u64Expire;
|
---|
[87792] | 1620 | tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
|
---|
[37517] | 1621 | rc = VINF_SUCCESS;
|
---|
| 1622 | break;
|
---|
| 1623 |
|
---|
| 1624 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 1625 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 1626 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
| 1627 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
| 1628 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
| 1629 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 1630 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 1631 | case TMTIMERSTATE_DESTROY:
|
---|
| 1632 | case TMTIMERSTATE_FREE:
|
---|
[87773] | 1633 | AssertLogRelMsgFailed(("Invalid timer state %s: %s\n", tmTimerState(enmState), pTimer->szName));
|
---|
[37517] | 1634 | rc = VERR_TM_INVALID_STATE;
|
---|
| 1635 | break;
|
---|
| 1636 |
|
---|
| 1637 | default:
|
---|
[87773] | 1638 | AssertMsgFailed(("Unknown timer state %d: %s\n", enmState, pTimer->szName));
|
---|
[37517] | 1639 | rc = VERR_TM_UNKNOWN_STATE;
|
---|
| 1640 | break;
|
---|
| 1641 | }
|
---|
| 1642 |
|
---|
| 1643 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelativeVs), a);
|
---|
[90346] | 1644 | PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
|
---|
[37517] | 1645 | return rc;
|
---|
| 1646 | }
|
---|
| 1647 |
|
---|
| 1648 |
|
---|
| 1649 | /**
|
---|
[20733] | 1650 | * Arm a timer with a expire time relative to the current time.
|
---|
| 1651 | *
|
---|
[58170] | 1652 | * @returns VBox status code.
|
---|
[87766] | 1653 | * @param pVM The cross context VM structure.
|
---|
[87770] | 1654 | * @param pTimer The timer to arm.
|
---|
[20733] | 1655 | * @param cTicksToNext Clock ticks until the next time expiration.
|
---|
| 1656 | * @param pu64Now Where to return the current time stamp used.
|
---|
| 1657 | * Optional.
|
---|
[87814] | 1658 | * @param pQueueCC The context specific queue data (same as @a pQueue
|
---|
| 1659 | * for ring-3).
|
---|
| 1660 | * @param pQueue The shared queue data.
|
---|
[20733] | 1661 | */
|
---|
[87813] | 1662 | static int tmTimerSetRelative(PVMCC pVM, PTMTIMER pTimer, uint64_t cTicksToNext, uint64_t *pu64Now,
|
---|
[87814] | 1663 | PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue)
|
---|
[20733] | 1664 | {
|
---|
[82333] | 1665 | STAM_COUNTER_INC(&pTimer->StatSetRelative);
|
---|
[37517] | 1666 |
|
---|
| 1667 | /* Treat virtual sync timers specially. */
|
---|
[87814] | 1668 | if (pQueue->enmClock == TMCLOCK_VIRTUAL_SYNC)
|
---|
[37517] | 1669 | return tmTimerVirtualSyncSetRelative(pVM, pTimer, cTicksToNext, pu64Now);
|
---|
| 1670 |
|
---|
| 1671 | STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelative), a);
|
---|
[87771] | 1672 | TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
|
---|
[20733] | 1673 |
|
---|
[87773] | 1674 | DBGFTRACE_U64_TAG2(pVM, cTicksToNext, "TMTimerSetRelative", pTimer->szName);
|
---|
[37517] | 1675 |
|
---|
[20750] | 1676 | #ifdef VBOX_WITH_STATISTICS
|
---|
[37517] | 1677 | /*
|
---|
| 1678 | * Gather optimization info.
|
---|
| 1679 | */
|
---|
[20750] | 1680 | STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelative);
|
---|
| 1681 | TMTIMERSTATE enmOrgState = pTimer->enmState;
|
---|
| 1682 | switch (enmOrgState)
|
---|
[20733] | 1683 | {
|
---|
[20750] | 1684 | case TMTIMERSTATE_STOPPED: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStStopped); break;
|
---|
| 1685 | case TMTIMERSTATE_EXPIRED_DELIVER: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStExpDeliver); break;
|
---|
| 1686 | case TMTIMERSTATE_ACTIVE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStActive); break;
|
---|
| 1687 | case TMTIMERSTATE_PENDING_STOP: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStPendStop); break;
|
---|
| 1688 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStPendStopSched); break;
|
---|
| 1689 | case TMTIMERSTATE_PENDING_SCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStPendSched); break;
|
---|
| 1690 | case TMTIMERSTATE_PENDING_RESCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStPendResched); break;
|
---|
| 1691 | default: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStOther); break;
|
---|
| 1692 | }
|
---|
| 1693 | #endif
|
---|
| 1694 |
|
---|
| 1695 | /*
|
---|
| 1696 | * Try to take the TM lock and optimize the common cases.
|
---|
| 1697 | *
|
---|
| 1698 | * With the TM lock we can safely make optimizations like immediate
|
---|
| 1699 | * scheduling and we can also be 100% sure that we're not racing the
|
---|
| 1700 | * running of the timer queues. As an additional restraint we require the
|
---|
| 1701 | * timer to have a critical section associated with to be 100% there aren't
|
---|
| 1702 | * concurrent operations on the timer. (This latter isn't necessary any
|
---|
| 1703 | * longer as this isn't supported for any timers, critsect or not.)
|
---|
| 1704 | *
|
---|
[87813] | 1705 | * Note! Lock ordering doesn't apply when we only _try_ to
|
---|
[20750] | 1706 | * get the innermost locks.
|
---|
| 1707 | */
|
---|
[90346] | 1708 | bool fOwnTMLock = RT_SUCCESS_NP(PDMCritSectTryEnter(pVM, &pQueue->TimerLock));
|
---|
[20750] | 1709 | #if 1
|
---|
| 1710 | if ( fOwnTMLock
|
---|
| 1711 | && pTimer->pCritSect)
|
---|
| 1712 | {
|
---|
| 1713 | TMTIMERSTATE enmState = pTimer->enmState;
|
---|
| 1714 | if (RT_LIKELY( ( enmState == TMTIMERSTATE_EXPIRED_DELIVER
|
---|
| 1715 | || enmState == TMTIMERSTATE_STOPPED)
|
---|
| 1716 | && tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, enmState)))
|
---|
[20733] | 1717 | {
|
---|
[87814] | 1718 | tmTimerSetRelativeOptimizedStart(pVM, pTimer, cTicksToNext, pu64Now, pQueueCC, pQueue);
|
---|
[87771] | 1719 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelative), a);
|
---|
[90346] | 1720 | PDMCritSectLeave(pVM, &pQueue->TimerLock);
|
---|
[20750] | 1721 | return VINF_SUCCESS;
|
---|
[20733] | 1722 | }
|
---|
| 1723 |
|
---|
[20750] | 1724 | /* Optimize other states when it becomes necessary. */
|
---|
| 1725 | }
|
---|
| 1726 | #endif
|
---|
| 1727 |
|
---|
| 1728 | /*
|
---|
| 1729 | * Unoptimized path.
|
---|
| 1730 | */
|
---|
[87812] | 1731 | int rc;
|
---|
[20750] | 1732 | for (int cRetries = 1000; ; cRetries--)
|
---|
| 1733 | {
|
---|
[20733] | 1734 | /*
|
---|
| 1735 | * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
|
---|
| 1736 | */
|
---|
[20750] | 1737 | TMTIMERSTATE enmState = pTimer->enmState;
|
---|
[20733] | 1738 | switch (enmState)
|
---|
| 1739 | {
|
---|
[20750] | 1740 | case TMTIMERSTATE_STOPPED:
|
---|
[87813] | 1741 | if (pQueue->enmClock == TMCLOCK_VIRTUAL_SYNC)
|
---|
[20750] | 1742 | {
|
---|
| 1743 | /** @todo To fix assertion in tmR3TimerQueueRunVirtualSync:
|
---|
| 1744 | * Figure a safe way of activating this timer while the queue is
|
---|
| 1745 | * being run.
|
---|
| 1746 | * (99.9% sure this that the assertion is caused by DevAPIC.cpp
|
---|
[33540] | 1747 | * re-starting the timer in response to a initial_count write.) */
|
---|
[20750] | 1748 | }
|
---|
[69046] | 1749 | RT_FALL_THRU();
|
---|
[20733] | 1750 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
[87814] | 1751 | if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
|
---|
[20733] | 1752 | {
|
---|
[87792] | 1753 | Assert(pTimer->idxPrev == UINT32_MAX);
|
---|
| 1754 | Assert(pTimer->idxNext == UINT32_MAX);
|
---|
[87813] | 1755 | pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
|
---|
[20733] | 1756 | Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [EXP/STOP]\n",
|
---|
[87773] | 1757 | pTimer, tmTimerState(enmState), pTimer->szName, pTimer->u64Expire, cRetries));
|
---|
[20733] | 1758 | TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
|
---|
[87814] | 1759 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[20733] | 1760 | rc = VINF_SUCCESS;
|
---|
| 1761 | break;
|
---|
| 1762 | }
|
---|
| 1763 | rc = VERR_TRY_AGAIN;
|
---|
| 1764 | break;
|
---|
| 1765 |
|
---|
| 1766 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
| 1767 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
| 1768 | if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
|
---|
| 1769 | {
|
---|
[87813] | 1770 | pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
|
---|
[20733] | 1771 | Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [PEND_SCHED]\n",
|
---|
[87773] | 1772 | pTimer, tmTimerState(enmState), pTimer->szName, pTimer->u64Expire, cRetries));
|
---|
[20733] | 1773 | TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
|
---|
[87814] | 1774 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[20733] | 1775 | rc = VINF_SUCCESS;
|
---|
| 1776 | break;
|
---|
| 1777 | }
|
---|
| 1778 | rc = VERR_TRY_AGAIN;
|
---|
| 1779 | break;
|
---|
| 1780 |
|
---|
| 1781 |
|
---|
| 1782 | case TMTIMERSTATE_ACTIVE:
|
---|
[87814] | 1783 | if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
|
---|
[20733] | 1784 | {
|
---|
[87813] | 1785 | pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
|
---|
[20733] | 1786 | Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [ACTIVE]\n",
|
---|
[87773] | 1787 | pTimer, tmTimerState(enmState), pTimer->szName, pTimer->u64Expire, cRetries));
|
---|
[20733] | 1788 | TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
|
---|
[87814] | 1789 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[20733] | 1790 | rc = VINF_SUCCESS;
|
---|
| 1791 | break;
|
---|
| 1792 | }
|
---|
| 1793 | rc = VERR_TRY_AGAIN;
|
---|
| 1794 | break;
|
---|
| 1795 |
|
---|
| 1796 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 1797 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 1798 | if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
|
---|
| 1799 | {
|
---|
[87813] | 1800 | pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
|
---|
[20733] | 1801 | Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [PEND_RESCH/STOP]\n",
|
---|
[87773] | 1802 | pTimer, tmTimerState(enmState), pTimer->szName, pTimer->u64Expire, cRetries));
|
---|
[20733] | 1803 | TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
|
---|
[87814] | 1804 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[20733] | 1805 | rc = VINF_SUCCESS;
|
---|
| 1806 | break;
|
---|
| 1807 | }
|
---|
| 1808 | rc = VERR_TRY_AGAIN;
|
---|
| 1809 | break;
|
---|
| 1810 |
|
---|
| 1811 |
|
---|
| 1812 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
| 1813 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 1814 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 1815 | #ifdef IN_RING3
|
---|
| 1816 | if (!RTThreadYield())
|
---|
| 1817 | RTThreadSleep(1);
|
---|
| 1818 | #else
|
---|
| 1819 | /** @todo call host context and yield after a couple of iterations */
|
---|
| 1820 | #endif
|
---|
| 1821 | rc = VERR_TRY_AGAIN;
|
---|
| 1822 | break;
|
---|
| 1823 |
|
---|
| 1824 | /*
|
---|
| 1825 | * Invalid states.
|
---|
| 1826 | */
|
---|
| 1827 | case TMTIMERSTATE_DESTROY:
|
---|
| 1828 | case TMTIMERSTATE_FREE:
|
---|
[87773] | 1829 | AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, pTimer->szName));
|
---|
[20733] | 1830 | rc = VERR_TM_INVALID_STATE;
|
---|
| 1831 | break;
|
---|
| 1832 |
|
---|
| 1833 | default:
|
---|
[87773] | 1834 | AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
|
---|
[20733] | 1835 | rc = VERR_TM_UNKNOWN_STATE;
|
---|
| 1836 | break;
|
---|
| 1837 | }
|
---|
| 1838 |
|
---|
| 1839 | /* switch + loop is tedious to break out of. */
|
---|
| 1840 | if (rc == VINF_SUCCESS)
|
---|
| 1841 | break;
|
---|
| 1842 |
|
---|
| 1843 | if (rc != VERR_TRY_AGAIN)
|
---|
| 1844 | {
|
---|
[87813] | 1845 | tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
|
---|
[20733] | 1846 | break;
|
---|
| 1847 | }
|
---|
[20778] | 1848 | if (cRetries <= 0)
|
---|
[20733] | 1849 | {
|
---|
[87773] | 1850 | AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, pTimer->szName));
|
---|
[39402] | 1851 | rc = VERR_TM_TIMER_UNSTABLE_STATE;
|
---|
[87813] | 1852 | tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
|
---|
[20733] | 1853 | break;
|
---|
| 1854 | }
|
---|
[20750] | 1855 |
|
---|
| 1856 | /*
|
---|
| 1857 | * Retry to gain locks.
|
---|
| 1858 | */
|
---|
| 1859 | if (!fOwnTMLock)
|
---|
[90346] | 1860 | fOwnTMLock = RT_SUCCESS_NP(PDMCritSectTryEnter(pVM, &pQueue->TimerLock));
|
---|
[20750] | 1861 |
|
---|
[20733] | 1862 | } /* for (;;) */
|
---|
| 1863 |
|
---|
| 1864 | /*
|
---|
| 1865 | * Clean up and return.
|
---|
| 1866 | */
|
---|
[20750] | 1867 | if (fOwnTMLock)
|
---|
[90346] | 1868 | PDMCritSectLeave(pVM, &pQueue->TimerLock);
|
---|
[20733] | 1869 |
|
---|
[87771] | 1870 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelative), a);
|
---|
[20733] | 1871 | return rc;
|
---|
| 1872 | }
|
---|
| 1873 |
|
---|
| 1874 |
|
---|
| 1875 | /**
|
---|
[87766] | 1876 | * Arm a timer with a expire time relative to the current time.
|
---|
| 1877 | *
|
---|
| 1878 | * @returns VBox status code.
|
---|
| 1879 | * @param pVM The cross context VM structure.
|
---|
| 1880 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 1881 | * @param cTicksToNext Clock ticks until the next time expiration.
|
---|
| 1882 | * @param pu64Now Where to return the current time stamp used.
|
---|
| 1883 | * Optional.
|
---|
| 1884 | */
|
---|
| 1885 | VMMDECL(int) TMTimerSetRelative(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cTicksToNext, uint64_t *pu64Now)
|
---|
| 1886 | {
|
---|
[87813] | 1887 | TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
[87814] | 1888 | return tmTimerSetRelative(pVM, pTimer, cTicksToNext, pu64Now, pQueueCC, pQueue);
|
---|
[87766] | 1889 | }
|
---|
| 1890 |
|
---|
| 1891 |
|
---|
| 1892 | /**
|
---|
[32484] | 1893 | * Drops a hint about the frequency of the timer.
|
---|
| 1894 | *
|
---|
| 1895 | * This is used by TM and the VMM to calculate how often guest execution needs
|
---|
| 1896 | * to be interrupted. The hint is automatically cleared by TMTimerStop.
|
---|
| 1897 | *
|
---|
| 1898 | * @returns VBox status code.
|
---|
[87766] | 1899 | * @param pVM The cross context VM structure.
|
---|
| 1900 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 1901 | * @param uHzHint The frequency hint. Pass 0 to clear the hint.
|
---|
[32484] | 1902 | *
|
---|
| 1903 | * @remarks We're using an integer hertz value here since anything above 1 HZ
|
---|
| 1904 | * is not going to be any trouble satisfying scheduling wise. The
|
---|
| 1905 | * range where it makes sense is >= 100 HZ.
|
---|
| 1906 | */
|
---|
[87766] | 1907 | VMMDECL(int) TMTimerSetFrequencyHint(PVMCC pVM, TMTIMERHANDLE hTimer, uint32_t uHzHint)
|
---|
[32484] | 1908 | {
|
---|
[87812] | 1909 | TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
[87771] | 1910 | TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
|
---|
[32796] | 1911 |
|
---|
| 1912 | uint32_t const uHzOldHint = pTimer->uHzHint;
|
---|
[32484] | 1913 | pTimer->uHzHint = uHzHint;
|
---|
[32796] | 1914 |
|
---|
[87812] | 1915 | uint32_t const uMaxHzHint = pQueue->uMaxHzHint;
|
---|
[32796] | 1916 | if ( uHzHint > uMaxHzHint
|
---|
| 1917 | || uHzOldHint >= uMaxHzHint)
|
---|
[87812] | 1918 | ASMAtomicOrU64(&pVM->tm.s.HzHint.u64Combined, RT_BIT_32(idxQueue) | RT_BIT_32(idxQueue + 16));
|
---|
[32796] | 1919 |
|
---|
[32484] | 1920 | return VINF_SUCCESS;
|
---|
| 1921 | }
|
---|
| 1922 |
|
---|
| 1923 |
|
---|
| 1924 | /**
|
---|
[37517] | 1925 | * TMTimerStop for the virtual sync timer queue.
|
---|
| 1926 | *
|
---|
| 1927 | * This employs a greatly simplified state machine by always acquiring the
|
---|
| 1928 | * queue lock and bypassing the scheduling list.
|
---|
| 1929 | *
|
---|
| 1930 | * @returns VBox status code
|
---|
[58122] | 1931 | * @param pVM The cross context VM structure.
|
---|
[37517] | 1932 | * @param pTimer The timer handle.
|
---|
| 1933 | */
|
---|
[80281] | 1934 | static int tmTimerVirtualSyncStop(PVMCC pVM, PTMTIMER pTimer)
|
---|
[37517] | 1935 | {
|
---|
| 1936 | STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerStopVs), a);
|
---|
| 1937 | VM_ASSERT_EMT(pVM);
|
---|
[50385] | 1938 | TMTIMER_ASSERT_SYNC_CRITSECT_ORDER(pVM, pTimer);
|
---|
[90346] | 1939 | int rc = PDMCritSectEnter(pVM, &pVM->tm.s.VirtualSyncLock, VINF_SUCCESS);
|
---|
[37517] | 1940 | AssertRCReturn(rc, rc);
|
---|
| 1941 |
|
---|
| 1942 | /* Reset the HZ hint. */
|
---|
[87812] | 1943 | uint32_t uOldHzHint = pTimer->uHzHint;
|
---|
| 1944 | if (uOldHzHint)
|
---|
[37517] | 1945 | {
|
---|
[87812] | 1946 | if (uOldHzHint >= pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].uMaxHzHint)
|
---|
| 1947 | ASMAtomicOrU64(&pVM->tm.s.HzHint.u64Combined, RT_BIT_32(TMCLOCK_VIRTUAL_SYNC) | RT_BIT_32(TMCLOCK_VIRTUAL_SYNC + 16));
|
---|
[37517] | 1948 | pTimer->uHzHint = 0;
|
---|
| 1949 | }
|
---|
| 1950 |
|
---|
| 1951 | /* Update the timer state. */
|
---|
[87812] | 1952 | TMTIMERSTATE const enmState = pTimer->enmState;
|
---|
[37517] | 1953 | switch (enmState)
|
---|
| 1954 | {
|
---|
| 1955 | case TMTIMERSTATE_ACTIVE:
|
---|
[87792] | 1956 | {
|
---|
| 1957 | PTMTIMERQUEUE const pQueue = &pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC];
|
---|
| 1958 | tmTimerQueueUnlinkActive(pVM, TM_GET_TIMER_QUEUE_CC(pVM, TMCLOCK_VIRTUAL_SYNC, pQueue), pQueue, pTimer);
|
---|
[37517] | 1959 | TM_SET_STATE(pTimer, TMTIMERSTATE_STOPPED);
|
---|
| 1960 | rc = VINF_SUCCESS;
|
---|
| 1961 | break;
|
---|
[87792] | 1962 | }
|
---|
[37517] | 1963 |
|
---|
| 1964 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
| 1965 | TM_SET_STATE(pTimer, TMTIMERSTATE_STOPPED);
|
---|
| 1966 | rc = VINF_SUCCESS;
|
---|
| 1967 | break;
|
---|
| 1968 |
|
---|
| 1969 | case TMTIMERSTATE_STOPPED:
|
---|
| 1970 | rc = VINF_SUCCESS;
|
---|
| 1971 | break;
|
---|
| 1972 |
|
---|
| 1973 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 1974 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 1975 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
| 1976 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
| 1977 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
| 1978 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 1979 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 1980 | case TMTIMERSTATE_DESTROY:
|
---|
| 1981 | case TMTIMERSTATE_FREE:
|
---|
[87773] | 1982 | AssertLogRelMsgFailed(("Invalid timer state %s: %s\n", tmTimerState(enmState), pTimer->szName));
|
---|
[37517] | 1983 | rc = VERR_TM_INVALID_STATE;
|
---|
| 1984 | break;
|
---|
| 1985 |
|
---|
| 1986 | default:
|
---|
[87773] | 1987 | AssertMsgFailed(("Unknown timer state %d: %s\n", enmState, pTimer->szName));
|
---|
[37517] | 1988 | rc = VERR_TM_UNKNOWN_STATE;
|
---|
| 1989 | break;
|
---|
| 1990 | }
|
---|
| 1991 |
|
---|
| 1992 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStopVs), a);
|
---|
[90346] | 1993 | PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
|
---|
[37517] | 1994 | return rc;
|
---|
| 1995 | }
|
---|
| 1996 |
|
---|
| 1997 |
|
---|
| 1998 | /**
|
---|
[1] | 1999 | * Stop the timer.
|
---|
| 2000 | * Use TMR3TimerArm() to "un-stop" the timer.
|
---|
| 2001 | *
|
---|
[58170] | 2002 | * @returns VBox status code.
|
---|
[87766] | 2003 | * @param pVM The cross context VM structure.
|
---|
| 2004 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[1] | 2005 | */
|
---|
[87766] | 2006 | VMMDECL(int) TMTimerStop(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[1] | 2007 | {
|
---|
[87812] | 2008 | TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
[82333] | 2009 | STAM_COUNTER_INC(&pTimer->StatStop);
|
---|
[37517] | 2010 |
|
---|
| 2011 | /* Treat virtual sync timers specially. */
|
---|
[87812] | 2012 | if (idxQueue == TMCLOCK_VIRTUAL_SYNC)
|
---|
[37517] | 2013 | return tmTimerVirtualSyncStop(pVM, pTimer);
|
---|
| 2014 |
|
---|
| 2015 | STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
|
---|
[87771] | 2016 | TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
|
---|
[20089] | 2017 |
|
---|
[37517] | 2018 | /*
|
---|
| 2019 | * Reset the HZ hint.
|
---|
| 2020 | */
|
---|
[87812] | 2021 | uint32_t const uOldHzHint = pTimer->uHzHint;
|
---|
| 2022 | if (uOldHzHint)
|
---|
[32484] | 2023 | {
|
---|
[87812] | 2024 | if (uOldHzHint >= pQueue->uMaxHzHint)
|
---|
| 2025 | ASMAtomicOrU64(&pVM->tm.s.HzHint.u64Combined, RT_BIT_32(idxQueue) | RT_BIT_32(idxQueue + 16));
|
---|
[32484] | 2026 | pTimer->uHzHint = 0;
|
---|
| 2027 | }
|
---|
| 2028 |
|
---|
[1] | 2029 | /** @todo see if this function needs optimizing. */
|
---|
| 2030 | int cRetries = 1000;
|
---|
| 2031 | do
|
---|
| 2032 | {
|
---|
| 2033 | /*
|
---|
| 2034 | * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
|
---|
| 2035 | */
|
---|
| 2036 | TMTIMERSTATE enmState = pTimer->enmState;
|
---|
[19498] | 2037 | Log2(("TMTimerStop: %p:{.enmState=%s, .pszDesc='%s'} cRetries=%d\n",
|
---|
[87773] | 2038 | pTimer, tmTimerState(enmState), pTimer->szName, cRetries));
|
---|
[1] | 2039 | switch (enmState)
|
---|
| 2040 | {
|
---|
[20050] | 2041 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
[1] | 2042 | //AssertMsgFailed(("You don't stop an expired timer dude!\n"));
|
---|
| 2043 | return VERR_INVALID_PARAMETER;
|
---|
| 2044 |
|
---|
| 2045 | case TMTIMERSTATE_STOPPED:
|
---|
| 2046 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 2047 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
[37517] | 2048 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
|
---|
[1] | 2049 | return VINF_SUCCESS;
|
---|
| 2050 |
|
---|
| 2051 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
| 2052 | if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, enmState))
|
---|
| 2053 | {
|
---|
[87814] | 2054 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[37517] | 2055 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
|
---|
[1] | 2056 | return VINF_SUCCESS;
|
---|
| 2057 | }
|
---|
[65591] | 2058 | break;
|
---|
[1] | 2059 |
|
---|
| 2060 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 2061 | if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
|
---|
| 2062 | {
|
---|
[87814] | 2063 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[37517] | 2064 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
|
---|
[1] | 2065 | return VINF_SUCCESS;
|
---|
| 2066 | }
|
---|
| 2067 | break;
|
---|
| 2068 |
|
---|
| 2069 | case TMTIMERSTATE_ACTIVE:
|
---|
[87814] | 2070 | if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
|
---|
[1] | 2071 | {
|
---|
[87814] | 2072 | tmSchedule(pVM, pQueueCC, pQueue, pTimer);
|
---|
[37517] | 2073 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
|
---|
[1] | 2074 | return VINF_SUCCESS;
|
---|
| 2075 | }
|
---|
| 2076 | break;
|
---|
| 2077 |
|
---|
[20050] | 2078 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
[1] | 2079 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 2080 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 2081 | #ifdef IN_RING3
|
---|
| 2082 | if (!RTThreadYield())
|
---|
| 2083 | RTThreadSleep(1);
|
---|
| 2084 | #else
|
---|
[63560] | 2085 | /** @todo call host and yield cpu after a while. */
|
---|
[1] | 2086 | #endif
|
---|
| 2087 | break;
|
---|
| 2088 |
|
---|
| 2089 | /*
|
---|
| 2090 | * Invalid states.
|
---|
| 2091 | */
|
---|
[19537] | 2092 | case TMTIMERSTATE_DESTROY:
|
---|
[1] | 2093 | case TMTIMERSTATE_FREE:
|
---|
[87773] | 2094 | AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, pTimer->szName));
|
---|
[1] | 2095 | return VERR_TM_INVALID_STATE;
|
---|
| 2096 | default:
|
---|
[87773] | 2097 | AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
|
---|
[1] | 2098 | return VERR_TM_UNKNOWN_STATE;
|
---|
| 2099 | }
|
---|
| 2100 | } while (cRetries-- > 0);
|
---|
| 2101 |
|
---|
[87773] | 2102 | AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, pTimer->szName));
|
---|
[37517] | 2103 | STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
|
---|
[39402] | 2104 | return VERR_TM_TIMER_UNSTABLE_STATE;
|
---|
[1] | 2105 | }
|
---|
| 2106 |
|
---|
| 2107 |
|
---|
| 2108 | /**
|
---|
| 2109 | * Get the current clock time.
|
---|
| 2110 | * Handy for calculating the new expire time.
|
---|
| 2111 | *
|
---|
| 2112 | * @returns Current clock time.
|
---|
[87766] | 2113 | * @param pVM The cross context VM structure.
|
---|
| 2114 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[1] | 2115 | */
|
---|
[87766] | 2116 | VMMDECL(uint64_t) TMTimerGet(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[1] | 2117 | {
|
---|
[87813] | 2118 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
[82333] | 2119 | STAM_COUNTER_INC(&pTimer->StatGet);
|
---|
[37517] | 2120 |
|
---|
[1] | 2121 | uint64_t u64;
|
---|
[87813] | 2122 | switch (pQueue->enmClock)
|
---|
[1] | 2123 | {
|
---|
| 2124 | case TMCLOCK_VIRTUAL:
|
---|
| 2125 | u64 = TMVirtualGet(pVM);
|
---|
| 2126 | break;
|
---|
| 2127 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
[2248] | 2128 | u64 = TMVirtualSyncGet(pVM);
|
---|
[1] | 2129 | break;
|
---|
| 2130 | case TMCLOCK_REAL:
|
---|
| 2131 | u64 = TMRealGet(pVM);
|
---|
| 2132 | break;
|
---|
| 2133 | default:
|
---|
[87813] | 2134 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[37517] | 2135 | return UINT64_MAX;
|
---|
[1] | 2136 | }
|
---|
[20050] | 2137 | //Log2(("TMTimerGet: returns %'RU64 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
|
---|
[87773] | 2138 | // u64, pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
|
---|
[1] | 2139 | return u64;
|
---|
| 2140 | }
|
---|
| 2141 |
|
---|
| 2142 |
|
---|
| 2143 | /**
|
---|
[33540] | 2144 | * Get the frequency of the timer clock.
|
---|
[1] | 2145 | *
|
---|
| 2146 | * @returns Clock frequency (as Hz of course).
|
---|
[87766] | 2147 | * @param pVM The cross context VM structure.
|
---|
| 2148 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[1] | 2149 | */
|
---|
[87766] | 2150 | VMMDECL(uint64_t) TMTimerGetFreq(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[1] | 2151 | {
|
---|
[87813] | 2152 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 2153 | switch (pQueue->enmClock)
|
---|
[1] | 2154 | {
|
---|
| 2155 | case TMCLOCK_VIRTUAL:
|
---|
| 2156 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2157 | return TMCLOCK_FREQ_VIRTUAL;
|
---|
| 2158 |
|
---|
| 2159 | case TMCLOCK_REAL:
|
---|
| 2160 | return TMCLOCK_FREQ_REAL;
|
---|
| 2161 |
|
---|
| 2162 | default:
|
---|
[87813] | 2163 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[1] | 2164 | return 0;
|
---|
| 2165 | }
|
---|
| 2166 | }
|
---|
| 2167 |
|
---|
| 2168 |
|
---|
| 2169 | /**
|
---|
[37527] | 2170 | * Get the expire time of the timer.
|
---|
| 2171 | * Only valid for active timers.
|
---|
| 2172 | *
|
---|
| 2173 | * @returns Expire time of the timer.
|
---|
[87766] | 2174 | * @param pVM The cross context VM structure.
|
---|
| 2175 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[37527] | 2176 | */
|
---|
[87766] | 2177 | VMMDECL(uint64_t) TMTimerGetExpire(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[37527] | 2178 | {
|
---|
[87813] | 2179 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, UINT64_MAX); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
[87771] | 2180 | TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
|
---|
[37527] | 2181 | int cRetries = 1000;
|
---|
| 2182 | do
|
---|
| 2183 | {
|
---|
[87813] | 2184 | TMTIMERSTATE enmState = pTimer->enmState;
|
---|
[37527] | 2185 | switch (enmState)
|
---|
| 2186 | {
|
---|
| 2187 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
| 2188 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
| 2189 | case TMTIMERSTATE_STOPPED:
|
---|
| 2190 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 2191 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
| 2192 | Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
|
---|
[87773] | 2193 | pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
|
---|
[87766] | 2194 | return UINT64_MAX;
|
---|
[37527] | 2195 |
|
---|
| 2196 | case TMTIMERSTATE_ACTIVE:
|
---|
| 2197 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 2198 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
| 2199 | Log2(("TMTimerGetExpire: returns %'RU64 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
|
---|
[87773] | 2200 | pTimer->u64Expire, pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
|
---|
[37527] | 2201 | return pTimer->u64Expire;
|
---|
| 2202 |
|
---|
| 2203 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 2204 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 2205 | #ifdef IN_RING3
|
---|
| 2206 | if (!RTThreadYield())
|
---|
| 2207 | RTThreadSleep(1);
|
---|
| 2208 | #endif
|
---|
| 2209 | break;
|
---|
| 2210 |
|
---|
| 2211 | /*
|
---|
| 2212 | * Invalid states.
|
---|
| 2213 | */
|
---|
| 2214 | case TMTIMERSTATE_DESTROY:
|
---|
| 2215 | case TMTIMERSTATE_FREE:
|
---|
[87773] | 2216 | AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, pTimer->szName));
|
---|
[37527] | 2217 | Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
|
---|
[87773] | 2218 | pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
|
---|
[87766] | 2219 | return UINT64_MAX;
|
---|
[37527] | 2220 | default:
|
---|
[87773] | 2221 | AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
|
---|
[87766] | 2222 | return UINT64_MAX;
|
---|
[37527] | 2223 | }
|
---|
| 2224 | } while (cRetries-- > 0);
|
---|
| 2225 |
|
---|
[87773] | 2226 | AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, pTimer->szName));
|
---|
[37527] | 2227 | Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
|
---|
[87773] | 2228 | pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
|
---|
[87766] | 2229 | return UINT64_MAX;
|
---|
[37527] | 2230 | }
|
---|
| 2231 |
|
---|
| 2232 |
|
---|
| 2233 | /**
|
---|
| 2234 | * Checks if a timer is active or not.
|
---|
| 2235 | *
|
---|
| 2236 | * @returns True if active.
|
---|
| 2237 | * @returns False if not active.
|
---|
[87766] | 2238 | * @param pVM The cross context VM structure.
|
---|
| 2239 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[37527] | 2240 | */
|
---|
[87766] | 2241 | VMMDECL(bool) TMTimerIsActive(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[37527] | 2242 | {
|
---|
[87813] | 2243 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, false); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
[37527] | 2244 | TMTIMERSTATE enmState = pTimer->enmState;
|
---|
| 2245 | switch (enmState)
|
---|
| 2246 | {
|
---|
| 2247 | case TMTIMERSTATE_STOPPED:
|
---|
| 2248 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
| 2249 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
| 2250 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 2251 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
| 2252 | Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
|
---|
[87773] | 2253 | pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
|
---|
[37527] | 2254 | return false;
|
---|
| 2255 |
|
---|
| 2256 | case TMTIMERSTATE_ACTIVE:
|
---|
| 2257 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
| 2258 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
| 2259 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 2260 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 2261 | Log2(("TMTimerIsActive: returns true (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
|
---|
[87773] | 2262 | pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
|
---|
[37527] | 2263 | return true;
|
---|
| 2264 |
|
---|
| 2265 | /*
|
---|
| 2266 | * Invalid states.
|
---|
| 2267 | */
|
---|
| 2268 | case TMTIMERSTATE_DESTROY:
|
---|
| 2269 | case TMTIMERSTATE_FREE:
|
---|
[87773] | 2270 | AssertMsgFailed(("Invalid timer state %s (%s)\n", tmTimerState(enmState), pTimer->szName));
|
---|
[37527] | 2271 | Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
|
---|
[87773] | 2272 | pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
|
---|
[37527] | 2273 | return false;
|
---|
| 2274 | default:
|
---|
[87773] | 2275 | AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
|
---|
[37527] | 2276 | return false;
|
---|
| 2277 | }
|
---|
| 2278 | }
|
---|
| 2279 |
|
---|
| 2280 |
|
---|
| 2281 | /* -=-=-=-=-=-=- Convenience APIs -=-=-=-=-=-=- */
|
---|
| 2282 |
|
---|
| 2283 |
|
---|
| 2284 | /**
|
---|
| 2285 | * Arm a timer with a (new) expire time relative to current time.
|
---|
| 2286 | *
|
---|
[58170] | 2287 | * @returns VBox status code.
|
---|
[87766] | 2288 | * @param pVM The cross context VM structure.
|
---|
| 2289 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[37527] | 2290 | * @param cMilliesToNext Number of milliseconds to the next tick.
|
---|
| 2291 | */
|
---|
[87766] | 2292 | VMMDECL(int) TMTimerSetMillies(PVMCC pVM, TMTIMERHANDLE hTimer, uint32_t cMilliesToNext)
|
---|
[37527] | 2293 | {
|
---|
[87813] | 2294 | TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 2295 | switch (pQueue->enmClock)
|
---|
[37527] | 2296 | {
|
---|
| 2297 | case TMCLOCK_VIRTUAL:
|
---|
| 2298 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[87814] | 2299 | return tmTimerSetRelative(pVM, pTimer, cMilliesToNext * UINT64_C(1000000), NULL, pQueueCC, pQueue);
|
---|
[37527] | 2300 |
|
---|
| 2301 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2302 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[87814] | 2303 | return tmTimerSetRelative(pVM, pTimer, cMilliesToNext * UINT64_C(1000000), NULL, pQueueCC, pQueue);
|
---|
[37527] | 2304 |
|
---|
| 2305 | case TMCLOCK_REAL:
|
---|
| 2306 | AssertCompile(TMCLOCK_FREQ_REAL == 1000);
|
---|
[87814] | 2307 | return tmTimerSetRelative(pVM, pTimer, cMilliesToNext, NULL, pQueueCC, pQueue);
|
---|
[37527] | 2308 |
|
---|
| 2309 | default:
|
---|
[87813] | 2310 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[39402] | 2311 | return VERR_TM_TIMER_BAD_CLOCK;
|
---|
[37527] | 2312 | }
|
---|
| 2313 | }
|
---|
| 2314 |
|
---|
| 2315 |
|
---|
| 2316 | /**
|
---|
| 2317 | * Arm a timer with a (new) expire time relative to current time.
|
---|
| 2318 | *
|
---|
[58170] | 2319 | * @returns VBox status code.
|
---|
[87766] | 2320 | * @param pVM The cross context VM structure.
|
---|
| 2321 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[37527] | 2322 | * @param cMicrosToNext Number of microseconds to the next tick.
|
---|
| 2323 | */
|
---|
[87766] | 2324 | VMMDECL(int) TMTimerSetMicro(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cMicrosToNext)
|
---|
[37527] | 2325 | {
|
---|
[87813] | 2326 | TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 2327 | switch (pQueue->enmClock)
|
---|
[37527] | 2328 | {
|
---|
| 2329 | case TMCLOCK_VIRTUAL:
|
---|
| 2330 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[87814] | 2331 | return tmTimerSetRelative(pVM, pTimer, cMicrosToNext * 1000, NULL, pQueueCC, pQueue);
|
---|
[37527] | 2332 |
|
---|
| 2333 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2334 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[87814] | 2335 | return tmTimerSetRelative(pVM, pTimer, cMicrosToNext * 1000, NULL, pQueueCC, pQueue);
|
---|
[37527] | 2336 |
|
---|
| 2337 | case TMCLOCK_REAL:
|
---|
| 2338 | AssertCompile(TMCLOCK_FREQ_REAL == 1000);
|
---|
[87814] | 2339 | return tmTimerSetRelative(pVM, pTimer, cMicrosToNext / 1000, NULL, pQueueCC, pQueue);
|
---|
[37527] | 2340 |
|
---|
| 2341 | default:
|
---|
[87813] | 2342 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[39402] | 2343 | return VERR_TM_TIMER_BAD_CLOCK;
|
---|
[37527] | 2344 | }
|
---|
| 2345 | }
|
---|
| 2346 |
|
---|
| 2347 |
|
---|
| 2348 | /**
|
---|
| 2349 | * Arm a timer with a (new) expire time relative to current time.
|
---|
| 2350 | *
|
---|
[58170] | 2351 | * @returns VBox status code.
|
---|
[87766] | 2352 | * @param pVM The cross context VM structure.
|
---|
| 2353 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[37527] | 2354 | * @param cNanosToNext Number of nanoseconds to the next tick.
|
---|
| 2355 | */
|
---|
[87766] | 2356 | VMMDECL(int) TMTimerSetNano(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cNanosToNext)
|
---|
[37527] | 2357 | {
|
---|
[87813] | 2358 | TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
[87814] | 2359 | switch (pQueue->enmClock)
|
---|
[37527] | 2360 | {
|
---|
| 2361 | case TMCLOCK_VIRTUAL:
|
---|
| 2362 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[87814] | 2363 | return tmTimerSetRelative(pVM, pTimer, cNanosToNext, NULL, pQueueCC, pQueue);
|
---|
[37527] | 2364 |
|
---|
| 2365 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2366 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[87814] | 2367 | return tmTimerSetRelative(pVM, pTimer, cNanosToNext, NULL, pQueueCC, pQueue);
|
---|
[37527] | 2368 |
|
---|
| 2369 | case TMCLOCK_REAL:
|
---|
| 2370 | AssertCompile(TMCLOCK_FREQ_REAL == 1000);
|
---|
[87814] | 2371 | return tmTimerSetRelative(pVM, pTimer, cNanosToNext / 1000000, NULL, pQueueCC, pQueue);
|
---|
[37527] | 2372 |
|
---|
| 2373 | default:
|
---|
[87814] | 2374 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[39402] | 2375 | return VERR_TM_TIMER_BAD_CLOCK;
|
---|
[37527] | 2376 | }
|
---|
| 2377 | }
|
---|
| 2378 |
|
---|
| 2379 |
|
---|
| 2380 | /**
|
---|
[1] | 2381 | * Get the current clock time as nanoseconds.
|
---|
| 2382 | *
|
---|
| 2383 | * @returns The timer clock as nanoseconds.
|
---|
[87766] | 2384 | * @param pVM The cross context VM structure.
|
---|
| 2385 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[1] | 2386 | */
|
---|
[87766] | 2387 | VMMDECL(uint64_t) TMTimerGetNano(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[1] | 2388 | {
|
---|
[87766] | 2389 | return TMTimerToNano(pVM, hTimer, TMTimerGet(pVM, hTimer));
|
---|
[1] | 2390 | }
|
---|
| 2391 |
|
---|
| 2392 |
|
---|
| 2393 | /**
|
---|
| 2394 | * Get the current clock time as microseconds.
|
---|
| 2395 | *
|
---|
| 2396 | * @returns The timer clock as microseconds.
|
---|
[87766] | 2397 | * @param pVM The cross context VM structure.
|
---|
| 2398 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[1] | 2399 | */
|
---|
[87766] | 2400 | VMMDECL(uint64_t) TMTimerGetMicro(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[1] | 2401 | {
|
---|
[87766] | 2402 | return TMTimerToMicro(pVM, hTimer, TMTimerGet(pVM, hTimer));
|
---|
[1] | 2403 | }
|
---|
| 2404 |
|
---|
| 2405 |
|
---|
| 2406 | /**
|
---|
| 2407 | * Get the current clock time as milliseconds.
|
---|
| 2408 | *
|
---|
| 2409 | * @returns The timer clock as milliseconds.
|
---|
[87766] | 2410 | * @param pVM The cross context VM structure.
|
---|
| 2411 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
[1] | 2412 | */
|
---|
[87766] | 2413 | VMMDECL(uint64_t) TMTimerGetMilli(PVMCC pVM, TMTIMERHANDLE hTimer)
|
---|
[1] | 2414 | {
|
---|
[87766] | 2415 | return TMTimerToMilli(pVM, hTimer, TMTimerGet(pVM, hTimer));
|
---|
[1] | 2416 | }
|
---|
| 2417 |
|
---|
| 2418 |
|
---|
| 2419 | /**
|
---|
| 2420 | * Converts the specified timer clock time to nanoseconds.
|
---|
| 2421 | *
|
---|
| 2422 | * @returns nanoseconds.
|
---|
[87766] | 2423 | * @param pVM The cross context VM structure.
|
---|
| 2424 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 2425 | * @param cTicks The clock ticks.
|
---|
[33540] | 2426 | * @remark There could be rounding errors here. We just do a simple integer divide
|
---|
[1] | 2427 | * without any adjustments.
|
---|
| 2428 | */
|
---|
[87766] | 2429 | VMMDECL(uint64_t) TMTimerToNano(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cTicks)
|
---|
[1] | 2430 | {
|
---|
[87813] | 2431 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 2432 | switch (pQueue->enmClock)
|
---|
[1] | 2433 | {
|
---|
| 2434 | case TMCLOCK_VIRTUAL:
|
---|
| 2435 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2436 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[87766] | 2437 | return cTicks;
|
---|
[1] | 2438 |
|
---|
| 2439 | case TMCLOCK_REAL:
|
---|
| 2440 | AssertCompile(TMCLOCK_FREQ_REAL == 1000);
|
---|
[87766] | 2441 | return cTicks * 1000000;
|
---|
[1] | 2442 |
|
---|
| 2443 | default:
|
---|
[87813] | 2444 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[1] | 2445 | return 0;
|
---|
| 2446 | }
|
---|
| 2447 | }
|
---|
| 2448 |
|
---|
| 2449 |
|
---|
| 2450 | /**
|
---|
| 2451 | * Converts the specified timer clock time to microseconds.
|
---|
| 2452 | *
|
---|
| 2453 | * @returns microseconds.
|
---|
[87766] | 2454 | * @param pVM The cross context VM structure.
|
---|
| 2455 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 2456 | * @param cTicks The clock ticks.
|
---|
[33540] | 2457 | * @remark There could be rounding errors here. We just do a simple integer divide
|
---|
[1] | 2458 | * without any adjustments.
|
---|
| 2459 | */
|
---|
[87766] | 2460 | VMMDECL(uint64_t) TMTimerToMicro(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cTicks)
|
---|
[1] | 2461 | {
|
---|
[87813] | 2462 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 2463 | switch (pQueue->enmClock)
|
---|
[1] | 2464 | {
|
---|
| 2465 | case TMCLOCK_VIRTUAL:
|
---|
| 2466 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2467 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[87766] | 2468 | return cTicks / 1000;
|
---|
[1] | 2469 |
|
---|
| 2470 | case TMCLOCK_REAL:
|
---|
| 2471 | AssertCompile(TMCLOCK_FREQ_REAL == 1000);
|
---|
[87766] | 2472 | return cTicks * 1000;
|
---|
[1] | 2473 |
|
---|
| 2474 | default:
|
---|
[87813] | 2475 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[1] | 2476 | return 0;
|
---|
| 2477 | }
|
---|
| 2478 | }
|
---|
| 2479 |
|
---|
| 2480 |
|
---|
| 2481 | /**
|
---|
| 2482 | * Converts the specified timer clock time to milliseconds.
|
---|
| 2483 | *
|
---|
| 2484 | * @returns milliseconds.
|
---|
[87766] | 2485 | * @param pVM The cross context VM structure.
|
---|
| 2486 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 2487 | * @param cTicks The clock ticks.
|
---|
[33540] | 2488 | * @remark There could be rounding errors here. We just do a simple integer divide
|
---|
[1] | 2489 | * without any adjustments.
|
---|
| 2490 | */
|
---|
[87766] | 2491 | VMMDECL(uint64_t) TMTimerToMilli(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cTicks)
|
---|
[1] | 2492 | {
|
---|
[87813] | 2493 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 2494 | switch (pQueue->enmClock)
|
---|
[1] | 2495 | {
|
---|
| 2496 | case TMCLOCK_VIRTUAL:
|
---|
| 2497 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2498 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[87766] | 2499 | return cTicks / 1000000;
|
---|
[1] | 2500 |
|
---|
| 2501 | case TMCLOCK_REAL:
|
---|
| 2502 | AssertCompile(TMCLOCK_FREQ_REAL == 1000);
|
---|
[87766] | 2503 | return cTicks;
|
---|
[1] | 2504 |
|
---|
| 2505 | default:
|
---|
[87813] | 2506 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[1] | 2507 | return 0;
|
---|
| 2508 | }
|
---|
| 2509 | }
|
---|
| 2510 |
|
---|
| 2511 |
|
---|
| 2512 | /**
|
---|
| 2513 | * Converts the specified nanosecond timestamp to timer clock ticks.
|
---|
| 2514 | *
|
---|
| 2515 | * @returns timer clock ticks.
|
---|
[87766] | 2516 | * @param pVM The cross context VM structure.
|
---|
| 2517 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 2518 | * @param cNanoSecs The nanosecond value ticks to convert.
|
---|
[1] | 2519 | * @remark There could be rounding and overflow errors here.
|
---|
| 2520 | */
|
---|
[87766] | 2521 | VMMDECL(uint64_t) TMTimerFromNano(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cNanoSecs)
|
---|
[1] | 2522 | {
|
---|
[87813] | 2523 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 2524 | switch (pQueue->enmClock)
|
---|
[1] | 2525 | {
|
---|
| 2526 | case TMCLOCK_VIRTUAL:
|
---|
| 2527 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2528 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[37517] | 2529 | return cNanoSecs;
|
---|
[1] | 2530 |
|
---|
| 2531 | case TMCLOCK_REAL:
|
---|
| 2532 | AssertCompile(TMCLOCK_FREQ_REAL == 1000);
|
---|
[37517] | 2533 | return cNanoSecs / 1000000;
|
---|
[1] | 2534 |
|
---|
| 2535 | default:
|
---|
[87813] | 2536 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[1] | 2537 | return 0;
|
---|
| 2538 | }
|
---|
| 2539 | }
|
---|
| 2540 |
|
---|
| 2541 |
|
---|
| 2542 | /**
|
---|
| 2543 | * Converts the specified microsecond timestamp to timer clock ticks.
|
---|
| 2544 | *
|
---|
| 2545 | * @returns timer clock ticks.
|
---|
[87766] | 2546 | * @param pVM The cross context VM structure.
|
---|
| 2547 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 2548 | * @param cMicroSecs The microsecond value ticks to convert.
|
---|
[1] | 2549 | * @remark There could be rounding and overflow errors here.
|
---|
| 2550 | */
|
---|
[87766] | 2551 | VMMDECL(uint64_t) TMTimerFromMicro(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cMicroSecs)
|
---|
[1] | 2552 | {
|
---|
[87813] | 2553 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 2554 | switch (pQueue->enmClock)
|
---|
[1] | 2555 | {
|
---|
| 2556 | case TMCLOCK_VIRTUAL:
|
---|
| 2557 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2558 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[37517] | 2559 | return cMicroSecs * 1000;
|
---|
[1] | 2560 |
|
---|
| 2561 | case TMCLOCK_REAL:
|
---|
| 2562 | AssertCompile(TMCLOCK_FREQ_REAL == 1000);
|
---|
[37517] | 2563 | return cMicroSecs / 1000;
|
---|
[1] | 2564 |
|
---|
| 2565 | default:
|
---|
[87813] | 2566 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[1] | 2567 | return 0;
|
---|
| 2568 | }
|
---|
| 2569 | }
|
---|
| 2570 |
|
---|
| 2571 |
|
---|
| 2572 | /**
|
---|
| 2573 | * Converts the specified millisecond timestamp to timer clock ticks.
|
---|
| 2574 | *
|
---|
| 2575 | * @returns timer clock ticks.
|
---|
[87766] | 2576 | * @param pVM The cross context VM structure.
|
---|
| 2577 | * @param hTimer Timer handle as returned by one of the create functions.
|
---|
| 2578 | * @param cMilliSecs The millisecond value ticks to convert.
|
---|
[1] | 2579 | * @remark There could be rounding and overflow errors here.
|
---|
| 2580 | */
|
---|
[87766] | 2581 | VMMDECL(uint64_t) TMTimerFromMilli(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cMilliSecs)
|
---|
[1] | 2582 | {
|
---|
[87813] | 2583 | TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
|
---|
| 2584 | switch (pQueue->enmClock)
|
---|
[1] | 2585 | {
|
---|
| 2586 | case TMCLOCK_VIRTUAL:
|
---|
| 2587 | case TMCLOCK_VIRTUAL_SYNC:
|
---|
| 2588 | AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
|
---|
[37517] | 2589 | return cMilliSecs * 1000000;
|
---|
[1] | 2590 |
|
---|
| 2591 | case TMCLOCK_REAL:
|
---|
| 2592 | AssertCompile(TMCLOCK_FREQ_REAL == 1000);
|
---|
[37517] | 2593 | return cMilliSecs;
|
---|
[1] | 2594 |
|
---|
| 2595 | default:
|
---|
[87813] | 2596 | AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
|
---|
[1] | 2597 | return 0;
|
---|
| 2598 | }
|
---|
| 2599 | }
|
---|
| 2600 |
|
---|
| 2601 |
|
---|
| 2602 | /**
|
---|
| 2603 | * Convert state to string.
|
---|
| 2604 | *
|
---|
| 2605 | * @returns Readonly status name.
|
---|
| 2606 | * @param enmState State.
|
---|
| 2607 | */
|
---|
| 2608 | const char *tmTimerState(TMTIMERSTATE enmState)
|
---|
| 2609 | {
|
---|
| 2610 | switch (enmState)
|
---|
| 2611 | {
|
---|
[19498] | 2612 | #define CASE(num, state) \
|
---|
| 2613 | case TMTIMERSTATE_##state: \
|
---|
| 2614 | AssertCompile(TMTIMERSTATE_##state == (num)); \
|
---|
| 2615 | return #num "-" #state
|
---|
[87792] | 2616 | CASE( 0,INVALID);
|
---|
[19498] | 2617 | CASE( 1,STOPPED);
|
---|
| 2618 | CASE( 2,ACTIVE);
|
---|
[20050] | 2619 | CASE( 3,EXPIRED_GET_UNLINK);
|
---|
| 2620 | CASE( 4,EXPIRED_DELIVER);
|
---|
| 2621 | CASE( 5,PENDING_STOP);
|
---|
| 2622 | CASE( 6,PENDING_STOP_SCHEDULE);
|
---|
| 2623 | CASE( 7,PENDING_SCHEDULE_SET_EXPIRE);
|
---|
| 2624 | CASE( 8,PENDING_SCHEDULE);
|
---|
| 2625 | CASE( 9,PENDING_RESCHEDULE_SET_EXPIRE);
|
---|
| 2626 | CASE(10,PENDING_RESCHEDULE);
|
---|
| 2627 | CASE(11,DESTROY);
|
---|
| 2628 | CASE(12,FREE);
|
---|
[1] | 2629 | default:
|
---|
| 2630 | AssertMsgFailed(("Invalid state enmState=%d\n", enmState));
|
---|
| 2631 | return "Invalid state!";
|
---|
| 2632 | #undef CASE
|
---|
| 2633 | }
|
---|
| 2634 | }
|
---|
| 2635 |
|
---|
| 2636 |
|
---|
[92712] | 2637 | #if defined(IN_RING0) || defined(IN_RING3)
|
---|
[1] | 2638 | /**
|
---|
[92712] | 2639 | * Copies over old timers and initialized newly allocted ones.
|
---|
| 2640 | *
|
---|
| 2641 | * Helper for TMR0TimerQueueGrow an tmR3TimerQueueGrow.
|
---|
| 2642 | *
|
---|
| 2643 | * @param paTimers The new timer allocation.
|
---|
| 2644 | * @param paOldTimers The old timers.
|
---|
| 2645 | * @param cNewTimers Number of new timers.
|
---|
| 2646 | * @param cOldTimers Number of old timers.
|
---|
| 2647 | */
|
---|
| 2648 | void tmHCTimerQueueGrowInit(PTMTIMER paTimers, TMTIMER const *paOldTimers, uint32_t cNewTimers, uint32_t cOldTimers)
|
---|
| 2649 | {
|
---|
| 2650 | Assert(cOldTimers < cNewTimers);
|
---|
| 2651 |
|
---|
| 2652 | /*
|
---|
| 2653 | * Copy over the old info and initialize the new handles.
|
---|
| 2654 | */
|
---|
| 2655 | if (cOldTimers > 0)
|
---|
| 2656 | memcpy(paTimers, paOldTimers, sizeof(TMTIMER) * cOldTimers);
|
---|
| 2657 |
|
---|
| 2658 | size_t i = cNewTimers;
|
---|
| 2659 | while (i-- > cOldTimers)
|
---|
| 2660 | {
|
---|
| 2661 | paTimers[i].u64Expire = UINT64_MAX;
|
---|
| 2662 | paTimers[i].enmType = TMTIMERTYPE_INVALID;
|
---|
| 2663 | paTimers[i].enmState = TMTIMERSTATE_FREE;
|
---|
| 2664 | paTimers[i].idxScheduleNext = UINT32_MAX;
|
---|
| 2665 | paTimers[i].idxNext = UINT32_MAX;
|
---|
| 2666 | paTimers[i].idxPrev = UINT32_MAX;
|
---|
| 2667 | paTimers[i].hSelf = NIL_TMTIMERHANDLE;
|
---|
| 2668 | }
|
---|
| 2669 |
|
---|
| 2670 | /*
|
---|
| 2671 | * Mark the zero'th entry as allocated but invalid if we just allocated it.
|
---|
| 2672 | */
|
---|
| 2673 | if (cOldTimers == 0)
|
---|
| 2674 | {
|
---|
| 2675 | paTimers[0].enmState = TMTIMERSTATE_INVALID;
|
---|
| 2676 | paTimers[0].szName[0] = 'n';
|
---|
| 2677 | paTimers[0].szName[1] = 'i';
|
---|
| 2678 | paTimers[0].szName[2] = 'l';
|
---|
| 2679 | paTimers[0].szName[3] = '\0';
|
---|
| 2680 | }
|
---|
| 2681 | }
|
---|
| 2682 | #endif /* IN_RING0 || IN_RING3 */
|
---|
| 2683 |
|
---|
| 2684 |
|
---|
| 2685 | /**
|
---|
[87812] | 2686 | * The slow path of tmGetFrequencyHint() where we try to recalculate the value.
|
---|
[32484] | 2687 | *
|
---|
| 2688 | * @returns The highest frequency. 0 if no timers care.
|
---|
[87812] | 2689 | * @param pVM The cross context VM structure.
|
---|
| 2690 | * @param uOldMaxHzHint The old global hint.
|
---|
[32484] | 2691 | */
|
---|
[87812] | 2692 | DECL_NO_INLINE(static, uint32_t) tmGetFrequencyHintSlow(PVMCC pVM, uint32_t uOldMaxHzHint)
|
---|
[32484] | 2693 | {
|
---|
[87812] | 2694 | /* Set two bits, though not entirely sure it's needed (too exhaused to think clearly)
|
---|
| 2695 | but it should force other callers thru the slow path while we're recalculating and
|
---|
| 2696 | help us detect changes while we're recalculating. */
|
---|
| 2697 | AssertCompile(RT_ELEMENTS(pVM->tm.s.aTimerQueues) <= 16);
|
---|
| 2698 |
|
---|
[32484] | 2699 | /*
|
---|
| 2700 | * The "right" highest frequency value isn't so important that we'll block
|
---|
[87812] | 2701 | * waiting on the timer semaphores.
|
---|
[32484] | 2702 | */
|
---|
[87812] | 2703 | uint32_t uMaxHzHint = 0;
|
---|
| 2704 | for (uint32_t idxQueue = 0; idxQueue < RT_ELEMENTS(pVM->tm.s.aTimerQueues); idxQueue++)
|
---|
[32484] | 2705 | {
|
---|
[87812] | 2706 | PTMTIMERQUEUE pQueue = &pVM->tm.s.aTimerQueues[idxQueue];
|
---|
| 2707 |
|
---|
| 2708 | /* Get the max Hz hint for the queue. */
|
---|
| 2709 | uint32_t uMaxHzHintQueue;
|
---|
| 2710 | if ( !(ASMAtomicUoReadU64(&pVM->tm.s.HzHint.u64Combined) & (RT_BIT_32(idxQueue) | RT_BIT_32(idxQueue + 16)))
|
---|
[90346] | 2711 | || RT_FAILURE_NP(PDMCritSectTryEnter(pVM, &pQueue->TimerLock)))
|
---|
[87812] | 2712 | uMaxHzHintQueue = ASMAtomicReadU32(&pQueue->uMaxHzHint);
|
---|
| 2713 | else
|
---|
[32484] | 2714 | {
|
---|
[87812] | 2715 | /* Is it still necessary to do updating? */
|
---|
| 2716 | if (ASMAtomicUoReadU64(&pVM->tm.s.HzHint.u64Combined) & (RT_BIT_32(idxQueue) | RT_BIT_32(idxQueue + 16)))
|
---|
| 2717 | {
|
---|
| 2718 | ASMAtomicAndU64(&pVM->tm.s.HzHint.u64Combined, ~RT_BIT_64(idxQueue + 16)); /* clear one flag up front */
|
---|
[32484] | 2719 |
|
---|
[87792] | 2720 | PTMTIMERQUEUECC pQueueCC = TM_GET_TIMER_QUEUE_CC(pVM, idxQueue, pQueue);
|
---|
[87812] | 2721 | uMaxHzHintQueue = 0;
|
---|
| 2722 | for (PTMTIMER pCur = tmTimerQueueGetHead(pQueueCC, pQueue);
|
---|
| 2723 | pCur;
|
---|
| 2724 | pCur = tmTimerGetNext(pQueueCC, pCur))
|
---|
[32484] | 2725 | {
|
---|
| 2726 | uint32_t uHzHint = ASMAtomicUoReadU32(&pCur->uHzHint);
|
---|
[87812] | 2727 | if (uHzHint > uMaxHzHintQueue)
|
---|
[32484] | 2728 | {
|
---|
[87812] | 2729 | TMTIMERSTATE enmState = pCur->enmState;
|
---|
| 2730 | switch (enmState)
|
---|
[32484] | 2731 | {
|
---|
| 2732 | case TMTIMERSTATE_ACTIVE:
|
---|
| 2733 | case TMTIMERSTATE_EXPIRED_GET_UNLINK:
|
---|
| 2734 | case TMTIMERSTATE_EXPIRED_DELIVER:
|
---|
| 2735 | case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
|
---|
| 2736 | case TMTIMERSTATE_PENDING_SCHEDULE:
|
---|
| 2737 | case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
|
---|
| 2738 | case TMTIMERSTATE_PENDING_RESCHEDULE:
|
---|
[87812] | 2739 | uMaxHzHintQueue = uHzHint;
|
---|
[32484] | 2740 | break;
|
---|
| 2741 |
|
---|
| 2742 | case TMTIMERSTATE_STOPPED:
|
---|
| 2743 | case TMTIMERSTATE_PENDING_STOP:
|
---|
| 2744 | case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
|
---|
| 2745 | case TMTIMERSTATE_DESTROY:
|
---|
| 2746 | case TMTIMERSTATE_FREE:
|
---|
[87792] | 2747 | case TMTIMERSTATE_INVALID:
|
---|
[32484] | 2748 | break;
|
---|
| 2749 | /* no default, want gcc warnings when adding more states. */
|
---|
| 2750 | }
|
---|
| 2751 | }
|
---|
| 2752 | }
|
---|
[87812] | 2753 |
|
---|
| 2754 | /* Write the new Hz hint for the quest and clear the other update flag. */
|
---|
| 2755 | ASMAtomicUoWriteU32(&pQueue->uMaxHzHint, uMaxHzHintQueue);
|
---|
| 2756 | ASMAtomicAndU64(&pVM->tm.s.HzHint.u64Combined, ~RT_BIT_64(idxQueue));
|
---|
[32484] | 2757 | }
|
---|
[87812] | 2758 | else
|
---|
| 2759 | uMaxHzHintQueue = ASMAtomicUoReadU32(&pQueue->uMaxHzHint);
|
---|
| 2760 |
|
---|
[90346] | 2761 | PDMCritSectLeave(pVM, &pQueue->TimerLock);
|
---|
[32484] | 2762 | }
|
---|
[87812] | 2763 |
|
---|
| 2764 | /* Update the global max Hz hint. */
|
---|
| 2765 | if (uMaxHzHint < uMaxHzHintQueue)
|
---|
| 2766 | uMaxHzHint = uMaxHzHintQueue;
|
---|
[32484] | 2767 | }
|
---|
[87812] | 2768 |
|
---|
| 2769 | /*
|
---|
| 2770 | * Update the frequency hint if no pending frequency changes and we didn't race anyone thru here.
|
---|
| 2771 | */
|
---|
| 2772 | uint64_t u64Actual = RT_MAKE_U64(0 /*no pending updates*/, uOldMaxHzHint);
|
---|
| 2773 | if (ASMAtomicCmpXchgExU64(&pVM->tm.s.HzHint.u64Combined, RT_MAKE_U64(0, uMaxHzHint), u64Actual, &u64Actual))
|
---|
| 2774 | Log(("tmGetFrequencyHintSlow: New value %u Hz\n", uMaxHzHint));
|
---|
| 2775 | else
|
---|
| 2776 | for (uint32_t iTry = 1;; iTry++)
|
---|
| 2777 | {
|
---|
| 2778 | if (RT_LO_U32(u64Actual) != 0)
|
---|
| 2779 | Log(("tmGetFrequencyHintSlow: Outdated value %u Hz (%#x, try %u)\n", uMaxHzHint, RT_LO_U32(u64Actual), iTry));
|
---|
| 2780 | else if (iTry >= 4)
|
---|
| 2781 | Log(("tmGetFrequencyHintSlow: Unable to set %u Hz (try %u)\n", uMaxHzHint, iTry));
|
---|
| 2782 | else if (ASMAtomicCmpXchgExU64(&pVM->tm.s.HzHint.u64Combined, RT_MAKE_U64(0, uMaxHzHint), u64Actual, &u64Actual))
|
---|
| 2783 | Log(("tmGetFrequencyHintSlow: New value %u Hz (try %u)\n", uMaxHzHint, iTry));
|
---|
| 2784 | else
|
---|
| 2785 | continue;
|
---|
| 2786 | break;
|
---|
| 2787 | }
|
---|
[32484] | 2788 | return uMaxHzHint;
|
---|
| 2789 | }
|
---|
[32489] | 2790 |
|
---|
| 2791 |
|
---|
| 2792 | /**
|
---|
[87812] | 2793 | * Gets the highest frequency hint for all the important timers.
|
---|
| 2794 | *
|
---|
| 2795 | * @returns The highest frequency. 0 if no timers care.
|
---|
| 2796 | * @param pVM The cross context VM structure.
|
---|
| 2797 | */
|
---|
| 2798 | DECLINLINE(uint32_t) tmGetFrequencyHint(PVMCC pVM)
|
---|
| 2799 | {
|
---|
| 2800 | /*
|
---|
| 2801 | * Query the value, recalculate it if necessary.
|
---|
| 2802 | */
|
---|
| 2803 | uint64_t u64Combined = ASMAtomicReadU64(&pVM->tm.s.HzHint.u64Combined);
|
---|
| 2804 | if (RT_HI_U32(u64Combined) == 0)
|
---|
| 2805 | return RT_LO_U32(u64Combined); /* hopefully somewhat likely */
|
---|
| 2806 | return tmGetFrequencyHintSlow(pVM, RT_LO_U32(u64Combined));
|
---|
| 2807 | }
|
---|
| 2808 |
|
---|
| 2809 |
|
---|
| 2810 | /**
|
---|
[32489] | 2811 | * Calculates a host timer frequency that would be suitable for the current
|
---|
| 2812 | * timer load.
|
---|
| 2813 | *
|
---|
| 2814 | * This will take the highest timer frequency, adjust for catch-up and warp
|
---|
| 2815 | * driver, and finally add a little fudge factor. The caller (VMM) will use
|
---|
| 2816 | * the result to adjust the per-cpu preemption timer.
|
---|
| 2817 | *
|
---|
| 2818 | * @returns The highest frequency. 0 if no important timers around.
|
---|
[58122] | 2819 | * @param pVM The cross context VM structure.
|
---|
[58123] | 2820 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
[32489] | 2821 | */
|
---|
[80281] | 2822 | VMM_INT_DECL(uint32_t) TMCalcHostTimerFrequency(PVMCC pVM, PVMCPUCC pVCpu)
|
---|
[32489] | 2823 | {
|
---|
| 2824 | uint32_t uHz = tmGetFrequencyHint(pVM);
|
---|
| 2825 |
|
---|
[32796] | 2826 | /* Catch up, we have to be more aggressive than the % indicates at the
|
---|
| 2827 | beginning of the effort. */
|
---|
[32489] | 2828 | if (ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
|
---|
| 2829 | {
|
---|
| 2830 | uint32_t u32Pct = ASMAtomicReadU32(&pVM->tm.s.u32VirtualSyncCatchUpPercentage);
|
---|
| 2831 | if (ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
|
---|
| 2832 | {
|
---|
[32796] | 2833 | if (u32Pct <= 100)
|
---|
| 2834 | u32Pct = u32Pct * pVM->tm.s.cPctHostHzFudgeFactorCatchUp100 / 100;
|
---|
| 2835 | else if (u32Pct <= 200)
|
---|
| 2836 | u32Pct = u32Pct * pVM->tm.s.cPctHostHzFudgeFactorCatchUp200 / 100;
|
---|
| 2837 | else if (u32Pct <= 400)
|
---|
| 2838 | u32Pct = u32Pct * pVM->tm.s.cPctHostHzFudgeFactorCatchUp400 / 100;
|
---|
[32489] | 2839 | uHz *= u32Pct + 100;
|
---|
| 2840 | uHz /= 100;
|
---|
| 2841 | }
|
---|
| 2842 | }
|
---|
| 2843 |
|
---|
[32796] | 2844 | /* Warp drive. */
|
---|
[32489] | 2845 | if (ASMAtomicUoReadBool(&pVM->tm.s.fVirtualWarpDrive))
|
---|
| 2846 | {
|
---|
| 2847 | uint32_t u32Pct = ASMAtomicReadU32(&pVM->tm.s.u32VirtualWarpDrivePercentage);
|
---|
| 2848 | if (ASMAtomicReadBool(&pVM->tm.s.fVirtualWarpDrive))
|
---|
| 2849 | {
|
---|
| 2850 | uHz *= u32Pct;
|
---|
| 2851 | uHz /= 100;
|
---|
| 2852 | }
|
---|
| 2853 | }
|
---|
| 2854 |
|
---|
| 2855 | /* Fudge factor. */
|
---|
[32572] | 2856 | if (pVCpu->idCpu == pVM->tm.s.idTimerCpu)
|
---|
[32796] | 2857 | uHz *= pVM->tm.s.cPctHostHzFudgeFactorTimerCpu;
|
---|
[32572] | 2858 | else
|
---|
[32796] | 2859 | uHz *= pVM->tm.s.cPctHostHzFudgeFactorOtherCpu;
|
---|
[32489] | 2860 | uHz /= 100;
|
---|
| 2861 |
|
---|
[32796] | 2862 | /* Make sure it isn't too high. */
|
---|
| 2863 | if (uHz > pVM->tm.s.cHostHzMax)
|
---|
| 2864 | uHz = pVM->tm.s.cHostHzMax;
|
---|
| 2865 |
|
---|
[32489] | 2866 | return uHz;
|
---|
| 2867 | }
|
---|
[54065] | 2868 |
|
---|
[54845] | 2869 |
|
---|
| 2870 | /**
|
---|
| 2871 | * Whether the guest virtual clock is ticking.
|
---|
| 2872 | *
|
---|
| 2873 | * @returns true if ticking, false otherwise.
|
---|
[58122] | 2874 | * @param pVM The cross context VM structure.
|
---|
[54845] | 2875 | */
|
---|
| 2876 | VMM_INT_DECL(bool) TMVirtualIsTicking(PVM pVM)
|
---|
| 2877 | {
|
---|
| 2878 | return RT_BOOL(pVM->tm.s.cVirtualTicking);
|
---|
| 2879 | }
|
---|
| 2880 |
|
---|