VirtualBox

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

Last change on this file since 26624 was 26315, checked in by vboxsync, 15 years ago

gcc false positive warning

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