VirtualBox

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

Last change on this file since 55747 was 54845, checked in by vboxsync, 10 years ago

VMM/GIM,TM: paranoia assertions.

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