VirtualBox

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

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

UCTOffset (ns).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.7 KB
Line 
1/* $Id: TMAllVirtual.cpp 2611 2007-05-14 04:58:51Z 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# include <iprt/thread.h>
31#endif
32#include "TMInternal.h"
33#include <VBox/vm.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
42
43/*******************************************************************************
44* Internal Functions *
45*******************************************************************************/
46static DECLCALLBACK(int) tmVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent);
47
48
49
50/**
51 * Get the time when we're not running at 100%
52 *
53 * @returns The timestamp.
54 * @param pVM The VM handle.
55 */
56static uint64_t tmVirtualGetRawNonNormal(PVM pVM)
57{
58 /*
59 * Recalculate the RTTimeNanoTS() value for the period where
60 * warp drive has been enabled.
61 */
62 uint64_t u64 = RTTimeNanoTS();
63 u64 -= pVM->tm.s.u64VirtualWarpDriveStart;
64 u64 *= pVM->tm.s.u32VirtualWarpDrivePercentage;
65 u64 /= 100;
66 u64 += pVM->tm.s.u64VirtualWarpDriveStart;
67
68 /*
69 * Now we apply the virtual time offset.
70 * (Which is the negate RTTimeNanoTS() value for when the virtual machine
71 * started if it had been running continuously without any suspends.)
72 */
73 u64 -= pVM->tm.s.u64VirtualOffset;
74 return u64;
75}
76
77
78/**
79 * Get the raw virtual time.
80 *
81 * @returns The current time stamp.
82 * @param pVM The VM handle.
83 */
84DECLINLINE(uint64_t) tmVirtualGetRaw(PVM pVM)
85{
86 if (RT_LIKELY(!pVM->tm.s.fVirtualWarpDrive))
87 return RTTimeNanoTS() - pVM->tm.s.u64VirtualOffset;
88 return tmVirtualGetRawNonNormal(pVM);
89}
90
91
92/**
93 * Inlined version of tmVirtualGetEx.
94 */
95DECLINLINE(uint64_t) tmVirtualGet(PVM pVM, bool fCheckTimers)
96{
97 uint64_t u64;
98 if (RT_LIKELY(pVM->tm.s.fVirtualTicking))
99 {
100 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGet);
101 u64 = tmVirtualGetRaw(pVM);
102
103 /*
104 * Use the chance to check for expired timers.
105 */
106 if ( fCheckTimers
107 && !VM_FF_ISSET(pVM, VM_FF_TIMER)
108 && ( pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64
109 || ( pVM->tm.s.fVirtualSyncTicking
110 && pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire <= u64 - pVM->tm.s.offVirtualSync
111 )
112 )
113 )
114 {
115 VM_FF_SET(pVM, VM_FF_TIMER);
116 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSetFF);
117#ifdef IN_RING3
118 REMR3NotifyTimerPending(pVM);
119 VMR3NotifyFF(pVM, true);
120#endif
121 }
122 }
123 else
124 u64 = pVM->tm.s.u64Virtual;
125 return u64;
126}
127
128
129/**
130 * Gets the current TMCLOCK_VIRTUAL time
131 *
132 * @returns The timestamp.
133 * @param pVM VM handle.
134 *
135 * @remark While the flow of time will never go backwards, the speed of the
136 * progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be
137 * influenced by power saving (SpeedStep, PowerNow!), while the former
138 * makes use of TSC and kernel timers.
139 */
140TMDECL(uint64_t) TMVirtualGet(PVM pVM)
141{
142 return TMVirtualGetEx(pVM, true /* check timers */);
143}
144
145
146/**
147 * Gets the current TMCLOCK_VIRTUAL time
148 *
149 * @returns The timestamp.
150 * @param pVM VM handle.
151 * @param fCheckTimers Check timers or not
152 *
153 * @remark While the flow of time will never go backwards, the speed of the
154 * progress varies due to inaccurate RTTimeNanoTS and TSC. The latter can be
155 * influenced by power saving (SpeedStep, PowerNow!), while the former
156 * makes use of TSC and kernel timers.
157 */
158TMDECL(uint64_t) TMVirtualGetEx(PVM pVM, bool fCheckTimers)
159{
160 return tmVirtualGet(pVM, fCheckTimers);
161}
162
163
164/**
165 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
166 *
167 * @returns The timestamp.
168 * @param pVM VM handle.
169 * @param fCheckTimers Check timers or not
170 * @thread EMT.
171 */
172TMDECL(uint64_t) TMVirtualSyncGetEx(PVM pVM, bool fCheckTimers)
173{
174 VM_ASSERT_EMT(pVM);
175
176 uint64_t u64;
177 if (pVM->tm.s.fVirtualSyncTicking)
178 {
179 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSync);
180
181 /*
182 * Query the virtual clock and do the usual expired timer check.
183 */
184 Assert(pVM->tm.s.fVirtualTicking);
185 u64 = tmVirtualGetRaw(pVM);
186 if ( fCheckTimers
187 && !VM_FF_ISSET(pVM, VM_FF_TIMER)
188 && pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL].u64Expire <= u64)
189 {
190 VM_FF_SET(pVM, VM_FF_TIMER);
191#ifdef IN_RING3
192 REMR3NotifyTimerPending(pVM);
193 VMR3NotifyFF(pVM, true);
194#endif
195 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSyncSetFF);
196 }
197
198 /*
199 * Read the offset and adjust if we're playing catch-up.
200 *
201 * The catch-up adjusting work by us decrementing the offset by a percentage of
202 * the time elapsed since the previous TMVirtualGetSync call.
203 *
204 * It's possible to get a very long or even negative interval between two read
205 * for the following reasons:
206 * - Someone might have suspended the process execution, frequently the case when
207 * debugging the process.
208 * - We might be on a different CPU which TSC isn't quite in sync with the
209 * other CPUs in the system.
210 * - RTTimeNanoTS() is returning sligtly different values in GC, R0 and R3 because
211 * of the static variable it uses with the previous read time.
212 * - Another thread is racing us and we might have been preemnted while inside
213 * this function.
214 *
215 * Assuming nano second virtual time, we can simply ignore any intervals which has
216 * any of the upper 32 bits set.
217 */
218 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
219 uint64_t off = pVM->tm.s.offVirtualSync;
220 if (pVM->tm.s.fVirtualSyncCatchUp)
221 {
222 const uint64_t u64Prev = pVM->tm.s.u64VirtualSyncCatchUpPrev;
223 uint64_t u64Delta = u64 - u64Prev;
224 if (RT_LIKELY(!(u64Delta >> 32)))
225 {
226 uint64_t u64Sub = ASMMultU64ByU32DivByU32(u64Delta, pVM->tm.s.u32VirtualSyncCatchUpPercentage, 100);
227 if (off > u64Sub + pVM->tm.s.offVirtualSyncGivenUp)
228 {
229 off -= u64Sub;
230 ASMAtomicXchgU64(&pVM->tm.s.offVirtualSync, off);
231 pVM->tm.s.u64VirtualSyncCatchUpPrev = u64;
232 Log4(("TM: %RU64/%RU64: sub %RU32\n", u64 - off, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp, u64Sub));
233 }
234 else
235 {
236 /* we've completely caught up. */
237 STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatVirtualSyncCatchup, c);
238 off = pVM->tm.s.offVirtualSyncGivenUp;
239 ASMAtomicXchgU64(&pVM->tm.s.offVirtualSync, off);
240 ASMAtomicXchgBool(&pVM->tm.s.fVirtualSyncCatchUp, false);
241 pVM->tm.s.u64VirtualSyncCatchUpPrev = u64;
242 Log4(("TM: %RU64/0: caught up\n", u64));
243 }
244 }
245 else
246 {
247 /* More than 4 seconds since last time (or negative), ignore it. */
248 if (!(u64Delta & RT_BIT_64(63)))
249 pVM->tm.s.u64VirtualSyncCatchUpPrev = u64;
250 Log(("TMVirtualGetSync: u64Delta=%RX64\n", u64Delta));
251 }
252 }
253
254 /*
255 * Complete the calculation of the current TMCLOCK_VIRTUAL_SYNC time. The current
256 * approach is to never pass the head timer. So, when we do stop the clock and
257 * set the the timer pending flag.
258 */
259 u64 -= off;
260 const uint64_t u64Expire = pVM->tm.s.CTXALLSUFF(paTimerQueues)[TMCLOCK_VIRTUAL_SYNC].u64Expire;
261 if (u64 >= u64Expire)
262 {
263 u64 = u64Expire;
264 ASMAtomicXchgU64(&pVM->tm.s.u64VirtualSync, u64);
265 ASMAtomicXchgBool(&pVM->tm.s.fVirtualSyncTicking, false);
266 if ( fCheckTimers
267 && !VM_FF_ISSET(pVM, VM_FF_TIMER))
268 {
269 VM_FF_SET(pVM, VM_FF_TIMER);
270#ifdef IN_RING3
271 REMR3NotifyTimerPending(pVM);
272 VMR3NotifyFF(pVM, true);
273#endif
274 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualGetSyncSetFF);
275 Log4(("TM: %RU64/%RU64: exp tmr=>ff\n", u64, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp));
276 }
277 else
278 Log4(("TM: %RU64/%RU64: exp tmr\n", u64, pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp));
279 }
280 }
281 else
282 u64 = pVM->tm.s.u64VirtualSync;
283 return u64;
284}
285
286
287/**
288 * Gets the current TMCLOCK_VIRTUAL_SYNC time.
289 *
290 * @returns The timestamp.
291 * @param pVM VM handle.
292 * @thread EMT.
293 */
294TMDECL(uint64_t) TMVirtualSyncGet(PVM pVM)
295{
296 return TMVirtualSyncGetEx(pVM, true /* check timers */);
297}
298
299
300/**
301 * Gets the current lag of the synchronous virtual clock (relative to the virtual clock).
302 *
303 * @return The current lag.
304 * @param pVM VM handle.
305 */
306TMDECL(uint64_t) TMVirtualSyncGetLag(PVM pVM)
307{
308 return pVM->tm.s.offVirtualSync - pVM->tm.s.offVirtualSyncGivenUp;
309}
310
311
312/**
313 * Get the current catch-up percent.
314 *
315 * @return The current catch0up percent. 0 means running at the same speed as the virtual clock.
316 * @param pVM VM handle.
317 */
318TMDECL(uint32_t) TMVirtualSyncGetCatchUpPct(PVM pVM)
319{
320 if (pVM->tm.s.fVirtualSyncCatchUp)
321 return pVM->tm.s.u32VirtualSyncCatchUpPercentage;
322 return 0;
323}
324
325
326/**
327 * Gets the current TMCLOCK_VIRTUAL frequency.
328 *
329 * @returns The freqency.
330 * @param pVM VM handle.
331 */
332TMDECL(uint64_t) TMVirtualGetFreq(PVM pVM)
333{
334 return TMCLOCK_FREQ_VIRTUAL;
335}
336
337
338/**
339 * Resumes the virtual clock.
340 *
341 * @returns VINF_SUCCESS on success.
342 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
343 * @param pVM VM handle.
344 */
345TMDECL(int) TMVirtualResume(PVM pVM)
346{
347 if (!pVM->tm.s.fVirtualTicking)
348 {
349 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualResume);
350 pVM->tm.s.u64VirtualWarpDriveStart = RTTimeNanoTS();
351 pVM->tm.s.u64VirtualOffset = pVM->tm.s.u64VirtualWarpDriveStart - pVM->tm.s.u64Virtual;
352 pVM->tm.s.fVirtualTicking = true;
353 pVM->tm.s.fVirtualSyncTicking = true;
354 return VINF_SUCCESS;
355 }
356
357 AssertFailed();
358 return VERR_INTERNAL_ERROR;
359}
360
361
362/**
363 * Pauses the virtual clock.
364 *
365 * @returns VINF_SUCCESS on success.
366 * @returns VINF_INTERNAL_ERROR and VBOX_STRICT assertion if called out of order.
367 * @param pVM VM handle.
368 */
369TMDECL(int) TMVirtualPause(PVM pVM)
370{
371 if (pVM->tm.s.fVirtualTicking)
372 {
373 STAM_COUNTER_INC(&pVM->tm.s.StatVirtualPause);
374 pVM->tm.s.u64Virtual = tmVirtualGetRaw(pVM);
375 pVM->tm.s.fVirtualSyncTicking = false;
376 pVM->tm.s.fVirtualTicking = false;
377 return VINF_SUCCESS;
378 }
379
380 AssertFailed();
381 return VERR_INTERNAL_ERROR;
382}
383
384
385/**
386 * Gets the current warp drive percent.
387 *
388 * @returns The warp drive percent.
389 * @param pVM The VM handle.
390 */
391TMDECL(uint32_t) TMVirtualGetWarpDrive(PVM pVM)
392{
393 return pVM->tm.s.u32VirtualWarpDrivePercentage;
394}
395
396
397/**
398 * Sets the warp drive percent of the virtual time.
399 *
400 * @returns VBox status code.
401 * @param pVM The VM handle.
402 * @param u32Percent The new percentage. 100 means normal operation.
403 */
404TMDECL(int) TMVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
405{
406/** @todo This isn't a feature specific to virtual time, move to TM level. (It
407 * should affect the TMR3UCTNow as well! */
408#ifdef IN_RING3
409 PVMREQ pReq;
410 int rc = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT, (PFNRT)tmVirtualSetWarpDrive, 2, pVM, u32Percent);
411 if (VBOX_SUCCESS(rc))
412 rc = pReq->iStatus;
413 VMR3ReqFree(pReq);
414 return rc;
415#else
416
417 return tmVirtualSetWarpDrive(pVM, u32Percent);
418#endif
419}
420
421
422/**
423 * EMT worker for tmVirtualSetWarpDrive.
424 *
425 * @returns VBox status code.
426 * @param pVM The VM handle.
427 * @param u32Percent See TMVirtualSetWarpDrive().
428 * @internal
429 */
430static DECLCALLBACK(int) tmVirtualSetWarpDrive(PVM pVM, uint32_t u32Percent)
431{
432 /*
433 * Validate it.
434 */
435 AssertMsgReturn(u32Percent >= 2 && u32Percent <= 20000,
436 ("%RX32 is not between 2 and 20000 (inclusive).\n", u32Percent),
437 VERR_INVALID_PARAMETER);
438
439 /*
440 * If the time is running we'll have to pause it before we can change
441 * the warp drive settings.
442 */
443 bool fPaused = pVM->tm.s.fVirtualTicking;
444 if (fPaused)
445 {
446 int rc = TMVirtualPause(pVM);
447 AssertRCReturn(rc, rc);
448 rc = TMCpuTickPause(pVM);
449 AssertRCReturn(rc, rc);
450 }
451
452 pVM->tm.s.u32VirtualWarpDrivePercentage = u32Percent;
453 pVM->tm.s.fVirtualWarpDrive = u32Percent != 100;
454 LogRel(("TM: u32VirtualWarpDrivePercentage=%RI32 fVirtualWarpDrive=%RTbool\n",
455 pVM->tm.s.u32VirtualWarpDrivePercentage, pVM->tm.s.fVirtualWarpDrive));
456
457 if (fPaused)
458 {
459 int rc = TMVirtualResume(pVM);
460 AssertRCReturn(rc, rc);
461 rc = TMCpuTickResume(pVM);
462 AssertRCReturn(rc, rc);
463 }
464
465 return VINF_SUCCESS;
466}
467
468
469/**
470 * Converts from virtual ticks to nanoseconds.
471 *
472 * @returns nanoseconds.
473 * @param pVM The VM handle.
474 * @param u64VirtualTicks The virtual ticks to convert.
475 * @remark There could be rounding errors here. We just do a simple integere divide
476 * without any adjustments.
477 */
478TMDECL(uint64_t) TMVirtualToNano(PVM pVM, uint64_t u64VirtualTicks)
479{
480 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
481 return u64VirtualTicks;
482}
483
484
485/**
486 * Converts from virtual ticks to microseconds.
487 *
488 * @returns microseconds.
489 * @param pVM The VM handle.
490 * @param u64VirtualTicks The virtual ticks to convert.
491 * @remark There could be rounding errors here. We just do a simple integere divide
492 * without any adjustments.
493 */
494TMDECL(uint64_t) TMVirtualToMicro(PVM pVM, uint64_t u64VirtualTicks)
495{
496 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
497 return u64VirtualTicks / 1000;
498}
499
500
501/**
502 * Converts from virtual ticks to milliseconds.
503 *
504 * @returns milliseconds.
505 * @param pVM The VM handle.
506 * @param u64VirtualTicks The virtual ticks to convert.
507 * @remark There could be rounding errors here. We just do a simple integere divide
508 * without any adjustments.
509 */
510TMDECL(uint64_t) TMVirtualToMilli(PVM pVM, uint64_t u64VirtualTicks)
511{
512 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
513 return u64VirtualTicks / 1000000;
514}
515
516
517/**
518 * Converts from nanoseconds to virtual ticks.
519 *
520 * @returns virtual ticks.
521 * @param pVM The VM handle.
522 * @param u64NanoTS The nanosecond value ticks to convert.
523 * @remark There could be rounding and overflow errors here.
524 */
525TMDECL(uint64_t) TMVirtualFromNano(PVM pVM, uint64_t u64NanoTS)
526{
527 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
528 return u64NanoTS;
529}
530
531
532/**
533 * Converts from microseconds to virtual ticks.
534 *
535 * @returns virtual ticks.
536 * @param pVM The VM handle.
537 * @param u64MicroTS The microsecond value ticks to convert.
538 * @remark There could be rounding and overflow errors here.
539 */
540TMDECL(uint64_t) TMVirtualFromMicro(PVM pVM, uint64_t u64MicroTS)
541{
542 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
543 return u64MicroTS * 1000;
544}
545
546
547/**
548 * Converts from milliseconds to virtual ticks.
549 *
550 * @returns virtual ticks.
551 * @param pVM The VM handle.
552 * @param u64MilliTS The millisecond value ticks to convert.
553 * @remark There could be rounding and overflow errors here.
554 */
555TMDECL(uint64_t) TMVirtualFromMilli(PVM pVM, uint64_t u64MilliTS)
556{
557 AssertCompile(TMCLOCK_FREQ_VIRTUAL == 1000000000);
558 return u64MilliTS * 1000000;
559}
560
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