VirtualBox

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

Last change on this file since 50381 was 50381, checked in by vboxsync, 11 years ago

tmTimerVirtualSyncStop: Removed non-sensical lock ownership assertion.

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