VirtualBox

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

Last change on this file since 32419 was 32419, checked in by vboxsync, 14 years ago

TM,HWVMXR0: Use the preemption timer to make sure we stop executing guest code in time for the next TMCLOCK_VIRTUAL_SYNC timer deadline.

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