VirtualBox

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

最後變更 在這個檔案從19032是 19032,由 vboxsync 提交於 16 年 前

Split TM for SMP guests.

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

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