VirtualBox

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

Last change on this file since 44346 was 44346, checked in by vboxsync, 12 years ago

TM: PVM -> PUVM in two APIs used by Main. VMReq: Validate pUVM properly for external acalls as well.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 89.2 KB
Line 
1/* $Id: TMAll.cpp 44346 2013-01-23 23:39:39Z vboxsync $ */
2/** @file
3 * TM - Timeout Manager, all contexts.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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 is about to start.
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_ISSET(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
486 /*
487 * Dequeue the scheduling list and iterate it.
488 */
489 int32_t offNext = ASMAtomicXchgS32(&pQueue->offSchedule, 0);
490 Log2(("tmTimerQueueSchedule: pQueue=%p:{.enmClock=%d, offNext=%RI32, .u64Expired=%'RU64}\n", pQueue, pQueue->enmClock, offNext, pQueue->u64Expire));
491 if (!offNext)
492 return;
493 PTMTIMER pNext = (PTMTIMER)((intptr_t)pQueue + offNext);
494 while (pNext)
495 {
496 /*
497 * Unlink the head timer and find the next one.
498 */
499 PTMTIMER pTimer = pNext;
500 pNext = pNext->offScheduleNext ? (PTMTIMER)((intptr_t)pNext + pNext->offScheduleNext) : NULL;
501 pTimer->offScheduleNext = 0;
502
503 /*
504 * Do the scheduling.
505 */
506 Log2(("tmTimerQueueSchedule: %p:{.enmState=%s, .enmClock=%d, .enmType=%d, .pszDesc=%s}\n",
507 pTimer, tmTimerState(pTimer->enmState), pTimer->enmClock, pTimer->enmType, R3STRING(pTimer->pszDesc)));
508 tmTimerQueueScheduleOne(pQueue, pTimer);
509 Log2(("tmTimerQueueSchedule: %p: new %s\n", pTimer, tmTimerState(pTimer->enmState)));
510 } /* foreach timer in current schedule batch. */
511 Log2(("tmTimerQueueSchedule: u64Expired=%'RU64\n", pQueue->u64Expire));
512}
513
514
515#ifdef VBOX_STRICT
516/**
517 * Checks that the timer queues are sane.
518 *
519 * @param pVM Pointer to the VM.
520 *
521 * @remarks Called while owning the lock.
522 */
523void tmTimerQueuesSanityChecks(PVM pVM, const char *pszWhere)
524{
525 TM_ASSERT_TIMER_LOCK_OWNERSHIP(pVM);
526
527 /*
528 * Check the linking of the active lists.
529 */
530 bool fHaveVirtualSyncLock = false;
531 for (int i = 0; i < TMCLOCK_MAX; i++)
532 {
533 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTX_SUFF(paTimerQueues)[i];
534 Assert((int)pQueue->enmClock == i);
535 if (pQueue->enmClock == TMCLOCK_VIRTUAL_SYNC)
536 {
537 if (PDMCritSectTryEnter(&pVM->tm.s.VirtualSyncLock) != VINF_SUCCESS)
538 continue;
539 fHaveVirtualSyncLock = true;
540 }
541 PTMTIMER pPrev = NULL;
542 for (PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue); pCur; pPrev = pCur, pCur = TMTIMER_GET_NEXT(pCur))
543 {
544 AssertMsg((int)pCur->enmClock == i, ("%s: %d != %d\n", pszWhere, pCur->enmClock, i));
545 AssertMsg(TMTIMER_GET_PREV(pCur) == pPrev, ("%s: %p != %p\n", pszWhere, TMTIMER_GET_PREV(pCur), pPrev));
546 TMTIMERSTATE enmState = pCur->enmState;
547 switch (enmState)
548 {
549 case TMTIMERSTATE_ACTIVE:
550 AssertMsg( !pCur->offScheduleNext
551 || pCur->enmState != TMTIMERSTATE_ACTIVE,
552 ("%s: %RI32\n", pszWhere, pCur->offScheduleNext));
553 break;
554 case TMTIMERSTATE_PENDING_STOP:
555 case TMTIMERSTATE_PENDING_RESCHEDULE:
556 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
557 break;
558 default:
559 AssertMsgFailed(("%s: Invalid state enmState=%d %s\n", pszWhere, enmState, tmTimerState(enmState)));
560 break;
561 }
562 }
563 }
564
565
566# ifdef IN_RING3
567 /*
568 * Do the big list and check that active timers all are in the active lists.
569 */
570 PTMTIMERR3 pPrev = NULL;
571 for (PTMTIMERR3 pCur = pVM->tm.s.pCreated; pCur; pPrev = pCur, pCur = pCur->pBigNext)
572 {
573 Assert(pCur->pBigPrev == pPrev);
574 Assert((unsigned)pCur->enmClock < (unsigned)TMCLOCK_MAX);
575
576 TMTIMERSTATE enmState = pCur->enmState;
577 switch (enmState)
578 {
579 case TMTIMERSTATE_ACTIVE:
580 case TMTIMERSTATE_PENDING_STOP:
581 case TMTIMERSTATE_PENDING_RESCHEDULE:
582 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
583 if (fHaveVirtualSyncLock || pCur->enmClock != TMCLOCK_VIRTUAL_SYNC)
584 {
585 PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTX_SUFF(paTimerQueues)[pCur->enmClock]);
586 Assert(pCur->offPrev || pCur == pCurAct);
587 while (pCurAct && pCurAct != pCur)
588 pCurAct = TMTIMER_GET_NEXT(pCurAct);
589 Assert(pCurAct == pCur);
590 }
591 break;
592
593 case TMTIMERSTATE_PENDING_SCHEDULE:
594 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
595 case TMTIMERSTATE_STOPPED:
596 case TMTIMERSTATE_EXPIRED_DELIVER:
597 if (fHaveVirtualSyncLock || pCur->enmClock != TMCLOCK_VIRTUAL_SYNC)
598 {
599 Assert(!pCur->offNext);
600 Assert(!pCur->offPrev);
601 for (PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTX_SUFF(paTimerQueues)[pCur->enmClock]);
602 pCurAct;
603 pCurAct = TMTIMER_GET_NEXT(pCurAct))
604 {
605 Assert(pCurAct != pCur);
606 Assert(TMTIMER_GET_NEXT(pCurAct) != pCur);
607 Assert(TMTIMER_GET_PREV(pCurAct) != pCur);
608 }
609 }
610 break;
611
612 /* ignore */
613 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
614 break;
615
616 /* shouldn't get here! */
617 case TMTIMERSTATE_EXPIRED_GET_UNLINK:
618 case TMTIMERSTATE_DESTROY:
619 default:
620 AssertMsgFailed(("Invalid state enmState=%d %s\n", enmState, tmTimerState(enmState)));
621 break;
622 }
623 }
624# endif /* IN_RING3 */
625
626 if (fHaveVirtualSyncLock)
627 PDMCritSectLeave(&pVM->tm.s.VirtualSyncLock);
628}
629#endif /* !VBOX_STRICT */
630
631#ifdef VBOX_HIGH_RES_TIMERS_HACK
632
633/**
634 * Worker for tmTimerPollInternal that handles misses when the dedicated timer
635 * EMT is polling.
636 *
637 * @returns See tmTimerPollInternal.
638 * @param pVM Pointer to the VM.
639 * @param u64Now Current virtual clock timestamp.
640 * @param u64Delta The delta to the next even in ticks of the
641 * virtual clock.
642 * @param pu64Delta Where to return the delta.
643 * @param pCounter The statistics counter to update.
644 */
645DECLINLINE(uint64_t) tmTimerPollReturnMiss(PVM pVM, uint64_t u64Now, uint64_t u64Delta, uint64_t *pu64Delta)
646{
647 Assert(!(u64Delta & RT_BIT_64(63)));
648
649 if (!pVM->tm.s.fVirtualWarpDrive)
650 {
651 *pu64Delta = u64Delta;
652 return u64Delta + u64Now + pVM->tm.s.u64VirtualOffset;
653 }
654
655 /*
656 * Warp drive adjustments - this is the reverse of what tmVirtualGetRaw is doing.
657 */
658 uint64_t const u64Start = pVM->tm.s.u64VirtualWarpDriveStart;
659 uint32_t const u32Pct = pVM->tm.s.u32VirtualWarpDrivePercentage;
660
661 uint64_t u64GipTime = u64Delta + u64Now + pVM->tm.s.u64VirtualOffset;
662 u64GipTime -= u64Start; /* the start is GIP time. */
663 if (u64GipTime >= u64Delta)
664 {
665 ASMMultU64ByU32DivByU32(u64GipTime, 100, u32Pct);
666 ASMMultU64ByU32DivByU32(u64Delta, 100, u32Pct);
667 }
668 else
669 {
670 u64Delta -= u64GipTime;
671 ASMMultU64ByU32DivByU32(u64GipTime, 100, u32Pct);
672 u64Delta += u64GipTime;
673 }
674 *pu64Delta = u64Delta;
675 u64GipTime += u64Start;
676 return u64GipTime;
677}
678
679
680/**
681 * Worker for tmTimerPollInternal dealing with returns on virtual CPUs other
682 * than the one dedicated to timer work.
683 *
684 * @returns See tmTimerPollInternal.
685 * @param pVM Pointer to the VM.
686 * @param u64Now Current virtual clock timestamp.
687 * @param pu64Delta Where to return the delta.
688 */
689DECL_FORCE_INLINE(uint64_t) tmTimerPollReturnOtherCpu(PVM pVM, uint64_t u64Now, uint64_t *pu64Delta)
690{
691 static const uint64_t s_u64OtherRet = 500000000; /* 500 ms for non-timer EMTs. */
692 *pu64Delta = s_u64OtherRet;
693 return u64Now + pVM->tm.s.u64VirtualOffset + s_u64OtherRet;
694}
695
696
697/**
698 * Worker for tmTimerPollInternal.
699 *
700 * @returns See tmTimerPollInternal.
701 * @param pVM Pointer to the VM.
702 * @param pVCpu Pointer to the shared VMCPU structure of the
703 * caller.
704 * @param pVCpuDst Pointer to the shared VMCPU structure of the
705 * dedicated timer EMT.
706 * @param u64Now Current virtual clock timestamp.
707 * @param pu64Delta Where to return the delta.
708 * @param pCounter The statistics counter to update.
709 */
710DECL_FORCE_INLINE(uint64_t) tmTimerPollReturnHit(PVM pVM, PVMCPU pVCpu, PVMCPU pVCpuDst, uint64_t u64Now,
711 uint64_t *pu64Delta, PSTAMCOUNTER pCounter)
712{
713 STAM_COUNTER_INC(pCounter);
714 if (pVCpuDst != pVCpu)
715 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
716 *pu64Delta = 0;
717 return 0;
718}
719
720/**
721 * Common worker for TMTimerPollGIP and TMTimerPoll.
722 *
723 * This function is called before FFs are checked in the inner execution EM loops.
724 *
725 * @returns The GIP timestamp of the next event.
726 * 0 if the next event has already expired.
727 *
728 * @param pVM Pointer to the VM.
729 * @param pVCpu Pointer to the shared VMCPU structure of the caller.
730 * @param pu64Delta Where to store the delta.
731 *
732 * @thread The emulation thread.
733 *
734 * @remarks GIP uses ns ticks.
735 */
736DECL_FORCE_INLINE(uint64_t) tmTimerPollInternal(PVM pVM, PVMCPU pVCpu, uint64_t *pu64Delta)
737{
738 PVMCPU pVCpuDst = &pVM->aCpus[pVM->tm.s.idTimerCpu];
739 const uint64_t u64Now = TMVirtualGetNoCheck(pVM);
740 STAM_COUNTER_INC(&pVM->tm.s.StatPoll);
741
742 /*
743 * Return straight away if the timer FF is already set ...
744 */
745 if (VMCPU_FF_ISSET(pVCpuDst, VMCPU_FF_TIMER))
746 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollAlreadySet);
747
748 /*
749 * ... or if timers are being run.
750 */
751 if (ASMAtomicReadBool(&pVM->tm.s.fRunningQueues))
752 {
753 STAM_COUNTER_INC(&pVM->tm.s.StatPollRunning);
754 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
755 }
756
757 /*
758 * Check for TMCLOCK_VIRTUAL expiration.
759 */
760 const uint64_t u64Expire1 = ASMAtomicReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire);
761 const int64_t i64Delta1 = u64Expire1 - u64Now;
762 if (i64Delta1 <= 0)
763 {
764 if (!VMCPU_FF_ISSET(pVCpuDst, VMCPU_FF_TIMER))
765 {
766 Log5(("TMAll(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_ISPENDING(pVCpuDst, VMCPU_FF_TIMER)));
767 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
768#if defined(IN_RING3) && defined(VBOX_WITH_REM)
769 REMR3NotifyTimerPending(pVM, pVCpuDst);
770#endif
771 }
772 LogFlow(("TMTimerPoll: expire1=%'RU64 <= now=%'RU64\n", u64Expire1, u64Now));
773 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtual);
774 }
775
776 /*
777 * Check for TMCLOCK_VIRTUAL_SYNC expiration.
778 * This isn't quite as straight forward if in a catch-up, not only do
779 * we have to adjust the 'now' but when have to adjust the delta as well.
780 */
781
782 /*
783 * Optimistic lockless approach.
784 */
785 uint64_t u64VirtualSyncNow;
786 uint64_t u64Expire2 = ASMAtomicUoReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire);
787 if (ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncTicking))
788 {
789 if (!ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
790 {
791 u64VirtualSyncNow = ASMAtomicReadU64(&pVM->tm.s.offVirtualSync);
792 if (RT_LIKELY( ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncTicking)
793 && !ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncCatchUp)
794 && u64VirtualSyncNow == ASMAtomicReadU64(&pVM->tm.s.offVirtualSync)
795 && u64Expire2 == ASMAtomicUoReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire)))
796 {
797 u64VirtualSyncNow = u64Now - u64VirtualSyncNow;
798 int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
799 if (i64Delta2 > 0)
800 {
801 STAM_COUNTER_INC(&pVM->tm.s.StatPollSimple);
802 STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
803
804 if (pVCpu == pVCpuDst)
805 return tmTimerPollReturnMiss(pVM, u64Now, RT_MIN(i64Delta1, i64Delta2), pu64Delta);
806 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
807 }
808
809 if ( !pVM->tm.s.fRunningQueues
810 && !VMCPU_FF_ISSET(pVCpuDst, VMCPU_FF_TIMER))
811 {
812 Log5(("TMAll(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_ISPENDING(pVCpuDst, VMCPU_FF_TIMER)));
813 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
814#if defined(IN_RING3) && defined(VBOX_WITH_REM)
815 REMR3NotifyTimerPending(pVM, pVCpuDst);
816#endif
817 }
818
819 STAM_COUNTER_INC(&pVM->tm.s.StatPollSimple);
820 LogFlow(("TMTimerPoll: expire2=%'RU64 <= now=%'RU64\n", u64Expire2, u64Now));
821 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
822 }
823 }
824 }
825 else
826 {
827 STAM_COUNTER_INC(&pVM->tm.s.StatPollSimple);
828 LogFlow(("TMTimerPoll: stopped\n"));
829 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
830 }
831
832 /*
833 * Complicated lockless approach.
834 */
835 uint64_t off;
836 uint32_t u32Pct = 0;
837 bool fCatchUp;
838 int cOuterTries = 42;
839 for (;; cOuterTries--)
840 {
841 fCatchUp = ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp);
842 off = ASMAtomicReadU64(&pVM->tm.s.offVirtualSync);
843 u64Expire2 = ASMAtomicReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire);
844 if (fCatchUp)
845 {
846 /* No changes allowed, try get a consistent set of parameters. */
847 uint64_t const u64Prev = ASMAtomicReadU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev);
848 uint64_t const offGivenUp = ASMAtomicReadU64(&pVM->tm.s.offVirtualSyncGivenUp);
849 u32Pct = ASMAtomicReadU32(&pVM->tm.s.u32VirtualSyncCatchUpPercentage);
850 if ( ( u64Prev == ASMAtomicReadU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev)
851 && offGivenUp == ASMAtomicReadU64(&pVM->tm.s.offVirtualSyncGivenUp)
852 && u32Pct == ASMAtomicReadU32(&pVM->tm.s.u32VirtualSyncCatchUpPercentage)
853 && off == ASMAtomicReadU64(&pVM->tm.s.offVirtualSync)
854 && u64Expire2 == ASMAtomicReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire)
855 && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp)
856 && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncTicking))
857 || cOuterTries <= 0)
858 {
859 uint64_t u64Delta = u64Now - u64Prev;
860 if (RT_LIKELY(!(u64Delta >> 32)))
861 {
862 uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, u32Pct, 100);
863 if (off > u64Sub + offGivenUp)
864 off -= u64Sub;
865 else /* we've completely caught up. */
866 off = offGivenUp;
867 }
868 else
869 /* More than 4 seconds since last time (or negative), ignore it. */
870 Log(("TMVirtualGetSync: u64Delta=%RX64 (NoLock)\n", u64Delta));
871
872 /* Check that we're still running and in catch up. */
873 if ( ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncTicking)
874 && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
875 break;
876 }
877 }
878 else if ( off == ASMAtomicReadU64(&pVM->tm.s.offVirtualSync)
879 && u64Expire2 == ASMAtomicReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire)
880 && !ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp)
881 && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncTicking))
882 break; /* Got an consistent offset */
883
884 /* Repeat the initial checks before iterating. */
885 if (VMCPU_FF_ISSET(pVCpuDst, VMCPU_FF_TIMER))
886 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollAlreadySet);
887 if (ASMAtomicUoReadBool(&pVM->tm.s.fRunningQueues))
888 {
889 STAM_COUNTER_INC(&pVM->tm.s.StatPollRunning);
890 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
891 }
892 if (!ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncTicking))
893 {
894 LogFlow(("TMTimerPoll: stopped\n"));
895 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
896 }
897 if (cOuterTries <= 0)
898 break; /* that's enough */
899 }
900 if (cOuterTries <= 0)
901 STAM_COUNTER_INC(&pVM->tm.s.StatPollELoop);
902 u64VirtualSyncNow = u64Now - off;
903
904 /* Calc delta and see if we've got a virtual sync hit. */
905 int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
906 if (i64Delta2 <= 0)
907 {
908 if ( !pVM->tm.s.fRunningQueues
909 && !VMCPU_FF_ISSET(pVCpuDst, VMCPU_FF_TIMER))
910 {
911 Log5(("TMAll(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_ISPENDING(pVCpuDst, VMCPU_FF_TIMER)));
912 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
913#if defined(IN_RING3) && defined(VBOX_WITH_REM)
914 REMR3NotifyTimerPending(pVM, pVCpuDst);
915#endif
916 }
917 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtualSync);
918 LogFlow(("TMTimerPoll: expire2=%'RU64 <= now=%'RU64\n", u64Expire2, u64Now));
919 return tmTimerPollReturnHit(pVM, pVCpu, pVCpuDst, u64Now, pu64Delta, &pVM->tm.s.StatPollVirtualSync);
920 }
921
922 /*
923 * Return the time left to the next event.
924 */
925 STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
926 if (pVCpu == pVCpuDst)
927 {
928 if (fCatchUp)
929 i64Delta2 = ASMMultU64ByU32DivByU32(i64Delta2, 100, u32Pct + 100);
930 return tmTimerPollReturnMiss(pVM, u64Now, RT_MIN(i64Delta1, i64Delta2), pu64Delta);
931 }
932 return tmTimerPollReturnOtherCpu(pVM, u64Now, pu64Delta);
933}
934
935
936/**
937 * Set FF if we've passed the next virtual event.
938 *
939 * This function is called before FFs are checked in the inner execution EM loops.
940 *
941 * @returns true if timers are pending, false if not.
942 *
943 * @param pVM Pointer to the VM.
944 * @param pVCpu Pointer to the shared VMCPU structure of the caller.
945 * @thread The emulation thread.
946 */
947VMMDECL(bool) TMTimerPollBool(PVM pVM, PVMCPU pVCpu)
948{
949 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
950 uint64_t off = 0;
951 tmTimerPollInternal(pVM, pVCpu, &off);
952 return off == 0;
953}
954
955
956/**
957 * Set FF if we've passed the next virtual event.
958 *
959 * This function is called before FFs are checked in the inner execution EM loops.
960 *
961 * @param pVM Pointer to the VM.
962 * @param pVCpu Pointer to the shared VMCPU structure of the caller.
963 * @thread The emulation thread.
964 */
965VMM_INT_DECL(void) TMTimerPollVoid(PVM pVM, PVMCPU pVCpu)
966{
967 uint64_t off;
968 tmTimerPollInternal(pVM, pVCpu, &off);
969}
970
971
972/**
973 * Set FF if we've passed the next virtual event.
974 *
975 * This function is called before FFs are checked in the inner execution EM loops.
976 *
977 * @returns The GIP timestamp of the next event.
978 * 0 if the next event has already expired.
979 * @param pVM Pointer to the VM.
980 * @param pVCpu Pointer to the shared VMCPU structure of the caller.
981 * @param pu64Delta Where to store the delta.
982 * @thread The emulation thread.
983 */
984VMM_INT_DECL(uint64_t) TMTimerPollGIP(PVM pVM, PVMCPU pVCpu, uint64_t *pu64Delta)
985{
986 return tmTimerPollInternal(pVM, pVCpu, pu64Delta);
987}
988
989#endif /* VBOX_HIGH_RES_TIMERS_HACK */
990
991/**
992 * Gets the host context ring-3 pointer of the timer.
993 *
994 * @returns HC R3 pointer.
995 * @param pTimer Timer handle as returned by one of the create functions.
996 */
997VMMDECL(PTMTIMERR3) TMTimerR3Ptr(PTMTIMER pTimer)
998{
999 return (PTMTIMERR3)MMHyperCCToR3(pTimer->CTX_SUFF(pVM), pTimer);
1000}
1001
1002
1003/**
1004 * Gets the host context ring-0 pointer of the timer.
1005 *
1006 * @returns HC R0 pointer.
1007 * @param pTimer Timer handle as returned by one of the create functions.
1008 */
1009VMMDECL(PTMTIMERR0) TMTimerR0Ptr(PTMTIMER pTimer)
1010{
1011 return (PTMTIMERR0)MMHyperCCToR0(pTimer->CTX_SUFF(pVM), pTimer);
1012}
1013
1014
1015/**
1016 * Gets the RC pointer of the timer.
1017 *
1018 * @returns RC pointer.
1019 * @param pTimer Timer handle as returned by one of the create functions.
1020 */
1021VMMDECL(PTMTIMERRC) TMTimerRCPtr(PTMTIMER pTimer)
1022{
1023 return (PTMTIMERRC)MMHyperCCToRC(pTimer->CTX_SUFF(pVM), pTimer);
1024}
1025
1026
1027/**
1028 * Locks the timer clock.
1029 *
1030 * @returns VINF_SUCCESS on success, @a rcBusy if busy, and VERR_NOT_SUPPORTED
1031 * if the clock does not have a lock.
1032 * @param pTimer The timer which clock lock we wish to take.
1033 * @param rcBusy What to return in ring-0 and raw-mode context
1034 * if the lock is busy. Pass VINF_SUCCESS to
1035 * acquired the critical section thru a ring-3
1036 call if necessary.
1037 *
1038 * @remarks Currently only supported on timers using the virtual sync clock.
1039 */
1040VMMDECL(int) TMTimerLock(PTMTIMER pTimer, int rcBusy)
1041{
1042 AssertPtr(pTimer);
1043 AssertReturn(pTimer->enmClock == TMCLOCK_VIRTUAL_SYNC, VERR_NOT_SUPPORTED);
1044 return PDMCritSectEnter(&pTimer->CTX_SUFF(pVM)->tm.s.VirtualSyncLock, rcBusy);
1045}
1046
1047
1048/**
1049 * Unlocks a timer clock locked by TMTimerLock.
1050 *
1051 * @param pTimer The timer which clock to unlock.
1052 */
1053VMMDECL(void) TMTimerUnlock(PTMTIMER pTimer)
1054{
1055 AssertPtr(pTimer);
1056 AssertReturnVoid(pTimer->enmClock == TMCLOCK_VIRTUAL_SYNC);
1057 PDMCritSectLeave(&pTimer->CTX_SUFF(pVM)->tm.s.VirtualSyncLock);
1058}
1059
1060
1061/**
1062 * Checks if the current thread owns the timer clock lock.
1063 *
1064 * @returns @c true if its the owner, @c false if not.
1065 * @param pTimer The timer handle.
1066 */
1067VMMDECL(bool) TMTimerIsLockOwner(PTMTIMER pTimer)
1068{
1069 AssertPtr(pTimer);
1070 AssertReturn(pTimer->enmClock == TMCLOCK_VIRTUAL_SYNC, false);
1071 return PDMCritSectIsOwner(&pTimer->CTX_SUFF(pVM)->tm.s.VirtualSyncLock);
1072}
1073
1074
1075/**
1076 * Optimized TMTimerSet code path for starting an inactive timer.
1077 *
1078 * @returns VBox status code.
1079 *
1080 * @param pVM Pointer to the VM.
1081 * @param pTimer The timer handle.
1082 * @param u64Expire The new expire time.
1083 */
1084static int tmTimerSetOptimizedStart(PVM pVM, PTMTIMER pTimer, uint64_t u64Expire)
1085{
1086 Assert(!pTimer->offPrev);
1087 Assert(!pTimer->offNext);
1088 Assert(pTimer->enmState == TMTIMERSTATE_ACTIVE);
1089
1090 TMCLOCK const enmClock = pTimer->enmClock;
1091
1092 /*
1093 * Calculate and set the expiration time.
1094 */
1095 if (enmClock == TMCLOCK_VIRTUAL_SYNC)
1096 {
1097 uint64_t u64Last = ASMAtomicReadU64(&pVM->tm.s.u64VirtualSync);
1098 AssertMsgStmt(u64Expire >= u64Last,
1099 ("exp=%#llx last=%#llx\n", u64Expire, u64Last),
1100 u64Expire = u64Last);
1101 }
1102 ASMAtomicWriteU64(&pTimer->u64Expire, u64Expire);
1103 Log2(("tmTimerSetOptimizedStart: %p:{.pszDesc='%s', .u64Expire=%'RU64}\n", pTimer, R3STRING(pTimer->pszDesc), u64Expire));
1104
1105 /*
1106 * Link the timer into the active list.
1107 */
1108 tmTimerQueueLinkActive(&pVM->tm.s.CTX_SUFF(paTimerQueues)[enmClock], pTimer, u64Expire);
1109
1110 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetOpt);
1111 TM_UNLOCK_TIMERS(pVM);
1112 return VINF_SUCCESS;
1113}
1114
1115
1116/**
1117 * TMTimerSet for the virtual sync timer queue.
1118 *
1119 * This employs a greatly simplified state machine by always acquiring the
1120 * queue lock and bypassing the scheduling list.
1121 *
1122 * @returns VBox status code
1123 * @param pVM Pointer to the VM.
1124 * @param pTimer The timer handle.
1125 * @param u64Expire The expiration time.
1126 */
1127static int tmTimerVirtualSyncSet(PVM pVM, PTMTIMER pTimer, uint64_t u64Expire)
1128{
1129 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetVs), a);
1130 VM_ASSERT_EMT(pVM);
1131 Assert(PDMCritSectIsOwner(&pVM->tm.s.VirtualSyncLock));
1132 int rc = PDMCritSectEnter(&pVM->tm.s.VirtualSyncLock, VINF_SUCCESS);
1133 AssertRCReturn(rc, rc);
1134
1135 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC];
1136 TMTIMERSTATE enmState = pTimer->enmState;
1137 switch (enmState)
1138 {
1139 case TMTIMERSTATE_EXPIRED_DELIVER:
1140 case TMTIMERSTATE_STOPPED:
1141 if (enmState == TMTIMERSTATE_EXPIRED_DELIVER)
1142 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetVsStExpDeliver);
1143 else
1144 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetVsStStopped);
1145
1146 AssertMsg(u64Expire >= pVM->tm.s.u64VirtualSync,
1147 ("%'RU64 < %'RU64 %s\n", u64Expire, pVM->tm.s.u64VirtualSync, R3STRING(pTimer->pszDesc)));
1148 pTimer->u64Expire = u64Expire;
1149 TM_SET_STATE(pTimer, TMTIMERSTATE_ACTIVE);
1150 tmTimerQueueLinkActive(pQueue, pTimer, u64Expire);
1151 rc = VINF_SUCCESS;
1152 break;
1153
1154 case TMTIMERSTATE_ACTIVE:
1155 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetVsStActive);
1156 tmTimerQueueUnlinkActive(pQueue, pTimer);
1157 pTimer->u64Expire = u64Expire;
1158 tmTimerQueueLinkActive(pQueue, pTimer, u64Expire);
1159 rc = VINF_SUCCESS;
1160 break;
1161
1162 case TMTIMERSTATE_PENDING_RESCHEDULE:
1163 case TMTIMERSTATE_PENDING_STOP:
1164 case TMTIMERSTATE_PENDING_SCHEDULE:
1165 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1166 case TMTIMERSTATE_EXPIRED_GET_UNLINK:
1167 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1168 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1169 case TMTIMERSTATE_DESTROY:
1170 case TMTIMERSTATE_FREE:
1171 AssertLogRelMsgFailed(("Invalid timer state %s: %s\n", tmTimerState(enmState), R3STRING(pTimer->pszDesc)));
1172 rc = VERR_TM_INVALID_STATE;
1173 break;
1174
1175 default:
1176 AssertMsgFailed(("Unknown timer state %d: %s\n", enmState, R3STRING(pTimer->pszDesc)));
1177 rc = VERR_TM_UNKNOWN_STATE;
1178 break;
1179 }
1180
1181 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetVs), a);
1182 PDMCritSectLeave(&pVM->tm.s.VirtualSyncLock);
1183 return rc;
1184}
1185
1186
1187/**
1188 * Arm a timer with a (new) expire time.
1189 *
1190 * @returns VBox status.
1191 * @param pTimer Timer handle as returned by one of the create functions.
1192 * @param u64Expire New expire time.
1193 */
1194VMMDECL(int) TMTimerSet(PTMTIMER pTimer, uint64_t u64Expire)
1195{
1196 PVM pVM = pTimer->CTX_SUFF(pVM);
1197
1198 /* Treat virtual sync timers specially. */
1199 if (pTimer->enmClock == TMCLOCK_VIRTUAL_SYNC)
1200 return tmTimerVirtualSyncSet(pVM, pTimer, u64Expire);
1201
1202 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1203 TMTIMER_ASSERT_CRITSECT(pTimer);
1204
1205 DBGFTRACE_U64_TAG2(pVM, u64Expire, "TMTimerSet", R3STRING(pTimer->pszDesc));
1206
1207#ifdef VBOX_WITH_STATISTICS
1208 /*
1209 * Gather optimization info.
1210 */
1211 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSet);
1212 TMTIMERSTATE enmOrgState = pTimer->enmState;
1213 switch (enmOrgState)
1214 {
1215 case TMTIMERSTATE_STOPPED: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStStopped); break;
1216 case TMTIMERSTATE_EXPIRED_DELIVER: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStExpDeliver); break;
1217 case TMTIMERSTATE_ACTIVE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStActive); break;
1218 case TMTIMERSTATE_PENDING_STOP: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStPendStop); break;
1219 case TMTIMERSTATE_PENDING_STOP_SCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStPendStopSched); break;
1220 case TMTIMERSTATE_PENDING_SCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStPendSched); break;
1221 case TMTIMERSTATE_PENDING_RESCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStPendResched); break;
1222 default: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetStOther); break;
1223 }
1224#endif
1225
1226 /*
1227 * The most common case is setting the timer again during the callback.
1228 * The second most common case is starting a timer at some other time.
1229 */
1230#if 1
1231 TMTIMERSTATE enmState1 = pTimer->enmState;
1232 if ( enmState1 == TMTIMERSTATE_EXPIRED_DELIVER
1233 || ( enmState1 == TMTIMERSTATE_STOPPED
1234 && pTimer->pCritSect))
1235 {
1236 /* Try take the TM lock and check the state again. */
1237 if (RT_SUCCESS_NP(TM_TRY_LOCK_TIMERS(pVM)))
1238 {
1239 if (RT_LIKELY(tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, enmState1)))
1240 {
1241 tmTimerSetOptimizedStart(pVM, pTimer, u64Expire);
1242 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1243 return VINF_SUCCESS;
1244 }
1245 TM_UNLOCK_TIMERS(pVM);
1246 }
1247 }
1248#endif
1249
1250 /*
1251 * Unoptimized code path.
1252 */
1253 int cRetries = 1000;
1254 do
1255 {
1256 /*
1257 * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
1258 */
1259 TMTIMERSTATE enmState = pTimer->enmState;
1260 Log2(("TMTimerSet: %p:{.enmState=%s, .pszDesc='%s'} cRetries=%d u64Expire=%'RU64\n",
1261 pTimer, tmTimerState(enmState), R3STRING(pTimer->pszDesc), cRetries, u64Expire));
1262 switch (enmState)
1263 {
1264 case TMTIMERSTATE_EXPIRED_DELIVER:
1265 case TMTIMERSTATE_STOPPED:
1266 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
1267 {
1268 Assert(!pTimer->offPrev);
1269 Assert(!pTimer->offNext);
1270 pTimer->u64Expire = u64Expire;
1271 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
1272 tmSchedule(pTimer);
1273 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1274 return VINF_SUCCESS;
1275 }
1276 break;
1277
1278 case TMTIMERSTATE_PENDING_SCHEDULE:
1279 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1280 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
1281 {
1282 pTimer->u64Expire = u64Expire;
1283 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
1284 tmSchedule(pTimer);
1285 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1286 return VINF_SUCCESS;
1287 }
1288 break;
1289
1290
1291 case TMTIMERSTATE_ACTIVE:
1292 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
1293 {
1294 pTimer->u64Expire = u64Expire;
1295 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
1296 tmSchedule(pTimer);
1297 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1298 return VINF_SUCCESS;
1299 }
1300 break;
1301
1302 case TMTIMERSTATE_PENDING_RESCHEDULE:
1303 case TMTIMERSTATE_PENDING_STOP:
1304 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
1305 {
1306 pTimer->u64Expire = u64Expire;
1307 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
1308 tmSchedule(pTimer);
1309 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1310 return VINF_SUCCESS;
1311 }
1312 break;
1313
1314
1315 case TMTIMERSTATE_EXPIRED_GET_UNLINK:
1316 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1317 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1318#ifdef IN_RING3
1319 if (!RTThreadYield())
1320 RTThreadSleep(1);
1321#else
1322/** @todo call host context and yield after a couple of iterations */
1323#endif
1324 break;
1325
1326 /*
1327 * Invalid states.
1328 */
1329 case TMTIMERSTATE_DESTROY:
1330 case TMTIMERSTATE_FREE:
1331 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1332 return VERR_TM_INVALID_STATE;
1333 default:
1334 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1335 return VERR_TM_UNKNOWN_STATE;
1336 }
1337 } while (cRetries-- > 0);
1338
1339 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, R3STRING(pTimer->pszDesc)));
1340 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSet), a);
1341 return VERR_TM_TIMER_UNSTABLE_STATE;
1342}
1343
1344
1345/**
1346 * Return the current time for the specified clock, setting pu64Now if not NULL.
1347 *
1348 * @returns Current time.
1349 * @param pVM Pointer to the VM.
1350 * @param enmClock The clock to query.
1351 * @param pu64Now Optional pointer where to store the return time
1352 */
1353DECL_FORCE_INLINE(uint64_t) tmTimerSetRelativeNowWorker(PVM pVM, TMCLOCK enmClock, uint64_t *pu64Now)
1354{
1355 uint64_t u64Now;
1356 switch (enmClock)
1357 {
1358 case TMCLOCK_VIRTUAL_SYNC:
1359 u64Now = TMVirtualSyncGet(pVM);
1360 break;
1361 case TMCLOCK_VIRTUAL:
1362 u64Now = TMVirtualGet(pVM);
1363 break;
1364 case TMCLOCK_REAL:
1365 u64Now = TMRealGet(pVM);
1366 break;
1367 default:
1368 AssertFatalMsgFailed(("%d\n", enmClock));
1369 }
1370
1371 if (pu64Now)
1372 *pu64Now = u64Now;
1373 return u64Now;
1374}
1375
1376
1377/**
1378 * Optimized TMTimerSetRelative code path.
1379 *
1380 * @returns VBox status code.
1381 *
1382 * @param pVM Pointer to the VM.
1383 * @param pTimer The timer handle.
1384 * @param cTicksToNext Clock ticks until the next time expiration.
1385 * @param pu64Now Where to return the current time stamp used.
1386 * Optional.
1387 */
1388static int tmTimerSetRelativeOptimizedStart(PVM pVM, PTMTIMER pTimer, uint64_t cTicksToNext, uint64_t *pu64Now)
1389{
1390 Assert(!pTimer->offPrev);
1391 Assert(!pTimer->offNext);
1392 Assert(pTimer->enmState == TMTIMERSTATE_ACTIVE);
1393
1394 /*
1395 * Calculate and set the expiration time.
1396 */
1397 TMCLOCK const enmClock = pTimer->enmClock;
1398 uint64_t const u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, enmClock, pu64Now);
1399 pTimer->u64Expire = u64Expire;
1400 Log2(("tmTimerSetRelativeOptimizedStart: %p:{.pszDesc='%s', .u64Expire=%'RU64} cTicksToNext=%'RU64\n", pTimer, R3STRING(pTimer->pszDesc), u64Expire, cTicksToNext));
1401
1402 /*
1403 * Link the timer into the active list.
1404 */
1405 DBGFTRACE_U64_TAG2(pVM, u64Expire, "tmTimerSetRelativeOptimizedStart", R3STRING(pTimer->pszDesc));
1406 tmTimerQueueLinkActive(&pVM->tm.s.CTX_SUFF(paTimerQueues)[enmClock], pTimer, u64Expire);
1407
1408 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeOpt);
1409 TM_UNLOCK_TIMERS(pVM);
1410 return VINF_SUCCESS;
1411}
1412
1413
1414/**
1415 * TMTimerSetRelative for the virtual sync timer queue.
1416 *
1417 * This employs a greatly simplified state machine by always acquiring the
1418 * queue lock and bypassing the scheduling list.
1419 *
1420 * @returns VBox status code
1421 * @param pVM Pointer to the VM.
1422 * @param cTicksToNext Clock ticks until the next time expiration.
1423 * @param pu64Now Where to return the current time stamp used.
1424 * Optional.
1425 */
1426static int tmTimerVirtualSyncSetRelative(PVM pVM, PTMTIMER pTimer, uint64_t cTicksToNext, uint64_t *pu64Now)
1427{
1428 STAM_PROFILE_START(pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelativeVs), a);
1429 VM_ASSERT_EMT(pVM);
1430 Assert(PDMCritSectIsOwner(&pVM->tm.s.VirtualSyncLock));
1431 int rc = PDMCritSectEnter(&pVM->tm.s.VirtualSyncLock, VINF_SUCCESS);
1432 AssertRCReturn(rc, rc);
1433
1434 /* Calculate the expiration tick. */
1435 uint64_t u64Expire = TMVirtualSyncGetNoCheck(pVM);
1436 if (pu64Now)
1437 *pu64Now = u64Expire;
1438 u64Expire += cTicksToNext;
1439
1440 /* Update the timer. */
1441 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC];
1442 TMTIMERSTATE enmState = pTimer->enmState;
1443 switch (enmState)
1444 {
1445 case TMTIMERSTATE_EXPIRED_DELIVER:
1446 case TMTIMERSTATE_STOPPED:
1447 if (enmState == TMTIMERSTATE_EXPIRED_DELIVER)
1448 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeVsStExpDeliver);
1449 else
1450 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeVsStStopped);
1451 pTimer->u64Expire = u64Expire;
1452 TM_SET_STATE(pTimer, TMTIMERSTATE_ACTIVE);
1453 tmTimerQueueLinkActive(pQueue, pTimer, u64Expire);
1454 rc = VINF_SUCCESS;
1455 break;
1456
1457 case TMTIMERSTATE_ACTIVE:
1458 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeVsStActive);
1459 tmTimerQueueUnlinkActive(pQueue, pTimer);
1460 pTimer->u64Expire = u64Expire;
1461 tmTimerQueueLinkActive(pQueue, pTimer, u64Expire);
1462 rc = VINF_SUCCESS;
1463 break;
1464
1465 case TMTIMERSTATE_PENDING_RESCHEDULE:
1466 case TMTIMERSTATE_PENDING_STOP:
1467 case TMTIMERSTATE_PENDING_SCHEDULE:
1468 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1469 case TMTIMERSTATE_EXPIRED_GET_UNLINK:
1470 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1471 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1472 case TMTIMERSTATE_DESTROY:
1473 case TMTIMERSTATE_FREE:
1474 AssertLogRelMsgFailed(("Invalid timer state %s: %s\n", tmTimerState(enmState), R3STRING(pTimer->pszDesc)));
1475 rc = VERR_TM_INVALID_STATE;
1476 break;
1477
1478 default:
1479 AssertMsgFailed(("Unknown timer state %d: %s\n", enmState, R3STRING(pTimer->pszDesc)));
1480 rc = VERR_TM_UNKNOWN_STATE;
1481 break;
1482 }
1483
1484 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelativeVs), a);
1485 PDMCritSectLeave(&pVM->tm.s.VirtualSyncLock);
1486 return rc;
1487}
1488
1489
1490/**
1491 * Arm a timer with a expire time relative to the current time.
1492 *
1493 * @returns VBox status.
1494 * @param pTimer Timer handle as returned by one of the create functions.
1495 * @param cTicksToNext Clock ticks until the next time expiration.
1496 * @param pu64Now Where to return the current time stamp used.
1497 * Optional.
1498 */
1499VMMDECL(int) TMTimerSetRelative(PTMTIMER pTimer, uint64_t cTicksToNext, uint64_t *pu64Now)
1500{
1501 PVM pVM = pTimer->CTX_SUFF(pVM);
1502
1503 /* Treat virtual sync timers specially. */
1504 if (pTimer->enmClock == TMCLOCK_VIRTUAL_SYNC)
1505 return tmTimerVirtualSyncSetRelative(pVM, pTimer, cTicksToNext, pu64Now);
1506
1507 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerSetRelative), a);
1508 TMTIMER_ASSERT_CRITSECT(pTimer);
1509
1510 DBGFTRACE_U64_TAG2(pVM, cTicksToNext, "TMTimerSetRelative", R3STRING(pTimer->pszDesc));
1511
1512#ifdef VBOX_WITH_STATISTICS
1513 /*
1514 * Gather optimization info.
1515 */
1516 STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelative);
1517 TMTIMERSTATE enmOrgState = pTimer->enmState;
1518 switch (enmOrgState)
1519 {
1520 case TMTIMERSTATE_STOPPED: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStStopped); break;
1521 case TMTIMERSTATE_EXPIRED_DELIVER: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStExpDeliver); break;
1522 case TMTIMERSTATE_ACTIVE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStActive); break;
1523 case TMTIMERSTATE_PENDING_STOP: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStPendStop); break;
1524 case TMTIMERSTATE_PENDING_STOP_SCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStPendStopSched); break;
1525 case TMTIMERSTATE_PENDING_SCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStPendSched); break;
1526 case TMTIMERSTATE_PENDING_RESCHEDULE: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStPendResched); break;
1527 default: STAM_COUNTER_INC(&pVM->tm.s.StatTimerSetRelativeStOther); break;
1528 }
1529#endif
1530
1531 /*
1532 * Try to take the TM lock and optimize the common cases.
1533 *
1534 * With the TM lock we can safely make optimizations like immediate
1535 * scheduling and we can also be 100% sure that we're not racing the
1536 * running of the timer queues. As an additional restraint we require the
1537 * timer to have a critical section associated with to be 100% there aren't
1538 * concurrent operations on the timer. (This latter isn't necessary any
1539 * longer as this isn't supported for any timers, critsect or not.)
1540 *
1541 * Note! Lock ordering doesn't apply when we only tries to
1542 * get the innermost locks.
1543 */
1544 bool fOwnTMLock = RT_SUCCESS_NP(TM_TRY_LOCK_TIMERS(pVM));
1545#if 1
1546 if ( fOwnTMLock
1547 && pTimer->pCritSect)
1548 {
1549 TMTIMERSTATE enmState = pTimer->enmState;
1550 if (RT_LIKELY( ( enmState == TMTIMERSTATE_EXPIRED_DELIVER
1551 || enmState == TMTIMERSTATE_STOPPED)
1552 && tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, enmState)))
1553 {
1554 tmTimerSetRelativeOptimizedStart(pVM, pTimer, cTicksToNext, pu64Now);
1555 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSetRelative), a);
1556 return VINF_SUCCESS;
1557 }
1558
1559 /* Optimize other states when it becomes necessary. */
1560 }
1561#endif
1562
1563 /*
1564 * Unoptimized path.
1565 */
1566 int rc;
1567 TMCLOCK const enmClock = pTimer->enmClock;
1568 for (int cRetries = 1000; ; cRetries--)
1569 {
1570 /*
1571 * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
1572 */
1573 TMTIMERSTATE enmState = pTimer->enmState;
1574 switch (enmState)
1575 {
1576 case TMTIMERSTATE_STOPPED:
1577 if (enmClock == TMCLOCK_VIRTUAL_SYNC)
1578 {
1579 /** @todo To fix assertion in tmR3TimerQueueRunVirtualSync:
1580 * Figure a safe way of activating this timer while the queue is
1581 * being run.
1582 * (99.9% sure this that the assertion is caused by DevAPIC.cpp
1583 * re-starting the timer in response to a initial_count write.) */
1584 }
1585 /* fall thru */
1586 case TMTIMERSTATE_EXPIRED_DELIVER:
1587 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
1588 {
1589 Assert(!pTimer->offPrev);
1590 Assert(!pTimer->offNext);
1591 pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, enmClock, pu64Now);
1592 Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [EXP/STOP]\n",
1593 pTimer, tmTimerState(enmState), R3STRING(pTimer->pszDesc), pTimer->u64Expire, cRetries));
1594 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
1595 tmSchedule(pTimer);
1596 rc = VINF_SUCCESS;
1597 break;
1598 }
1599 rc = VERR_TRY_AGAIN;
1600 break;
1601
1602 case TMTIMERSTATE_PENDING_SCHEDULE:
1603 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1604 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
1605 {
1606 pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, enmClock, pu64Now);
1607 Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [PEND_SCHED]\n",
1608 pTimer, tmTimerState(enmState), R3STRING(pTimer->pszDesc), pTimer->u64Expire, cRetries));
1609 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
1610 tmSchedule(pTimer);
1611 rc = VINF_SUCCESS;
1612 break;
1613 }
1614 rc = VERR_TRY_AGAIN;
1615 break;
1616
1617
1618 case TMTIMERSTATE_ACTIVE:
1619 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
1620 {
1621 pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, enmClock, pu64Now);
1622 Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [ACTIVE]\n",
1623 pTimer, tmTimerState(enmState), R3STRING(pTimer->pszDesc), pTimer->u64Expire, cRetries));
1624 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
1625 tmSchedule(pTimer);
1626 rc = VINF_SUCCESS;
1627 break;
1628 }
1629 rc = VERR_TRY_AGAIN;
1630 break;
1631
1632 case TMTIMERSTATE_PENDING_RESCHEDULE:
1633 case TMTIMERSTATE_PENDING_STOP:
1634 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
1635 {
1636 pTimer->u64Expire = cTicksToNext + tmTimerSetRelativeNowWorker(pVM, enmClock, pu64Now);
1637 Log2(("TMTimerSetRelative: %p:{.enmState=%s, .pszDesc='%s', .u64Expire=%'RU64} cRetries=%d [PEND_RESCH/STOP]\n",
1638 pTimer, tmTimerState(enmState), R3STRING(pTimer->pszDesc), pTimer->u64Expire, cRetries));
1639 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
1640 tmSchedule(pTimer);
1641 rc = VINF_SUCCESS;
1642 break;
1643 }
1644 rc = VERR_TRY_AGAIN;
1645 break;
1646
1647
1648 case TMTIMERSTATE_EXPIRED_GET_UNLINK:
1649 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1650 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1651#ifdef IN_RING3
1652 if (!RTThreadYield())
1653 RTThreadSleep(1);
1654#else
1655/** @todo call host context and yield after a couple of iterations */
1656#endif
1657 rc = VERR_TRY_AGAIN;
1658 break;
1659
1660 /*
1661 * Invalid states.
1662 */
1663 case TMTIMERSTATE_DESTROY:
1664 case TMTIMERSTATE_FREE:
1665 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1666 rc = VERR_TM_INVALID_STATE;
1667 break;
1668
1669 default:
1670 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1671 rc = VERR_TM_UNKNOWN_STATE;
1672 break;
1673 }
1674
1675 /* switch + loop is tedious to break out of. */
1676 if (rc == VINF_SUCCESS)
1677 break;
1678
1679 if (rc != VERR_TRY_AGAIN)
1680 {
1681 tmTimerSetRelativeNowWorker(pVM, enmClock, pu64Now);
1682 break;
1683 }
1684 if (cRetries <= 0)
1685 {
1686 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, R3STRING(pTimer->pszDesc)));
1687 rc = VERR_TM_TIMER_UNSTABLE_STATE;
1688 tmTimerSetRelativeNowWorker(pVM, enmClock, pu64Now);
1689 break;
1690 }
1691
1692 /*
1693 * Retry to gain locks.
1694 */
1695 if (!fOwnTMLock)
1696 fOwnTMLock = RT_SUCCESS_NP(TM_TRY_LOCK_TIMERS(pVM));
1697
1698 } /* for (;;) */
1699
1700 /*
1701 * Clean up and return.
1702 */
1703 if (fOwnTMLock)
1704 TM_UNLOCK_TIMERS(pVM);
1705
1706 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSetRelative), a);
1707 return rc;
1708}
1709
1710
1711/**
1712 * Drops a hint about the frequency of the timer.
1713 *
1714 * This is used by TM and the VMM to calculate how often guest execution needs
1715 * to be interrupted. The hint is automatically cleared by TMTimerStop.
1716 *
1717 * @returns VBox status code.
1718 * @param pTimer Timer handle as returned by one of the create
1719 * functions.
1720 * @param uHzHint The frequency hint. Pass 0 to clear the hint.
1721 *
1722 * @remarks We're using an integer hertz value here since anything above 1 HZ
1723 * is not going to be any trouble satisfying scheduling wise. The
1724 * range where it makes sense is >= 100 HZ.
1725 */
1726VMMDECL(int) TMTimerSetFrequencyHint(PTMTIMER pTimer, uint32_t uHzHint)
1727{
1728 TMTIMER_ASSERT_CRITSECT(pTimer);
1729
1730 uint32_t const uHzOldHint = pTimer->uHzHint;
1731 pTimer->uHzHint = uHzHint;
1732
1733 PVM pVM = pTimer->CTX_SUFF(pVM);
1734 uint32_t const uMaxHzHint = pVM->tm.s.uMaxHzHint;
1735 if ( uHzHint > uMaxHzHint
1736 || uHzOldHint >= uMaxHzHint)
1737 ASMAtomicWriteBool(&pVM->tm.s.fHzHintNeedsUpdating, true);
1738
1739 return VINF_SUCCESS;
1740}
1741
1742
1743/**
1744 * TMTimerStop for the virtual sync timer queue.
1745 *
1746 * This employs a greatly simplified state machine by always acquiring the
1747 * queue lock and bypassing the scheduling list.
1748 *
1749 * @returns VBox status code
1750 * @param pVM Pointer to the VM.
1751 * @param pTimer The timer handle.
1752 */
1753static int tmTimerVirtualSyncStop(PVM pVM, PTMTIMER pTimer)
1754{
1755 STAM_PROFILE_START(&pVM->tm.s.CTX_SUFF_Z(StatTimerStopVs), a);
1756 VM_ASSERT_EMT(pVM);
1757 Assert(PDMCritSectIsOwner(&pVM->tm.s.VirtualSyncLock));
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