VirtualBox

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

Last change on this file since 19476 was 19444, checked in by vboxsync, 16 years ago

TM: Serialize EMT access using a critsect.

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