VirtualBox

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

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

Implemented Warp drive. This can be configured using the WarpDrivePercentage (2..20000) or the TMVirtualSetWarpDrive API.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.5 KB
Line 
1/* $Id: TMAllVirtual.cpp 443 2007-01-30 21:53:52Z 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 uint64_t u64;
105 if (pVM->tm.s.fVirtualTicking)
106 {
107 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGet);
108 u64 = tmVirtualGetRaw(pVM);
109
110 /*
111 * Use the chance to check for expired timers.
112 */
113 if ( !VM_FF_ISSET(pVM, VM_FF_TIMER)
114 && ( pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64
115 || ( pVM->tm.s.fVirtualSyncTicking
116 && pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire <= u64 - pVM->tm.s.u64VirtualSyncOffset
117 )
118 )
119 )
120 {
121 VM_FF_SET(pVM, VM_FF_TIMER);
122#ifdef IN_RING3
123 REMR3NotifyTimerPending(pVM);
124 VMR3NotifyFF(pVM, true);
125#endif
126 }
127 }
128 else
129 u64 = pVM->tm.s.u64Virtual;
130 return u64;
131}
132
133
134/**
135 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
136 *
137 * @returns The timestamp.
138 * @param pVM VM handle.
139 */
140TMDECL(uint64_t) TMVirtualGetSync(PVM pVM)
141{
142 uint64_t u64;
143 if (pVM->tm.s.fVirtualSyncTicking)
144 {
145 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSync);
146
147 /*
148 * Do TMVirtualGet() to get the current TMCLOCK_VIRTUAL time.
149 */
150 Assert(pVM->tm.s.fVirtualTicking);
151 u64 = tmVirtualGetRaw(pVM);
152 if ( !VM_FF_ISSET(pVM, VM_FF_TIMER)
153 && pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64)
154 {
155 VM_FF_SET(pVM, VM_FF_TIMER);
156#ifdef IN_RING3
157 REMR3NotifyTimerPending(pVM);
158 VMR3NotifyFF(pVM, true);
159#endif
160 }
161
162 /*
163 * Read the offset and adjust if we're playing catch-up.
164 *
165 * The catch-up adjusting work by us decrementing the offset by a percentage of
166 * the time elapsed since the previous TMVritualGetSync call. We take some simple
167 * precautions against racing other threads here, but assume that this isn't going
168 * to be much of a problem since calls to this function is unlikely from threads
169 * other than the EMT.
170 *
171 * It's possible to get a very long or even negative interval between two read
172 * for the following reasons:
173 * - Someone might have suspended the process execution, frequently the case when
174 * debugging the process.
175 * - We might be on a different CPU which TSC isn't quite in sync with the
176 * other CPUs in the system.
177 * - RTTimeNanoTS() is returning sligtly different values in GC, R0 and R3 because
178 * of the static variable it uses with the previous read time.
179 * - Another thread is racing us and we might have been preemnted while inside
180 * this function.
181 *
182 * Assuming nano second virtual time, we can simply ignore any intervals which has
183 * any of the upper 32 bits set. This will have the nice sideeffect of allowing us
184 * to use (faster) 32-bit math.
185 */
186 AssertCompile(TMCLOCK_FREQ_VIRTUAL <= 2000000000); /* (assumes low 32-bit >= 2 seconds) */
187 uint64_t u64Offset = pVM->tm.s.u64VirtualSyncOffset;
188 if (pVM->tm.s.fVirtualSyncCatchUp)
189 {
190 const uint64_t u64Prev = pVM->tm.s.u64VirtualSyncCatchUpPrev;
191 uint64_t u64Delta = u64 - u64Prev;
192 if (!(u64Delta >> 32))
193 {
194 uint32_t u32Sub = ASMDivU64ByU32RetU32(ASMMult2xU32RetU64((uint32_t)u64Delta, pVM->tm.s.u32VirtualSyncCatchupPercentage),
195 100);
196 if (u32Sub < (uint32_t)u64Delta)
197 {
198 const uint64_t u64NewOffset = u64Offset - u32Sub;
199 if (ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev, u64, u64Prev))
200 ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncOffset, u64NewOffset, u64Offset);
201 u64Offset = u64NewOffset;
202 }
203 else
204 {
205 /* we've completely caught up. */
206 if ( ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev, u64, u64Prev)
207 && ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncOffset, 0, u64Offset))
208 ASMAtomicXchgSize(&pVM->tm.s.fVirtualSyncCatchUp, false);
209 }
210 }
211 else
212 {
213 /* Update the previous TMVirtualGetSync time it's not a negative delta. */
214 if (!(u64Delta >> 63))
215 ASMAtomicCmpXchgU64(&pVM->tm.s.u64VirtualSyncCatchUpPrev, u64, u64Prev);
216 Log(("TMVirtualGetSync: u64Delta=%VRU64\n", u64Delta));
217 }
218 }
219
220 /*
221 * Complete the calculation of the current TMCLOCK_VIRTUAL_SYNC time.
222 * The current approach will not let us pass any expired timer.
223 */
224 u64 -= u64Offset;
225 if (pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire <= u64)
226 {
227 if (!VM_FF_ISSET(pVM, VM_FF_TIMER))
228 {
229 VM_FF_SET(pVM, VM_FF_TIMER);
230#ifdef IN_RING3
231 REMR3NotifyTimerPending(pVM);
232 VMR3NotifyFF(pVM, true);
233#endif
234 }
235 const uint64_t u64Expire = pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire;
236 if (u64Expire < u64)
237 u64 = u64Expire;
238 }
239 }
240 else
241 u64 = pVM->tm.s.u64VirtualSync;
242 return u64;
243}
244
245
246/**
247 * Gets the current TMCLOCK_VIRTUAL frequency.
248 *
249 * @returns The freqency.
250 * @param pVM VM handle.
251 */
252TMDECL(uint64_t) TMVirtualGetFreq(PVM pVM)
253{
254 return TMCLOCK_FREQ_VIRTUAL;
255}
256
257
258//#define TM_CONTINUOUS_TIME
259
260/**
261 * Resumes the virtual clock.
262 *
263 * @returns VINF_SUCCESS on success.
264 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
265 * @param pVM VM handle.
266 */
267TMDECL(int) TMVirtualResume(PVM pVM)
268{
269 if (!pVM->tm.s.fVirtualTicking)
270 {
271 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualResume);
272 pVM->tm.s.u64VirtualWarpDriveStart = RTTimeNanoTS();
273 pVM->tm.s.u64VirtualOffset = pVM->tm.s.u64VirtualWarpDriveStart - pVM->tm.s.u64Virtual;
274 pVM->tm.s.fVirtualTicking = true;
275 pVM->tm.s.fVirtualSyncTicking = true;
276 return VINF_SUCCESS;
277 }
278
279#ifndef TM_CONTINUOUS_TIME
280 AssertFailed();
281 return VERR_INTERNAL_ERROR;
282#else
283 return VINF_SUCCESS;
284#endif
285}
286
287
288/**
289 * Pauses the virtual clock.
290 *
291 * @returns VINF_SUCCESS on success.
292 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
293 * @param pVM VM handle.
294 */
295TMDECL(int) TMVirtualPause(PVM pVM)
296{
297 if (pVM->tm.s.fVirtualTicking)
298 {
299#ifndef TM_CONTINUOUS_TIME
300 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualPause);
301 pVM->tm.s.u64Virtual = tmVirtualGetRaw(pVM);
302 pVM->tm.s.fVirtualSyncTicking = false;
303 pVM->tm.s.fVirtualTicking = false;
304#endif
305 return VINF_SUCCESS;
306 }
307
308 AssertFailed();
309 return VERR_INTERNAL_ERROR;
310}
311
312
313/**
314 * Gets the current warp drive percent.
315 *
316 * @returns The warp drive percent.
317 * @param pVM The VM handle.
318 */
319TMDECL(uint32_t) TMVirtualGetWarpDrive(PVM pVM)
320{
321 return pVM->tm.s.u32VirtualWarpDrivePercentage;
322}
323
324
325/**
326 * Sets the warp drive percent of the virtual time.
327 *
328 * @returns VBox status code.
329 * @param pVM The VM handle.
330 * @param u32Percent The new percentage. 100 means normal operation.
331 */
332TMDECL(int) TMVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
333{
334#ifdef IN_RING3
335 PVMREQ pReq;
336 int rc = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT, (PFNRT)tmVirtualSetWarpDrive, 2, pVM, u32Percent);
337 if (VBOX_SUCCESS(rc))
338 rc = pReq->iStatus;
339 VMR3ReqFree(pReq);
340 return rc;
341#else
342
343 return tmVirtualSetWarpDrive(pVM, u32Percent);
344#endif
345}
346
347
348/**
349 * EMT worker for tmVirtualSetWarpDrive.
350 *
351 * @returns VBox status code.
352 * @param pVM The VM handle.
353 * @param u32Percent See TMVirtualSetWarpDrive().
354 * @internal
355 */
356static DECLCALLBACK(int) tmVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
357{
358 /*
359 * Validate it.
360 */
361 AssertMsgReturn(u32Percent >= 2 && u32Percent <= 20000,
362 ("%RX32 is not between 2 and 20000 (inclusive).\n", u32Percent),
363 VERR_INVALID_PARAMETER);
364
365 /*
366 * If the time is running we'll have to pause it before we can change
367 * the warp drive settings.
368 */
369 bool fPaused = pVM->tm.s.fVirtualTicking;
370 if (fPaused)
371 {
372 int rc = TMVirtualPause(pVM);
373 AssertRCReturn(rc, rc);
374 }
375
376 pVM->tm.s.u32VirtualWarpDrivePercentage = u32Percent;
377 pVM->tm.s.fVirtualWarpDrive = u32Percent != 100;
378 LogRel(("TM: u32VirtualWarpDrivePercentage=%RI32 fVirtualWarpDrive=%RTbool\n",
379 pVM->tm.s.u32VirtualWarpDrivePercentage, pVM->tm.s.fVirtualWarpDrive));
380
381 if (fPaused)
382 {
383 int rc = TMVirtualResume(pVM);
384 AssertRCReturn(rc, rc);
385 }
386
387 return VINF_SUCCESS;
388}
389
390
391/**
392 * Converts from virtual ticks to nanoseconds.
393 *
394 * @returns nanoseconds.
395 * @param pVM The VM handle.
396 * @param u64VirtualTicks The virtual ticks to convert.
397 * @remark There could be rounding errors here. We just do a simple integere divide
398 * without any adjustments.
399 */
400TMDECL(uint64_t) TMVirtualToNano(PVM pVM, uint64_t u64VirtualTicks)
401{
402 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
403 return u64VirtualTicks;
404}
405
406
407/**
408 * Converts from virtual ticks to microseconds.
409 *
410 * @returns microseconds.
411 * @param pVM The VM handle.
412 * @param u64VirtualTicks The virtual ticks to convert.
413 * @remark There could be rounding errors here. We just do a simple integere divide
414 * without any adjustments.
415 */
416TMDECL(uint64_t) TMVirtualToMicro(PVM pVM, uint64_t u64VirtualTicks)
417{
418 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
419 return u64VirtualTicks / 1000;
420}
421
422
423/**
424 * Converts from virtual ticks to milliseconds.
425 *
426 * @returns milliseconds.
427 * @param pVM The VM handle.
428 * @param u64VirtualTicks The virtual ticks to convert.
429 * @remark There could be rounding errors here. We just do a simple integere divide
430 * without any adjustments.
431 */
432TMDECL(uint64_t) TMVirtualToMilli(PVM pVM, uint64_t u64VirtualTicks)
433{
434 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
435 return u64VirtualTicks / 1000000;
436}
437
438
439/**
440 * Converts from nanoseconds to virtual ticks.
441 *
442 * @returns virtual ticks.
443 * @param pVM The VM handle.
444 * @param u64NanoTS The nanosecond value ticks to convert.
445 * @remark There could be rounding and overflow errors here.
446 */
447TMDECL(uint64_t) TMVirtualFromNano(PVM pVM, uint64_t u64NanoTS)
448{
449 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
450 return u64NanoTS;
451}
452
453
454/**
455 * Converts from microseconds to virtual ticks.
456 *
457 * @returns virtual ticks.
458 * @param pVM The VM handle.
459 * @param u64MicroTS The microsecond value ticks to convert.
460 * @remark There could be rounding and overflow errors here.
461 */
462TMDECL(uint64_t) TMVirtualFromMicro(PVM pVM, uint64_t u64MicroTS)
463{
464 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
465 return u64MicroTS * 1000;
466}
467
468
469/**
470 * Converts from milliseconds to virtual ticks.
471 *
472 * @returns virtual ticks.
473 * @param pVM The VM handle.
474 * @param u64MilliTS The millisecond value ticks to convert.
475 * @remark There could be rounding and overflow errors here.
476 */
477TMDECL(uint64_t) TMVirtualFromMilli(PVM pVM, uint64_t u64MilliTS)
478{
479 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
480 return u64MilliTS * 1000000;
481}
482
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