VirtualBox

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

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

TMAll.cpp: removed two assertions in TMTimerStop as well.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 55.7 KB
Line 
1/* $Id: TMAll.cpp 19502 2009-05-07 18:37:09Z 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: %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: %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 pTimer->u64Expire = u64Expire;
682 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_SCHEDULE);
683 tmSchedule(pTimer);
684 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSet), a);
685 return VINF_SUCCESS;
686 }
687 break;
688
689
690 case TMTIMERSTATE_ACTIVE:
691 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
692 {
693 pTimer->u64Expire = u64Expire;
694 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
695 tmSchedule(pTimer);
696 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSet), a);
697 return VINF_SUCCESS;
698 }
699 break;
700
701 case TMTIMERSTATE_PENDING_RESCHEDULE:
702 case TMTIMERSTATE_PENDING_STOP:
703 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE, enmState))
704 {
705 pTimer->u64Expire = u64Expire;
706 TM_SET_STATE(pTimer, TMTIMERSTATE_PENDING_RESCHEDULE);
707 tmSchedule(pTimer);
708 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSet), a);
709 return VINF_SUCCESS;
710 }
711 break;
712
713
714 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
715 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
716#ifdef IN_RING3
717 if (!RTThreadYield())
718 RTThreadSleep(1);
719#else
720/** @todo call host context and yield after a couple of iterations */
721#endif
722 break;
723
724 /*
725 * Invalid states.
726 */
727 case TMTIMERSTATE_PENDING_DESTROY:
728 case TMTIMERSTATE_PENDING_STOP_DESTROY:
729 case TMTIMERSTATE_FREE:
730 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
731 return VERR_TM_INVALID_STATE;
732 default:
733 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
734 return VERR_TM_UNKNOWN_STATE;
735 }
736 } while (cRetries-- > 0);
737
738 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, R3STRING(pTimer->pszDesc)));
739 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerSet), a);
740 return VERR_INTERNAL_ERROR;
741}
742
743
744/**
745 * Arm a timer with a (new) expire time relative to current time.
746 *
747 * @returns VBox status.
748 * @param pTimer Timer handle as returned by one of the create functions.
749 * @param cMilliesToNext Number of millieseconds to the next tick.
750 */
751VMMDECL(int) TMTimerSetMillies(PTMTIMER pTimer, uint32_t cMilliesToNext)
752{
753 PVM pVM = pTimer->CTX_SUFF(pVM);
754 PVMCPU pVCpu = &pVM->aCpus[0]; /* just take the first VCPU */
755
756 switch (pTimer->enmClock)
757 {
758 case TMCLOCK_VIRTUAL:
759 return TMTimerSet(pTimer, cMilliesToNext * (uint64_t)TMCLOCK_FREQ_VIRTUAL / 1000 + TMVirtualGet(pVM));
760 case TMCLOCK_VIRTUAL_SYNC:
761 return TMTimerSet(pTimer, cMilliesToNext * (uint64_t)TMCLOCK_FREQ_VIRTUAL / 1000 + TMVirtualSyncGet(pVM));
762 case TMCLOCK_REAL:
763 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
764 return TMTimerSet(pTimer, cMilliesToNext + TMRealGet(pVM));
765 case TMCLOCK_TSC:
766 return TMTimerSet(pTimer, cMilliesToNext * pVM->tm.s.cTSCTicksPerSecond / 1000 + TMCpuTickGet(pVCpu));
767
768 default:
769 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
770 return VERR_INTERNAL_ERROR;
771 }
772}
773
774
775/**
776 * Arm a timer with a (new) expire time relative to current time.
777 *
778 * @returns VBox status.
779 * @param pTimer Timer handle as returned by one of the create functions.
780 * @param cMicrosToNext Number of microseconds to the next tick.
781 */
782VMMDECL(int) TMTimerSetMicro(PTMTIMER pTimer, uint64_t cMicrosToNext)
783{
784 PVM pVM = pTimer->CTX_SUFF(pVM);
785 PVMCPU pVCpu = &pVM->aCpus[0]; /* just take the first VCPU */
786
787 switch (pTimer->enmClock)
788 {
789 case TMCLOCK_VIRTUAL:
790 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
791 return TMTimerSet(pTimer, cMicrosToNext * 1000 + TMVirtualGet(pVM));
792
793 case TMCLOCK_VIRTUAL_SYNC:
794 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
795 return TMTimerSet(pTimer, cMicrosToNext * 1000 + TMVirtualSyncGet(pVM));
796
797 case TMCLOCK_REAL:
798 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
799 return TMTimerSet(pTimer, cMicrosToNext / 1000 + TMRealGet(pVM));
800
801 case TMCLOCK_TSC:
802 return TMTimerSet(pTimer, TMTimerFromMicro(pTimer, cMicrosToNext) + TMCpuTickGet(pVCpu));
803
804 default:
805 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
806 return VERR_INTERNAL_ERROR;
807 }
808}
809
810
811/**
812 * Arm a timer with a (new) expire time relative to current time.
813 *
814 * @returns VBox status.
815 * @param pTimer Timer handle as returned by one of the create functions.
816 * @param cNanosToNext Number of nanoseconds to the next tick.
817 */
818VMMDECL(int) TMTimerSetNano(PTMTIMER pTimer, uint64_t cNanosToNext)
819{
820 PVM pVM = pTimer->CTX_SUFF(pVM);
821 PVMCPU pVCpu = &pVM->aCpus[0]; /* just take the first VCPU */
822
823 switch (pTimer->enmClock)
824 {
825 case TMCLOCK_VIRTUAL:
826 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
827 return TMTimerSet(pTimer, cNanosToNext + TMVirtualGet(pVM));
828
829 case TMCLOCK_VIRTUAL_SYNC:
830 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
831 return TMTimerSet(pTimer, cNanosToNext + TMVirtualSyncGet(pVM));
832
833 case TMCLOCK_REAL:
834 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
835 return TMTimerSet(pTimer, cNanosToNext / 1000000 + TMRealGet(pVM));
836
837 case TMCLOCK_TSC:
838 return TMTimerSet(pTimer, TMTimerFromNano(pTimer, cNanosToNext) + TMCpuTickGet(pVCpu));
839
840 default:
841 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
842 return VERR_INTERNAL_ERROR;
843 }
844}
845
846
847/**
848 * Stop the timer.
849 * Use TMR3TimerArm() to "un-stop" the timer.
850 *
851 * @returns VBox status.
852 * @param pTimer Timer handle as returned by one of the create functions.
853 */
854VMMDECL(int) TMTimerStop(PTMTIMER pTimer)
855{
856 STAM_PROFILE_START(&pTimer->CTX_SUFF(pVM)->tm.s.CTXALLSUFF(StatTimerStop), a);
857 /** @todo see if this function needs optimizing. */
858 int cRetries = 1000;
859 do
860 {
861 /*
862 * Change to any of the SET_EXPIRE states if valid and then to SCHEDULE or RESCHEDULE.
863 */
864 TMTIMERSTATE enmState = pTimer->enmState;
865 Log2(("TMTimerStop: %p:{.enmState=%s, .pszDesc='%s'} cRetries=%d\n",
866 pTimer, tmTimerState(enmState), R3STRING(pTimer->pszDesc), cRetries));
867 switch (enmState)
868 {
869 case TMTIMERSTATE_EXPIRED:
870 //AssertMsgFailed(("You don't stop an expired timer dude!\n"));
871 return VERR_INVALID_PARAMETER;
872
873 case TMTIMERSTATE_STOPPED:
874 case TMTIMERSTATE_PENDING_STOP:
875 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
876 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
877 return VINF_SUCCESS;
878
879 case TMTIMERSTATE_PENDING_SCHEDULE:
880 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, enmState))
881 {
882 tmSchedule(pTimer);
883 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
884 return VINF_SUCCESS;
885 }
886
887 case TMTIMERSTATE_PENDING_RESCHEDULE:
888 if (tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
889 {
890 tmSchedule(pTimer);
891 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
892 return VINF_SUCCESS;
893 }
894 break;
895
896 case TMTIMERSTATE_ACTIVE:
897 if (tmTimerTryWithLink(pTimer, TMTIMERSTATE_PENDING_STOP, enmState))
898 {
899 tmSchedule(pTimer);
900 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
901 return VINF_SUCCESS;
902 }
903 break;
904
905 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
906 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
907#ifdef IN_RING3
908 if (!RTThreadYield())
909 RTThreadSleep(1);
910#else
911/**@todo call host and yield cpu after a while. */
912#endif
913 break;
914
915 /*
916 * Invalid states.
917 */
918 case TMTIMERSTATE_PENDING_DESTROY:
919 case TMTIMERSTATE_PENDING_STOP_DESTROY:
920 case TMTIMERSTATE_FREE:
921 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
922 return VERR_TM_INVALID_STATE;
923 default:
924 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
925 return VERR_TM_UNKNOWN_STATE;
926 }
927 } while (cRetries-- > 0);
928
929 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, R3STRING(pTimer->pszDesc)));
930 STAM_PROFILE_STOP(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatTimerStop), a);
931 return VERR_INTERNAL_ERROR;
932}
933
934
935/**
936 * Get the current clock time.
937 * Handy for calculating the new expire time.
938 *
939 * @returns Current clock time.
940 * @param pTimer Timer handle as returned by one of the create functions.
941 */
942VMMDECL(uint64_t) TMTimerGet(PTMTIMER pTimer)
943{
944 uint64_t u64;
945 PVM pVM = pTimer->CTX_SUFF(pVM);
946
947 switch (pTimer->enmClock)
948 {
949 case TMCLOCK_VIRTUAL:
950 u64 = TMVirtualGet(pVM);
951 break;
952 case TMCLOCK_VIRTUAL_SYNC:
953 u64 = TMVirtualSyncGet(pVM);
954 break;
955 case TMCLOCK_REAL:
956 u64 = TMRealGet(pVM);
957 break;
958 case TMCLOCK_TSC:
959 {
960 PVMCPU pVCpu = &pVM->aCpus[0]; /* just take the first VCPU */
961 u64 = TMCpuTickGet(pVCpu);
962 break;
963 }
964 default:
965 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
966 return ~(uint64_t)0;
967 }
968 //Log2(("TMTimerGet: returns %llu (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
969 // u64, pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
970 return u64;
971}
972
973
974/**
975 * Get the freqency of the timer clock.
976 *
977 * @returns Clock frequency (as Hz of course).
978 * @param pTimer Timer handle as returned by one of the create functions.
979 */
980VMMDECL(uint64_t) TMTimerGetFreq(PTMTIMER pTimer)
981{
982 switch (pTimer->enmClock)
983 {
984 case TMCLOCK_VIRTUAL:
985 case TMCLOCK_VIRTUAL_SYNC:
986 return TMCLOCK_FREQ_VIRTUAL;
987
988 case TMCLOCK_REAL:
989 return TMCLOCK_FREQ_REAL;
990
991 case TMCLOCK_TSC:
992 return TMCpuTicksPerSecond(pTimer->CTX_SUFF(pVM));
993
994 default:
995 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
996 return 0;
997 }
998}
999
1000
1001/**
1002 * Get the current clock time as nanoseconds.
1003 *
1004 * @returns The timer clock as nanoseconds.
1005 * @param pTimer Timer handle as returned by one of the create functions.
1006 */
1007VMMDECL(uint64_t) TMTimerGetNano(PTMTIMER pTimer)
1008{
1009 return TMTimerToNano(pTimer, TMTimerGet(pTimer));
1010}
1011
1012
1013/**
1014 * Get the current clock time as microseconds.
1015 *
1016 * @returns The timer clock as microseconds.
1017 * @param pTimer Timer handle as returned by one of the create functions.
1018 */
1019VMMDECL(uint64_t) TMTimerGetMicro(PTMTIMER pTimer)
1020{
1021 return TMTimerToMicro(pTimer, TMTimerGet(pTimer));
1022}
1023
1024
1025/**
1026 * Get the current clock time as milliseconds.
1027 *
1028 * @returns The timer clock as milliseconds.
1029 * @param pTimer Timer handle as returned by one of the create functions.
1030 */
1031VMMDECL(uint64_t) TMTimerGetMilli(PTMTIMER pTimer)
1032{
1033 return TMTimerToMilli(pTimer, TMTimerGet(pTimer));
1034}
1035
1036
1037/**
1038 * Converts the specified timer clock time to nanoseconds.
1039 *
1040 * @returns nanoseconds.
1041 * @param pTimer Timer handle as returned by one of the create functions.
1042 * @param u64Ticks The clock ticks.
1043 * @remark There could be rounding errors here. We just do a simple integere divide
1044 * without any adjustments.
1045 */
1046VMMDECL(uint64_t) TMTimerToNano(PTMTIMER pTimer, uint64_t u64Ticks)
1047{
1048 switch (pTimer->enmClock)
1049 {
1050 case TMCLOCK_VIRTUAL:
1051 case TMCLOCK_VIRTUAL_SYNC:
1052 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1053 return u64Ticks;
1054
1055 case TMCLOCK_REAL:
1056 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1057 return u64Ticks * 1000000;
1058
1059 case TMCLOCK_TSC:
1060 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1061 return 0;
1062
1063 default:
1064 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1065 return 0;
1066 }
1067}
1068
1069
1070/**
1071 * Converts the specified timer clock time to microseconds.
1072 *
1073 * @returns microseconds.
1074 * @param pTimer Timer handle as returned by one of the create functions.
1075 * @param u64Ticks The clock ticks.
1076 * @remark There could be rounding errors here. We just do a simple integere divide
1077 * without any adjustments.
1078 */
1079VMMDECL(uint64_t) TMTimerToMicro(PTMTIMER pTimer, uint64_t u64Ticks)
1080{
1081 switch (pTimer->enmClock)
1082 {
1083 case TMCLOCK_VIRTUAL:
1084 case TMCLOCK_VIRTUAL_SYNC:
1085 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1086 return u64Ticks / 1000;
1087
1088 case TMCLOCK_REAL:
1089 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1090 return u64Ticks * 1000;
1091
1092 case TMCLOCK_TSC:
1093 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1094 return 0;
1095
1096 default:
1097 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1098 return 0;
1099 }
1100}
1101
1102
1103/**
1104 * Converts the specified timer clock time to milliseconds.
1105 *
1106 * @returns milliseconds.
1107 * @param pTimer Timer handle as returned by one of the create functions.
1108 * @param u64Ticks The clock ticks.
1109 * @remark There could be rounding errors here. We just do a simple integere divide
1110 * without any adjustments.
1111 */
1112VMMDECL(uint64_t) TMTimerToMilli(PTMTIMER pTimer, uint64_t u64Ticks)
1113{
1114 switch (pTimer->enmClock)
1115 {
1116 case TMCLOCK_VIRTUAL:
1117 case TMCLOCK_VIRTUAL_SYNC:
1118 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1119 return u64Ticks / 1000000;
1120
1121 case TMCLOCK_REAL:
1122 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1123 return u64Ticks;
1124
1125 case TMCLOCK_TSC:
1126 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1127 return 0;
1128
1129 default:
1130 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1131 return 0;
1132 }
1133}
1134
1135
1136/**
1137 * Converts the specified nanosecond timestamp to timer clock ticks.
1138 *
1139 * @returns timer clock ticks.
1140 * @param pTimer Timer handle as returned by one of the create functions.
1141 * @param u64NanoTS The nanosecond value ticks to convert.
1142 * @remark There could be rounding and overflow errors here.
1143 */
1144VMMDECL(uint64_t) TMTimerFromNano(PTMTIMER pTimer, uint64_t u64NanoTS)
1145{
1146 switch (pTimer->enmClock)
1147 {
1148 case TMCLOCK_VIRTUAL:
1149 case TMCLOCK_VIRTUAL_SYNC:
1150 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1151 return u64NanoTS;
1152
1153 case TMCLOCK_REAL:
1154 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1155 return u64NanoTS / 1000000;
1156
1157 case TMCLOCK_TSC:
1158 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1159 return 0;
1160
1161 default:
1162 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1163 return 0;
1164 }
1165}
1166
1167
1168/**
1169 * Converts the specified microsecond timestamp to timer clock ticks.
1170 *
1171 * @returns timer clock ticks.
1172 * @param pTimer Timer handle as returned by one of the create functions.
1173 * @param u64MicroTS The microsecond value ticks to convert.
1174 * @remark There could be rounding and overflow errors here.
1175 */
1176VMMDECL(uint64_t) TMTimerFromMicro(PTMTIMER pTimer, uint64_t u64MicroTS)
1177{
1178 switch (pTimer->enmClock)
1179 {
1180 case TMCLOCK_VIRTUAL:
1181 case TMCLOCK_VIRTUAL_SYNC:
1182 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1183 return u64MicroTS * 1000;
1184
1185 case TMCLOCK_REAL:
1186 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1187 return u64MicroTS / 1000;
1188
1189 case TMCLOCK_TSC:
1190 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1191 return 0;
1192
1193 default:
1194 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1195 return 0;
1196 }
1197}
1198
1199
1200/**
1201 * Converts the specified millisecond timestamp to timer clock ticks.
1202 *
1203 * @returns timer clock ticks.
1204 * @param pTimer Timer handle as returned by one of the create functions.
1205 * @param u64MilliTS The millisecond value ticks to convert.
1206 * @remark There could be rounding and overflow errors here.
1207 */
1208VMMDECL(uint64_t) TMTimerFromMilli(PTMTIMER pTimer, uint64_t u64MilliTS)
1209{
1210 switch (pTimer->enmClock)
1211 {
1212 case TMCLOCK_VIRTUAL:
1213 case TMCLOCK_VIRTUAL_SYNC:
1214 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
1215 return u64MilliTS * 1000000;
1216
1217 case TMCLOCK_REAL:
1218 AssertCompile(TMCLOCK_FREQ_REAL == 1000);
1219 return u64MilliTS;
1220
1221 case TMCLOCK_TSC:
1222 AssertReleaseMsgFailed(("TMCLOCK_TSC conversions are not implemented\n"));
1223 return 0;
1224
1225 default:
1226 AssertMsgFailed(("Invalid enmClock=%d\n", pTimer->enmClock));
1227 return 0;
1228 }
1229}
1230
1231
1232/**
1233 * Get the expire time of the timer.
1234 * Only valid for active timers.
1235 *
1236 * @returns Expire time of the timer.
1237 * @param pTimer Timer handle as returned by one of the create functions.
1238 */
1239VMMDECL(uint64_t) TMTimerGetExpire(PTMTIMER pTimer)
1240{
1241 int cRetries = 1000;
1242 do
1243 {
1244 TMTIMERSTATE enmState = pTimer->enmState;
1245 switch (enmState)
1246 {
1247 case TMTIMERSTATE_EXPIRED:
1248 case TMTIMERSTATE_STOPPED:
1249 case TMTIMERSTATE_PENDING_STOP:
1250 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1251 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1252 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1253 return ~(uint64_t)0;
1254
1255 case TMTIMERSTATE_ACTIVE:
1256 case TMTIMERSTATE_PENDING_RESCHEDULE:
1257 case TMTIMERSTATE_PENDING_SCHEDULE:
1258 Log2(("TMTimerGetExpire: returns %llu (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1259 pTimer->u64Expire, pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1260 return pTimer->u64Expire;
1261
1262 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1263 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1264#ifdef IN_RING3
1265 if (!RTThreadYield())
1266 RTThreadSleep(1);
1267#endif
1268 break;
1269
1270 /*
1271 * Invalid states.
1272 */
1273 case TMTIMERSTATE_PENDING_DESTROY:
1274 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1275 case TMTIMERSTATE_FREE:
1276 AssertMsgFailed(("Invalid timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1277 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1278 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1279 return ~(uint64_t)0;
1280 default:
1281 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1282 return ~(uint64_t)0;
1283 }
1284 } while (cRetries-- > 0);
1285
1286 AssertMsgFailed(("Failed waiting for stable state. state=%d (%s)\n", pTimer->enmState, R3STRING(pTimer->pszDesc)));
1287 Log2(("TMTimerGetExpire: returns ~0 (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1288 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1289 return ~(uint64_t)0;
1290}
1291
1292
1293/**
1294 * Checks if a timer is active or not.
1295 *
1296 * @returns True if active.
1297 * @returns False if not active.
1298 * @param pTimer Timer handle as returned by one of the create functions.
1299 */
1300VMMDECL(bool) TMTimerIsActive(PTMTIMER pTimer)
1301{
1302 TMTIMERSTATE enmState = pTimer->enmState;
1303 switch (enmState)
1304 {
1305 case TMTIMERSTATE_STOPPED:
1306 case TMTIMERSTATE_EXPIRED:
1307 case TMTIMERSTATE_PENDING_STOP:
1308 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1309 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1310 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1311 return false;
1312
1313 case TMTIMERSTATE_ACTIVE:
1314 case TMTIMERSTATE_PENDING_RESCHEDULE:
1315 case TMTIMERSTATE_PENDING_SCHEDULE:
1316 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1317 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1318 Log2(("TMTimerIsActive: returns true (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1319 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1320 return true;
1321
1322 /*
1323 * Invalid states.
1324 */
1325 case TMTIMERSTATE_PENDING_DESTROY:
1326 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1327 case TMTIMERSTATE_FREE:
1328 AssertMsgFailed(("Invalid timer state %s (%s)\n", tmTimerState(enmState), R3STRING(pTimer->pszDesc)));
1329 Log2(("TMTimerIsActive: returns false (pTimer=%p:{.enmState=%s, .pszDesc='%s'})\n",
1330 pTimer, tmTimerState(pTimer->enmState), R3STRING(pTimer->pszDesc)));
1331 return false;
1332 default:
1333 AssertMsgFailed(("Unknown timer state %d (%s)\n", enmState, R3STRING(pTimer->pszDesc)));
1334 return false;
1335 }
1336}
1337
1338
1339/**
1340 * Convert state to string.
1341 *
1342 * @returns Readonly status name.
1343 * @param enmState State.
1344 */
1345const char *tmTimerState(TMTIMERSTATE enmState)
1346{
1347 switch (enmState)
1348 {
1349#define CASE(num, state) \
1350 case TMTIMERSTATE_##state: \
1351 AssertCompile(TMTIMERSTATE_##state == (num)); \
1352 return #num "-" #state
1353 CASE( 1,STOPPED);
1354 CASE( 2,ACTIVE);
1355 CASE( 3,EXPIRED);
1356 CASE( 4,PENDING_STOP);
1357 CASE( 5,PENDING_STOP_SCHEDULE);
1358 CASE( 6,PENDING_SCHEDULE_SET_EXPIRE);
1359 CASE( 7,PENDING_SCHEDULE);
1360 CASE( 8,PENDING_RESCHEDULE_SET_EXPIRE);
1361 CASE( 9,PENDING_RESCHEDULE);
1362 CASE(10,PENDING_STOP_DESTROY);
1363 CASE(11,PENDING_DESTROY);
1364 CASE(12,FREE);
1365 default:
1366 AssertMsgFailed(("Invalid state enmState=%d\n", enmState));
1367 return "Invalid state!";
1368#undef CASE
1369 }
1370}
1371
1372
1373/**
1374 * Schedules the given timer on the given queue.
1375 *
1376 * @param pQueue The timer queue.
1377 * @param pTimer The timer that needs scheduling.
1378 *
1379 * @remarks Called while owning the lock.
1380 */
1381DECLINLINE(void) tmTimerQueueScheduleOne(PTMTIMERQUEUE pQueue, PTMTIMER pTimer)
1382{
1383 /*
1384 * Processing.
1385 */
1386 unsigned cRetries = 2;
1387 do
1388 {
1389 TMTIMERSTATE enmState = pTimer->enmState;
1390 switch (enmState)
1391 {
1392 /*
1393 * Reschedule timer (in the active list).
1394 */
1395 case TMTIMERSTATE_PENDING_RESCHEDULE:
1396 {
1397 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_PENDING_SCHEDULE, TMTIMERSTATE_PENDING_RESCHEDULE)))
1398 break; /* retry */
1399
1400 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1401 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1402 if (pPrev)
1403 TMTIMER_SET_NEXT(pPrev, pNext);
1404 else
1405 {
1406 TMTIMER_SET_HEAD(pQueue, pNext);
1407 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1408 }
1409 if (pNext)
1410 TMTIMER_SET_PREV(pNext, pPrev);
1411 pTimer->offNext = 0;
1412 pTimer->offPrev = 0;
1413 /* fall thru */
1414 }
1415
1416 /*
1417 * Schedule timer (insert into the active list).
1418 */
1419 case TMTIMERSTATE_PENDING_SCHEDULE:
1420 {
1421 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1422 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_ACTIVE, TMTIMERSTATE_PENDING_SCHEDULE)))
1423 break; /* retry */
1424
1425 PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue);
1426 if (pCur)
1427 {
1428 const uint64_t u64Expire = pTimer->u64Expire;
1429 for (;; pCur = TMTIMER_GET_NEXT(pCur))
1430 {
1431 if (pCur->u64Expire > u64Expire)
1432 {
1433 const PTMTIMER pPrev = TMTIMER_GET_PREV(pCur);
1434 TMTIMER_SET_NEXT(pTimer, pCur);
1435 TMTIMER_SET_PREV(pTimer, pPrev);
1436 if (pPrev)
1437 TMTIMER_SET_NEXT(pPrev, pTimer);
1438 else
1439 {
1440 TMTIMER_SET_HEAD(pQueue, pTimer);
1441 pQueue->u64Expire = u64Expire;
1442 }
1443 TMTIMER_SET_PREV(pCur, pTimer);
1444 return;
1445 }
1446 if (!pCur->offNext)
1447 {
1448 TMTIMER_SET_NEXT(pCur, pTimer);
1449 TMTIMER_SET_PREV(pTimer, pCur);
1450 return;
1451 }
1452 }
1453 }
1454 else
1455 {
1456 TMTIMER_SET_HEAD(pQueue, pTimer);
1457 pQueue->u64Expire = pTimer->u64Expire;
1458 }
1459 return;
1460 }
1461
1462 /*
1463 * Stop the timer in active list.
1464 */
1465 case TMTIMERSTATE_PENDING_STOP:
1466 {
1467 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_PENDING_STOP_SCHEDULE, TMTIMERSTATE_PENDING_STOP)))
1468 break; /* retry */
1469
1470 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1471 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1472 if (pPrev)
1473 TMTIMER_SET_NEXT(pPrev, pNext);
1474 else
1475 {
1476 TMTIMER_SET_HEAD(pQueue, pNext);
1477 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1478 }
1479 if (pNext)
1480 TMTIMER_SET_PREV(pNext, pPrev);
1481 pTimer->offNext = 0;
1482 pTimer->offPrev = 0;
1483 /* fall thru */
1484 }
1485
1486 /*
1487 * Stop the timer (not on the active list).
1488 */
1489 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1490 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1491 if (RT_UNLIKELY(!tmTimerTry(pTimer, TMTIMERSTATE_STOPPED, TMTIMERSTATE_PENDING_STOP_SCHEDULE)))
1492 break;
1493 return;
1494
1495 /*
1496 * Stop & destroy the timer.
1497 */
1498 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1499 {
1500 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1501 const PTMTIMER pNext = TMTIMER_GET_NEXT(pTimer);
1502 if (pPrev)
1503 TMTIMER_SET_NEXT(pPrev, pNext);
1504 else
1505 {
1506 TMTIMER_SET_HEAD(pQueue, pNext);
1507 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1508 }
1509 if (pNext)
1510 TMTIMER_SET_PREV(pNext, pPrev);
1511 pTimer->offNext = 0;
1512 pTimer->offPrev = 0;
1513 /* fall thru */
1514 }
1515
1516 /*
1517 * Destroy the timer.
1518 */
1519 case TMTIMERSTATE_PENDING_DESTROY:
1520 {
1521 Assert(!pTimer->offNext); Assert(!pTimer->offPrev);
1522 PVM pVM = pTimer->CTX_SUFF(pVM);
1523 const PTMTIMER pBigPrev = (PTMTIMER)(pTimer->pBigPrev ? MMHyperR3ToCC(pVM, pTimer->pBigPrev) : NULL);
1524 const PTMTIMER pBigNext = (PTMTIMER)(pTimer->pBigNext ? MMHyperR3ToCC(pVM, pTimer->pBigNext) : NULL);
1525
1526 /* unlink from created list */
1527 if (pBigPrev)
1528 pBigPrev->pBigNext = pTimer->pBigNext;
1529 else
1530 pVM->tm.s.pCreated = pTimer->pBigNext;
1531 if (pBigNext)
1532 pBigNext->pBigPrev = pTimer->pBigPrev;
1533 pTimer->pBigNext = 0;
1534 pTimer->pBigPrev = 0;
1535
1536 /* free */
1537 Log2(("TM: Inserting %p into the free list ahead of %p!\n", pTimer, pVM->tm.s.pFree));
1538 pTimer->pBigNext = pVM->tm.s.pFree;
1539 pVM->tm.s.pFree = (PTMTIMERR3)MMHyperCCToR3(pVM, pTimer);
1540 TM_SET_STATE(pTimer, TMTIMERSTATE_FREE);
1541 return;
1542 }
1543
1544 /*
1545 * Postpone these until they get into the right state.
1546 */
1547 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1548 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1549 tmTimerLink(pQueue, pTimer);
1550 STAM_COUNTER_INC(&pTimer->CTX_SUFF(pVM)->tm.s.CTX_SUFF_Z(StatPostponed));
1551 return;
1552
1553 /*
1554 * None of these can be in the schedule.
1555 */
1556 case TMTIMERSTATE_FREE:
1557 case TMTIMERSTATE_STOPPED:
1558 case TMTIMERSTATE_ACTIVE:
1559 case TMTIMERSTATE_EXPIRED:
1560 default:
1561 AssertMsgFailed(("Timer (%p) in the scheduling list has an invalid state %s (%d)!",
1562 pTimer, tmTimerState(pTimer->enmState), pTimer->enmState));
1563 return;
1564 }
1565 } while (cRetries-- > 0);
1566}
1567
1568
1569/**
1570 * Schedules the specified timer queue.
1571 *
1572 * @param pVM The VM to run the timers for.
1573 * @param pQueue The queue to schedule.
1574 *
1575 * @remarks Called while owning the lock.
1576 */
1577void tmTimerQueueSchedule(PVM pVM, PTMTIMERQUEUE pQueue)
1578{
1579 TM_ASSERT_EMT_LOCK(pVM);
1580
1581 /*
1582 * Dequeue the scheduling list and iterate it.
1583 */
1584 int32_t offNext = ASMAtomicXchgS32(&pQueue->offSchedule, 0);
1585 Log2(("tmTimerQueueSchedule: pQueue=%p:{.enmClock=%d, offNext=%RI32}\n", pQueue, pQueue->enmClock, offNext));
1586 if (!offNext)
1587 return;
1588 PTMTIMER pNext = (PTMTIMER)((intptr_t)pQueue + offNext);
1589 while (pNext)
1590 {
1591 /*
1592 * Unlink the head timer and find the next one.
1593 */
1594 PTMTIMER pTimer = pNext;
1595 pNext = pNext->offScheduleNext ? (PTMTIMER)((intptr_t)pNext + pNext->offScheduleNext) : NULL;
1596 pTimer->offScheduleNext = 0;
1597
1598 /*
1599 * Do the scheduling.
1600 */
1601 Log2(("tmTimerQueueSchedule: %p:{.enmState=%s, .enmClock=%d, .enmType=%d, .pszDesc=%s}\n",
1602 pTimer, tmTimerState(pTimer->enmState), pTimer->enmClock, pTimer->enmType, R3STRING(pTimer->pszDesc)));
1603 tmTimerQueueScheduleOne(pQueue, pTimer);
1604 Log2(("tmTimerQueueSchedule: %p: new %s\n", pTimer, tmTimerState(pTimer->enmState)));
1605 } /* foreach timer in current schedule batch. */
1606}
1607
1608
1609#ifdef VBOX_STRICT
1610/**
1611 * Checks that the timer queues are sane.
1612 *
1613 * @param pVM VM handle.
1614 *
1615 * @remarks Called while owning the lock.
1616 */
1617void tmTimerQueuesSanityChecks(PVM pVM, const char *pszWhere)
1618{
1619 TM_ASSERT_EMT_LOCK(pVM);
1620
1621 /*
1622 * Check the linking of the active lists.
1623 */
1624 for (int i = 0; i < TMCLOCK_MAX; i++)
1625 {
1626 PTMTIMERQUEUE pQueue = &pVM->tm.s.CTX_SUFF(paTimerQueues)[i];
1627 Assert((int)pQueue->enmClock == i);
1628 PTMTIMER pPrev = NULL;
1629 for (PTMTIMER pCur = TMTIMER_GET_HEAD(pQueue); pCur; pPrev = pCur, pCur = TMTIMER_GET_NEXT(pCur))
1630 {
1631 AssertMsg((int)pCur->enmClock == i, ("%s: %d != %d\n", pszWhere, pCur->enmClock, i));
1632 AssertMsg(TMTIMER_GET_PREV(pCur) == pPrev, ("%s: %p != %p\n", pszWhere, TMTIMER_GET_PREV(pCur), pPrev));
1633 TMTIMERSTATE enmState = pCur->enmState;
1634 switch (enmState)
1635 {
1636 case TMTIMERSTATE_ACTIVE:
1637 AssertMsg( !pCur->offScheduleNext
1638 || pCur->enmState != TMTIMERSTATE_ACTIVE,
1639 ("%s: %RI32\n", pszWhere, pCur->offScheduleNext));
1640 break;
1641 case TMTIMERSTATE_PENDING_STOP:
1642 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1643 case TMTIMERSTATE_PENDING_RESCHEDULE:
1644 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1645 break;
1646 default:
1647 AssertMsgFailed(("%s: Invalid state enmState=%d %s\n", pszWhere, enmState, tmTimerState(enmState)));
1648 break;
1649 }
1650 }
1651 }
1652
1653
1654# ifdef IN_RING3
1655 /*
1656 * Do the big list and check that active timers all are in the active lists.
1657 */
1658 PTMTIMERR3 pPrev = NULL;
1659 for (PTMTIMERR3 pCur = pVM->tm.s.pCreated; pCur; pPrev = pCur, pCur = pCur->pBigNext)
1660 {
1661 Assert(pCur->pBigPrev == pPrev);
1662 Assert((unsigned)pCur->enmClock < (unsigned)TMCLOCK_MAX);
1663
1664 TMTIMERSTATE enmState = pCur->enmState;
1665 switch (enmState)
1666 {
1667 case TMTIMERSTATE_ACTIVE:
1668 case TMTIMERSTATE_PENDING_STOP:
1669 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1670 case TMTIMERSTATE_PENDING_RESCHEDULE:
1671 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1672 {
1673 PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTX_SUFF(paTimerQueues)[pCur->enmClock]);
1674 Assert(pCur->offPrev || pCur == pCurAct);
1675 while (pCurAct && pCurAct != pCur)
1676 pCurAct = TMTIMER_GET_NEXT(pCurAct);
1677 Assert(pCurAct == pCur);
1678 break;
1679 }
1680
1681 case TMTIMERSTATE_PENDING_DESTROY:
1682 case TMTIMERSTATE_PENDING_SCHEDULE:
1683 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1684 case TMTIMERSTATE_STOPPED:
1685 case TMTIMERSTATE_EXPIRED:
1686 {
1687 Assert(!pCur->offNext);
1688 Assert(!pCur->offPrev);
1689 for (PTMTIMERR3 pCurAct = TMTIMER_GET_HEAD(&pVM->tm.s.CTX_SUFF(paTimerQueues)[pCur->enmClock]);
1690 pCurAct;
1691 pCurAct = TMTIMER_GET_NEXT(pCurAct))
1692 {
1693 Assert(pCurAct != pCur);
1694 Assert(TMTIMER_GET_NEXT(pCurAct) != pCur);
1695 Assert(TMTIMER_GET_PREV(pCurAct) != pCur);
1696 }
1697 break;
1698 }
1699
1700 /* ignore */
1701 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1702 break;
1703
1704 /* shouldn't get here! */
1705 default:
1706 AssertMsgFailed(("Invalid state enmState=%d %s\n", enmState, tmTimerState(enmState)));
1707 break;
1708 }
1709 }
1710# endif /* IN_RING3 */
1711}
1712#endif /* !VBOX_STRICT */
1713
1714
1715/**
1716 * Gets the current warp drive percent.
1717 *
1718 * @returns The warp drive percent.
1719 * @param pVM The VM handle.
1720 */
1721VMMDECL(uint32_t) TMGetWarpDrive(PVM pVM)
1722{
1723 return pVM->tm.s.u32VirtualWarpDrivePercentage;
1724}
1725
Note: See TracBrowser for help on using the repository browser.

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