VirtualBox

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

Last change on this file since 19487 was 19486, checked in by vboxsync, 16 years ago

TM: Added lock stubbing for debugging purposes (disabled).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 55.5 KB
Line 
1/* $Id: TMAll.cpp 19486 2009-05-07 12:57:32Z vboxsync $ */
2/** @file
3 * TM - Timeout Manager, all contexts.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_TM
27#include <VBox/tm.h>
28#include <VBox/mm.h>
29#ifdef IN_RING3
30# include <VBox/rem.h>
31#endif
32#include "TMInternal.h"
33#include <VBox/vm.h>
34
35#include <VBox/param.h>
36#include <VBox/err.h>
37#include <VBox/log.h>
38#include <VBox/sup.h>
39#include <iprt/time.h>
40#include <iprt/assert.h>
41#include <iprt/asm.h>
42#ifdef IN_RING3
43# include <iprt/thread.h>
44#endif
45
46
47#ifndef tmLock
48
49/**
50 * Try take the EMT/TM lock, wait in ring-3 return VERR_SEM_BUSY in R0/RC.
51 *
52 * @retval VINF_SUCCESS on success (always in ring-3).
53 * @retval VERR_SEM_BUSY in RC and R0 if the semaphore is busy.
54 *
55 * @param pVM The VM handle.
56 */
57int tmLock(PVM pVM)
58{
59 VM_ASSERT_EMT(pVM);
60 int rc = PDMCritSectEnter(&pVM->tm.s.EmtLock, VERR_SEM_BUSY);
61 return rc;
62}
63
64
65/**
66 * Try take the EMT/TM lock, no waiting.
67 *
68 * @retval VINF_SUCCESS on success.
69 * @retval VERR_SEM_BUSY if busy.
70 *
71 * @param pVM The VM handle.
72 */
73int tmTryLock(PVM pVM)
74{
75 VM_ASSERT_EMT(pVM);
76 int rc = PDMCritSectTryEnter(&pVM->tm.s.EmtLock);
77 return rc;
78}
79
80
81/**
82 * Release EMT/TM lock.
83 *
84 * @param pVM The VM handle.
85 */
86void tmUnlock(PVM pVM)
87{
88 PDMCritSectLeave(&pVM->tm.s.EmtLock);
89}
90
91#endif /* ! macros */
92
93/**
94 * Notification that execution is about to start.
95 *
96 * This call must always be paired with a TMNotifyEndOfExecution call.
97 *
98 * The function may, depending on the configuration, resume the TSC and future
99 * clocks that only ticks when we're executing guest code.
100 *
101 * @param pVCpu The VMCPU to operate on.
102 */
103VMMDECL(void) TMNotifyStartOfExecution(PVMCPU pVCpu)
104{
105 PVM pVM = pVCpu->CTX_SUFF(pVM);
106
107 if (pVM->tm.s.fTSCTiedToExecution)
108 tmCpuTickResume(pVM, pVCpu);
109}
110
111
112/**
113 * Notification that execution is about to start.
114 *
115 * This call must always be paired with a TMNotifyStartOfExecution call.
116 *
117 * The function may, depending on the configuration, suspend the TSC and future
118 * clocks that only ticks when we're executing guest code.
119 *
120 * @param pVCpu The VMCPU to operate on.
121 */
122VMMDECL(void) TMNotifyEndOfExecution(PVMCPU pVCpu)
123{
124 PVM pVM = pVCpu->CTX_SUFF(pVM);
125
126 if (pVM->tm.s.fTSCTiedToExecution)
127 tmCpuTickPause(pVM, pVCpu);
128}
129
130
131/**
132 * Notification that the cpu is entering the halt state
133 *
134 * This call must always be paired with a TMNotifyEndOfExecution call.
135 *
136 * The function may, depending on the configuration, resume the TSC and future
137 * clocks that only ticks when we're halted.
138 *
139 * @param pVCpu The VMCPU to operate on.
140 */
141VMMDECL(void) TMNotifyStartOfHalt(PVMCPU pVCpu)
142{
143 PVM pVM = pVCpu->CTX_SUFF(pVM);
144
145 if ( pVM->tm.s.fTSCTiedToExecution
146 && !pVM->tm.s.fTSCNotTiedToHalt)
147 tmCpuTickResume(pVM, pVCpu);
148}
149
150
151/**
152 * Notification that the cpu is leaving the halt state
153 *
154 * This call must always be paired with a TMNotifyStartOfHalt call.
155 *
156 * The function may, depending on the configuration, suspend the TSC and future
157 * clocks that only ticks when we're halted.
158 *
159 * @param pVCpu The VMCPU to operate on.
160 */
161VMMDECL(void) TMNotifyEndOfHalt(PVMCPU pVCpu)
162{
163 PVM pVM = pVCpu->CTX_SUFF(pVM);
164
165 if ( pVM->tm.s.fTSCTiedToExecution
166 && !pVM->tm.s.fTSCNotTiedToHalt)
167 tmCpuTickPause(pVM, pVCpu);
168}
169
170
171/**
172 * Schedule the queue which was changed.
173 */
174DECLINLINE(void) tmSchedule(PTMTIMER pTimer)
175{
176 PVM pVM = pTimer->CTX_SUFF(pVM);
177 if ( VM_IS_EMT(pVM)
178 && RT_SUCCESS(tmTryLock(pVM)))
179 {
180 STAM_PROFILE_START(&pVM->tm.s.CTXALLSUFF(StatScheduleOne), a);
181 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTX_SUFF(paTimerQueues)[pTimer->enmClock];
182 Log3(("tmSchedule: tmTimerQueueSchedule\n"));
183 tmTimerQueueSchedule(pVM, pQueue);
184#ifdef VBOX_STRICT
185 tmTimerQueuesSanityChecks(pVM, "tmSchedule");
186#endif
187 STAM_PROFILE_STOP(&pVM->tm.s.CTX_SUFF_Z(StatScheduleOne), a);
188 tmUnlock(pVM);
189 }
190 else if (!VM_FF_ISSET(pVM, VM_FF_TIMER)) /**@todo only do this when arming the timer. */
191 {
192 STAM_COUNTER_INC(&pVM->tm.s.StatScheduleSetFF);
193 VM_FF_SET(pVM, VM_FF_TIMER);
194#ifdef IN_RING3
195 REMR3NotifyTimerPending(pVM);
196 VMR3NotifyGlobalFFU(pVM->pUVM, VMNOTIFYFF_FLAGS_DONE_REM);
197#endif
198 }
199}
200
201
202/**
203 * Try change the state to enmStateNew from enmStateOld
204 * and link the timer into the scheduling queue.
205 *
206 * @returns Success indicator.
207 * @param pTimer Timer in question.
208 * @param enmStateNew The new timer state.
209 * @param enmStateOld The old timer state.
210 */
211DECLINLINE(bool) tmTimerTry(PTMTIMER pTimer, TMTIMERSTATE enmStateNew, TMTIMERSTATE enmStateOld)
212{
213 /*
214 * Attempt state change.
215 */
216 bool fRc;
217 TM_TRY_SET_STATE(pTimer, enmStateNew, enmStateOld, fRc);
218 return fRc;
219}
220
221
222/**
223 * Links the timer onto the scheduling queue.
224 *
225 * @param pQueue The timer queue the timer belongs to.
226 * @param pTimer The timer.
227 */
228DECLINLINE(void) tmTimerLink(PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
229{
230 Assert(!pTimer->offScheduleNext);
231 const int32_t offHeadNew = (intptr_t)pTimer - (intptr_t)pQueue;
232 int32_t offHead;
233 do
234 {
235 offHead = pQueue->offSchedule;
236 if (offHead)
237 pTimer->offScheduleNext = ((intptr_t)pQueue + offHead) - (intptr_t)pTimer;
238 else
239 pTimer->offScheduleNext = 0;
240 } while (!ASMAtomicCmpXchgS32(&pQueue->offSchedule, offHeadNew, offHead));
241}
242
243
244/**
245 * Try change the state to enmStateNew from enmStateOld
246 * and link the timer into the scheduling queue.
247 *
248 * @returns Success indicator.
249 * @param pTimer Timer in question.
250 * @param enmStateNew The new timer state.
251 * @param enmStateOld The old timer state.
252 */
253DECLINLINE(bool) tmTimerTryWithLink(PTMTIMER pTimer, TMTIMERSTATE enmStateNew, TMTIMERSTATE enmStateOld)
254{
255 if (tmTimerTry(pTimer, enmStateNew, enmStateOld))
256 {
257 tmTimerLink(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF(paTimerQueues)[pTimer->enmClock], pTimer);
258 return true;
259 }
260 return false;
261}
262
263
264#ifdef VBOX_HIGH_RES_TIMERS_HACK
265/**
266 * Set FF if we've passed the next virtual event.
267 *
268 * This function is called before FFs are checked in the inner execution EM loops.
269 *
270 * @returns Virtual timer ticks to the next event.
271 * @param pVM Pointer to the shared VM structure.
272 * @thread The emulation thread.
273 */
274VMMDECL(uint64_t) TMTimerPoll(PVM pVM)
275{
276 int rc = tmLock(pVM); /* play safe for now */
277
278 /*
279 * Return straight away if the timer FF is already set.
280 */
281 if (VM_FF_ISSET(pVM, VM_FF_TIMER))
282 {
283 STAM_COUNTER_INC(&pVM->tm.s.StatPollAlreadySet);
284#ifndef IN_RING3
285 if (RT_SUCCESS(rc))
286#endif
287 tmUnlock(pVM);
288 return 0;
289 }
290
291 /*
292 * Get current time and check the expire times of the two relevant queues.
293 */
294 const uint64_t u64Now = TMVirtualGet(pVM);
295
296 /*
297 * TMCLOCK_VIRTUAL
298 */
299 const uint64_t u64Expire1 = pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire;
300 const int64_t i64Delta1 = u64Expire1 - u64Now;
301 if (i64Delta1 <= 0)
302 {
303 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtual);
304 LogFlow(("TMTimerPoll: expire1=%RU64 <= now=%RU64\n", u64Expire1, u64Now));
305#ifndef IN_RING3
306 if (RT_SUCCESS(rc))
307#endif
308 tmUnlock(pVM);
309 VM_FF_SET(pVM, VM_FF_TIMER);
310#ifdef IN_RING3
311 REMR3NotifyTimerPending(pVM);
312#endif
313 return 0;
314 }
315
316 /*
317 * TMCLOCK_VIRTUAL_SYNC
318 * This isn't quite as stright forward if in a catch-up, not only do
319 * we have to adjust the 'now' but when have to adjust the delta as well.
320 */
321 const uint64_t u64Expire2 = pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire;
322 uint64_t u64VirtualSyncNow;
323 if (!pVM->tm.s.fVirtualSyncTicking)
324 u64VirtualSyncNow = pVM->tm.s.u64VirtualSync;
325 else
326 {
327 if (!pVM->tm.s.fVirtualSyncCatchUp)
328 u64VirtualSyncNow = u64Now - pVM->tm.s.offVirtualSync;
329 else
330 {
331 uint64_t off = pVM->tm.s.offVirtualSync;
332 uint64_t u64Delta = u64Now - pVM->tm.s.u64VirtualSyncCatchUpPrev;
333 if (RT_LIKELY(!(u64Delta >> 32)))
334 {
335 uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, pVM->tm.s.u32VirtualSyncCatchUpPercentage, 100);
336 if (off > u64Sub + pVM->tm.s.offVirtualSyncGivenUp)
337 off -= u64Sub;
338 else
339 off = pVM->tm.s.offVirtualSyncGivenUp;
340 }
341 u64VirtualSyncNow = u64Now - off;
342 }
343 }
344 int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
345 if (i64Delta2 <= 0)
346 {
347 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtualSync);
348#ifndef IN_RING3
349 if (RT_SUCCESS(rc))
350#endif
351 tmUnlock(pVM);
352 LogFlow(("TMTimerPoll: expire2=%RU64 <= now=%RU64\n", u64Expire2, u64Now));
353 VM_FF_SET(pVM, VM_FF_TIMER);
354#ifdef IN_RING3
355 REMR3NotifyTimerPending(pVM);
356#endif
357 return 0;
358 }
359 if (pVM->tm.s.fVirtualSyncCatchUp)
360 i64Delta2 = ASMMultU64ByU32DivByU32(i64Delta2, 100, pVM->tm.s.u32VirtualSyncCatchUpPercentage + 100);
361
362 /*
363 * Return the time left to the next event.
364 */
365 STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
366#ifndef IN_RING3
367 if (RT_SUCCESS(rc))
368#endif
369 tmUnlock(pVM);
370 return RT_MIN(i64Delta1, i64Delta2);
371}
372
373
374/**
375 * Set FF if we've passed the next virtual event.
376 *
377 * This function is called before FFs are checked in the inner execution EM loops.
378 *
379 * @returns The GIP timestamp of the next event.
380 * 0 if the next event has already expired.
381 * @param pVM Pointer to the shared VM structure.
382 * @param pVM Pointer to the shared VM structure.
383 * @param pu64Delta Where to store the delta.
384 * @thread The emulation thread.
385 */
386VMMDECL(uint64_t) TMTimerPollGIP(PVM pVM, uint64_t *pu64Delta)
387{
388 int rc = tmLock(pVM); /* play safe for now. */
389
390 /*
391 * Return straight away if the timer FF is already set.
392 */
393 if (VM_FF_ISSET(pVM, VM_FF_TIMER))
394 {
395 STAM_COUNTER_INC(&pVM->tm.s.StatPollAlreadySet);
396#ifndef IN_RING3
397 if (RT_SUCCESS(rc))
398#endif
399 tmUnlock(pVM);
400 *pu64Delta = 0;
401 return 0;
402 }
403
404 /*
405 * Get current time and check the expire times of the two relevant queues.
406 */
407 const uint64_t u64Now = TMVirtualGet(pVM);
408
409 /*
410 * TMCLOCK_VIRTUAL
411 */
412 const uint64_t u64Expire1 = pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire;
413 const int64_t i64Delta1 = u64Expire1 - u64Now;
414 if (i64Delta1 <= 0)
415 {
416 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtual);
417#ifndef IN_RING3
418 if (RT_SUCCESS(rc))
419#endif
420 tmUnlock(pVM);
421 LogFlow(("TMTimerPoll: expire1=%RU64 <= now=%RU64\n", u64Expire1, u64Now));
422 VM_FF_SET(pVM, VM_FF_TIMER);
423#ifdef IN_RING3
424 REMR3NotifyTimerPending(pVM);
425#endif
426 *pu64Delta = 0;
427 return 0;
428 }
429
430 /*
431 * TMCLOCK_VIRTUAL_SYNC
432 * This isn't quite as stright forward if in a catch-up, not only do
433 * we have to adjust the 'now' but when have to adjust the delta as well.
434 */
435 const uint64_t u64Expire2 = pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire;
436 uint64_t u64VirtualSyncNow;
437 if (!pVM->tm.s.fVirtualSyncTicking)
438 u64VirtualSyncNow = pVM->tm.s.u64VirtualSync;
439 else
440 {
441 if (!pVM->tm.s.fVirtualSyncCatchUp)
442 u64VirtualSyncNow = u64Now - pVM->tm.s.offVirtualSync;
443 else
444 {
445 uint64_t off = pVM->tm.s.offVirtualSync;
446 uint64_t u64Delta = u64Now - pVM->tm.s.u64VirtualSyncCatchUpPrev;
447 if (RT_LIKELY(!(u64Delta >> 32)))
448 {
449 uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, pVM->tm.s.u32VirtualSyncCatchUpPercentage, 100);
450 if (off > u64Sub + pVM->tm.s.offVirtualSyncGivenUp)
451 off -= u64Sub;
452 else
453 off = pVM->tm.s.offVirtualSyncGivenUp;
454 }
455 u64VirtualSyncNow = u64Now - off;
456 }
457 }
458
459 int64_t i64Delta2 = u64Expire2 - u64VirtualSyncNow;
460 if (i64Delta2 <= 0)
461 {
462 STAM_COUNTER_INC(&pVM->tm.s.StatPollVirtualSync);
463#ifndef IN_RING3
464 if (RT_SUCCESS(rc))
465#endif
466 tmUnlock(pVM);
467 LogFlow(("TMTimerPoll: expire2=%RU64 <= now=%RU64\n", u64Expire2, u64Now));
468 VM_FF_SET(pVM, VM_FF_TIMER);
469#ifdef IN_RING3
470 REMR3NotifyTimerPending(pVM);
471#endif
472 *pu64Delta = 0;
473 return 0;
474 }
475 if (pVM->tm.s.fVirtualSyncCatchUp)
476 i64Delta2 = ASMMultU64ByU32DivByU32(i64Delta2, 100, pVM->tm.s.u32VirtualSyncCatchUpPercentage + 100);
477
478 /*
479 * Return the GIP time of the next event.
480 * This is the reverse of what tmVirtualGetRaw is doing.
481 */
482 STAM_COUNTER_INC(&pVM->tm.s.StatPollMiss);
483 uint64_t u64GipTime = RT_MIN(i64Delta1, i64Delta2);
484 *pu64Delta = u64GipTime;
485 u64GipTime += u64Now + pVM->tm.s.u64VirtualOffset;
486 if (RT_UNLIKELY(!pVM->tm.s.fVirtualWarpDrive))
487 {
488 u64GipTime -= pVM->tm.s.u64VirtualWarpDriveStart; /* the start is GIP time. */
489 u64GipTime *= 100;
490 u64GipTime /= pVM->tm.s.u32VirtualWarpDrivePercentage;
491 u64GipTime += pVM->tm.s.u64VirtualWarpDriveStart;
492 }
493
494#ifndef IN_RING3
495 if (RT_SUCCESS(rc))
496#endif
497 tmUnlock(pVM);
498 return u64GipTime;
499}
500#endif
501
502
503/**
504 * Gets the host context ring-3 pointer of the timer.
505 *
506 * @returns HC R3 pointer.
507 * @param pTimer Timer handle as returned by one of the create functions.
508 */
509VMMDECL(PTMTIMERR3) TMTimerR3Ptr(PTMTIMER pTimer)
510{
511 return (PTMTIMERR3)MMHyperCCToR3(pTimer->CTX_SUFF(pVM), pTimer);
512}
513
514
515/**
516 * Gets the host context ring-0 pointer of the timer.
517 *
518 * @returns HC R0 pointer.
519 * @param pTimer Timer handle as returned by one of the create functions.
520 */
521VMMDECL(PTMTIMERR0) TMTimerR0Ptr(PTMTIMER pTimer)
522{
523 return (PTMTIMERR0)MMHyperCCToR0(pTimer->CTX_SUFF(pVM), pTimer);
524}
525
526
527/**
528 * Gets the RC pointer of the timer.
529 *
530 * @returns RC pointer.
531 * @param pTimer Timer handle as returned by one of the create functions.
532 */
533VMMDECL(PTMTIMERRC) TMTimerRCPtr(PTMTIMER pTimer)
534{
535 return (PTMTIMERRC)MMHyperCCToRC(pTimer->CTX_SUFF(pVM), pTimer);
536}
537
538
539/**
540 * Destroy a timer
541 *
542 * @returns VBox status.
543 * @param pTimer Timer handle as returned by one of the create functions.
544 */
545VMMDECL(int) TMTimerDestroy(PTMTIMER pTimer)
546{
547 int cRetries = 1000;
548 do
549 {
550 /*
551 * Change to any of the DESTROY states if valid.
552 */
553 TMTIMERSTATE enmState = pTimer->enmState;
554 Log2(("TMTimerDestroy: pTimer=%p:{.enmState=%s, .pszDesc='%s'} cRetries=%d\n",
555 pTimer, tmTimerState(enmState), R3STRING(pTimer->pszDesc), cRetries));
556 switch (enmState)
557 {
558 case TMTIMERSTATE_EXPIRED:
559 if (!VM_IS_EMT(pTimer->CTX_SUFF(pVM)))
560 {
561 AssertMsgFailed(("Attempted timer destruction from other thread while expire pending! (%s)\n", R3STRING(pTimer->pszDesc)));
562 return VERR_INVALID_PARAMETER;
563 }
564 /* fall thru */
565 case TMTIMERSTATE_STOPPED:
566 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_DESTROY, enmState))
567 {
568 tmSchedule(pTimer);
569 return VINF_SUCCESS;
570 }
571 break;
572
573 case TMTIMERSTATE_ACTIVE:
574 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_STOP_DESTROY, enmState))
575 {
576 tmSchedule(pTimer);
577 return VINF_SUCCESS;
578 }
579 break;
580
581 case TMTIMERSTATE_PENDING_STOP:
582 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
583 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_DESTROY, enmState))
584 {
585 tmSchedule(pTimer);
586 return VINF_SUCCESS;
587 }
588 break;
589
590 case TMTIMERSTATE_PENDING_DESTROY:
591 case TMTIMERSTATE_PENDING_STOP_DESTROY:
592 AssertMsgFailed(("How many times do you think you can destroy the same timer... (%s)\n", R3STRING(pTimer->pszDesc)));
593 return VERR_INVALID_PARAMETER;
594
595 case TMTIMERSTATE_PENDING_RESCHEDULE:
596 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_DESTROY, enmState))
597 {
598 tmSchedule(pTimer);
599 return VINF_SUCCESS;
600 }
601 break;
602
603 case TMTIMERSTATE_PENDING_SCHEDULE:
604 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_DESTROY, enmState))
605 {
606 tmSchedule(pTimer);
607 return VINF_SUCCESS;
608 }
609 break;
610
611 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
612 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
613#ifdef IN_RING3
614 if (!RTThreadYield())
615 RTThreadSleep(1);
616#endif
617 break;
618
619 /*
620 * Invalid states.
621 */
622 case TMTIMERSTATE_FREE:
623 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
624 return VERR_TM_INVALID_STATE;
625 default:
626 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
627 return VERR_TM_UNKNOWN_STATE;
628 }
629 } while (cRetries-- > 0);
630
631 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, R3STRING(pTimer->pszDesc)));
632 return VERR_INTERNAL_ERROR;
633}
634
635
636/**
637 * Arm a timer with a (new) expire time.
638 *
639 * @returns VBox status.
640 * @param pTimer Timer handle as returned by one of the create functions.
641 * @param u64Expire New expire time.
642 */
643VMMDECL(int) TMTimerSet(PTMTIMER pTimer, uint64_t u64Expire)
644{
645 STAM_PROFILE_START(&pTimer->CTX_SUFF(pVM)->tm.s.CTXALLSUFF(StatTimerSet), a);
646
647 /** @todo find the most frequently used paths and make them skip tmSchedule and tmTimerTryWithLink. */
648 int cRetries = 1000;
649 do
650 {
651 /*
652 * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
653 */
654 TMTIMERSTATE enmState = pTimer->enmState;
655 Log2(("TMTimerSet: pTimer=%p:{.enmState=%s, .pszDesc='%s'} cRetries=%d u64Expire=%llu\n",
656 pTimer, tmTimerState(enmState), R3STRING(pTimer->pszDesc), cRetries, u64Expire));
657 switch (enmState)
658 {
659 case TMTIMERSTATE_EXPIRED:
660 case TMTIMERSTATE_STOPPED:
661 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
662 {
663 Assert(!pTimer->offPrev);
664 Assert(!pTimer->offNext);
665 AssertMsg( pTimer->enmClock != TMCLOCK_VIRTUAL_SYNC
666 || pTimer->CTX_SUFF(pVM)->tm.s.fVirtualSyncTicking
667 || u64Expire >= pTimer->CTX_SUFF(pVM)->tm.s.u64VirtualSync,
668 ("%RU64 < %RU64 %s\n", u64Expire, pTimer->CTX_SUFF(pVM)->tm.s.u64VirtualSync, R3STRING(pTimer->pszDesc)));
669 pTimer->u64Expire = u64Expire;
670 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
671 tmSchedule(pTimer);
672 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSet), a);
673 return VINF_SUCCESS;
674 }
675 break;
676
677 case TMTIMERSTATE_PENDING_SCHEDULE:
678 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
679 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE, enmState))
680 {
681 Assert(!pTimer->offPrev);
682 Assert(!pTimer->offNext);
683 pTimer->u64Expire = u64Expire;
684 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
685 tmSchedule(pTimer);
686 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSet), a);
687 return VINF_SUCCESS;
688 }
689 break;
690
691
692 case TMTIMERSTATE_ACTIVE:
693 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
694 {
695 pTimer->u64Expire = u64Expire;
696 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
697 tmSchedule(pTimer);
698 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSet), a);
699 return VINF_SUCCESS;
700 }
701 break;
702
703 case TMTIMERSTATE_PENDING_RESCHEDULE:
704 case TMTIMERSTATE_PENDING_STOP:
705 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
706 {
707 pTimer->u64Expire = u64Expire;
708 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
709 tmSchedule(pTimer);
710 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSet), a);
711 return VINF_SUCCESS;
712 }
713 break;
714
715
716 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
717 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
718#ifdef IN_RING3
719 if (!RTThreadYield())
720 RTThreadSleep(1);
721#else
722/** @todo call host context and yield after a couple of iterations */
723#endif
724 break;
725
726 /*
727 * Invalid states.
728 */
729 case TMTIMERSTATE_PENDING_DESTROY:
730 case TMTIMERSTATE_PENDING_STOP_DESTROY:
731 case TMTIMERSTATE_FREE:
732 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
733 return VERR_TM_INVALID_STATE;
734 default:
735 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
736 return VERR_TM_UNKNOWN_STATE;
737 }
738 } while (cRetries-- > 0);
739
740 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, R3STRING(pTimer->pszDesc)));
741 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSet), a);
742 return VERR_INTERNAL_ERROR;
743}
744
745
746/**
747 * Arm a timer with a (new) expire time relative to current time.
748 *
749 * @returns VBox status.
750 * @param pTimer Timer handle as returned by one of the create functions.
751 * @param cMilliesToNext Number of millieseconds to the next tick.
752 */
753VMMDECL(int) TMTimerSetMillies(PTMTIMER pTimer, uint32_t cMilliesToNext)
754{
755 PVM pVM = pTimer->CTX_SUFF(pVM);
756 PVMCPU pVCpu = &pVM->aCpus[0]; /* just take the first VCPU */
757
758 switch (pTimer->enmClock)
759 {
760 case TMCLOCK_VIRTUAL:
761 return TMTimerSet(pTimer, cMilliesToNext * (uint64_t)TMCLOCK_FREQ_VIRTUAL / 1000 + TMVirtualGet(pVM));
762 case TMCLOCK_VIRTUAL_SYNC:
763 return TMTimerSet(pTimer, cMilliesToNext * (uint64_t)TMCLOCK_FREQ_VIRTUAL / 1000 + TMVirtualSyncGet(pVM));
764 case TMCLOCK_REAL:
765 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
766 return TMTimerSet(pTimer, cMilliesToNext + TMRealGet(pVM));
767 case TMCLOCK_TSC:
768 return TMTimerSet(pTimer, cMilliesToNext * pVM->tm.s.cTSCTicksPerSecond / 1000 + TMCpuTickGet(pVCpu));
769
770 default:
771 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
772 return VERR_INTERNAL_ERROR;
773 }
774}
775
776
777/**
778 * Arm a timer with a (new) expire time relative to current time.
779 *
780 * @returns VBox status.
781 * @param pTimer Timer handle as returned by one of the create functions.
782 * @param cMicrosToNext Number of microseconds to the next tick.
783 */
784VMMDECL(int) TMTimerSetMicro(PTMTIMER pTimer, uint64_t cMicrosToNext)
785{
786 PVM pVM = pTimer->CTX_SUFF(pVM);
787 PVMCPU pVCpu = &pVM->aCpus[0]; /* just take the first VCPU */
788
789 switch (pTimer->enmClock)
790 {
791 case TMCLOCK_VIRTUAL:
792 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
793 return TMTimerSet(pTimer, cMicrosToNext * 1000 + TMVirtualGet(pVM));
794
795 case TMCLOCK_VIRTUAL_SYNC:
796 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
797 return TMTimerSet(pTimer, cMicrosToNext * 1000 + TMVirtualSyncGet(pVM));
798
799 case TMCLOCK_REAL:
800 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
801 return TMTimerSet(pTimer, cMicrosToNext / 1000 + TMRealGet(pVM));
802
803 case TMCLOCK_TSC:
804 return TMTimerSet(pTimer, TMTimerFromMicro(pTimer, cMicrosToNext) + TMCpuTickGet(pVCpu));
805
806 default:
807 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
808 return VERR_INTERNAL_ERROR;
809 }
810}
811
812
813/**
814 * Arm a timer with a (new) expire time relative to current time.
815 *
816 * @returns VBox status.
817 * @param pTimer Timer handle as returned by one of the create functions.
818 * @param cNanosToNext Number of nanoseconds to the next tick.
819 */
820VMMDECL(int) TMTimerSetNano(PTMTIMER pTimer, uint64_t cNanosToNext)
821{
822 PVM pVM = pTimer->CTX_SUFF(pVM);
823 PVMCPU pVCpu = &pVM->aCpus[0]; /* just take the first VCPU */
824
825 switch (pTimer->enmClock)
826 {
827 case TMCLOCK_VIRTUAL:
828 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
829 return TMTimerSet(pTimer, cNanosToNext + TMVirtualGet(pVM));
830
831 case TMCLOCK_VIRTUAL_SYNC:
832 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
833 return TMTimerSet(pTimer, cNanosToNext + TMVirtualSyncGet(pVM));
834
835 case TMCLOCK_REAL:
836 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
837 return TMTimerSet(pTimer, cNanosToNext / 1000000 + TMRealGet(pVM));
838
839 case TMCLOCK_TSC:
840 return TMTimerSet(pTimer, TMTimerFromNano(pTimer, cNanosToNext) + TMCpuTickGet(pVCpu));
841
842 default:
843 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
844 return VERR_INTERNAL_ERROR;
845 }
846}
847
848
849/**
850 * Stop the timer.
851 * Use TMR3TimerArm() to "un-stop" the timer.
852 *
853 * @returns VBox status.
854 * @param pTimer Timer handle as returned by one of the create functions.
855 */
856VMMDECL(int) TMTimerStop(PTMTIMER pTimer)
857{
858 STAM_PROFILE_START(&pTimer->CTX_SUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
859 /** @todo see if this function needs optimizing. */
860 int cRetries = 1000;
861 do
862 {
863 /*
864 * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
865 */
866 TMTIMERSTATE enmState = pTimer->enmState;
867 Log2(("TMTimerStop: pTimer=%p:{.enmState=%s, .pszDesc='%s'} cRetries=%d\n",
868 pTimer, tmTimerState(enmState), R3STRING(pTimer->pszDesc), cRetries));
869 switch (enmState)
870 {
871 case TMTIMERSTATE_EXPIRED:
872 //AssertMsgFailed(("You don't stop an expired timer dude!\n"));
873 return VERR_INVALID_PARAMETER;
874
875 case TMTIMERSTATE_STOPPED:
876 case TMTIMERSTATE_PENDING_STOP:
877 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
878 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
879 return VINF_SUCCESS;
880
881 case TMTIMERSTATE_PENDING_SCHEDULE:
882 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, enmState))
883 {
884 Assert(!pTimer->offPrev);
885 Assert(!pTimer->offNext);
886 tmSchedule(pTimer);
887 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
888 return VINF_SUCCESS;
889 }
890
891 case TMTIMERSTATE_PENDING_RESCHEDULE:
892 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
893 {
894 tmSchedule(pTimer);
895 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
896 return VINF_SUCCESS;
897 }
898 break;
899
900 case TMTIMERSTATE_ACTIVE:
901 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
902 {
903 tmSchedule(pTimer);
904 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
905 return VINF_SUCCESS;
906 }
907 break;
908
909 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
910 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
911#ifdef IN_RING3
912 if (!RTThreadYield())
913 RTThreadSleep(1);
914#else
915/**@todo call host and yield cpu after a while. */
916#endif
917 break;
918
919 /*
920 * Invalid states.
921 */
922 case TMTIMERSTATE_PENDING_DESTROY:
923 case TMTIMERSTATE_PENDING_STOP_DESTROY:
924 case TMTIMERSTATE_FREE:
925 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
926 return VERR_TM_INVALID_STATE;
927 default:
928 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
929 return VERR_TM_UNKNOWN_STATE;
930 }
931 } while (cRetries-- > 0);
932
933 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, R3STRING(pTimer->pszDesc)));
934 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
935 return VERR_INTERNAL_ERROR;
936}
937
938
939/**
940 * Get the current clock time.
941 * Handy for calculating the new expire time.
942 *
943 * @returns Current clock time.
944 * @param pTimer Timer handle as returned by one of the create functions.
945 */
946VMMDECL(uint64_t) TMTimerGet(PTMTIMER pTimer)
947{
948 uint64_t u64;
949 PVM pVM = pTimer->CTX_SUFF(pVM);
950
951 switch (pTimer->enmClock)
952 {
953 case TMCLOCK_VIRTUAL:
954 u64 = TMVirtualGet(pVM);
955 break;
956 case TMCLOCK_VIRTUAL_SYNC:
957 u64 = TMVirtualSyncGet(pVM);
958 break;
959 case TMCLOCK_REAL:
960 u64 = TMRealGet(pVM);
961 break;
962 case TMCLOCK_TSC:
963 {
964 PVMCPU pVCpu = &pVM->aCpus[0]; /* just take the first VCPU */
965 u64 = TMCpuTickGet(pVCpu);
966 break;
967 }
968 default:
969 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
970 return ~(uint64_t)0;
971 }
972 //Log2(("TMTimerGet: returns %llu (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
973 // u64, pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
974 return u64;
975}
976
977
978/**
979 * Get the freqency of the timer clock.
980 *
981 * @returns Clock frequency (as Hz of course).
982 * @param pTimer Timer handle as returned by one of the create functions.
983 */
984VMMDECL(uint64_t) TMTimerGetFreq(PTMTIMER pTimer)
985{
986 switch (pTimer->enmClock)
987 {
988 case TMCLOCK_VIRTUAL:
989 case TMCLOCK_VIRTUAL_SYNC:
990 return TMCLOCK_FREQ_VIRTUAL;
991
992 case TMCLOCK_REAL:
993 return TMCLOCK_FREQ_REAL;
994
995 case TMCLOCK_TSC:
996 return TMCpuTicksPerSecond(pTimer->CTX_SUFF(pVM));
997
998 default:
999 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1000 return 0;
1001 }
1002}
1003
1004
1005/**
1006 * Get the current clock time as nanoseconds.
1007 *
1008 * @returns The timer clock as nanoseconds.
1009 * @param pTimer Timer handle as returned by one of the create functions.
1010 */
1011VMMDECL(uint64_t) TMTimerGetNano(PTMTIMER pTimer)
1012{
1013 return TMTimerToNano(pTimer, TMTimerGet(pTimer));
1014}
1015
1016
1017/**
1018 * Get the current clock time as microseconds.
1019 *
1020 * @returns The timer clock as microseconds.
1021 * @param pTimer Timer handle as returned by one of the create functions.
1022 */
1023VMMDECL(uint64_t) TMTimerGetMicro(PTMTIMER pTimer)
1024{
1025 return TMTimerToMicro(pTimer, TMTimerGet(pTimer));
1026}
1027
1028
1029/**
1030 * Get the current clock time as milliseconds.
1031 *
1032 * @returns The timer clock as milliseconds.
1033 * @param pTimer Timer handle as returned by one of the create functions.
1034 */
1035VMMDECL(uint64_t) TMTimerGetMilli(PTMTIMER pTimer)
1036{
1037 return TMTimerToMilli(pTimer, TMTimerGet(pTimer));
1038}
1039
1040
1041/**
1042 * Converts the specified timer clock time to nanoseconds.
1043 *
1044 * @returns nanoseconds.
1045 * @param pTimer Timer handle as returned by one of the create functions.
1046 * @param u64Ticks The clock ticks.
1047 * @remark There could be rounding errors here. We just do a simple integere divide
1048 * without any adjustments.
1049 */
1050VMMDECL(uint64_t) TMTimerToNano(PTMTIMER pTimer, uint64_t u64Ticks)
1051{
1052 switch (pTimer->enmClock)
1053 {
1054 case TMCLOCK_VIRTUAL:
1055 case TMCLOCK_VIRTUAL_SYNC:
1056 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1057 return u64Ticks;
1058
1059 case TMCLOCK_REAL:
1060 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1061 return u64Ticks * 1000000;
1062
1063 case TMCLOCK_TSC:
1064 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1065 return 0;
1066
1067 default:
1068 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1069 return 0;
1070 }
1071}
1072
1073
1074/**
1075 * Converts the specified timer clock time to microseconds.
1076 *
1077 * @returns microseconds.
1078 * @param pTimer Timer handle as returned by one of the create functions.
1079 * @param u64Ticks The clock ticks.
1080 * @remark There could be rounding errors here. We just do a simple integere divide
1081 * without any adjustments.
1082 */
1083VMMDECL(uint64_t) TMTimerToMicro(PTMTIMER pTimer, uint64_t u64Ticks)
1084{
1085 switch (pTimer->enmClock)
1086 {
1087 case TMCLOCK_VIRTUAL:
1088 case TMCLOCK_VIRTUAL_SYNC:
1089 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1090 return u64Ticks / 1000;
1091
1092 case TMCLOCK_REAL:
1093 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1094 return u64Ticks * 1000;
1095
1096 case TMCLOCK_TSC:
1097 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1098 return 0;
1099
1100 default:
1101 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1102 return 0;
1103 }
1104}
1105
1106
1107/**
1108 * Converts the specified timer clock time to milliseconds.
1109 *
1110 * @returns milliseconds.
1111 * @param pTimer Timer handle as returned by one of the create functions.
1112 * @param u64Ticks The clock ticks.
1113 * @remark There could be rounding errors here. We just do a simple integere divide
1114 * without any adjustments.
1115 */
1116VMMDECL(uint64_t) TMTimerToMilli(PTMTIMER pTimer, uint64_t u64Ticks)
1117{
1118 switch (pTimer->enmClock)
1119 {
1120 case TMCLOCK_VIRTUAL:
1121 case TMCLOCK_VIRTUAL_SYNC:
1122 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1123 return u64Ticks / 1000000;
1124
1125 case TMCLOCK_REAL:
1126 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1127 return u64Ticks;
1128
1129 case TMCLOCK_TSC:
1130 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1131 return 0;
1132
1133 default:
1134 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1135 return 0;
1136 }
1137}
1138
1139
1140/**
1141 * Converts the specified nanosecond timestamp to timer clock ticks.
1142 *
1143 * @returns timer clock ticks.
1144 * @param pTimer Timer handle as returned by one of the create functions.
1145 * @param u64NanoTS The nanosecond value ticks to convert.
1146 * @remark There could be rounding and overflow errors here.
1147 */
1148VMMDECL(uint64_t) TMTimerFromNano(PTMTIMER pTimer, uint64_t u64NanoTS)
1149{
1150 switch (pTimer->enmClock)
1151 {
1152 case TMCLOCK_VIRTUAL:
1153 case TMCLOCK_VIRTUAL_SYNC:
1154 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1155 return u64NanoTS;
1156
1157 case TMCLOCK_REAL:
1158 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1159 return u64NanoTS / 1000000;
1160
1161 case TMCLOCK_TSC:
1162 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1163 return 0;
1164
1165 default:
1166 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1167 return 0;
1168 }
1169}
1170
1171
1172/**
1173 * Converts the specified microsecond timestamp to timer clock ticks.
1174 *
1175 * @returns timer clock ticks.
1176 * @param pTimer Timer handle as returned by one of the create functions.
1177 * @param u64MicroTS The microsecond value ticks to convert.
1178 * @remark There could be rounding and overflow errors here.
1179 */
1180VMMDECL(uint64_t) TMTimerFromMicro(PTMTIMER pTimer, uint64_t u64MicroTS)
1181{
1182 switch (pTimer->enmClock)
1183 {
1184 case TMCLOCK_VIRTUAL:
1185 case TMCLOCK_VIRTUAL_SYNC:
1186 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1187 return u64MicroTS * 1000;
1188
1189 case TMCLOCK_REAL:
1190 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1191 return u64MicroTS / 1000;
1192
1193 case TMCLOCK_TSC:
1194 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1195 return 0;
1196
1197 default:
1198 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1199 return 0;
1200 }
1201}
1202
1203
1204/**
1205 * Converts the specified millisecond timestamp to timer clock ticks.
1206 *
1207 * @returns timer clock ticks.
1208 * @param pTimer Timer handle as returned by one of the create functions.
1209 * @param u64MilliTS The millisecond value ticks to convert.
1210 * @remark There could be rounding and overflow errors here.
1211 */
1212VMMDECL(uint64_t) TMTimerFromMilli(PTMTIMER pTimer, uint64_t u64MilliTS)
1213{
1214 switch (pTimer->enmClock)
1215 {
1216 case TMCLOCK_VIRTUAL:
1217 case TMCLOCK_VIRTUAL_SYNC:
1218 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1219 return u64MilliTS * 1000000;
1220
1221 case TMCLOCK_REAL:
1222 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1223 return u64MilliTS;
1224
1225 case TMCLOCK_TSC:
1226 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1227 return 0;
1228
1229 default:
1230 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1231 return 0;
1232 }
1233}
1234
1235
1236/**
1237 * Get the expire time of the timer.
1238 * Only valid for active timers.
1239 *
1240 * @returns Expire time of the timer.
1241 * @param pTimer Timer handle as returned by one of the create functions.
1242 */
1243VMMDECL(uint64_t) TMTimerGetExpire(PTMTIMER pTimer)
1244{
1245 int cRetries = 1000;
1246 do
1247 {
1248 TMTIMERSTATE enmState = pTimer->enmState;
1249 switch (enmState)
1250 {
1251 case TMTIMERSTATE_EXPIRED:
1252 case TMTIMERSTATE_STOPPED:
1253 case TMTIMERSTATE_PENDING_STOP:
1254 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1255 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1256 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1257 return ~(uint64_t)0;
1258
1259 case TMTIMERSTATE_ACTIVE:
1260 case TMTIMERSTATE_PENDING_RESCHEDULE:
1261 case TMTIMERSTATE_PENDING_SCHEDULE:
1262 Log2(("TMTimerGetExpire: returns %llu (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1263 pTimer->u64Expire, pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1264 return pTimer->u64Expire;
1265
1266 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1267 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1268#ifdef IN_RING3
1269 if (!RTThreadYield())
1270 RTThreadSleep(1);
1271#endif
1272 break;
1273
1274 /*
1275 * Invalid states.
1276 */
1277 case TMTIMERSTATE_PENDING_DESTROY:
1278 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1279 case TMTIMERSTATE_FREE:
1280 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1281 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1282 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1283 return ~(uint64_t)0;
1284 default:
1285 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1286 return ~(uint64_t)0;
1287 }
1288 } while (cRetries-- > 0);
1289
1290 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, R3STRING(pTimer->pszDesc)));
1291 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1292 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1293 return ~(uint64_t)0;
1294}
1295
1296
1297/**
1298 * Checks if a timer is active or not.
1299 *
1300 * @returns True if active.
1301 * @returns False if not active.
1302 * @param pTimer Timer handle as returned by one of the create functions.
1303 */
1304VMMDECL(bool) TMTimerIsActive(PTMTIMER pTimer)
1305{
1306 TMTIMERSTATE enmState = pTimer->enmState;
1307 switch (enmState)
1308 {
1309 case TMTIMERSTATE_STOPPED:
1310 case TMTIMERSTATE_EXPIRED:
1311 case TMTIMERSTATE_PENDING_STOP:
1312 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1313 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1314 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1315 return false;
1316
1317 case TMTIMERSTATE_ACTIVE:
1318 case TMTIMERSTATE_PENDING_RESCHEDULE:
1319 case TMTIMERSTATE_PENDING_SCHEDULE:
1320 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1321 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1322 Log2(("TMTimerIsActive: returns true (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1323 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1324 return true;
1325
1326 /*
1327 * Invalid states.
1328 */
1329 case TMTIMERSTATE_PENDING_DESTROY:
1330 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1331 case TMTIMERSTATE_FREE:
1332 AssertMsgFailed(("Invalid timer state %s (%s)\n", tmTimerState(enmState), R3STRING(pTimer->pszDesc)));
1333 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1334 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1335 return false;
1336 default:
1337 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1338 return false;
1339 }
1340}
1341
1342
1343/**
1344 * Convert state to string.
1345 *
1346 * @returns Readonly status name.
1347 * @param enmState State.
1348 */
1349const char *tmTimerState(TMTIMERSTATE enmState)
1350{
1351 switch (enmState)
1352 {
1353#define CASE(state) case state: return #state + sizeof("TMTIMERSTATE_") - 1
1354 CASE(TMTIMERSTATE_STOPPED);
1355 CASE(TMTIMERSTATE_ACTIVE);
1356 CASE(TMTIMERSTATE_EXPIRED);
1357 CASE(TMTIMERSTATE_PENDING_STOP);
1358 CASE(TMTIMERSTATE_PENDING_STOP_SCHEDULE);
1359 CASE(TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE);
1360 CASE(TMTIMERSTATE_PENDING_SCHEDULE);
1361 CASE(TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE);
1362 CASE(TMTIMERSTATE_PENDING_RESCHEDULE);
1363 CASE(TMTIMERSTATE_PENDING_STOP_DESTROY);
1364 CASE(TMTIMERSTATE_PENDING_DESTROY);
1365 CASE(TMTIMERSTATE_FREE);
1366 default:
1367 AssertMsgFailed(("Invalid state enmState=%d\n", enmState));
1368 return "Invalid state!";
1369#undef CASE
1370 }
1371}
1372
1373
1374/**
1375 * Schedules the given timer on the given queue.
1376 *
1377 * @param pQueue The timer queue.
1378 * @param pTimer The timer that needs scheduling.
1379 *
1380 * @remarks Called while owning the lock.
1381 */
1382DECLINLINE(void) tmTimerQueueScheduleOne(PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
1383{
1384 /*
1385 * Processing.
1386 */
1387 unsigned cRetries = 2;
1388 do
1389 {
1390 TMTIMERSTATE enmState = pTimer->enmState;
1391 switch (enmState)
1392 {
1393 /*
1394 * Reschedule timer (in the active list).
1395 */
1396 case TMTIMERSTATE_PENDING_RESCHEDULE:
1397 {
1398 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE, TMTIMERSTATE_PENDING_RESCHEDULE)))
1399 break; /* retry */
1400
1401 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1402 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1403 if (pPrev)
1404 TMTIMER_SET_NEXT(pPrev, pNext);
1405 else
1406 {
1407 TMTIMER_SET_HEAD(pQueue, pNext);
1408 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1409 }
1410 if (pNext)
1411 TMTIMER_SET_PREV(pNext, pPrev);
1412 pTimer->offNext = 0;
1413 pTimer->offPrev = 0;
1414 /* fall thru */
1415 }
1416
1417 /*
1418 * Schedule timer (insert into the active list).
1419 */
1420 case TMTIMERSTATE_PENDING_SCHEDULE:
1421 {
1422 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1423 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, TMTIMERSTATE_PENDING_SCHEDULE)))
1424 break; /* retry */
1425
1426 PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue);
1427 if (pCur)
1428 {
1429 const uint64_t u64Expire = pTimer->u64Expire;
1430 for (;; pCur = TMTIMER_GET_NEXT(pCur))
1431 {
1432 if (pCur->u64Expire > u64Expire)
1433 {
1434 const PTMTIMER pPrev = TMTIMER_GET_PREV(pCur);
1435 TMTIMER_SET_NEXT(pTimer, pCur);
1436 TMTIMER_SET_PREV(pTimer, pPrev);
1437 if (pPrev)
1438 TMTIMER_SET_NEXT(pPrev, pTimer);
1439 else
1440 {
1441 TMTIMER_SET_HEAD(pQueue, pTimer);
1442 pQueue->u64Expire = u64Expire;
1443 }
1444 TMTIMER_SET_PREV(pCur, pTimer);
1445 return;
1446 }
1447 if (!pCur->offNext)
1448 {
1449 TMTIMER_SET_NEXT(pCur, pTimer);
1450 TMTIMER_SET_PREV(pTimer, pCur);
1451 return;
1452 }
1453 }
1454 }
1455 else
1456 {
1457 TMTIMER_SET_HEAD(pQueue, pTimer);
1458 pQueue->u64Expire = pTimer->u64Expire;
1459 }
1460 return;
1461 }
1462
1463 /*
1464 * Stop the timer in active list.
1465 */
1466 case TMTIMERSTATE_PENDING_STOP:
1467 {
1468 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, TMTIMERSTATE_PENDING_STOP)))
1469 break; /* retry */
1470
1471 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1472 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1473 if (pPrev)
1474 TMTIMER_SET_NEXT(pPrev, pNext);
1475 else
1476 {
1477 TMTIMER_SET_HEAD(pQueue, pNext);
1478 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1479 }
1480 if (pNext)
1481 TMTIMER_SET_PREV(pNext, pPrev);
1482 pTimer->offNext = 0;
1483 pTimer->offPrev = 0;
1484 /* fall thru */
1485 }
1486
1487 /*
1488 * Stop the timer (not on the active list).
1489 */
1490 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1491 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1492 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_STOPPED, TMTIMERSTATE_PENDING_STOP_SCHEDULE)))
1493 break;
1494 return;
1495
1496 /*
1497 * Stop & destroy the timer.
1498 */
1499 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1500 {
1501 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1502 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1503 if (pPrev)
1504 TMTIMER_SET_NEXT(pPrev, pNext);
1505 else
1506 {
1507 TMTIMER_SET_HEAD(pQueue, pNext);
1508 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1509 }
1510 if (pNext)
1511 TMTIMER_SET_PREV(pNext, pPrev);
1512 pTimer->offNext = 0;
1513 pTimer->offPrev = 0;
1514 /* fall thru */
1515 }
1516
1517 /*
1518 * Destroy the timer.
1519 */
1520 case TMTIMERSTATE_PENDING_DESTROY:
1521 {
1522 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1523 PVM pVM = pTimer->CTX_SUFF(pVM);
1524 const PTMTIMER pBigPrev = (PTMTIMER)(pTimer->pBigPrev ? MMHyperR3ToCC(pVM, pTimer->pBigPrev) : NULL);
1525 const PTMTIMER pBigNext = (PTMTIMER)(pTimer->pBigNext ? MMHyperR3ToCC(pVM, pTimer->pBigNext) : NULL);
1526
1527 /* unlink from created list */
1528 if (pBigPrev)
1529 pBigPrev->pBigNext = pTimer->pBigNext;
1530 else
1531 pVM->tm.s.pCreated = pTimer->pBigNext;
1532 if (pBigNext)
1533 pBigNext->pBigPrev = pTimer->pBigPrev;
1534 pTimer->pBigNext = 0;
1535 pTimer->pBigPrev = 0;
1536
1537 /* free */
1538 Log2(("TM: Inserting %p into the free list ahead of %p!\n", pTimer, pVM->tm.s.pFree));
1539 pTimer->pBigNext = pVM->tm.s.pFree;
1540 pVM->tm.s.pFree = (PTMTIMERR3)MMHyperCCToR3(pVM, pTimer);
1541 TM_SET_STATE(pTimer, TMTIMERSTATE_FREE);
1542 return;
1543 }
1544
1545 /*
1546 * Postpone these until they get into the right state.
1547 */
1548 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1549 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1550 tmTimerLink(pQueue, pTimer);
1551 STAM_COUNTER_INC(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatPostponed));
1552 return;
1553
1554 /*
1555 * None of these can be in the schedule.
1556 */
1557 case TMTIMERSTATE_FREE:
1558 case TMTIMERSTATE_STOPPED:
1559 case TMTIMERSTATE_ACTIVE:
1560 case TMTIMERSTATE_EXPIRED:
1561 default:
1562 AssertMsgFailed(("Timer (%p) in the scheduling list has an invalid state %s (%d)!",
1563 pTimer, tmTimerState(pTimer->enmState), pTimer->enmState));
1564 return;
1565 }
1566 } while (cRetries-- > 0);
1567}
1568
1569
1570/**
1571 * Schedules the specified timer queue.
1572 *
1573 * @param pVM The VM to run the timers for.
1574 * @param pQueue The queue to schedule.
1575 *
1576 * @remarks Called while owning the lock.
1577 */
1578void tmTimerQueueSchedule(PVM pVM, PTMTIMERQUEUE pQueue)
1579{
1580 TM_ASSERT_EMT_LOCK(pVM);
1581
1582 /*
1583 * Dequeue the scheduling list and iterate it.
1584 */
1585 int32_t offNext = ASMAtomicXchgS32(&pQueue->offSchedule, 0);
1586 Log2(("tmTimerQueueSchedule: pQueue=%p:{.enmClock=%d, offNext=%RI32}\n", pQueue, pQueue->enmClock, offNext));
1587 if (!offNext)
1588 return;
1589 PTMTIMER pNext = (PTMTIMER)((intptr_t)pQueue + offNext);
1590 while (pNext)
1591 {
1592 /*
1593 * Unlink the head timer and find the next one.
1594 */
1595 PTMTIMER pTimer = pNext;
1596 pNext = pNext->offScheduleNext ? (PTMTIMER)((intptr_t)pNext + pNext->offScheduleNext) : NULL;
1597 pTimer->offScheduleNext = 0;
1598
1599 /*
1600 * Do the scheduling.
1601 */
1602 Log2(("tmTimerQueueSchedule: pTimer=%p:{.enmState=%s, .enmClock=%d, .enmType=%d, .pszDesc=%s}\n",
1603 pTimer, tmTimerState(pTimer->enmState), pTimer->enmClock, pTimer->enmType, R3STRING(pTimer->pszDesc)));
1604 tmTimerQueueScheduleOne(pQueue, pTimer);
1605 Log2(("tmTimerQueueSchedule: new %s\n", tmTimerState(pTimer->enmState)));
1606 } /* foreach timer in current schedule batch. */
1607}
1608
1609
1610#ifdef VBOX_STRICT
1611/**
1612 * Checks that the timer queues are sane.
1613 *
1614 * @param pVM VM handle.
1615 *
1616 * @remarks Called while owning the lock.
1617 */
1618void tmTimerQueuesSanityChecks(PVM pVM, const char *pszWhere)
1619{
1620 TM_ASSERT_EMT_LOCK(pVM);
1621
1622 /*
1623 * Check the linking of the active lists.
1624 */
1625 for (int i = 0; i < TMCLOCK_MAX; i++)
1626 {
1627 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTX_SUFF(paTimerQueues)[i];
1628 Assert((int)pQueue->enmClock == i);
1629 PTMTIMER pPrev = NULL;
1630 for (PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue); pCur; pPrev = pCur, pCur = TMTIMER_GET_NEXT(pCur))
1631 {
1632 AssertMsg((int)pCur->enmClock == i, ("%s: %d != %d\n", pszWhere, pCur->enmClock, i));
1633 AssertMsg(TMTIMER_GET_PREV(pCur) == pPrev, ("%s: %p != %p\n", pszWhere, TMTIMER_GET_PREV(pCur), pPrev));
1634 TMTIMERSTATE enmState = pCur->enmState;
1635 switch (enmState)
1636 {
1637 case TMTIMERSTATE_ACTIVE:
1638 AssertMsg(!pCur->offScheduleNext, ("%s: %RI32\n", pszWhere, pCur->offScheduleNext));
1639 break;
1640 case TMTIMERSTATE_PENDING_STOP:
1641 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1642 case TMTIMERSTATE_PENDING_RESCHEDULE:
1643 break;
1644 default:
1645 AssertMsgFailed(("%s: Invalid state enmState=%d %s\n", pszWhere, enmState, tmTimerState(enmState)));
1646 break;
1647 }
1648 }
1649 }
1650
1651
1652# ifdef IN_RING3
1653 /*
1654 * Do the big list and check that active timers all are in the active lists.
1655 */
1656 PTMTIMERR3 pPrev = NULL;
1657 for (PTMTIMERR3 pCur = pVM->tm.s.pCreated; pCur; pPrev = pCur, pCur = pCur->pBigNext)
1658 {
1659 Assert(pCur->pBigPrev == pPrev);
1660 Assert((unsigned)pCur->enmClock < (unsigned)TMCLOCK_MAX);
1661
1662 TMTIMERSTATE enmState = pCur->enmState;
1663 switch (enmState)
1664 {
1665 case TMTIMERSTATE_ACTIVE:
1666 case TMTIMERSTATE_PENDING_STOP:
1667 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1668 case TMTIMERSTATE_PENDING_RESCHEDULE:
1669 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1670 {
1671 PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTX_SUFF(paTimerQueues)[pCur->enmClock]);
1672 Assert(pCur->offPrev || pCur == pCurAct);
1673 while (pCurAct && pCurAct != pCur)
1674 pCurAct = TMTIMER_GET_NEXT(pCurAct);
1675 Assert(pCurAct == pCur);
1676 break;
1677 }
1678
1679 case TMTIMERSTATE_PENDING_DESTROY:
1680 case TMTIMERSTATE_PENDING_SCHEDULE:
1681 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1682 case TMTIMERSTATE_STOPPED:
1683 case TMTIMERSTATE_EXPIRED:
1684 {
1685 Assert(!pCur->offNext);
1686 Assert(!pCur->offPrev);
1687 for (PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTX_SUFF(paTimerQueues)[pCur->enmClock]);
1688 pCurAct;
1689 pCurAct = TMTIMER_GET_NEXT(pCurAct))
1690 {
1691 Assert(pCurAct != pCur);
1692 Assert(TMTIMER_GET_NEXT(pCurAct) != pCur);
1693 Assert(TMTIMER_GET_PREV(pCurAct) != pCur);
1694 }
1695 break;
1696 }
1697
1698 /* ignore */
1699 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1700 break;
1701
1702 /* shouldn't get here! */
1703 default:
1704 AssertMsgFailed(("Invalid state enmState=%d %s\n", enmState, tmTimerState(enmState)));
1705 break;
1706 }
1707 }
1708# endif /* IN_RING3 */
1709}
1710#endif /* !VBOX_STRICT */
1711
Note: See TracBrowser for help on using the repository browser.

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