VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/TMAll.cpp

Last change on this file was 101088, checked in by vboxsync, 8 months ago

VMM/IEM,VMM/TM: Basic TB managment and allocation rewrite. bugref:10369

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 108.0 KB
Line 
1/* $Id: TMAll.cpp 101088 2023-09-12 10:22:20Z vboxsync $ */
2/** @file
3 * TM - Timeout Manager, all contexts.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
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
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_TM
33#ifdef DEBUG_bird
34# define DBGFTRACE_DISABLED /* annoying */
35#endif
36#include <VBox/vmm/tm.h>
37#include <VBox/vmm/mm.h>
38#include <VBox/vmm/dbgftrace.h>
39#ifdef IN_RING3
40#endif
41#include <VBox/vmm/pdmdev.h> /* (for TMTIMER_GET_CRITSECT implementation) */
42#include "TMInternal.h"
43#include <VBox/vmm/vmcc.h>
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>
52#include <iprt/asm-math.h>
53#include <iprt/string.h>
54#ifdef IN_RING3
55# include <iprt/thread.h>
56#endif
57
58#include "TMInline.h"
59
60
61/*********************************************************************************************************************************
62* Defined Constants And Macros *
63*********************************************************************************************************************************/
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
70# define TMTIMER_GET_CRITSECT(a_pVM, a_pTimer) ((a_pTimer)->pCritSect)
71# else
72# define TMTIMER_GET_CRITSECT(a_pVM, a_pTimer) tmRZTimerGetCritSect(a_pVM, a_pTimer)
73# endif
74#endif
75
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
80# define TMTIMER_ASSERT_CRITSECT(a_pVM, a_pTimer) \
81 do { \
82 if ((a_pTimer)->pCritSect) \
83 { \
84 VMSTATE enmState; \
85 PPDMCRITSECT pCritSect = TMTIMER_GET_CRITSECT(a_pVM, a_pTimer); \
86 AssertMsg( pCritSect \
87 && ( PDMCritSectIsOwner((a_pVM), pCritSect) \
88 || (enmState = (a_pVM)->enmVMState) == VMSTATE_CREATING \
89 || enmState == VMSTATE_RESETTING \
90 || enmState == VMSTATE_RESETTING_LS ),\
91 ("pTimer=%p (%s) pCritSect=%p (%s)\n", a_pTimer, (a_pTimer)->szName, \
92 (a_pTimer)->pCritSect, R3STRING(PDMR3CritSectName((a_pTimer)->pCritSect)) )); \
93 } \
94 } while (0)
95#else
96# define TMTIMER_ASSERT_CRITSECT(pVM, pTimer) do { } while (0)
97#endif
98
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; \
118 PPDMCRITSECT pCritSect = TMTIMER_GET_CRITSECT(pVM, pTimer); \
119 AssertMsg( pCritSect \
120 && ( !PDMCritSectIsOwner((pVM), pCritSect) \
121 || PDMCritSectIsOwner((pVM), &(pVM)->tm.s.VirtualSyncLock) \
122 || (enmState = (pVM)->enmVMState) == VMSTATE_CREATING \
123 || enmState == VMSTATE_RESETTING \
124 || enmState == VMSTATE_RESETTING_LS ),\
125 ("pTimer=%p (%s) pCritSect=%p (%s)\n", pTimer, pTimer->szName, \
126 (pTimer)->pCritSect, R3STRING(PDMR3CritSectName((pTimer)->pCritSect)) )); \
127 } \
128 } while (0)
129#else
130# define TMTIMER_ASSERT_SYNC_CRITSECT_ORDER(pVM, pTimer) do { } while (0)
131#endif
132
133
134#if defined(VBOX_STRICT) && defined(IN_RING0)
135/**
136 * Helper for TMTIMER_GET_CRITSECT
137 * @todo This needs a redo!
138 */
139DECLINLINE(PPDMCRITSECT) tmRZTimerGetCritSect(PVMCC pVM, PTMTIMER pTimer)
140{
141 if (pTimer->enmType == TMTIMERTYPE_DEV)
142 {
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! */
145 ASMSetFlags(fSavedFlags);
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 }
153 RT_NOREF(pVM);
154 Assert(pTimer->pCritSect == NULL);
155 return NULL;
156}
157#endif /* VBOX_STRICT && IN_RING0 */
158
159
160/**
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 *
168 * @param pVM The cross context VM structure.
169 * @param pVCpu The cross context virtual CPU structure.
170 */
171VMMDECL(void) TMNotifyStartOfExecution(PVMCC pVM, PVMCPUCC pVCpu)
172{
173#ifndef VBOX_WITHOUT_NS_ACCOUNTING
174 pVCpu->tm.s.uTscStartExecuting = SUPReadTsc();
175 pVCpu->tm.s.fExecuting = true;
176#endif
177 if (pVM->tm.s.fTSCTiedToExecution)
178 tmCpuTickResume(pVM, pVCpu);
179}
180
181
182/**
183 * Notification that execution has ended.
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 *
190 * @param pVM The cross context VM structure.
191 * @param pVCpu The cross context virtual CPU structure.
192 * @param uTsc TSC value when exiting guest context.
193 */
194VMMDECL(void) TMNotifyEndOfExecution(PVMCC pVM, PVMCPUCC pVCpu, uint64_t uTsc)
195{
196 if (pVM->tm.s.fTSCTiedToExecution)
197 tmCpuTickPause(pVCpu); /** @todo use uTsc here if we can. */
198
199#ifndef VBOX_WITHOUT_NS_ACCOUNTING
200 /*
201 * Calculate the elapsed tick count and convert it to nanoseconds.
202 */
203# ifdef IN_RING3
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;
207# else
208 uint64_t cTicks = uTsc - pVCpu->tm.s.uTscStartExecuting - SUPGetTscDeltaByCpuSetIndex(pVCpu->iHostCpuSet);
209 uint64_t const uCpuHz = SUPGetCpuHzFromGipBySetIndex(g_pSUPGlobalInfoPage, pVCpu->iHostCpuSet);
210# endif
211 AssertStmt(cTicks <= uCpuHz << 2, cTicks = uCpuHz << 2); /* max 4 sec */
212
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.
226 *
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.
231 */
232 uint64_t const cNsExecutingNew = pVCpu->tm.s.cNsExecuting + cNsExecutingDelta;
233 uint32_t uGen = ASMAtomicUoIncU32(&pVCpu->tm.s.uTimesGen); Assert(uGen & 1);
234 ASMCompilerBarrier();
235 pVCpu->tm.s.fExecuting = false;
236 pVCpu->tm.s.cNsExecuting = cNsExecutingNew;
237 pVCpu->tm.s.cPeriodsExecuting++;
238 ASMCompilerBarrier();
239 ASMAtomicUoWriteU32(&pVCpu->tm.s.uTimesGen, (uGen | 1) + 1);
240
241 /*
242 * Update stats.
243 */
244# if defined(VBOX_WITH_STATISTICS) || defined(VBOX_WITH_NS_ACCOUNTING_STATS)
245 STAM_REL_PROFILE_ADD_PERIOD(&pVCpu->tm.s.StatNsExecuting, cNsExecutingDelta);
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);
252# endif
253
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
275#endif
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 *
287 * @param pVCpu The cross context virtual CPU structure.
288 */
289VMM_INT_DECL(void) TMNotifyStartOfHalt(PVMCPUCC pVCpu)
290{
291 PVMCC pVM = pVCpu->CTX_SUFF(pVM);
292
293#ifndef VBOX_WITHOUT_NS_ACCOUNTING
294 pVCpu->tm.s.nsStartHalting = RTTimeNanoTS();
295 pVCpu->tm.s.fHalting = true;
296#endif
297
298 if ( pVM->tm.s.fTSCTiedToExecution
299 && !pVM->tm.s.fTSCNotTiedToHalt)
300 tmCpuTickResume(pVM, pVCpu);
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 *
312 * @param pVCpu The cross context virtual CPU structure.
313 */
314VMM_INT_DECL(void) TMNotifyEndOfHalt(PVMCPUCC pVCpu)
315{
316 PVM pVM = pVCpu->CTX_SUFF(pVM);
317
318 if ( pVM->tm.s.fTSCTiedToExecution
319 && !pVM->tm.s.fTSCNotTiedToHalt)
320 tmCpuTickPause(pVCpu);
321
322#ifndef VBOX_WITHOUT_NS_ACCOUNTING
323 uint64_t const u64NsTs = RTTimeNanoTS();
324 uint64_t const cNsTotalNew = u64NsTs - pVCpu->tm.s.nsStartTotal;
325 uint64_t const cNsHaltedDelta = u64NsTs - pVCpu->tm.s.nsStartHalting;
326 uint64_t const cNsHaltedNew = pVCpu->tm.s.cNsHalted + cNsHaltedDelta;
327 uint64_t const cNsOtherNew = cNsTotalNew - pVCpu->tm.s.cNsExecuting - cNsHaltedNew;
328
329 uint32_t uGen = ASMAtomicUoIncU32(&pVCpu->tm.s.uTimesGen); Assert(uGen & 1);
330 ASMCompilerBarrier();
331 pVCpu->tm.s.fHalting = false;
332 pVCpu->tm.s.fUpdateStats = false;
333 pVCpu->tm.s.cNsHalted = cNsHaltedNew;
334 pVCpu->tm.s.cPeriodsHalted++;
335 ASMCompilerBarrier();
336 ASMAtomicUoWriteU32(&pVCpu->tm.s.uTimesGen, (uGen | 1) + 1);
337
338# if defined(VBOX_WITH_STATISTICS) || defined(VBOX_WITH_NS_ACCOUNTING_STATS)
339 STAM_REL_PROFILE_ADD_PERIOD(&pVCpu->tm.s.StatNsHalted, cNsHaltedDelta);
340 STAM_REL_COUNTER_ADD(&pVCpu->tm.s.StatNsTotal, cNsTotalNew - pVCpu->tm.s.cNsTotalStat);
341 int64_t const cNsOtherNewDelta = cNsOtherNew - pVCpu->tm.s.cNsOtherStat;
342 if (cNsOtherNewDelta > 0)
343 STAM_REL_COUNTER_ADD(&pVCpu->tm.s.StatNsOther, (uint64_t)cNsOtherNewDelta);
344# endif
345 pVCpu->tm.s.cNsTotalStat = cNsTotalNew;
346 pVCpu->tm.s.cNsOtherStat = cNsOtherNew;
347#endif
348}
349
350
351/**
352 * Raise the timer force action flag and notify the dedicated timer EMT.
353 *
354 * @param pVM The cross context VM structure.
355 */
356DECLINLINE(void) tmScheduleNotify(PVMCC pVM)
357{
358 VMCPUID idCpu = pVM->tm.s.idTimerCpu;
359 AssertReturnVoid(idCpu < pVM->cCpus);
360 PVMCPUCC pVCpuDst = VMCC_GET_CPU(pVM, idCpu);
361
362 if (!VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
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/**
375 * Schedule the queue which was changed.
376 */
377DECLINLINE(void) tmSchedule(PVMCC pVM, PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
378{
379 int rc = PDMCritSectTryEnter(pVM, &pQueue->TimerLock);
380 if (RT_SUCCESS_NP(rc))
381 {
382 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatScheduleOne), a);
383 Log3(("tmSchedule: tmTimerQueueSchedule\n"));
384 tmTimerQueueSchedule(pVM, pQueueCC, pQueue);
385#ifdef VBOX_STRICT
386 tmTimerQueuesSanityChecks(pVM, "tmSchedule");
387#endif
388 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatScheduleOne), a);
389 PDMCritSectLeave(pVM, &pQueue->TimerLock);
390 return;
391 }
392
393 TMTIMERSTATE enmState = pTimer->enmState;
394 if (TMTIMERSTATE_IS_PENDING_SCHEDULING(enmState))
395 tmScheduleNotify(pVM);
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 */
408DECLINLINE(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 *
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.
426 *
427 * @todo FIXME: Look into potential race with the thread running the queues
428 * and stuff.
429 */
430DECLINLINE(void) tmTimerLinkSchedule(PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
431{
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;
437 do
438 {
439 idxHead = pQueue->idxSchedule;
440 Assert(idxHead == UINT32_MAX || idxHead < pQueueCC->cTimersAlloc);
441 pTimer->idxScheduleNext = idxHead;
442 } while (!ASMAtomicCmpXchgU32(&pQueue->idxSchedule, idxHeadNew, idxHead));
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.
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.
457 */
458DECLINLINE(bool) tmTimerTryWithLink(PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue, PTMTIMER pTimer,
459 TMTIMERSTATE enmStateNew, TMTIMERSTATE enmStateOld)
460{
461 if (tmTimerTry(pTimer, enmStateNew, enmStateOld))
462 {
463 tmTimerLinkSchedule(pQueueCC, pQueue, pTimer);
464 return true;
465 }
466 return false;
467}
468
469
470/**
471 * Links a timer into the active list of a timer queue.
472 *
473 * @param pVM The cross context VM structure.
474 * @param pQueueCC The current context queue (same as @a pQueue for
475 * ring-3).
476 * @param pQueue The shared queue data.
477 * @param pTimer The timer.
478 * @param u64Expire The timer expiration time.
479 *
480 * @remarks Called while owning the relevant queue lock.
481 */
482DECL_FORCE_INLINE(void) tmTimerQueueLinkActive(PVMCC pVM, PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue,
483 PTMTIMER pTimer, uint64_t u64Expire)
484{
485 Assert(pTimer->idxNext == UINT32_MAX);
486 Assert(pTimer->idxPrev == UINT32_MAX);
487 Assert(pTimer->enmState == TMTIMERSTATE_ACTIVE || pQueue->enmClock != TMCLOCK_VIRTUAL_SYNC); /* (active is not a stable state) */
488 RT_NOREF(pVM);
489
490 PTMTIMER pCur = tmTimerQueueGetHead(pQueueCC, pQueue);
491 if (pCur)
492 {
493 for (;; pCur = tmTimerGetNext(pQueueCC, pCur))
494 {
495 if (pCur->u64Expire > u64Expire)
496 {
497 const PTMTIMER pPrev = tmTimerGetPrev(pQueueCC, pCur);
498 tmTimerSetNext(pQueueCC, pTimer, pCur);
499 tmTimerSetPrev(pQueueCC, pTimer, pPrev);
500 if (pPrev)
501 tmTimerSetNext(pQueueCC, pPrev, pTimer);
502 else
503 {
504 tmTimerQueueSetHead(pQueueCC, pQueue, pTimer);
505 ASMAtomicWriteU64(&pQueue->u64Expire, u64Expire);
506 DBGFTRACE_U64_TAG2(pVM, u64Expire, "tmTimerQueueLinkActive head", pTimer->szName);
507 }
508 tmTimerSetPrev(pQueueCC, pCur, pTimer);
509 return;
510 }
511 if (pCur->idxNext == UINT32_MAX)
512 {
513 tmTimerSetNext(pQueueCC, pCur, pTimer);
514 tmTimerSetPrev(pQueueCC, pTimer, pCur);
515 DBGFTRACE_U64_TAG2(pVM, u64Expire, "tmTimerQueueLinkActive tail", pTimer->szName);
516 return;
517 }
518 }
519 }
520 else
521 {
522 tmTimerQueueSetHead(pQueueCC, pQueue, pTimer);
523 ASMAtomicWriteU64(&pQueue->u64Expire, u64Expire);
524 DBGFTRACE_U64_TAG2(pVM, u64Expire, "tmTimerQueueLinkActive empty", pTimer->szName);
525 }
526}
527
528
529
530/**
531 * Schedules the given timer on the given queue.
532 *
533 * @param pVM The cross context VM structure.
534 * @param pQueueCC The current context queue (same as @a pQueue for
535 * ring-3).
536 * @param pQueue The shared queue data.
537 * @param pTimer The timer that needs scheduling.
538 *
539 * @remarks Called while owning the lock.
540 */
541DECLINLINE(void) tmTimerQueueScheduleOne(PVMCC pVM, PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
542{
543 Assert(pQueue->enmClock != TMCLOCK_VIRTUAL_SYNC);
544 RT_NOREF(pVM);
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 */
561 tmTimerQueueUnlinkActive(pVM, pQueueCC, pQueue, pTimer);
562 RT_FALL_THRU();
563
564 /*
565 * Schedule timer (insert into the active list).
566 */
567 case TMTIMERSTATE_PENDING_SCHEDULE:
568 Assert(pTimer->idxNext == UINT32_MAX); Assert(pTimer->idxPrev == UINT32_MAX);
569 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, TMTIMERSTATE_PENDING_SCHEDULE)))
570 break; /* retry */
571 tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, pTimer->u64Expire);
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 */
580 tmTimerQueueUnlinkActive(pVM, pQueueCC, pQueue, pTimer);
581 RT_FALL_THRU();
582
583 /*
584 * Stop the timer (not on the active list).
585 */
586 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
587 Assert(pTimer->idxNext == UINT32_MAX); Assert(pTimer->idxPrev == UINT32_MAX);
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:
604 tmTimerLinkSchedule(pQueueCC, pQueue, pTimer);
605 STAM_COUNTER_INC(&pVM->tm.s.CTX_SUFF_Z(StatPostponed));
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 *
628 * @param pVM The cross context VM structure.
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.
632 *
633 * @remarks Called while owning the lock.
634 */
635void tmTimerQueueSchedule(PVMCC pVM, PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue)
636{
637 Assert(PDMCritSectIsOwner(pVM, &pQueue->TimerLock));
638
639 /*
640 * Dequeue the scheduling list and iterate it.
641 */
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)
645 {
646 AssertBreak(idxNext < pQueueCC->cTimersAlloc);
647
648 /*
649 * Unlink the head timer and take down the index of the next one.
650 */
651 PTMTIMER pTimer = &pQueueCC->paTimers[idxNext];
652 idxNext = pTimer->idxScheduleNext;
653 pTimer->idxScheduleNext = UINT32_MAX;
654
655 /*
656 * Do the scheduling.
657 */
658 Log2(("tmTimerQueueSchedule: %p:{.enmState=%s, .enmClock=%d, .enmType=%d, .szName=%s}\n",
659 pTimer, tmTimerState(pTimer->enmState), pQueue->enmClock, pTimer->enmType, pTimer->szName));
660 tmTimerQueueScheduleOne(pVM, pQueueCC, pQueue, pTimer);
661 Log2(("tmTimerQueueSchedule: %p: new %s\n", pTimer, tmTimerState(pTimer->enmState)));
662 }
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 *
671 * @param pVM The cross context VM structure.
672 * @param pszWhere Caller location clue.
673 */
674void tmTimerQueuesSanityChecks(PVMCC pVM, const char *pszWhere)
675{
676 for (uint32_t idxQueue = 0; idxQueue < RT_ELEMENTS(pVM->tm.s.aTimerQueues); idxQueue++)
677 {
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
682 int rc = PDMCritSectTryEnter(pVM, &pQueue->TimerLock);
683 if (RT_SUCCESS(rc))
684 {
685 if ( pQueue->enmClock != TMCLOCK_VIRTUAL_SYNC
686 || PDMCritSectTryEnter(pVM, &pVM->tm.s.VirtualSyncLock) == VINF_SUCCESS)
687 {
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))
693 {
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 }
711 }
712
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)
718 {
719 PTMTIMER const pTimer = &pQueue->paTimers[idxTimer];
720 TMTIMERSTATE const enmState = pTimer->enmState;
721 switch (enmState)
722 {
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;
772 }
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 }
780 }
781 Assert(cFree == pQueue->cTimersFree);
782# endif /* IN_RING3 */
783
784 if (pQueue->enmClock == TMCLOCK_VIRTUAL_SYNC)
785 PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
786 }
787 PDMCritSectLeave(pVM, &pQueue->TimerLock);
788 }
789 }
790}
791#endif /* !VBOX_STRICT */
792
793#ifdef VBOX_HIGH_RES_TIMERS_HACK
794
795/**
796 * Worker for tmTimerPollInternal that handles misses when the dedicated timer
797 * EMT is polling.
798 *
799 * @returns See tmTimerPollInternal.
800 * @param pVM The cross context VM structure.
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 */
806DECLINLINE(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.
846 * @param pVM The cross context VM structure.
847 * @param u64Now Current virtual clock timestamp.
848 * @param pu64Delta Where to return the delta.
849 */
850DECL_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.
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.
869 */
870DECL_FORCE_INLINE(uint64_t) tmTimerPollReturnHit(PVM pVM, PVMCPU pVCpu, PVMCPU pVCpuDst, uint64_t u64Now,
871 uint64_t *pu64Delta, PSTAMCOUNTER pCounter)
872{
873 STAM_COUNTER_INC(pCounter); NOREF(pCounter);
874 if (pVCpuDst != pVCpu)
875 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
876 *pu64Delta = 0;
877 return 0;
878}
879
880
881/**
882 * Common worker for TMTimerPollGIP and TMTimerPoll.
883 *
884 * This function is called before FFs are checked in the inner execution EM loops.
885 *
886 * @returns The GIP timestamp of the next event.
887 * 0 if the next event has already expired.
888 *
889 * @param pVM The cross context VM structure.
890 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
891 * @param pu64Delta Where to store the delta.
892 * @param pu64Now Where to store the current time. Optional.
893 *
894 * @thread The emulation thread.
895 *
896 * @remarks GIP uses ns ticks.
897 */
898DECL_FORCE_INLINE(uint64_t) tmTimerPollInternal(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *pu64Delta, uint64_t *pu64Now)
899{
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);
905 STAM_COUNTER_INC(&pVM->tm.s.StatPoll);
906 if (pu64Now)
907 *pu64Now = u64Now;
908
909 /*
910 * Return straight away if the timer FF is already set ...
911 */
912 if (VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
913 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollAlreadySet);
914
915 /*
916 * ... or if timers are being run.
917 */
918 if (ASMAtomicReadBool(&pVM->tm.s.fRunningQueues))
919 {
920 STAM_COUNTER_INC(&pVM->tm.s.StatPollRunning);
921 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
922 }
923
924 /*
925 * Check for TMCLOCK_VIRTUAL expiration.
926 */
927 const uint64_t u64Expire1 = ASMAtomicReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL].u64Expire);
928 const int64_t i64Delta1 = u64Expire1 - u64Now;
929 if (i64Delta1 <= 0)
930 {
931 if (!VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
932 {
933 Log5(("TMAll(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER)));
934 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
935 }
936 LogFlow(("TMTimerPoll: expire1=%'RU64 <= now=%'RU64\n", u64Expire1, u64Now));
937 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtual);
938 }
939
940 /*
941 * Check for TMCLOCK_VIRTUAL_SYNC expiration.
942 * This isn't quite as straight forward if in a catch-up, not only do
943 * we have to adjust the 'now' but when have to adjust the delta as well.
944 */
945
946 /*
947 * Optimistic lockless approach.
948 */
949 uint64_t u64VirtualSyncNow;
950 uint64_t u64Expire2 = ASMAtomicUoReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire);
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)
959 && u64Expire2 == ASMAtomicUoReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire)))
960 {
961 u64VirtualSyncNow = u64Now - u64VirtualSyncNow;
962 int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
963 if (i64Delta2 > 0)
964 {
965 STAM_COUNTER_INC(&pVM->tm.s.StatPollSimple);
966 STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
967
968 if (pVCpu == pVCpuDst)
969 return tmTimerPollReturnMiss(pVM, u64Now, RT_MIN(i64Delta1, i64Delta2), pu64Delta);
970 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
971 }
972
973 if ( !pVM->tm.s.fRunningQueues
974 && !VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
975 {
976 Log5(("TMAll(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER)));
977 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
978 }
979
980 STAM_COUNTER_INC(&pVM->tm.s.StatPollSimple);
981 LogFlow(("TMTimerPoll: expire2=%'RU64 <= now=%'RU64\n", u64Expire2, u64Now));
982 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
983 }
984 }
985 }
986 else
987 {
988 STAM_COUNTER_INC(&pVM->tm.s.StatPollSimple);
989 LogFlow(("TMTimerPoll: stopped\n"));
990 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
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);
1004 u64Expire2 = ASMAtomicReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire);
1005 if (fCatchUp)
1006 {
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)
1015 && u64Expire2 == ASMAtomicReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire)
1016 && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp)
1017 && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncTicking))
1018 || cOuterTries <= 0)
1019 {
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 }
1029 else
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;
1037 }
1038 }
1039 else if ( off == ASMAtomicReadU64(&pVM->tm.s.offVirtualSync)
1040 && u64Expire2 == ASMAtomicReadU64(&pVM->tm.s.aTimerQueues[TMCLOCK_VIRTUAL_SYNC].u64Expire)
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. */
1046 if (VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
1047 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollAlreadySet);
1048 if (ASMAtomicUoReadBool(&pVM->tm.s.fRunningQueues))
1049 {
1050 STAM_COUNTER_INC(&pVM->tm.s.StatPollRunning);
1051 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
1052 }
1053 if (!ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncTicking))
1054 {
1055 LogFlow(("TMTimerPoll: stopped\n"));
1056 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
1057 }
1058 if (cOuterTries <= 0)
1059 break; /* that's enough */
1060 }
1061 if (cOuterTries <= 0)
1062 STAM_COUNTER_INC(&pVM->tm.s.StatPollELoop);
1063 u64VirtualSyncNow = u64Now - off;
1064
1065 /* Calc delta and see if we've got a virtual sync hit. */
1066 int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
1067 if (i64Delta2 <= 0)
1068 {
1069 if ( !pVM->tm.s.fRunningQueues
1070 && !VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER))
1071 {
1072 Log5(("TMAll(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_IS_SET(pVCpuDst, VMCPU_FF_TIMER)));
1073 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
1074 }
1075 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtualSync);
1076 LogFlow(("TMTimerPoll: expire2=%'RU64 <= now=%'RU64\n", u64Expire2, u64Now));
1077 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
1078 }
1079
1080 /*
1081 * Return the time left to the next event.
1082 */
1083 STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
1084 if (pVCpu == pVCpuDst)
1085 {
1086 if (fCatchUp)
1087 i64Delta2 = ASMMultU64ByU32DivByU32(i64Delta2, 100, u32Pct + 100);
1088 return tmTimerPollReturnMiss(pVM, u64Now, RT_MIN(i64Delta1, i64Delta2), pu64Delta);
1089 }
1090 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
1091}
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 *
1099 * @returns true if timers are pending, false if not.
1100 *
1101 * @param pVM The cross context VM structure.
1102 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1103 * @thread The emulation thread.
1104 */
1105VMMDECL(bool) TMTimerPollBool(PVMCC pVM, PVMCPUCC pVCpu)
1106{
1107 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1108 uint64_t off = 0;
1109 tmTimerPollInternal(pVM, pVCpu, &off, NULL);
1110 return off == 0;
1111}
1112
1113
1114/**
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 */
1130VMMDECL(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/**
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 *
1146 * @param pVM The cross context VM structure.
1147 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1148 * @thread The emulation thread.
1149 */
1150VMM_INT_DECL(void) TMTimerPollVoid(PVMCC pVM, PVMCPUCC pVCpu)
1151{
1152 uint64_t off;
1153 tmTimerPollInternal(pVM, pVCpu, &off, NULL);
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 *
1162 * @returns The GIP timestamp of the next event.
1163 * 0 if the next event has already expired.
1164 * @param pVM The cross context VM structure.
1165 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1166 * @param pu64Delta Where to store the delta.
1167 * @thread The emulation thread.
1168 */
1169VMM_INT_DECL(uint64_t) TMTimerPollGIP(PVMCC pVM, PVMCPUCC pVCpu, uint64_t *pu64Delta)
1170{
1171 return tmTimerPollInternal(pVM, pVCpu, pu64Delta, NULL);
1172}
1173
1174#endif /* VBOX_HIGH_RES_TIMERS_HACK */
1175
1176/**
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.
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.
1186 *
1187 * @remarks Currently only supported on timers using the virtual sync clock.
1188 */
1189VMMDECL(int) TMTimerLock(PVMCC pVM, TMTIMERHANDLE hTimer, int rcBusy)
1190{
1191 TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
1192 AssertReturn(idxQueue == TMCLOCK_VIRTUAL_SYNC, VERR_NOT_SUPPORTED);
1193 return PDMCritSectEnter(pVM, &pVM->tm.s.VirtualSyncLock, rcBusy);
1194}
1195
1196
1197/**
1198 * Unlocks a timer clock locked by TMTimerLock.
1199 *
1200 * @param pVM The cross context VM structure.
1201 * @param hTimer Timer handle as returned by one of the create functions.
1202 */
1203VMMDECL(void) TMTimerUnlock(PVMCC pVM, TMTIMERHANDLE hTimer)
1204{
1205 TMTIMER_HANDLE_TO_VARS_RETURN_VOID(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
1206 AssertReturnVoid(idxQueue == TMCLOCK_VIRTUAL_SYNC);
1207 PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
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.
1215 * @param pVM The cross context VM structure.
1216 * @param hTimer Timer handle as returned by one of the create functions.
1217 */
1218VMMDECL(bool) TMTimerIsLockOwner(PVMCC pVM, TMTIMERHANDLE hTimer)
1219{
1220 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, false); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
1221 AssertReturn(idxQueue == TMCLOCK_VIRTUAL_SYNC, false);
1222 return PDMCritSectIsOwner(pVM, &pVM->tm.s.VirtualSyncLock);
1223}
1224
1225
1226/**
1227 * Optimized TMTimerSet code path for starting an inactive timer.
1228 *
1229 * @returns VBox status code.
1230 *
1231 * @param pVM The cross context VM structure.
1232 * @param pTimer The timer handle.
1233 * @param u64Expire The new expire time.
1234 * @param pQueue Pointer to the shared timer queue data.
1235 * @param idxQueue The queue index.
1236 */
1237static int tmTimerSetOptimizedStart(PVMCC pVM, PTMTIMER pTimer, uint64_t u64Expire, PTMTIMERQUEUE pQueue, uint32_t idxQueue)
1238{
1239 Assert(pTimer->idxPrev == UINT32_MAX);
1240 Assert(pTimer->idxNext == UINT32_MAX);
1241 Assert(pTimer->enmState == TMTIMERSTATE_ACTIVE);
1242
1243 /*
1244 * Calculate and set the expiration time.
1245 */
1246 if (idxQueue == TMCLOCK_VIRTUAL_SYNC)
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);
1254 Log2(("tmTimerSetOptimizedStart: %p:{.pszDesc='%s', .u64Expire=%'RU64}\n", pTimer, pTimer->szName, u64Expire));
1255
1256 /*
1257 * Link the timer into the active list.
1258 */
1259 tmTimerQueueLinkActive(pVM, TM_GET_TIMER_QUEUE_CC(pVM, idxQueue, pQueue), pQueue, pTimer, u64Expire);
1260
1261 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetOpt);
1262 return VINF_SUCCESS;
1263}
1264
1265
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
1273 * @param pVM The cross context VM structure.
1274 * @param pTimer The timer handle.
1275 * @param u64Expire The expiration time.
1276 */
1277static int tmTimerVirtualSyncSet(PVMCC pVM, PTMTIMER pTimer, uint64_t u64Expire)
1278{
1279 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetVs), a);
1280 VM_ASSERT_EMT(pVM);
1281 TMTIMER_ASSERT_SYNC_CRITSECT_ORDER(pVM, pTimer);
1282 int rc = PDMCritSectEnter(pVM, &pVM->tm.s.VirtualSyncLock, VINF_SUCCESS);
1283 AssertRCReturn(rc, rc);
1284
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;
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);
1296
1297 AssertMsg(u64Expire >= pVM->tm.s.u64VirtualSync,
1298 ("%'RU64 < %'RU64 %s\n", u64Expire, pVM->tm.s.u64VirtualSync, pTimer->szName));
1299 pTimer->u64Expire = u64Expire;
1300 TM_SET_STATE(pTimer, TMTIMERSTATE_ACTIVE);
1301 tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
1302 rc = VINF_SUCCESS;
1303 break;
1304
1305 case TMTIMERSTATE_ACTIVE:
1306 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetVsStActive);
1307 tmTimerQueueUnlinkActive(pVM, pQueueCC, pQueue, pTimer);
1308 pTimer->u64Expire = u64Expire;
1309 tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
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:
1322 AssertLogRelMsgFailed(("Invalid timer state %s: %s\n", tmTimerState(enmState), pTimer->szName));
1323 rc = VERR_TM_INVALID_STATE;
1324 break;
1325
1326 default:
1327 AssertMsgFailed(("Unknown timer state %d: %s\n", enmState, pTimer->szName));
1328 rc = VERR_TM_UNKNOWN_STATE;
1329 break;
1330 }
1331
1332 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetVs), a);
1333 PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
1334 return rc;
1335}
1336
1337
1338/**
1339 * Arm a timer with a (new) expire time.
1340 *
1341 * @returns VBox status code.
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.
1345 */
1346VMMDECL(int) TMTimerSet(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t u64Expire)
1347{
1348 TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
1349 STAM_COUNTER_INC(&pTimer->StatSetAbsolute);
1350
1351 /* Treat virtual sync timers specially. */
1352 if (idxQueue == TMCLOCK_VIRTUAL_SYNC)
1353 return tmTimerVirtualSyncSet(pVM, pTimer, u64Expire);
1354
1355 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1356 TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
1357
1358 DBGFTRACE_U64_TAG2(pVM, u64Expire, "TMTimerSet", pTimer->szName);
1359
1360#ifdef VBOX_WITH_STATISTICS
1361 /*
1362 * Gather optimization info.
1363 */
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
1379#if 1
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 */
1384 TMTIMERSTATE enmState1 = pTimer->enmState;
1385 if ( enmState1 == TMTIMERSTATE_EXPIRED_DELIVER
1386 || ( enmState1 == TMTIMERSTATE_STOPPED
1387 && pTimer->pCritSect))
1388 {
1389 /* Try take the TM lock and check the state again. */
1390 int rc = PDMCritSectTryEnter(pVM, &pQueue->TimerLock);
1391 if (RT_SUCCESS_NP(rc))
1392 {
1393 if (RT_LIKELY(tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, enmState1)))
1394 {
1395 tmTimerSetOptimizedStart(pVM, pTimer, u64Expire, pQueue, idxQueue);
1396 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1397 PDMCritSectLeave(pVM, &pQueue->TimerLock);
1398 return VINF_SUCCESS;
1399 }
1400 PDMCritSectLeave(pVM, &pQueue->TimerLock);
1401 }
1402 }
1403#endif
1404
1405 /*
1406 * Unoptimized code path.
1407 */
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 */
1414 TMTIMERSTATE enmState = pTimer->enmState;
1415 Log2(("TMTimerSet: %p:{.enmState=%s, .pszDesc='%s'} cRetries=%d u64Expire=%'RU64\n",
1416 pTimer, tmTimerState(enmState), pTimer->szName, cRetries, u64Expire));
1417 switch (enmState)
1418 {
1419 case TMTIMERSTATE_EXPIRED_DELIVER:
1420 case TMTIMERSTATE_STOPPED:
1421 if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
1422 {
1423 Assert(pTimer->idxPrev == UINT32_MAX);
1424 Assert(pTimer->idxNext == UINT32_MAX);
1425 pTimer->u64Expire = u64Expire;
1426 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
1427 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
1428 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1429 return VINF_SUCCESS;
1430 }
1431 break;
1432
1433 case TMTIMERSTATE_PENDING_SCHEDULE:
1434 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1435 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
1436 {
1437 pTimer->u64Expire = u64Expire;
1438 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
1439 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
1440 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1441 return VINF_SUCCESS;
1442 }
1443 break;
1444
1445
1446 case TMTIMERSTATE_ACTIVE:
1447 if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
1448 {
1449 pTimer->u64Expire = u64Expire;
1450 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
1451 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
1452 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
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);
1463 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
1464 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1465 return VINF_SUCCESS;
1466 }
1467 break;
1468
1469
1470 case TMTIMERSTATE_EXPIRED_GET_UNLINK:
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 */
1484 case TMTIMERSTATE_DESTROY:
1485 case TMTIMERSTATE_FREE:
1486 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, pTimer->szName));
1487 return VERR_TM_INVALID_STATE;
1488 default:
1489 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
1490 return VERR_TM_UNKNOWN_STATE;
1491 }
1492 } while (cRetries-- > 0);
1493
1494 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, pTimer->szName));
1495 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1496 return VERR_TM_TIMER_UNSTABLE_STATE;
1497}
1498
1499
1500/**
1501 * Return the current time for the specified clock, setting pu64Now if not NULL.
1502 *
1503 * @returns Current time.
1504 * @param pVM The cross context VM structure.
1505 * @param enmClock The clock to query.
1506 * @param pu64Now Optional pointer where to store the return time
1507 */
1508DECL_FORCE_INLINE(uint64_t) tmTimerSetRelativeNowWorker(PVMCC pVM, TMCLOCK enmClock, uint64_t *pu64Now)
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/**
1533 * Optimized TMTimerSetRelative code path.
1534 *
1535 * @returns VBox status code.
1536 *
1537 * @param pVM The cross context VM structure.
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.
1542 * @param pQueueCC The context specific queue data (same as @a pQueue
1543 * for ring-3).
1544 * @param pQueue The shared queue data.
1545 */
1546static int tmTimerSetRelativeOptimizedStart(PVMCC pVM, PTMTIMER pTimer, uint64_t cTicksToNext, uint64_t *pu64Now,
1547 PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue)
1548{
1549 Assert(pTimer->idxPrev == UINT32_MAX);
1550 Assert(pTimer->idxNext == UINT32_MAX);
1551 Assert(pTimer->enmState == TMTIMERSTATE_ACTIVE);
1552
1553 /*
1554 * Calculate and set the expiration time.
1555 */
1556 uint64_t const u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
1557 pTimer->u64Expire = u64Expire;
1558 Log2(("tmTimerSetRelativeOptimizedStart: %p:{.pszDesc='%s', .u64Expire=%'RU64} cTicksToNext=%'RU64\n", pTimer, pTimer->szName, u64Expire, cTicksToNext));
1559
1560 /*
1561 * Link the timer into the active list.
1562 */
1563 DBGFTRACE_U64_TAG2(pVM, u64Expire, "tmTimerSetRelativeOptimizedStart", pTimer->szName);
1564 tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
1565
1566 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeOpt);
1567 return VINF_SUCCESS;
1568}
1569
1570
1571/**
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
1578 * @param pVM The cross context VM structure.
1579 * @param pTimer The timer to (re-)arm.
1580 * @param cTicksToNext Clock ticks until the next time expiration.
1581 * @param pu64Now Where to return the current time stamp used.
1582 * Optional.
1583 */
1584static int tmTimerVirtualSyncSetRelative(PVMCC pVM, PTMTIMER pTimer, uint64_t cTicksToNext, uint64_t *pu64Now)
1585{
1586 STAM_PROFILE_START(pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelativeVs), a);
1587 VM_ASSERT_EMT(pVM);
1588 TMTIMER_ASSERT_SYNC_CRITSECT_ORDER(pVM, pTimer);
1589 int rc = PDMCritSectEnter(pVM, &pVM->tm.s.VirtualSyncLock, VINF_SUCCESS);
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. */
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;
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);
1612 tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
1613 rc = VINF_SUCCESS;
1614 break;
1615
1616 case TMTIMERSTATE_ACTIVE:
1617 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeVsStActive);
1618 tmTimerQueueUnlinkActive(pVM, pQueueCC, pQueue, pTimer);
1619 pTimer->u64Expire = u64Expire;
1620 tmTimerQueueLinkActive(pVM, pQueueCC, pQueue, pTimer, u64Expire);
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:
1633 AssertLogRelMsgFailed(("Invalid timer state %s: %s\n", tmTimerState(enmState), pTimer->szName));
1634 rc = VERR_TM_INVALID_STATE;
1635 break;
1636
1637 default:
1638 AssertMsgFailed(("Unknown timer state %d: %s\n", enmState, pTimer->szName));
1639 rc = VERR_TM_UNKNOWN_STATE;
1640 break;
1641 }
1642
1643 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelativeVs), a);
1644 PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
1645 return rc;
1646}
1647
1648
1649/**
1650 * Arm a timer with a expire time relative to the current time.
1651 *
1652 * @returns VBox status code.
1653 * @param pVM The cross context VM structure.
1654 * @param pTimer The timer to arm.
1655 * @param cTicksToNext Clock ticks until the next time expiration.
1656 * @param pu64Now Where to return the current time stamp used.
1657 * Optional.
1658 * @param pQueueCC The context specific queue data (same as @a pQueue
1659 * for ring-3).
1660 * @param pQueue The shared queue data.
1661 */
1662static int tmTimerSetRelative(PVMCC pVM, PTMTIMER pTimer, uint64_t cTicksToNext, uint64_t *pu64Now,
1663 PTMTIMERQUEUECC pQueueCC, PTMTIMERQUEUE pQueue)
1664{
1665 STAM_COUNTER_INC(&pTimer->StatSetRelative);
1666
1667 /* Treat virtual sync timers specially. */
1668 if (pQueue->enmClock == TMCLOCK_VIRTUAL_SYNC)
1669 return tmTimerVirtualSyncSetRelative(pVM, pTimer, cTicksToNext, pu64Now);
1670
1671 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelative), a);
1672 TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
1673
1674 DBGFTRACE_U64_TAG2(pVM, cTicksToNext, "TMTimerSetRelative", pTimer->szName);
1675
1676#ifdef VBOX_WITH_STATISTICS
1677 /*
1678 * Gather optimization info.
1679 */
1680 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelative);
1681 TMTIMERSTATE enmOrgState = pTimer->enmState;
1682 switch (enmOrgState)
1683 {
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 *
1705 * Note! Lock ordering doesn't apply when we only _try_ to
1706 * get the innermost locks.
1707 */
1708 bool fOwnTMLock = RT_SUCCESS_NP(PDMCritSectTryEnter(pVM, &pQueue->TimerLock));
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)))
1717 {
1718 tmTimerSetRelativeOptimizedStart(pVM, pTimer, cTicksToNext, pu64Now, pQueueCC, pQueue);
1719 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelative), a);
1720 PDMCritSectLeave(pVM, &pQueue->TimerLock);
1721 return VINF_SUCCESS;
1722 }
1723
1724 /* Optimize other states when it becomes necessary. */
1725 }
1726#endif
1727
1728 /*
1729 * Unoptimized path.
1730 */
1731 int rc;
1732 for (int cRetries = 1000; ; cRetries--)
1733 {
1734 /*
1735 * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
1736 */
1737 TMTIMERSTATE enmState = pTimer->enmState;
1738 switch (enmState)
1739 {
1740 case TMTIMERSTATE_STOPPED:
1741 if (pQueue->enmClock == TMCLOCK_VIRTUAL_SYNC)
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
1747 * re-starting the timer in response to a initial_count write.) */
1748 }
1749 RT_FALL_THRU();
1750 case TMTIMERSTATE_EXPIRED_DELIVER:
1751 if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
1752 {
1753 Assert(pTimer->idxPrev == UINT32_MAX);
1754 Assert(pTimer->idxNext == UINT32_MAX);
1755 pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
1756 Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [EXP/STOP]\n",
1757 pTimer, tmTimerState(enmState), pTimer->szName, pTimer->u64Expire, cRetries));
1758 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
1759 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
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 {
1770 pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
1771 Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [PEND_SCHED]\n",
1772 pTimer, tmTimerState(enmState), pTimer->szName, pTimer->u64Expire, cRetries));
1773 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
1774 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
1775 rc = VINF_SUCCESS;
1776 break;
1777 }
1778 rc = VERR_TRY_AGAIN;
1779 break;
1780
1781
1782 case TMTIMERSTATE_ACTIVE:
1783 if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
1784 {
1785 pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
1786 Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [ACTIVE]\n",
1787 pTimer, tmTimerState(enmState), pTimer->szName, pTimer->u64Expire, cRetries));
1788 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
1789 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
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 {
1800 pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
1801 Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [PEND_RESCH/STOP]\n",
1802 pTimer, tmTimerState(enmState), pTimer->szName, pTimer->u64Expire, cRetries));
1803 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
1804 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
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:
1829 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, pTimer->szName));
1830 rc = VERR_TM_INVALID_STATE;
1831 break;
1832
1833 default:
1834 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
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 {
1845 tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
1846 break;
1847 }
1848 if (cRetries <= 0)
1849 {
1850 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, pTimer->szName));
1851 rc = VERR_TM_TIMER_UNSTABLE_STATE;
1852 tmTimerSetRelativeNowWorker(pVM, pQueue->enmClock, pu64Now);
1853 break;
1854 }
1855
1856 /*
1857 * Retry to gain locks.
1858 */
1859 if (!fOwnTMLock)
1860 fOwnTMLock = RT_SUCCESS_NP(PDMCritSectTryEnter(pVM, &pQueue->TimerLock));
1861
1862 } /* for (;;) */
1863
1864 /*
1865 * Clean up and return.
1866 */
1867 if (fOwnTMLock)
1868 PDMCritSectLeave(pVM, &pQueue->TimerLock);
1869
1870 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelative), a);
1871 return rc;
1872}
1873
1874
1875/**
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 */
1885VMMDECL(int) TMTimerSetRelative(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cTicksToNext, uint64_t *pu64Now)
1886{
1887 TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
1888 return tmTimerSetRelative(pVM, pTimer, cTicksToNext, pu64Now, pQueueCC, pQueue);
1889}
1890
1891
1892/**
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.
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.
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 */
1907VMMDECL(int) TMTimerSetFrequencyHint(PVMCC pVM, TMTIMERHANDLE hTimer, uint32_t uHzHint)
1908{
1909 TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
1910 TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
1911
1912 uint32_t const uHzOldHint = pTimer->uHzHint;
1913 pTimer->uHzHint = uHzHint;
1914
1915 uint32_t const uMaxHzHint = pQueue->uMaxHzHint;
1916 if ( uHzHint > uMaxHzHint
1917 || uHzOldHint >= uMaxHzHint)
1918 ASMAtomicOrU64(&pVM->tm.s.HzHint.u64Combined, RT_BIT_32(idxQueue) | RT_BIT_32(idxQueue + 16));
1919
1920 return VINF_SUCCESS;
1921}
1922
1923
1924/**
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
1931 * @param pVM The cross context VM structure.
1932 * @param pTimer The timer handle.
1933 */
1934static int tmTimerVirtualSyncStop(PVMCC pVM, PTMTIMER pTimer)
1935{
1936 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerStopVs), a);
1937 VM_ASSERT_EMT(pVM);
1938 TMTIMER_ASSERT_SYNC_CRITSECT_ORDER(pVM, pTimer);
1939 int rc = PDMCritSectEnter(pVM, &pVM->tm.s.VirtualSyncLock, VINF_SUCCESS);
1940 AssertRCReturn(rc, rc);
1941
1942 /* Reset the HZ hint. */
1943 uint32_t uOldHzHint = pTimer->uHzHint;
1944 if (uOldHzHint)
1945 {
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));
1948 pTimer->uHzHint = 0;
1949 }
1950
1951 /* Update the timer state. */
1952 TMTIMERSTATE const enmState = pTimer->enmState;
1953 switch (enmState)
1954 {
1955 case TMTIMERSTATE_ACTIVE:
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);
1959 TM_SET_STATE(pTimer, TMTIMERSTATE_STOPPED);
1960 rc = VINF_SUCCESS;
1961 break;
1962 }
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:
1982 AssertLogRelMsgFailed(("Invalid timer state %s: %s\n", tmTimerState(enmState), pTimer->szName));
1983 rc = VERR_TM_INVALID_STATE;
1984 break;
1985
1986 default:
1987 AssertMsgFailed(("Unknown timer state %d: %s\n", enmState, pTimer->szName));
1988 rc = VERR_TM_UNKNOWN_STATE;
1989 break;
1990 }
1991
1992 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStopVs), a);
1993 PDMCritSectLeave(pVM, &pVM->tm.s.VirtualSyncLock);
1994 return rc;
1995}
1996
1997
1998/**
1999 * Stop the timer.
2000 * Use TMR3TimerArm() to "un-stop" the timer.
2001 *
2002 * @returns VBox status code.
2003 * @param pVM The cross context VM structure.
2004 * @param hTimer Timer handle as returned by one of the create functions.
2005 */
2006VMMDECL(int) TMTimerStop(PVMCC pVM, TMTIMERHANDLE hTimer)
2007{
2008 TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2009 STAM_COUNTER_INC(&pTimer->StatStop);
2010
2011 /* Treat virtual sync timers specially. */
2012 if (idxQueue == TMCLOCK_VIRTUAL_SYNC)
2013 return tmTimerVirtualSyncStop(pVM, pTimer);
2014
2015 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
2016 TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
2017
2018 /*
2019 * Reset the HZ hint.
2020 */
2021 uint32_t const uOldHzHint = pTimer->uHzHint;
2022 if (uOldHzHint)
2023 {
2024 if (uOldHzHint >= pQueue->uMaxHzHint)
2025 ASMAtomicOrU64(&pVM->tm.s.HzHint.u64Combined, RT_BIT_32(idxQueue) | RT_BIT_32(idxQueue + 16));
2026 pTimer->uHzHint = 0;
2027 }
2028
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;
2037 Log2(("TMTimerStop: %p:{.enmState=%s, .pszDesc='%s'} cRetries=%d\n",
2038 pTimer, tmTimerState(enmState), pTimer->szName, cRetries));
2039 switch (enmState)
2040 {
2041 case TMTIMERSTATE_EXPIRED_DELIVER:
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:
2048 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
2049 return VINF_SUCCESS;
2050
2051 case TMTIMERSTATE_PENDING_SCHEDULE:
2052 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, enmState))
2053 {
2054 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
2055 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
2056 return VINF_SUCCESS;
2057 }
2058 break;
2059
2060 case TMTIMERSTATE_PENDING_RESCHEDULE:
2061 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
2062 {
2063 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
2064 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
2065 return VINF_SUCCESS;
2066 }
2067 break;
2068
2069 case TMTIMERSTATE_ACTIVE:
2070 if (tmTimerTryWithLink(pQueueCC, pQueue, pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
2071 {
2072 tmSchedule(pVM, pQueueCC, pQueue, pTimer);
2073 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
2074 return VINF_SUCCESS;
2075 }
2076 break;
2077
2078 case TMTIMERSTATE_EXPIRED_GET_UNLINK:
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
2085/** @todo call host and yield cpu after a while. */
2086#endif
2087 break;
2088
2089 /*
2090 * Invalid states.
2091 */
2092 case TMTIMERSTATE_DESTROY:
2093 case TMTIMERSTATE_FREE:
2094 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, pTimer->szName));
2095 return VERR_TM_INVALID_STATE;
2096 default:
2097 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
2098 return VERR_TM_UNKNOWN_STATE;
2099 }
2100 } while (cRetries-- > 0);
2101
2102 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, pTimer->szName));
2103 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerStop), a);
2104 return VERR_TM_TIMER_UNSTABLE_STATE;
2105}
2106
2107
2108/**
2109 * Get the current clock time.
2110 * Handy for calculating the new expire time.
2111 *
2112 * @returns Current clock time.
2113 * @param pVM The cross context VM structure.
2114 * @param hTimer Timer handle as returned by one of the create functions.
2115 */
2116VMMDECL(uint64_t) TMTimerGet(PVMCC pVM, TMTIMERHANDLE hTimer)
2117{
2118 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2119 STAM_COUNTER_INC(&pTimer->StatGet);
2120
2121 uint64_t u64;
2122 switch (pQueue->enmClock)
2123 {
2124 case TMCLOCK_VIRTUAL:
2125 u64 = TMVirtualGet(pVM);
2126 break;
2127 case TMCLOCK_VIRTUAL_SYNC:
2128 u64 = TMVirtualSyncGet(pVM);
2129 break;
2130 case TMCLOCK_REAL:
2131 u64 = TMRealGet(pVM);
2132 break;
2133 default:
2134 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2135 return UINT64_MAX;
2136 }
2137 //Log2(("TMTimerGet: returns %'RU64 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
2138 // u64, pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
2139 return u64;
2140}
2141
2142
2143/**
2144 * Get the frequency of the timer clock.
2145 *
2146 * @returns Clock frequency (as Hz of course).
2147 * @param pVM The cross context VM structure.
2148 * @param hTimer Timer handle as returned by one of the create functions.
2149 */
2150VMMDECL(uint64_t) TMTimerGetFreq(PVMCC pVM, TMTIMERHANDLE hTimer)
2151{
2152 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2153 switch (pQueue->enmClock)
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:
2163 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2164 return 0;
2165 }
2166}
2167
2168
2169/**
2170 * Get the expire time of the timer.
2171 * Only valid for active timers.
2172 *
2173 * @returns Expire time of the timer.
2174 * @param pVM The cross context VM structure.
2175 * @param hTimer Timer handle as returned by one of the create functions.
2176 */
2177VMMDECL(uint64_t) TMTimerGetExpire(PVMCC pVM, TMTIMERHANDLE hTimer)
2178{
2179 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, UINT64_MAX); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2180 TMTIMER_ASSERT_CRITSECT(pVM, pTimer);
2181 int cRetries = 1000;
2182 do
2183 {
2184 TMTIMERSTATE enmState = pTimer->enmState;
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",
2193 pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
2194 return UINT64_MAX;
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",
2200 pTimer->u64Expire, pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
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:
2216 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, pTimer->szName));
2217 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
2218 pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
2219 return UINT64_MAX;
2220 default:
2221 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
2222 return UINT64_MAX;
2223 }
2224 } while (cRetries-- > 0);
2225
2226 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, pTimer->szName));
2227 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
2228 pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
2229 return UINT64_MAX;
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.
2238 * @param pVM The cross context VM structure.
2239 * @param hTimer Timer handle as returned by one of the create functions.
2240 */
2241VMMDECL(bool) TMTimerIsActive(PVMCC pVM, TMTIMERHANDLE hTimer)
2242{
2243 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, false); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
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",
2253 pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
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",
2262 pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
2263 return true;
2264
2265 /*
2266 * Invalid states.
2267 */
2268 case TMTIMERSTATE_DESTROY:
2269 case TMTIMERSTATE_FREE:
2270 AssertMsgFailed(("Invalid timer state %s (%s)\n", tmTimerState(enmState), pTimer->szName));
2271 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
2272 pTimer, tmTimerState(pTimer->enmState), pTimer->szName));
2273 return false;
2274 default:
2275 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, pTimer->szName));
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 *
2287 * @returns VBox status code.
2288 * @param pVM The cross context VM structure.
2289 * @param hTimer Timer handle as returned by one of the create functions.
2290 * @param cMilliesToNext Number of milliseconds to the next tick.
2291 */
2292VMMDECL(int) TMTimerSetMillies(PVMCC pVM, TMTIMERHANDLE hTimer, uint32_t cMilliesToNext)
2293{
2294 TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2295 switch (pQueue->enmClock)
2296 {
2297 case TMCLOCK_VIRTUAL:
2298 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2299 return tmTimerSetRelative(pVM, pTimer, cMilliesToNext * UINT64_C(1000000), NULL, pQueueCC, pQueue);
2300
2301 case TMCLOCK_VIRTUAL_SYNC:
2302 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2303 return tmTimerSetRelative(pVM, pTimer, cMilliesToNext * UINT64_C(1000000), NULL, pQueueCC, pQueue);
2304
2305 case TMCLOCK_REAL:
2306 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
2307 return tmTimerSetRelative(pVM, pTimer, cMilliesToNext, NULL, pQueueCC, pQueue);
2308
2309 default:
2310 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2311 return VERR_TM_TIMER_BAD_CLOCK;
2312 }
2313}
2314
2315
2316/**
2317 * Arm a timer with a (new) expire time relative to current time.
2318 *
2319 * @returns VBox status code.
2320 * @param pVM The cross context VM structure.
2321 * @param hTimer Timer handle as returned by one of the create functions.
2322 * @param cMicrosToNext Number of microseconds to the next tick.
2323 */
2324VMMDECL(int) TMTimerSetMicro(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cMicrosToNext)
2325{
2326 TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2327 switch (pQueue->enmClock)
2328 {
2329 case TMCLOCK_VIRTUAL:
2330 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2331 return tmTimerSetRelative(pVM, pTimer, cMicrosToNext * 1000, NULL, pQueueCC, pQueue);
2332
2333 case TMCLOCK_VIRTUAL_SYNC:
2334 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2335 return tmTimerSetRelative(pVM, pTimer, cMicrosToNext * 1000, NULL, pQueueCC, pQueue);
2336
2337 case TMCLOCK_REAL:
2338 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
2339 return tmTimerSetRelative(pVM, pTimer, cMicrosToNext / 1000, NULL, pQueueCC, pQueue);
2340
2341 default:
2342 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2343 return VERR_TM_TIMER_BAD_CLOCK;
2344 }
2345}
2346
2347
2348/**
2349 * Arm a timer with a (new) expire time relative to current time.
2350 *
2351 * @returns VBox status code.
2352 * @param pVM The cross context VM structure.
2353 * @param hTimer Timer handle as returned by one of the create functions.
2354 * @param cNanosToNext Number of nanoseconds to the next tick.
2355 */
2356VMMDECL(int) TMTimerSetNano(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cNanosToNext)
2357{
2358 TMTIMER_HANDLE_TO_VARS_RETURN(pVM, hTimer); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2359 switch (pQueue->enmClock)
2360 {
2361 case TMCLOCK_VIRTUAL:
2362 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2363 return tmTimerSetRelative(pVM, pTimer, cNanosToNext, NULL, pQueueCC, pQueue);
2364
2365 case TMCLOCK_VIRTUAL_SYNC:
2366 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2367 return tmTimerSetRelative(pVM, pTimer, cNanosToNext, NULL, pQueueCC, pQueue);
2368
2369 case TMCLOCK_REAL:
2370 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
2371 return tmTimerSetRelative(pVM, pTimer, cNanosToNext / 1000000, NULL, pQueueCC, pQueue);
2372
2373 default:
2374 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2375 return VERR_TM_TIMER_BAD_CLOCK;
2376 }
2377}
2378
2379
2380/**
2381 * Get the current clock time as nanoseconds.
2382 *
2383 * @returns The timer clock as nanoseconds.
2384 * @param pVM The cross context VM structure.
2385 * @param hTimer Timer handle as returned by one of the create functions.
2386 */
2387VMMDECL(uint64_t) TMTimerGetNano(PVMCC pVM, TMTIMERHANDLE hTimer)
2388{
2389 return TMTimerToNano(pVM, hTimer, TMTimerGet(pVM, hTimer));
2390}
2391
2392
2393/**
2394 * Get the current clock time as microseconds.
2395 *
2396 * @returns The timer clock as microseconds.
2397 * @param pVM The cross context VM structure.
2398 * @param hTimer Timer handle as returned by one of the create functions.
2399 */
2400VMMDECL(uint64_t) TMTimerGetMicro(PVMCC pVM, TMTIMERHANDLE hTimer)
2401{
2402 return TMTimerToMicro(pVM, hTimer, TMTimerGet(pVM, hTimer));
2403}
2404
2405
2406/**
2407 * Get the current clock time as milliseconds.
2408 *
2409 * @returns The timer clock as milliseconds.
2410 * @param pVM The cross context VM structure.
2411 * @param hTimer Timer handle as returned by one of the create functions.
2412 */
2413VMMDECL(uint64_t) TMTimerGetMilli(PVMCC pVM, TMTIMERHANDLE hTimer)
2414{
2415 return TMTimerToMilli(pVM, hTimer, TMTimerGet(pVM, hTimer));
2416}
2417
2418
2419/**
2420 * Converts the specified timer clock time to nanoseconds.
2421 *
2422 * @returns nanoseconds.
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.
2426 * @remark There could be rounding errors here. We just do a simple integer divide
2427 * without any adjustments.
2428 */
2429VMMDECL(uint64_t) TMTimerToNano(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cTicks)
2430{
2431 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2432 switch (pQueue->enmClock)
2433 {
2434 case TMCLOCK_VIRTUAL:
2435 case TMCLOCK_VIRTUAL_SYNC:
2436 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2437 return cTicks;
2438
2439 case TMCLOCK_REAL:
2440 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
2441 return cTicks * 1000000;
2442
2443 default:
2444 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2445 return 0;
2446 }
2447}
2448
2449
2450/**
2451 * Converts the specified timer clock time to microseconds.
2452 *
2453 * @returns microseconds.
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.
2457 * @remark There could be rounding errors here. We just do a simple integer divide
2458 * without any adjustments.
2459 */
2460VMMDECL(uint64_t) TMTimerToMicro(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cTicks)
2461{
2462 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2463 switch (pQueue->enmClock)
2464 {
2465 case TMCLOCK_VIRTUAL:
2466 case TMCLOCK_VIRTUAL_SYNC:
2467 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2468 return cTicks / 1000;
2469
2470 case TMCLOCK_REAL:
2471 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
2472 return cTicks * 1000;
2473
2474 default:
2475 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2476 return 0;
2477 }
2478}
2479
2480
2481/**
2482 * Converts the specified timer clock time to milliseconds.
2483 *
2484 * @returns milliseconds.
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.
2488 * @remark There could be rounding errors here. We just do a simple integer divide
2489 * without any adjustments.
2490 */
2491VMMDECL(uint64_t) TMTimerToMilli(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cTicks)
2492{
2493 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2494 switch (pQueue->enmClock)
2495 {
2496 case TMCLOCK_VIRTUAL:
2497 case TMCLOCK_VIRTUAL_SYNC:
2498 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2499 return cTicks / 1000000;
2500
2501 case TMCLOCK_REAL:
2502 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
2503 return cTicks;
2504
2505 default:
2506 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2507 return 0;
2508 }
2509}
2510
2511
2512/**
2513 * Converts the specified nanosecond timestamp to timer clock ticks.
2514 *
2515 * @returns timer clock ticks.
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.
2519 * @remark There could be rounding and overflow errors here.
2520 */
2521VMMDECL(uint64_t) TMTimerFromNano(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cNanoSecs)
2522{
2523 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2524 switch (pQueue->enmClock)
2525 {
2526 case TMCLOCK_VIRTUAL:
2527 case TMCLOCK_VIRTUAL_SYNC:
2528 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2529 return cNanoSecs;
2530
2531 case TMCLOCK_REAL:
2532 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
2533 return cNanoSecs / 1000000;
2534
2535 default:
2536 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2537 return 0;
2538 }
2539}
2540
2541
2542/**
2543 * Converts the specified microsecond timestamp to timer clock ticks.
2544 *
2545 * @returns timer clock ticks.
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.
2549 * @remark There could be rounding and overflow errors here.
2550 */
2551VMMDECL(uint64_t) TMTimerFromMicro(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cMicroSecs)
2552{
2553 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2554 switch (pQueue->enmClock)
2555 {
2556 case TMCLOCK_VIRTUAL:
2557 case TMCLOCK_VIRTUAL_SYNC:
2558 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2559 return cMicroSecs * 1000;
2560
2561 case TMCLOCK_REAL:
2562 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
2563 return cMicroSecs / 1000;
2564
2565 default:
2566 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2567 return 0;
2568 }
2569}
2570
2571
2572/**
2573 * Converts the specified millisecond timestamp to timer clock ticks.
2574 *
2575 * @returns timer clock ticks.
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.
2579 * @remark There could be rounding and overflow errors here.
2580 */
2581VMMDECL(uint64_t) TMTimerFromMilli(PVMCC pVM, TMTIMERHANDLE hTimer, uint64_t cMilliSecs)
2582{
2583 TMTIMER_HANDLE_TO_VARS_RETURN_EX(pVM, hTimer, 0); /* => pTimer, pQueueCC, pQueue, idxTimer, idxQueue */
2584 switch (pQueue->enmClock)
2585 {
2586 case TMCLOCK_VIRTUAL:
2587 case TMCLOCK_VIRTUAL_SYNC:
2588 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
2589 return cMilliSecs * 1000000;
2590
2591 case TMCLOCK_REAL:
2592 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
2593 return cMilliSecs;
2594
2595 default:
2596 AssertMsgFailed(("Invalid enmClock=%d\n", pQueue->enmClock));
2597 return 0;
2598 }
2599}
2600
2601
2602/**
2603 * Convert state to string.
2604 *
2605 * @returns Readonly status name.
2606 * @param enmState State.
2607 */
2608const char *tmTimerState(TMTIMERSTATE enmState)
2609{
2610 switch (enmState)
2611 {
2612#define CASE(num, state) \
2613 case TMTIMERSTATE_##state: \
2614 AssertCompile(TMTIMERSTATE_##state == (num)); \
2615 return #num "-" #state
2616 CASE( 0,INVALID);
2617 CASE( 1,STOPPED);
2618 CASE( 2,ACTIVE);
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);
2629 default:
2630 AssertMsgFailed(("Invalid state enmState=%d\n", enmState));
2631 return "Invalid state!";
2632#undef CASE
2633 }
2634}
2635
2636
2637#if defined(IN_RING0) || defined(IN_RING3)
2638/**
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 */
2648void 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/**
2686 * The slow path of tmGetFrequencyHint() where we try to recalculate the value.
2687 *
2688 * @returns The highest frequency. 0 if no timers care.
2689 * @param pVM The cross context VM structure.
2690 * @param uOldMaxHzHint The old global hint.
2691 */
2692DECL_NO_INLINE(static, uint32_t) tmGetFrequencyHintSlow(PVMCC pVM, uint32_t uOldMaxHzHint)
2693{
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
2699 /*
2700 * The "right" highest frequency value isn't so important that we'll block
2701 * waiting on the timer semaphores.
2702 */
2703 uint32_t uMaxHzHint = 0;
2704 for (uint32_t idxQueue = 0; idxQueue < RT_ELEMENTS(pVM->tm.s.aTimerQueues); idxQueue++)
2705 {
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)))
2711 || RT_FAILURE_NP(PDMCritSectTryEnter(pVM, &pQueue->TimerLock)))
2712 uMaxHzHintQueue = ASMAtomicReadU32(&pQueue->uMaxHzHint);
2713 else
2714 {
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 */
2719
2720 PTMTIMERQUEUECC pQueueCC = TM_GET_TIMER_QUEUE_CC(pVM, idxQueue, pQueue);
2721 uMaxHzHintQueue = 0;
2722 for (PTMTIMER pCur = tmTimerQueueGetHead(pQueueCC, pQueue);
2723 pCur;
2724 pCur = tmTimerGetNext(pQueueCC, pCur))
2725 {
2726 uint32_t uHzHint = ASMAtomicUoReadU32(&pCur->uHzHint);
2727 if (uHzHint > uMaxHzHintQueue)
2728 {
2729 TMTIMERSTATE enmState = pCur->enmState;
2730 switch (enmState)
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:
2739 uMaxHzHintQueue = uHzHint;
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:
2747 case TMTIMERSTATE_INVALID:
2748 break;
2749 /* no default, want gcc warnings when adding more states. */
2750 }
2751 }
2752 }
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));
2757 }
2758 else
2759 uMaxHzHintQueue = ASMAtomicUoReadU32(&pQueue->uMaxHzHint);
2760
2761 PDMCritSectLeave(pVM, &pQueue->TimerLock);
2762 }
2763
2764 /* Update the global max Hz hint. */
2765 if (uMaxHzHint < uMaxHzHintQueue)
2766 uMaxHzHint = uMaxHzHintQueue;
2767 }
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 }
2788 return uMaxHzHint;
2789}
2790
2791
2792/**
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 */
2798DECLINLINE(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/**
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.
2819 * @param pVM The cross context VM structure.
2820 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
2821 */
2822VMM_INT_DECL(uint32_t) TMCalcHostTimerFrequency(PVMCC pVM, PVMCPUCC pVCpu)
2823{
2824 uint32_t uHz = tmGetFrequencyHint(pVM);
2825
2826 /* Catch up, we have to be more aggressive than the % indicates at the
2827 beginning of the effort. */
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 {
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;
2839 uHz *= u32Pct + 100;
2840 uHz /= 100;
2841 }
2842 }
2843
2844 /* Warp drive. */
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. */
2856 if (pVCpu->idCpu == pVM->tm.s.idTimerCpu)
2857 uHz *= pVM->tm.s.cPctHostHzFudgeFactorTimerCpu;
2858 else
2859 uHz *= pVM->tm.s.cPctHostHzFudgeFactorOtherCpu;
2860 uHz /= 100;
2861
2862 /* Make sure it isn't too high. */
2863 if (uHz > pVM->tm.s.cHostHzMax)
2864 uHz = pVM->tm.s.cHostHzMax;
2865
2866 return uHz;
2867}
2868
2869
2870/**
2871 * Whether the guest virtual clock is ticking.
2872 *
2873 * @returns true if ticking, false otherwise.
2874 * @param pVM The cross context VM structure.
2875 */
2876VMM_INT_DECL(bool) TMVirtualIsTicking(PVM pVM)
2877{
2878 return RT_BOOL(pVM->tm.s.cVirtualTicking);
2879}
2880
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use