VirtualBox

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

Last change on this file since 2075 was 2075, checked in by vboxsync, 18 years ago

Added TMVirtualGetEx.
Changed TMCpuTickGetOffset to return virtual tsc - host tsc.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.2 KB
Line 
1/* $Id: TMAllVirtual.cpp 2075 2007-04-13 12:59:25Z vboxsync $ */
2/** @file
3 * TM - Timeout Manager, Virtual Time, All Contexts.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
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#endif
31#include "TMInternal.h"
32#include <VBox/vm.h>
33#include <VBox/err.h>
34#include <VBox/log.h>
35#include <VBox/sup.h>
36
37#include <iprt/time.h>
38#include <iprt/assert.h>
39#include <iprt/asm.h>
40
41
42/*******************************************************************************
43* Internal Functions *
44*******************************************************************************/
45static DECLCALLBACK(int) tmVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent);
46
47
48
49/**
50 * Get the time when we're not running at 100%
51 *
52 * @returns The timestamp.
53 * @param pVM The VM handle.
54 */
55uint64_t tmVirtualGetRawNonNormal(PVM pVM)
56{
57 /*
58 * Recalculate the RTTimeNanoTS() value for the period where
59 * warp drive has been enabled.
60 */
61 uint64_t u64 = RTTimeNanoTS();
62 u64 -= pVM->tm.s.u64VirtualWarpDriveStart;
63 u64 *= pVM->tm.s.u32VirtualWarpDrivePercentage;
64 u64 /= 100;
65 u64 += pVM->tm.s.u64VirtualWarpDriveStart;
66
67 /*
68 * Now we apply the virtual time offset.
69 * (Which is the negate RTTimeNanoTS() value for when the virtual machine
70 * started if it had been running continuously without any suspends.)
71 */
72 u64 -= pVM->tm.s.u64VirtualOffset;
73 return u64;
74}
75
76
77/**
78 * Get the raw virtual time.
79 *
80 * @returns The current time stamp.
81 * @param pVM The VM handle.
82 */
83DECLINLINE(uint64_t) tmVirtualGetRaw(PVM pVM)
84{
85 if (RT_LIKELY(!pVM->tm.s.fVirtualWarpDrive))
86 return RTTimeNanoTS() - pVM->tm.s.u64VirtualOffset;
87 return tmVirtualGetRawNonNormal(pVM);
88}
89
90
91/**
92 * Gets the current TMCLOCK_VIRTUAL time
93 *
94 * @returns The timestamp.
95 * @param pVM VM handle.
96 *
97 * @remark While the flow of time will never go backwards, the speed of the
98 * progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be
99 * influenced by power saving (SpeedStep, PowerNow!), while the former
100 * makes use of TSC and kernel timers.
101 */
102TMDECL(uint64_t) TMVirtualGet(PVM pVM)
103{
104 return TMVirtualGetEx(pVM, true /* check timers */);
105}
106
107/**
108 * Gets the current TMCLOCK_VIRTUAL time
109 *
110 * @returns The timestamp.
111 * @param pVM VM handle.
112 * @param fCheckTimers Check timers or not
113 *
114 * @remark While the flow of time will never go backwards, the speed of the
115 * progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be
116 * influenced by power saving (SpeedStep, PowerNow!), while the former
117 * makes use of TSC and kernel timers.
118 */
119TMDECL(uint64_t) TMVirtualGetEx(PVM pVM, bool fCheckTimers)
120{
121 uint64_t u64;
122 if (pVM->tm.s.fVirtualTicking)
123 {
124 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGet);
125 u64 = tmVirtualGetRaw(pVM);
126
127 /*
128 * Use the chance to check for expired timers.
129 */
130 if ( fCheckTimers
131 && !VM_FF_ISSET(pVM, VM_FF_TIMER)
132 && ( pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64
133 || ( pVM->tm.s.fVirtualSyncTicking
134 && pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire <= u64 - pVM->tm.s.u64VirtualSyncOffset
135 )
136 )
137 )
138 {
139 VM_FF_SET(pVM, VM_FF_TIMER);
140#ifdef IN_RING3
141 REMR3NotifyTimerPending(pVM);
142 VMR3NotifyFF(pVM, true);
143#endif
144 }
145 }
146 else
147 u64 = pVM->tm.s.u64Virtual;
148 return u64;
149}
150
151/**
152 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
153 *
154 * @returns The timestamp.
155 * @param pVM VM handle.
156 */
157TMDECL(uint64_t) TMVirtualGetSync(PVM pVM)
158{
159 uint64_t u64;
160 if (pVM->tm.s.fVirtualSyncTicking)
161 {
162 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSync);
163
164 /*
165 * Do TMVirtualGet() to get the current TMCLOCK_VIRTUAL time.
166 */
167 Assert(pVM->tm.s.fVirtualTicking);
168 u64 = tmVirtualGetRaw(pVM);
169 if ( !VM_FF_ISSET(pVM, VM_FF_TIMER)
170 && pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64)
171 {
172 VM_FF_SET(pVM, VM_FF_TIMER);
173#ifdef IN_RING3
174 REMR3NotifyTimerPending(pVM);
175 VMR3NotifyFF(pVM, true);
176#endif
177 }
178
179 /*
180 * Read the offset and adjust if we're playing catch-up.
181 *
182 * The catch-up adjusting work by us decrementing the offset by a percentage of
183 * the time elapsed since the previous TMVritualGetSync call. We take some simple
184 * precautions against racing other threads here, but assume that this isn't going
185 * to be much of a problem since calls to this function is unlikely from threads
186 * other than the EMT.
187 *
188 * It's possible to get a very long or even negative interval between two read
189 * for the following reasons:
190 * - Someone might have suspended the process execution, frequently the case when
191 * debugging the process.
192 * - We might be on a different CPU which TSC isn't quite in sync with the
193 * other CPUs in the system.
194 * - RTTimeNanoTS() is returning sligtly different values in GC, R0 and R3 because
195 * of the static variable it uses with the previous read time.
196 * - Another thread is racing us and we might have been preemnted while inside
197 * this function.
198 *
199 * Assuming nano second virtual time, we can simply ignore any intervals which has
200 * any of the upper 32 bits set. This will have the nice sideeffect of allowing us
201 * to use (faster) 32-bit math.
202 */
203 AssertCompile(TMCLOCK_FREQ_VIRTUAL <= 2000000000); /* (assumes low 32-bit >= 2 seconds) */
204 uint64_t u64Offset = pVM->tm.s.u64VirtualSyncOffset;
205 if (pVM->tm.s.fVirtualSyncCatchUp)
206 {
207 const uint64_t u64Prev = pVM->tm.s.u64VirtualSyncCatchUpPrev;
208 uint64_t u64Delta = u64 - u64Prev;
209 if (!(u64Delta >> 32))
210 {
211 uint32_t u32Sub = ASMDivU64ByU32RetU32(ASMMult2xU32RetU64((uint32_t)u64Delta, pVM->tm.s.u32VirtualSyncCatchupPercentage),
212 100);
213 if (u32Sub < (uint32_t)u64Delta)
214 {
215 const uint64_t u64NewOffset = u64Offset - u32Sub;
216 if (ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev, u64, u64Prev))
217 ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncOffset, u64NewOffset, u64Offset);
218 u64Offset = u64NewOffset;
219 }
220 else
221 {
222 /* we've completely caught up. */
223 if ( ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev, u64, u64Prev)
224 && ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncOffset, 0, u64Offset))
225 ASMAtomicXchgSize(&pVM->tm.s.fVirtualSyncCatchUp, false);
226 }
227 }
228 else
229 {
230 /* Update the previous TMVirtualGetSync time it's not a negative delta. */
231 if (!(u64Delta >> 63))
232 ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev, u64, u64Prev);
233 Log(("TMVirtualGetSync: u64Delta=%VRU64\n", u64Delta));
234 }
235 }
236
237 /*
238 * Complete the calculation of the current TMCLOCK_VIRTUAL_SYNC time.
239 * The current approach will not let us pass any expired timer.
240 */
241 u64 -= u64Offset;
242 if (pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire <= u64)
243 {
244 if (!VM_FF_ISSET(pVM, VM_FF_TIMER))
245 {
246 VM_FF_SET(pVM, VM_FF_TIMER);
247#ifdef IN_RING3
248 REMR3NotifyTimerPending(pVM);
249 VMR3NotifyFF(pVM, true);
250#endif
251 }
252 const uint64_t u64Expire = pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire;
253 if (u64Expire < u64)
254 u64 = u64Expire;
255 }
256 }
257 else
258 u64 = pVM->tm.s.u64VirtualSync;
259 return u64;
260}
261
262
263/**
264 * Gets the current TMCLOCK_VIRTUAL frequency.
265 *
266 * @returns The freqency.
267 * @param pVM VM handle.
268 */
269TMDECL(uint64_t) TMVirtualGetFreq(PVM pVM)
270{
271 return TMCLOCK_FREQ_VIRTUAL;
272}
273
274
275//#define TM_CONTINUOUS_TIME
276
277/**
278 * Resumes the virtual clock.
279 *
280 * @returns VINF_SUCCESS on success.
281 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
282 * @param pVM VM handle.
283 */
284TMDECL(int) TMVirtualResume(PVM pVM)
285{
286 if (!pVM->tm.s.fVirtualTicking)
287 {
288 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualResume);
289 pVM->tm.s.u64VirtualWarpDriveStart = RTTimeNanoTS();
290 pVM->tm.s.u64VirtualOffset = pVM->tm.s.u64VirtualWarpDriveStart - pVM->tm.s.u64Virtual;
291 pVM->tm.s.fVirtualTicking = true;
292 pVM->tm.s.fVirtualSyncTicking = true;
293 return VINF_SUCCESS;
294 }
295
296#ifndef TM_CONTINUOUS_TIME
297 AssertFailed();
298 return VERR_INTERNAL_ERROR;
299#else
300 return VINF_SUCCESS;
301#endif
302}
303
304
305/**
306 * Pauses the virtual clock.
307 *
308 * @returns VINF_SUCCESS on success.
309 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
310 * @param pVM VM handle.
311 */
312TMDECL(int) TMVirtualPause(PVM pVM)
313{
314 if (pVM->tm.s.fVirtualTicking)
315 {
316#ifndef TM_CONTINUOUS_TIME
317 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualPause);
318 pVM->tm.s.u64Virtual = tmVirtualGetRaw(pVM);
319 pVM->tm.s.fVirtualSyncTicking = false;
320 pVM->tm.s.fVirtualTicking = false;
321#endif
322 return VINF_SUCCESS;
323 }
324
325 AssertFailed();
326 return VERR_INTERNAL_ERROR;
327}
328
329
330/**
331 * Gets the current warp drive percent.
332 *
333 * @returns The warp drive percent.
334 * @param pVM The VM handle.
335 */
336TMDECL(uint32_t) TMVirtualGetWarpDrive(PVM pVM)
337{
338 return pVM->tm.s.u32VirtualWarpDrivePercentage;
339}
340
341
342/**
343 * Sets the warp drive percent of the virtual time.
344 *
345 * @returns VBox status code.
346 * @param pVM The VM handle.
347 * @param u32Percent The new percentage. 100 means normal operation.
348 */
349TMDECL(int) TMVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
350{
351#ifdef IN_RING3
352 PVMREQ pReq;
353 int rc = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT, (PFNRT)tmVirtualSetWarpDrive, 2, pVM, u32Percent);
354 if (VBOX_SUCCESS(rc))
355 rc = pReq->iStatus;
356 VMR3ReqFree(pReq);
357 return rc;
358#else
359
360 return tmVirtualSetWarpDrive(pVM, u32Percent);
361#endif
362}
363
364
365/**
366 * EMT worker for tmVirtualSetWarpDrive.
367 *
368 * @returns VBox status code.
369 * @param pVM The VM handle.
370 * @param u32Percent See TMVirtualSetWarpDrive().
371 * @internal
372 */
373static DECLCALLBACK(int) tmVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
374{
375 /*
376 * Validate it.
377 */
378 AssertMsgReturn(u32Percent >= 2 && u32Percent <= 20000,
379 ("%RX32 is not between 2 and 20000 (inclusive).\n", u32Percent),
380 VERR_INVALID_PARAMETER);
381
382 /*
383 * If the time is running we'll have to pause it before we can change
384 * the warp drive settings.
385 */
386 bool fPaused = pVM->tm.s.fVirtualTicking;
387 if (fPaused)
388 {
389 int rc = TMVirtualPause(pVM);
390 AssertRCReturn(rc, rc);
391 rc = TMCpuTickPause(pVM);
392 AssertRCReturn(rc, rc);
393 }
394
395 pVM->tm.s.u32VirtualWarpDrivePercentage = u32Percent;
396 pVM->tm.s.fVirtualWarpDrive = u32Percent != 100;
397 LogRel(("TM: u32VirtualWarpDrivePercentage=%RI32 fVirtualWarpDrive=%RTbool\n",
398 pVM->tm.s.u32VirtualWarpDrivePercentage, pVM->tm.s.fVirtualWarpDrive));
399
400 if (fPaused)
401 {
402 int rc = TMVirtualResume(pVM);
403 AssertRCReturn(rc, rc);
404 rc = TMCpuTickResume(pVM);
405 AssertRCReturn(rc, rc);
406 }
407
408 return VINF_SUCCESS;
409}
410
411
412/**
413 * Converts from virtual ticks to nanoseconds.
414 *
415 * @returns nanoseconds.
416 * @param pVM The VM handle.
417 * @param u64VirtualTicks The virtual ticks to convert.
418 * @remark There could be rounding errors here. We just do a simple integere divide
419 * without any adjustments.
420 */
421TMDECL(uint64_t) TMVirtualToNano(PVM pVM, uint64_t u64VirtualTicks)
422{
423 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
424 return u64VirtualTicks;
425}
426
427
428/**
429 * Converts from virtual ticks to microseconds.
430 *
431 * @returns microseconds.
432 * @param pVM The VM handle.
433 * @param u64VirtualTicks The virtual ticks to convert.
434 * @remark There could be rounding errors here. We just do a simple integere divide
435 * without any adjustments.
436 */
437TMDECL(uint64_t) TMVirtualToMicro(PVM pVM, uint64_t u64VirtualTicks)
438{
439 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
440 return u64VirtualTicks / 1000;
441}
442
443
444/**
445 * Converts from virtual ticks to milliseconds.
446 *
447 * @returns milliseconds.
448 * @param pVM The VM handle.
449 * @param u64VirtualTicks The virtual ticks to convert.
450 * @remark There could be rounding errors here. We just do a simple integere divide
451 * without any adjustments.
452 */
453TMDECL(uint64_t) TMVirtualToMilli(PVM pVM, uint64_t u64VirtualTicks)
454{
455 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
456 return u64VirtualTicks / 1000000;
457}
458
459
460/**
461 * Converts from nanoseconds to virtual ticks.
462 *
463 * @returns virtual ticks.
464 * @param pVM The VM handle.
465 * @param u64NanoTS The nanosecond value ticks to convert.
466 * @remark There could be rounding and overflow errors here.
467 */
468TMDECL(uint64_t) TMVirtualFromNano(PVM pVM, uint64_t u64NanoTS)
469{
470 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
471 return u64NanoTS;
472}
473
474
475/**
476 * Converts from microseconds to virtual ticks.
477 *
478 * @returns virtual ticks.
479 * @param pVM The VM handle.
480 * @param u64MicroTS The microsecond value ticks to convert.
481 * @remark There could be rounding and overflow errors here.
482 */
483TMDECL(uint64_t) TMVirtualFromMicro(PVM pVM, uint64_t u64MicroTS)
484{
485 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
486 return u64MicroTS * 1000;
487}
488
489
490/**
491 * Converts from milliseconds to virtual ticks.
492 *
493 * @returns virtual ticks.
494 * @param pVM The VM handle.
495 * @param u64MilliTS The millisecond value ticks to convert.
496 * @remark There could be rounding and overflow errors here.
497 */
498TMDECL(uint64_t) TMVirtualFromMilli(PVM pVM, uint64_t u64MilliTS)
499{
500 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
501 return u64MilliTS * 1000000;
502}
503
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