VirtualBox

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

Last change on this file since 87751 was 87751, checked in by vboxsync, 4 years ago

VMM: Eliminated one ASMReadTSC in TMNotifyEndOfExecution. Both VT-x and AMD-V has the TSC value handy. bugref:9941

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette