VirtualBox

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

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

Trapping and virtualizing TSC (both disabled).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.7 KB
Line 
1/* $Id: TMAllVirtual.cpp 1057 2007-02-23 20:38:37Z 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 rc = TMCpuTickPause(pVM);
375 AssertRCReturn(rc, rc);
376 }
377
378 pVM->tm.s.u32VirtualWarpDrivePercentage = u32Percent;
379 pVM->tm.s.fVirtualWarpDrive = u32Percent != 100;
380 LogRel(("TM: u32VirtualWarpDrivePercentage=%RI32 fVirtualWarpDrive=%RTbool\n",
381 pVM->tm.s.u32VirtualWarpDrivePercentage, pVM->tm.s.fVirtualWarpDrive));
382
383 if (fPaused)
384 {
385 int rc = TMVirtualResume(pVM);
386 AssertRCReturn(rc, rc);
387 rc = TMCpuTickResume(pVM);
388 AssertRCReturn(rc, rc);
389 }
390
391 return VINF_SUCCESS;
392}
393
394
395/**
396 * Converts from virtual ticks to nanoseconds.
397 *
398 * @returns nanoseconds.
399 * @param pVM The VM handle.
400 * @param u64VirtualTicks The virtual ticks to convert.
401 * @remark There could be rounding errors here. We just do a simple integere divide
402 * without any adjustments.
403 */
404TMDECL(uint64_t) TMVirtualToNano(PVM pVM, uint64_t u64VirtualTicks)
405{
406 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
407 return u64VirtualTicks;
408}
409
410
411/**
412 * Converts from virtual ticks to microseconds.
413 *
414 * @returns microseconds.
415 * @param pVM The VM handle.
416 * @param u64VirtualTicks The virtual ticks to convert.
417 * @remark There could be rounding errors here. We just do a simple integere divide
418 * without any adjustments.
419 */
420TMDECL(uint64_t) TMVirtualToMicro(PVM pVM, uint64_t u64VirtualTicks)
421{
422 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
423 return u64VirtualTicks / 1000;
424}
425
426
427/**
428 * Converts from virtual ticks to milliseconds.
429 *
430 * @returns milliseconds.
431 * @param pVM The VM handle.
432 * @param u64VirtualTicks The virtual ticks to convert.
433 * @remark There could be rounding errors here. We just do a simple integere divide
434 * without any adjustments.
435 */
436TMDECL(uint64_t) TMVirtualToMilli(PVM pVM, uint64_t u64VirtualTicks)
437{
438 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
439 return u64VirtualTicks / 1000000;
440}
441
442
443/**
444 * Converts from nanoseconds to virtual ticks.
445 *
446 * @returns virtual ticks.
447 * @param pVM The VM handle.
448 * @param u64NanoTS The nanosecond value ticks to convert.
449 * @remark There could be rounding and overflow errors here.
450 */
451TMDECL(uint64_t) TMVirtualFromNano(PVM pVM, uint64_t u64NanoTS)
452{
453 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
454 return u64NanoTS;
455}
456
457
458/**
459 * Converts from microseconds to virtual ticks.
460 *
461 * @returns virtual ticks.
462 * @param pVM The VM handle.
463 * @param u64MicroTS The microsecond value ticks to convert.
464 * @remark There could be rounding and overflow errors here.
465 */
466TMDECL(uint64_t) TMVirtualFromMicro(PVM pVM, uint64_t u64MicroTS)
467{
468 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
469 return u64MicroTS * 1000;
470}
471
472
473/**
474 * Converts from milliseconds to virtual ticks.
475 *
476 * @returns virtual ticks.
477 * @param pVM The VM handle.
478 * @param u64MilliTS The millisecond value ticks to convert.
479 * @remark There could be rounding and overflow errors here.
480 */
481TMDECL(uint64_t) TMVirtualFromMilli(PVM pVM, uint64_t u64MilliTS)
482{
483 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
484 return u64MilliTS * 1000000;
485}
486
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