VirtualBox

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

Last change on this file since 72596 was 69111, checked in by vboxsync, 7 years ago

(C) year

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