VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/TMAllVirtual.cpp@ 19753

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

More stats.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 27.7 KB
Line 
1/* $Id: TMAllVirtual.cpp 19753 2009-05-15 18:35:52Z vboxsync $ */
2/** @file
3 * TM - Timeout Manager, Virtual Time, 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#ifdef IN_RING3
29# include <VBox/rem.h>
30# include <iprt/thread.h>
31#endif
32#include "TMInternal.h"
33#include <VBox/vm.h>
34#include <VBox/vmm.h>
35#include <VBox/err.h>
36#include <VBox/log.h>
37#include <VBox/sup.h>
38
39#include <iprt/time.h>
40#include <iprt/assert.h>
41#include <iprt/asm.h>
42
43
44
45/**
46 * Helper function that's used by the assembly routines when something goes bust.
47 *
48 * @param pData Pointer to the data structure.
49 * @param u64NanoTS The calculated nano ts.
50 * @param u64DeltaPrev The delta relative to the previously returned timestamp.
51 * @param u64PrevNanoTS The previously returned timestamp (as it was read it).
52 */
53DECLEXPORT(void) tmVirtualNanoTSBad(PRTTIMENANOTSDATA pData, uint64_t u64NanoTS, uint64_t u64DeltaPrev, uint64_t u64PrevNanoTS)
54{
55 //PVM pVM = (PVM)((uint8_t *)pData - RT_OFFSETOF(VM, CTXALLSUFF(s.tm.VirtualGetRawData)));
56 pData->cBadPrev++;
57 if ((int64_t)u64DeltaPrev < 0)
58 LogRel(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64\n",
59 u64DeltaPrev, u64PrevNanoTS, u64NanoTS));
60 else
61 Log(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64 (debugging?)\n",
62 u64DeltaPrev, u64PrevNanoTS, u64NanoTS));
63}
64
65
66/**
67 * Called the first time somebody asks for the time or when the GIP
68 * is mapped/unmapped.
69 *
70 * This should never ever happen.
71 */
72DECLEXPORT(uint64_t) tmVirtualNanoTSRediscover(PRTTIMENANOTSDATA pData)
73{
74 //PVM pVM = (PVM)((uint8_t *)pData - RT_OFFSETOF(VM, CTXALLSUFF(s.tm.VirtualGetRawData)));
75 PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
76 AssertFatalMsgFailed(("pGip=%p u32Magic=%#x\n", pGip, VALID_PTR(pGip) ? pGip->u32Magic : 0));
77}
78
79
80#if 1
81
82/**
83 * Wrapper around the IPRT GIP time methods.
84 */
85DECLINLINE(uint64_t) tmVirtualGetRawNanoTS(PVM pVM)
86{
87#ifdef IN_RING3
88 return CTXALLSUFF(pVM->tm.s.pfnVirtualGetRaw)(&CTXALLSUFF(pVM->tm.s.VirtualGetRawData));
89# else /* !IN_RING3 */
90 uint32_t cPrevSteps = pVM->tm.s.CTX_SUFF(VirtualGetRawData).c1nsSteps;
91 uint64_t u64 = pVM->tm.s.CTX_SUFF(pfnVirtualGetRaw)(&pVM->tm.s.CTX_SUFF(VirtualGetRawData));
92 if (cPrevSteps != pVM->tm.s.CTX_SUFF(VirtualGetRawData).c1nsSteps)
93 VMCPU_FF_SET(VMMGetCpu(pVM), VMCPU_FF_TO_R3);
94 return u64;
95# endif /* !IN_RING3 */
96}
97
98#else
99
100/**
101 * This is (mostly) the same as rtTimeNanoTSInternal() except
102 * for the two globals which live in TM.
103 *
104 * @returns Nanosecond timestamp.
105 * @param pVM The VM handle.
106 */
107static uint64_t tmVirtualGetRawNanoTS(PVM pVM)
108{
109 uint64_t u64Delta;
110 uint32_t u32NanoTSFactor0;
111 uint64_t u64TSC;
112 uint64_t u64NanoTS;
113 uint32_t u32UpdateIntervalTSC;
114 uint64_t u64PrevNanoTS;
115
116 /*
117 * Read the GIP data and the previous value.
118 */
119 for (;;)
120 {
121 uint32_t u32TransactionId;
122 PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
123#ifdef IN_RING3
124 if (RT_UNLIKELY(!pGip || pGip->u32Magic != SUPGLOBALINFOPAGE_MAGIC))
125 return RTTimeSystemNanoTS();
126#endif
127
128 if (pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
129 {
130 u32TransactionId = pGip->aCPUs[0].u32TransactionId;
131#ifdef RT_OS_L4
132 Assert((u32TransactionId & 1) == 0);
133#endif
134 u32UpdateIntervalTSC = pGip->aCPUs[0].u32UpdateIntervalTSC;
135 u64NanoTS = pGip->aCPUs[0].u64NanoTS;
136 u64TSC = pGip->aCPUs[0].u64TSC;
137 u32NanoTSFactor0 = pGip->u32UpdateIntervalNS;
138 u64Delta = ASMReadTSC();
139 u64PrevNanoTS = ASMAtomicReadU64(&pVM->tm.s.u64VirtualRawPrev);
140 if (RT_UNLIKELY( pGip->aCPUs[0].u32TransactionId != u32TransactionId
141 || (u32TransactionId & 1)))
142 continue;
143 }
144 else
145 {
146 /* SUPGIPMODE_ASYNC_TSC */
147 PSUPGIPCPU pGipCpu;
148
149 uint8_t u8ApicId = ASMGetApicId();
150 if (RT_LIKELY(u8ApicId < RT_ELEMENTS(pGip->aCPUs)))
151 pGipCpu = &pGip->aCPUs[u8ApicId];
152 else
153 {
154 AssertMsgFailed(("%x\n", u8ApicId));
155 pGipCpu = &pGip->aCPUs[0];
156 }
157
158 u32TransactionId = pGipCpu->u32TransactionId;
159#ifdef RT_OS_L4
160 Assert((u32TransactionId & 1) == 0);
161#endif
162 u32UpdateIntervalTSC = pGipCpu->u32UpdateIntervalTSC;
163 u64NanoTS = pGipCpu->u64NanoTS;
164 u64TSC = pGipCpu->u64TSC;
165 u32NanoTSFactor0 = pGip->u32UpdateIntervalNS;
166 u64Delta = ASMReadTSC();
167 u64PrevNanoTS = ASMAtomicReadU64(&pVM->tm.s.u64VirtualRawPrev);
168#ifdef IN_RC
169 Assert(!(ASMGetFlags() & X86_EFL_IF));
170#else
171 if (RT_UNLIKELY(u8ApicId != ASMGetApicId()))
172 continue;
173 if (RT_UNLIKELY( pGipCpu->u32TransactionId != u32TransactionId
174 || (u32TransactionId & 1)))
175 continue;
176#endif
177 }
178 break;
179 }
180
181 /*
182 * Calc NanoTS delta.
183 */
184 u64Delta -= u64TSC;
185 if (u64Delta > u32UpdateIntervalTSC)
186 {
187 /*
188 * We've expired the interval, cap it. If we're here for the 2nd
189 * time without any GIP update inbetween, the checks against
190 * pVM->tm.s.u64VirtualRawPrev below will force 1ns stepping.
191 */
192 u64Delta = u32UpdateIntervalTSC;
193 }
194#if !defined(_MSC_VER) || defined(RT_ARCH_AMD64) /* GCC makes very pretty code from these two inline calls, while MSC cannot. */
195 u64Delta = ASMMult2xU32RetU64((uint32_t)u64Delta, u32NanoTSFactor0);
196 u64Delta = ASMDivU64ByU32RetU32(u64Delta, u32UpdateIntervalTSC);
197#else
198 __asm
199 {
200 mov eax, dword ptr [u64Delta]
201 mul dword ptr [u32NanoTSFactor0]
202 div dword ptr [u32UpdateIntervalTSC]
203 mov dword ptr [u64Delta], eax
204 xor edx, edx
205 mov dword ptr [u64Delta + 4], edx
206 }
207#endif
208
209 /*
210 * Calculate the time and compare it with the previously returned value.
211 *
212 * Since this function is called *very* frequently when the VM is running
213 * and then mostly on EMT, we can restrict the valid range of the delta
214 * (-1s to 2*GipUpdates) and simplify/optimize the default path.
215 */
216 u64NanoTS += u64Delta;
217 uint64_t u64DeltaPrev = u64NanoTS - u64PrevNanoTS;
218 if (RT_LIKELY(u64DeltaPrev < 1000000000 /* 1s */))
219 /* frequent - less than 1s since last call. */;
220 else if ( (int64_t)u64DeltaPrev < 0
221 && (int64_t)u64DeltaPrev + u32NanoTSFactor0 * 2 > 0)
222 {
223 /* occasional - u64NanoTS is in the 'past' relative to previous returns. */
224 ASMAtomicIncU32(&pVM->tm.s.CTX_SUFF(VirtualGetRawData).c1nsSteps);
225 u64NanoTS = u64PrevNanoTS + 1;
226#ifndef IN_RING3
227 VM_FF_SET(pVM, VM_FF_TO_R3); /* S10 hack */
228#endif
229 }
230 else if (u64PrevNanoTS)
231 {
232 /* Something has gone bust, if negative offset it's real bad. */
233 ASMAtomicIncU32(&pVM->tm.s.CTX_SUFF(VirtualGetRawData).cBadPrev);
234 if ((int64_t)u64DeltaPrev < 0)
235 LogRel(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64 u64Delta=%#RX64\n",
236 u64DeltaPrev, u64PrevNanoTS, u64NanoTS, u64Delta));
237 else
238 Log(("TM: u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64 u64Delta=%#RX64 (debugging?)\n",
239 u64DeltaPrev, u64PrevNanoTS, u64NanoTS, u64Delta));
240#ifdef DEBUG_bird
241 /** @todo there are some hickups during boot and reset that can cause 2-5 seconds delays. Investigate... */
242 AssertMsg(u64PrevNanoTS > UINT64_C(100000000000) /* 100s */,
243 ("u64DeltaPrev=%RI64 u64PrevNanoTS=0x%016RX64 u64NanoTS=0x%016RX64 u64Delta=%#RX64\n",
244 u64DeltaPrev, u64PrevNanoTS, u64NanoTS, u64Delta));
245#endif
246 }
247 /* else: We're resuming (see TMVirtualResume). */
248 if (RT_LIKELY(ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualRawPrev, u64NanoTS, u64PrevNanoTS)))
249 return u64NanoTS;
250
251 /*
252 * Attempt updating the previous value, provided we're still ahead of it.
253 *
254 * There is no point in recalculating u64NanoTS because we got preemted or if
255 * we raced somebody while the GIP was updated, since these are events
256 * that might occure at any point in the return path as well.
257 */
258 for (int cTries = 50;;)
259 {
260 u64PrevNanoTS = ASMAtomicReadU64(&pVM->tm.s.u64VirtualRawPrev);
261 if (u64PrevNanoTS >= u64NanoTS)
262 break;
263 if (ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualRawPrev, u64NanoTS, u64PrevNanoTS))
264 break;
265 AssertBreak(--cTries <= 0);
266 if (cTries < 25 && !VM_IS_EMT(pVM)) /* give up early */
267 break;
268 }
269
270 return u64NanoTS;
271}
272
273#endif
274
275
276/**
277 * Get the time when we're not running at 100%
278 *
279 * @returns The timestamp.
280 * @param pVM The VM handle.
281 */
282static uint64_t tmVirtualGetRawNonNormal(PVM pVM)
283{
284 /*
285 * Recalculate the RTTimeNanoTS() value for the period where
286 * warp drive has been enabled.
287 */
288 uint64_t u64 = tmVirtualGetRawNanoTS(pVM);
289 u64 -= pVM->tm.s.u64VirtualWarpDriveStart;
290 u64 *= pVM->tm.s.u32VirtualWarpDrivePercentage;
291 u64 /= 100;
292 u64 += pVM->tm.s.u64VirtualWarpDriveStart;
293
294 /*
295 * Now we apply the virtual time offset.
296 * (Which is the negated tmVirtualGetRawNanoTS() value for when the virtual
297 * machine started if it had been running continuously without any suspends.)
298 */
299 u64 -= pVM->tm.s.u64VirtualOffset;
300 return u64;
301}
302
303
304/**
305 * Get the raw virtual time.
306 *
307 * @returns The current time stamp.
308 * @param pVM The VM handle.
309 */
310DECLINLINE(uint64_t) tmVirtualGetRaw(PVM pVM)
311{
312 if (RT_LIKELY(!pVM->tm.s.fVirtualWarpDrive))
313 return tmVirtualGetRawNanoTS(pVM) - pVM->tm.s.u64VirtualOffset;
314 return tmVirtualGetRawNonNormal(pVM);
315}
316
317
318/**
319 * Inlined version of tmVirtualGetEx.
320 */
321DECLINLINE(uint64_t) tmVirtualGet(PVM pVM, bool fCheckTimers)
322{
323 uint64_t u64;
324 if (RT_LIKELY(pVM->tm.s.cVirtualTicking))
325 {
326 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGet);
327 u64 = tmVirtualGetRaw(pVM);
328
329 /*
330 * Use the chance to check for expired timers.
331 */
332 if (fCheckTimers)
333 {
334 PVMCPU pVCpuDst = &pVM->aCpus[pVM->tm.s.idTimerCpu];
335 if ( !VMCPU_FF_ISSET(pVCpuDst, VMCPU_FF_TIMER)
336 && !pVM->tm.s.fRunningQueues
337 && ( pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64
338 || ( pVM->tm.s.fVirtualSyncTicking
339 && pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire <= u64 - pVM->tm.s.offVirtualSync
340 )
341 )
342 && !pVM->tm.s.fRunningQueues
343 )
344 {
345 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSetFF);
346 Log5(("TMAllVirtual(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_ISPENDING(pVCpuDst, VMCPU_FF_TIMER)));
347 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
348#ifdef IN_RING3
349 REMR3NotifyTimerPending(pVM, pVCpuDst);
350 VMR3NotifyCpuFFU(pVCpuDst->pUVCpu, VMNOTIFYFF_FLAGS_DONE_REM);
351#endif
352 }
353 }
354 }
355 else
356 u64 = pVM->tm.s.u64Virtual;
357 return u64;
358}
359
360
361/**
362 * Gets the current TMCLOCK_VIRTUAL time
363 *
364 * @returns The timestamp.
365 * @param pVM VM handle.
366 *
367 * @remark While the flow of time will never go backwards, the speed of the
368 * progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be
369 * influenced by power saving (SpeedStep, PowerNow!), while the former
370 * makes use of TSC and kernel timers.
371 */
372VMMDECL(uint64_t) TMVirtualGet(PVM pVM)
373{
374 return tmVirtualGet(pVM, true /* check timers */);
375}
376
377
378/**
379 * Gets the current TMCLOCK_VIRTUAL time without checking
380 * timers or anything.
381 *
382 * Meaning, this has no side effect on FFs like TMVirtualGet may have.
383 *
384 * @returns The timestamp.
385 * @param pVM VM handle.
386 *
387 * @remarks See TMVirtualGet.
388 */
389VMMDECL(uint64_t) TMVirtualGetNoCheck(PVM pVM)
390{
391 return tmVirtualGet(pVM, false /*fCheckTimers*/);
392}
393
394
395/**
396 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
397 *
398 * @returns The timestamp.
399 * @param pVM VM handle.
400 * @param fCheckTimers Check timers or not
401 * @thread EMT.
402 */
403DECLINLINE(uint64_t) tmVirtualSyncGetEx(PVM pVM, bool fCheckTimers)
404{
405 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGet);
406
407 if (!pVM->tm.s.fVirtualSyncTicking)
408 return pVM->tm.s.u64VirtualSync;
409
410 /*
411 * Query the virtual clock and do the usual expired timer check.
412 */
413 Assert(pVM->tm.s.cVirtualTicking);
414 uint64_t u64 = tmVirtualGetRaw(pVM);
415 if (fCheckTimers)
416 {
417 PVMCPU pVCpuDst = &pVM->aCpus[pVM->tm.s.idTimerCpu];
418 if ( !VMCPU_FF_ISSET(pVCpuDst, VMCPU_FF_TIMER)
419 && pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64)
420 {
421 Log5(("TMAllVirtual(%u): FF: 0 -> 1\n", __LINE__));
422 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
423#ifdef IN_RING3
424 REMR3NotifyTimerPending(pVM, pVCpuDst);
425 VMR3NotifyCpuFFU(pVCpuDst->pUVCpu, VMNOTIFYFF_FLAGS_DONE_REM /** @todo |VMNOTIFYFF_FLAGS_POKE*/);
426#endif
427 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGetSetFF);
428 }
429 }
430
431 /*
432 * Read the offset and adjust if we're playing catch-up.
433 *
434 * The catch-up adjusting work by us decrementing the offset by a percentage of
435 * the time elapsed since the previous TMVirtualGetSync call.
436 *
437 * It's possible to get a very long or even negative interval between two read
438 * for the following reasons:
439 * - Someone might have suspended the process execution, frequently the case when
440 * debugging the process.
441 * - We might be on a different CPU which TSC isn't quite in sync with the
442 * other CPUs in the system.
443 * - Another thread is racing us and we might have been preemnted while inside
444 * this function.
445 *
446 * Assuming nano second virtual time, we can simply ignore any intervals which has
447 * any of the upper 32 bits set.
448 */
449 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
450 int cOuterTries = 42;
451 int rcLock = tmVirtualSyncTryLock(pVM);
452 uint64_t off;
453 for (;; cOuterTries--)
454 {
455 /* Re-check the ticking flag. */
456 if (!ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncTicking))
457 {
458 if (RT_SUCCESS(rcLock))
459 tmVirtualSyncUnlock(pVM);
460 return ASMAtomicUoReadU64(&pVM->tm.s.u64VirtualSync);
461 }
462
463 off = ASMAtomicUoReadU64(&pVM->tm.s.offVirtualSync);
464 if (ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
465 {
466 /* adjust the offset. */
467 if (RT_FAILURE(rcLock))
468 rcLock = tmVirtualSyncTryLock(pVM);
469 if (RT_SUCCESS(rcLock))
470 {
471 /* We own the lock and may make updates. */
472 const uint64_t u64Prev = pVM->tm.s.u64VirtualSyncCatchUpPrev;
473 uint64_t u64Delta = u64 - u64Prev;
474 if (RT_LIKELY(!(u64Delta >> 32)))
475 {
476 uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, pVM->tm.s.u32VirtualSyncCatchUpPercentage, 100);
477 if (off > u64Sub + pVM->tm.s.offVirtualSyncGivenUp)
478 {
479 off -= u64Sub;
480 ASMAtomicWriteU64(&pVM->tm.s.offVirtualSync, off);
481 ASMAtomicWriteU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev, u64);
482 Log4(("TM: %RU64/%RU64: sub %RU32\n", u64 - off, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp, u64Sub));
483 }
484 else
485 {
486 /* we've completely caught up. */
487 STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatVirtualSyncCatchup, c);
488 off = pVM->tm.s.offVirtualSyncGivenUp;
489 ASMAtomicWriteU64(&pVM->tm.s.offVirtualSync, off);
490 ASMAtomicWriteBool(&pVM->tm.s.fVirtualSyncCatchUp, false);
491 ASMAtomicWriteU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev, u64);
492 Log4(("TM: %RU64/0: caught up\n", u64));
493 }
494 }
495 else
496 {
497 /* More than 4 seconds since last time (or negative), ignore it. */
498 if (!(u64Delta & RT_BIT_64(63)))
499 ASMAtomicWriteU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev, u64);
500 Log(("TMVirtualGetSync: u64Delta=%RX64\n", u64Delta));
501 }
502 break;
503 }
504
505 /* No changes allowed, try get a consistent set of parameters. */
506 uint64_t const u64Prev = ASMAtomicUoReadU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev);
507 uint64_t const offGivenUp = ASMAtomicUoReadU64(&pVM->tm.s.offVirtualSyncGivenUp);
508 uint32_t const u32Pct = ASMAtomicReadU32(&pVM->tm.s.u32VirtualSyncCatchUpPercentage);
509 if ( ( u64Prev == ASMAtomicUoReadU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev)
510 && offGivenUp == ASMAtomicUoReadU64(&pVM->tm.s.offVirtualSyncGivenUp)
511 && u32Pct == ASMAtomicUoReadU32(&pVM->tm.s.u32VirtualSyncCatchUpPercentage)
512 && ASMAtomicReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
513 || cOuterTries <= 0)
514 {
515 uint64_t u64Delta = u64 - u64Prev;
516 if (RT_LIKELY(!(u64Delta >> 32)))
517 {
518 uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, u32Pct, 100);
519 if (off > u64Sub + offGivenUp)
520 {
521 off -= u64Sub;
522 Log4(("TM: %RU64/%RU64: sub %RU32 (NoLock)\n", u64 - off, pVM->tm.s.offVirtualSync - offGivenUp, u64Sub));
523 }
524 else
525 {
526 /* we've completely caught up. */
527 STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatVirtualSyncCatchup, c);
528 off = offGivenUp;
529 Log4(("TM: %RU64/0: caught up\n", u64));
530 }
531 }
532 else
533 /* More than 4 seconds since last time (or negative), ignore it. */
534 Log(("TMVirtualGetSync: u64Delta=%RX64 (NoLock)\n", u64Delta));
535
536 /* Check that we're still running and in catch up. */
537 if (pVM->tm.s.fVirtualSyncCatchUp)
538 break;
539 if (cOuterTries <= 0)
540 break;
541 }
542 }
543 else if ( off == ASMAtomicUoReadU64(&pVM->tm.s.offVirtualSync)
544 && !ASMAtomicUoReadBool(&pVM->tm.s.fVirtualSyncCatchUp))
545 break; /* Got an consistent offset */
546 }
547 if (cOuterTries <= 0)
548 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGetELoop);
549
550 /*
551 * Complete the calculation of the current TMCLOCK_VIRTUAL_SYNC time. The current
552 * approach is to never pass the head timer. So, when we do stop the clock and
553 * set the timer pending flag.
554 */
555 u64 -= off;
556 const uint64_t u64Expire = ASMAtomicReadU64(&pVM->tm.s.CTX_SUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire);
557 if (u64 >= u64Expire)
558 {
559 u64 = u64Expire;
560 PVMCPU pVCpuDst = &pVM->aCpus[pVM->tm.s.idTimerCpu];
561 if (RT_FAILURE(rcLock))
562 rcLock = tmVirtualSyncTryLock(pVM);
563 if (RT_SUCCESS(rcLock))
564 {
565 ASMAtomicWriteU64(&pVM->tm.s.u64VirtualSync, u64);
566 ASMAtomicWriteBool(&pVM->tm.s.fVirtualSyncTicking, false);
567 VM_FF_SET(pVM, VM_FF_TM_VIRTUAL_SYNC);
568 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
569 Log5(("TMAllVirtual(%u): FF: %d -> 1\n", __LINE__, VMCPU_FF_ISPENDING(pVCpuDst, VMCPU_FF_TIMER)));
570 tmVirtualSyncUnlock(pVM);
571#ifdef IN_RING3
572 REMR3NotifyTimerPending(pVM, pVCpuDst);
573 VMR3NotifyCpuFFU(pVCpuDst->pUVCpu, VMNOTIFYFF_FLAGS_DONE_REM);
574#endif
575 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGetLocked);
576 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGetSetFF);
577 Log4(("TM: %RU64/%RU64: exp tmr=>ff\n", u64, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp));
578 }
579 else if (!VMCPU_FF_ISSET(pVCpuDst, VMCPU_FF_TIMER))
580 {
581 Log5(("TMAllVirtual(%u): FF: %d -> 1 (NoLock)\n", __LINE__, VMCPU_FF_ISPENDING(pVCpuDst, VMCPU_FF_TIMER)));
582 VMCPU_FF_SET(pVCpuDst, VMCPU_FF_TIMER);
583#ifdef IN_RING3
584 REMR3NotifyTimerPending(pVM, pVCpuDst);
585 VMR3NotifyCpuFFU(pVCpuDst->pUVCpu, VMNOTIFYFF_FLAGS_DONE_REM);
586#endif
587 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGetSetFF);
588 Log4(("TM: %RU64/%RU64: exp tmr=>ff (NoLock)\n", u64, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp));
589 }
590 else
591 Log4(("TM: %RU64/%RU64: exp tmr (NoLock)\n", u64, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp));
592 }
593 else if (RT_SUCCESS(rcLock))
594 {
595 tmVirtualSyncUnlock(pVM);
596 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualSyncGetLocked);
597 }
598
599 return u64;
600}
601
602
603/**
604 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
605 *
606 * @returns The timestamp.
607 * @param pVM VM handle.
608 * @thread EMT.
609 * @remarks May set the timer and virtual sync FFs.
610 */
611VMMDECL(uint64_t) TMVirtualSyncGet(PVM pVM)
612{
613 return tmVirtualSyncGetEx(pVM, true /* check timers */);
614}
615
616
617/**
618 * Gets the current TMCLOCK_VIRTUAL_SYNC time without checking timers running on
619 * TMCLOCK_VIRTUAL.
620 *
621 * @returns The timestamp.
622 * @param pVM VM handle.
623 * @thread EMT.
624 * @remarks May set the timer and virtual sync FFs.
625 */
626VMMDECL(uint64_t) TMVirtualSyncGetNoCheck(PVM pVM)
627{
628 return tmVirtualSyncGetEx(pVM, false /* check timers */);
629}
630
631
632/**
633 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
634 *
635 * @returns The timestamp.
636 * @param pVM VM handle.
637 * @param fCheckTimers Check timers on the virtual clock or not.
638 * @thread EMT.
639 * @remarks May set the timer and virtual sync FFs.
640 */
641VMMDECL(uint64_t) TMVirtualSyncGetEx(PVM pVM, bool fCheckTimers)
642{
643 return tmVirtualSyncGetEx(pVM, fCheckTimers);
644}
645
646
647/**
648 * Gets the current lag of the synchronous virtual clock (relative to the virtual clock).
649 *
650 * @return The current lag.
651 * @param pVM VM handle.
652 */
653VMMDECL(uint64_t) TMVirtualSyncGetLag(PVM pVM)
654{
655 return pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp;
656}
657
658
659/**
660 * Get the current catch-up percent.
661 *
662 * @return The current catch0up percent. 0 means running at the same speed as the virtual clock.
663 * @param pVM VM handle.
664 */
665VMMDECL(uint32_t) TMVirtualSyncGetCatchUpPct(PVM pVM)
666{
667 if (pVM->tm.s.fVirtualSyncCatchUp)
668 return pVM->tm.s.u32VirtualSyncCatchUpPercentage;
669 return 0;
670}
671
672
673/**
674 * Gets the current TMCLOCK_VIRTUAL frequency.
675 *
676 * @returns The freqency.
677 * @param pVM VM handle.
678 */
679VMMDECL(uint64_t) TMVirtualGetFreq(PVM pVM)
680{
681 return TMCLOCK_FREQ_VIRTUAL;
682}
683
684
685/**
686 * Worker for TMR3PauseClocks.
687 *
688 * @returns VINF_SUCCESS or VERR_INTERNAL_ERROR (asserted).
689 * @param pVM The VM handle.
690 */
691int tmVirtualPauseLocked(PVM pVM)
692{
693 uint32_t c = ASMAtomicDecU32(&pVM->tm.s.cVirtualTicking);
694 AssertMsgReturn(c < pVM->cCPUs, ("%u vs %u\n", c, pVM->cCPUs), VERR_INTERNAL_ERROR);
695 if (c == 0)
696 {
697 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualPause);
698 pVM->tm.s.u64Virtual = tmVirtualGetRaw(pVM);
699 ASMAtomicWriteBool(&pVM->tm.s.fVirtualSyncTicking, false);
700 }
701 return VINF_SUCCESS;
702}
703
704
705/**
706 * Worker for TMR3ResumeClocks.
707 *
708 * @returns VINF_SUCCESS or VERR_INTERNAL_ERROR (asserted).
709 * @param pVM The VM handle.
710 */
711int tmVirtualResumeLocked(PVM pVM)
712{
713 uint32_t c = ASMAtomicIncU32(&pVM->tm.s.cVirtualTicking);
714 AssertMsgReturn(c <= pVM->cCPUs, ("%u vs %u\n", c, pVM->cCPUs), VERR_INTERNAL_ERROR);
715 if (c == 1)
716 {
717 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualResume);
718 pVM->tm.s.u64VirtualRawPrev = 0;
719 pVM->tm.s.u64VirtualWarpDriveStart = tmVirtualGetRawNanoTS(pVM);
720 pVM->tm.s.u64VirtualOffset = pVM->tm.s.u64VirtualWarpDriveStart - pVM->tm.s.u64Virtual;
721 ASMAtomicWriteBool(&pVM->tm.s.fVirtualSyncTicking, true);
722 }
723 return VINF_SUCCESS;
724}
725
726
727/**
728 * Converts from virtual ticks to nanoseconds.
729 *
730 * @returns nanoseconds.
731 * @param pVM The VM handle.
732 * @param u64VirtualTicks The virtual ticks to convert.
733 * @remark There could be rounding errors here. We just do a simple integere divide
734 * without any adjustments.
735 */
736VMMDECL(uint64_t) TMVirtualToNano(PVM pVM, uint64_t u64VirtualTicks)
737{
738 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
739 return u64VirtualTicks;
740}
741
742
743/**
744 * Converts from virtual ticks to microseconds.
745 *
746 * @returns microseconds.
747 * @param pVM The VM handle.
748 * @param u64VirtualTicks The virtual ticks to convert.
749 * @remark There could be rounding errors here. We just do a simple integere divide
750 * without any adjustments.
751 */
752VMMDECL(uint64_t) TMVirtualToMicro(PVM pVM, uint64_t u64VirtualTicks)
753{
754 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
755 return u64VirtualTicks / 1000;
756}
757
758
759/**
760 * Converts from virtual ticks to milliseconds.
761 *
762 * @returns milliseconds.
763 * @param pVM The VM handle.
764 * @param u64VirtualTicks The virtual ticks to convert.
765 * @remark There could be rounding errors here. We just do a simple integere divide
766 * without any adjustments.
767 */
768VMMDECL(uint64_t) TMVirtualToMilli(PVM pVM, uint64_t u64VirtualTicks)
769{
770 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
771 return u64VirtualTicks / 1000000;
772}
773
774
775/**
776 * Converts from nanoseconds to virtual ticks.
777 *
778 * @returns virtual ticks.
779 * @param pVM The VM handle.
780 * @param u64NanoTS The nanosecond value ticks to convert.
781 * @remark There could be rounding and overflow errors here.
782 */
783VMMDECL(uint64_t) TMVirtualFromNano(PVM pVM, uint64_t u64NanoTS)
784{
785 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
786 return u64NanoTS;
787}
788
789
790/**
791 * Converts from microseconds to virtual ticks.
792 *
793 * @returns virtual ticks.
794 * @param pVM The VM handle.
795 * @param u64MicroTS The microsecond value ticks to convert.
796 * @remark There could be rounding and overflow errors here.
797 */
798VMMDECL(uint64_t) TMVirtualFromMicro(PVM pVM, uint64_t u64MicroTS)
799{
800 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
801 return u64MicroTS * 1000;
802}
803
804
805/**
806 * Converts from milliseconds to virtual ticks.
807 *
808 * @returns virtual ticks.
809 * @param pVM The VM handle.
810 * @param u64MilliTS The millisecond value ticks to convert.
811 * @remark There could be rounding and overflow errors here.
812 */
813VMMDECL(uint64_t) TMVirtualFromMilli(PVM pVM, uint64_t u64MilliTS)
814{
815 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
816 return u64MilliTS * 1000000;
817}
818
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