VirtualBox

source: vbox/trunk/src/VBox/VMM/TM.cpp@ 1995

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

Fixed a couple of issues with virtualized TSC related to pausing and state loading. Also implemented support for TSC frequencies up to 4GHz and changed the default to the host CPU (virtualized TSC again).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 48.8 KB
Line 
1/* $Id: TM.cpp 1956 2007-04-05 15:26:03Z vboxsync $ */
2/** @file
3 * TM - Timeout Manager.
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/** @page pg_tm TM - The Time Manager
24 *
25 * The Time Manager abstracts the CPU clocks and manages timers used by VM device.
26 *
27 *
28 *
29 * @section sec_tm_timers Timers
30 *
31 * The timers supports multiple clocks. Currently there are two clocks in the
32 * TM, the host real time clock and the guest virtual clock. Each clock has it's
33 * own set of scheduling facilities which are identical but for the clock source.
34 *
35 * Take one such timer scheduling facility, or timer queue if you like. There are
36 * a few factors which makes it a bit complex. First there is the usual GC vs. HC
37 * thing. Then there is multiple threads, and then there is the fact that on Unix
38 * we might just as well take a timer signal which checks whether it's wise to
39 * schedule timers while we're scheduling them. On API level, all but the create
40 * and save APIs must be mulithreaded.
41 *
42 * The design is using a doubly linked HC list of active timers which is ordered
43 * by expire date. Updates to the list is batched in a singly linked list (linked
44 * by handle not pointer for atomically update support in both GC and HC) and
45 * will be processed by the emulation thread.
46 *
47 * For figuring out when there is need to schedule timers a high frequency
48 * asynchronous timer is employed using Host OS services. Its task is to check if
49 * there are anything batched up or if a head has expired. If this is the case
50 * a forced action is signals and the emulation thread will process this ASAP.
51 *
52 */
53
54
55
56
57/*******************************************************************************
58* Header Files *
59*******************************************************************************/
60#define LOG_GROUP LOG_GROUP_TM
61#include <VBox/tm.h>
62#include <VBox/vmm.h>
63#include <VBox/mm.h>
64#include <VBox/ssm.h>
65#include <VBox/dbgf.h>
66#include <VBox/rem.h>
67#include "TMInternal.h"
68#include <VBox/vm.h>
69
70#include <VBox/param.h>
71#include <VBox/err.h>
72
73#include <VBox/log.h>
74#include <iprt/asm.h>
75#include <iprt/assert.h>
76#include <iprt/thread.h>
77#include <iprt/time.h>
78#include <iprt/timer.h>
79#include <iprt/semaphore.h>
80#include <iprt/string.h>
81#include <iprt/env.h>
82
83
84/*******************************************************************************
85* Defined Constants And Macros *
86*******************************************************************************/
87/** The current saved state version.*/
88#define TM_SAVED_STATE_VERSION 2
89
90
91/*******************************************************************************
92* Internal Functions *
93*******************************************************************************/
94static uint64_t tmR3Calibrate(void);
95static DECLCALLBACK(int) tmR3Save(PVM pVM, PSSMHANDLE pSSM);
96static DECLCALLBACK(int) tmR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version);
97static DECLCALLBACK(void) tmR3TimerCallback(PRTTIMER pTimer, void *pvUser);
98static void tmR3TimerQueueRun(PVM pVM, PTMTIMERQUEUE pQueue);
99static DECLCALLBACK(void) tmR3TimerInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
100static DECLCALLBACK(void) tmR3TimerInfoActive(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
101static DECLCALLBACK(void) tmR3InfoClocks(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
102
103
104/**
105 * Internal function for getting the clock time.
106 *
107 * @returns clock time.
108 * @param pVM The VM handle.
109 * @param enmClock The clock.
110 */
111DECLINLINE(uint64_t) tmClock(PVM pVM, TMCLOCK enmClock)
112{
113 switch (enmClock)
114 {
115 case TMCLOCK_VIRTUAL: return TMVirtualGet(pVM);
116 case TMCLOCK_VIRTUAL_SYNC: return TMVirtualGetSync(pVM);
117 case TMCLOCK_REAL: return TMRealGet(pVM);
118 case TMCLOCK_TSC: return TMCpuTickGet(pVM);
119 default:
120 AssertMsgFailed(("enmClock=%d\n", enmClock));
121 return ~(uint64_t)0;
122 }
123}
124
125
126/**
127 * Initializes the TM.
128 *
129 * @returns VBox status code.
130 * @param pVM The VM to operate on.
131 */
132TMR3DECL(int) TMR3Init(PVM pVM)
133{
134 LogFlow(("TMR3Init:\n"));
135
136 /*
137 * Assert alignment and sizes.
138 */
139 AssertRelease(!(RT_OFFSETOF(VM, tm.s) & 31));
140 AssertRelease(sizeof(pVM->tm.s) <= sizeof(pVM->tm.padding));
141
142 /*
143 * Init the structure.
144 */
145 void *pv;
146 int rc = MMHyperAlloc(pVM, sizeof(pVM->tm.s.paTimerQueuesR3[0]) * TMCLOCK_MAX, 0, MM_TAG_TM, &pv);
147 AssertRCReturn(rc, rc);
148 pVM->tm.s.paTimerQueuesR3 = (PTMTIMERQUEUE)pv;
149
150 pVM->tm.s.offVM = RT_OFFSETOF(VM, tm.s);
151 pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL].enmClock = TMCLOCK_VIRTUAL;
152 pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL].u64Expire = INT64_MAX;
153 pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL_SYNC].enmClock = TMCLOCK_VIRTUAL_SYNC;
154 pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL_SYNC].u64Expire = INT64_MAX;
155 pVM->tm.s.paTimerQueuesR3[TMCLOCK_REAL].enmClock = TMCLOCK_REAL;
156 pVM->tm.s.paTimerQueuesR3[TMCLOCK_REAL].u64Expire = INT64_MAX;
157 pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC].enmClock = TMCLOCK_TSC;
158 pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC].u64Expire = INT64_MAX;
159
160 /*
161 * We indirectly - thru RTTimeNanoTS and RTTimeMilliTS - use the global
162 * info page (GIP) for both the virtual and the real clock. By mapping
163 * the GIP into guest context we can get just as accurate time even there.
164 * All that's required is that the g_pSUPGlobalInfoPage symbol is available
165 * to the GC Runtime.
166 */
167 pVM->tm.s.pvGIPR3 = (void *)g_pSUPGlobalInfoPage;
168 AssertMsgReturn(pVM->tm.s.pvGIPR3, ("GIP support is now required!\n"), VERR_INTERNAL_ERROR);
169 RTHCPHYS HCPhysGIP;
170 rc = SUPGipGetPhys(&HCPhysGIP);
171 AssertMsgRCReturn(rc, ("Failed to get GIP physical address!\n"), rc);
172
173 rc = MMR3HyperMapHCPhys(pVM, pVM->tm.s.pvGIPR3, HCPhysGIP, PAGE_SIZE, "GIP", &pVM->tm.s.pvGIPGC);
174 if (VBOX_FAILURE(rc))
175 {
176 AssertMsgFailed(("Failed to map GIP into GC, rc=%Vrc!\n", rc));
177 return rc;
178 }
179 LogFlow(("TMR3Init: HCPhysGIP=%RHp at %VGv\n", HCPhysGIP, pVM->tm.s.pvGIPGC));
180 MMR3HyperReserve(pVM, PAGE_SIZE, "fence", NULL);
181
182 /*
183 * Determin the TSC configuration and frequency.
184 */
185 /* mode */
186 rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "TSCVirtualized", &pVM->tm.s.fTSCVirtualized);
187 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
188#if 0 /* seems to kind of work... */
189 pVM->tm.s.fTSCVirtualized = true;
190#else
191 pVM->tm.s.fTSCVirtualized = false;
192#endif
193 else if (VBOX_FAILURE(rc))
194 return VMSetError(pVM, rc, RT_SRC_POS,
195 N_("Configuration error: Failed to querying bool value \"UseRealTSC\". (%Vrc)"), rc);
196
197 /* source */
198 rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "UseRealTSC", &pVM->tm.s.fTSCTicking);
199 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
200#if 0 /* doesn't seem to work reliably yet... xp takes several ~2 min to shutdown now. darn. */
201 pVM->tm.s.fTSCUseRealTSC = false; /* virtualize it */
202#else
203 pVM->tm.s.fTSCUseRealTSC = true; /* don't virtualize it */
204#endif
205 else if (VBOX_FAILURE(rc))
206 return VMSetError(pVM, rc, RT_SRC_POS,
207 N_("Configuration error: Failed to querying bool value \"UseRealTSC\". (%Vrc)"), rc);
208#if 1 /* temporary hack */
209 if (RTEnvExist("VBOX_TM_VIRTUALIZED_TSC"))
210 pVM->tm.s.fTSCUseRealTSC = false;
211#endif
212 if (!pVM->tm.s.fTSCUseRealTSC)
213 pVM->tm.s.fTSCVirtualized = true;
214
215 /* frequency */
216 rc = CFGMR3QueryU64(CFGMR3GetRoot(pVM), "TSCTicksPerSecond", &pVM->tm.s.cTSCTicksPerSecond);
217 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
218 {
219 pVM->tm.s.cTSCTicksPerSecond = tmR3Calibrate();
220 if ( !pVM->tm.s.fTSCUseRealTSC
221 && pVM->tm.s.cTSCTicksPerSecond >= _4G)
222 pVM->tm.s.cTSCTicksPerSecond = _4G - 1; /* (A limitation of our math code) */
223 }
224 else if (VBOX_FAILURE(rc))
225 return VMSetError(pVM, rc, RT_SRC_POS,
226 N_("Configuration error: Failed to querying uint64_t value \"TSCTicksPerSecond\". (%Vrc)"), rc);
227 else if ( pVM->tm.s.cTSCTicksPerSecond < _1M
228 || pVM->tm.s.cTSCTicksPerSecond >= _4G)
229 return VMSetError(pVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
230 N_("Configuration error: \"TSCTicksPerSecond\" = %RI64 is not in the range 1MHz..4GHz-1!"),
231 pVM->tm.s.cTSCTicksPerSecond);
232 else
233 {
234 pVM->tm.s.fTSCUseRealTSC = false;
235 pVM->tm.s.fTSCVirtualized = true;
236 }
237
238 /* setup and report */
239 if (pVM->tm.s.fTSCUseRealTSC)
240 CPUMR3SetCR4Feature(pVM, 0, ~X86_CR4_TSD);
241 else
242 CPUMR3SetCR4Feature(pVM, X86_CR4_TSD, ~X86_CR4_TSD);
243 LogRel(("TM: cTSCTicksPerSecond=%#RX64 (%RU64) fTSCVirtualized=%RTbool fTSCUseRealTSC=%RTbool\n",
244 pVM->tm.s.cTSCTicksPerSecond, pVM->tm.s.cTSCTicksPerSecond, pVM->tm.s.fTSCVirtualized, pVM->tm.s.fTSCUseRealTSC));
245
246 /*
247 * Register saved state.
248 */
249 rc = SSMR3RegisterInternal(pVM, "tm", 1, TM_SAVED_STATE_VERSION, sizeof(uint64_t) * 8,
250 NULL, tmR3Save, NULL,
251 NULL, tmR3Load, NULL);
252 if (VBOX_FAILURE(rc))
253 return rc;
254
255 /*
256 * Setup the warp drive.
257 */
258 rc = CFGMR3QueryU32(CFGMR3GetRoot(pVM), "WarpDrivePercentage", &pVM->tm.s.u32VirtualWarpDrivePercentage);
259 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
260 pVM->tm.s.u32VirtualWarpDrivePercentage = 100;
261 else if (VBOX_FAILURE(rc))
262 return VMSetError(pVM, rc, RT_SRC_POS,
263 N_("Configuration error: Failed to querying uint32_t value \"WarpDrivePercent\". (%Vrc)"), rc);
264 else if ( pVM->tm.s.u32VirtualWarpDrivePercentage < 2
265 || pVM->tm.s.u32VirtualWarpDrivePercentage > 20000)
266 return VMSetError(pVM, VERR_INVALID_PARAMETER, RT_SRC_POS,
267 N_("Configuration error: \"WarpDrivePercent\" = %RI32 is not in the range 2..20000!"),
268 pVM->tm.s.u32VirtualWarpDrivePercentage);
269 pVM->tm.s.fVirtualWarpDrive = pVM->tm.s.u32VirtualWarpDrivePercentage != 100;
270 if (pVM->tm.s.fVirtualWarpDrive)
271 LogRel(("TM: u32VirtualWarpDrivePercentage=%RI32\n", pVM->tm.s.u32VirtualWarpDrivePercentage));
272
273 /*
274 * Start the timer (guard against REM not yielding).
275 */
276 uint32_t u32Millies;
277 rc = CFGMR3QueryU32(CFGMR3GetRoot(pVM), "TimerMillies", &u32Millies);
278 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
279 u32Millies = 10;
280 else if (VBOX_FAILURE(rc))
281 return VMSetError(pVM, rc, RT_SRC_POS,
282 N_("Configuration error: Failed to query uint32_t value \"TimerMillies\", rc=%Vrc.\n"), rc);
283 rc = RTTimerCreate(&pVM->tm.s.pTimer, u32Millies, tmR3TimerCallback, pVM);
284 if (VBOX_FAILURE(rc))
285 {
286 AssertMsgFailed(("Failed to create timer, u32Millies=%d rc=%Vrc.\n", u32Millies, rc));
287 return rc;
288 }
289 Log(("TM: Created timer %p firing every %d millieseconds\n", pVM->tm.s.pTimer, u32Millies));
290 pVM->tm.s.u32TimerMillies = u32Millies;
291
292#ifdef VBOX_WITH_STATISTICS
293 /*
294 * Register statistics.
295 */
296 STAM_REG(pVM, &pVM->tm.s.StatDoQueues, STAMTYPE_PROFILE, "/TM/DoQueues", STAMUNIT_TICKS_PER_CALL, "Profiling timer TMR3TimerQueuesDo.");
297 STAM_REG(pVM, &pVM->tm.s.StatDoQueuesSchedule, STAMTYPE_PROFILE_ADV, "/TM/DoQueues/Schedule",STAMUNIT_TICKS_PER_CALL, "The scheduling part.");
298 STAM_REG(pVM, &pVM->tm.s.StatDoQueuesRun, STAMTYPE_PROFILE_ADV, "/TM/DoQueues/Run", STAMUNIT_TICKS_PER_CALL, "The run part.");
299
300 STAM_REG(pVM, &pVM->tm.s.StatPollAlreadySet, STAMTYPE_COUNTER, "/TM/PollAlreadySet", STAMUNIT_OCCURENCES, "TMTimerPoll calls where the FF was already set.");
301 STAM_REG(pVM, &pVM->tm.s.StatPollVirtual, STAMTYPE_COUNTER, "/TM/PollHitsVirtual", STAMUNIT_OCCURENCES, "The number of times TMTimerPoll found an expired TMCLOCK_VIRTUAL queue.");
302 STAM_REG(pVM, &pVM->tm.s.StatPollVirtualSync, STAMTYPE_COUNTER, "/TM/PollHitsVirtualSync",STAMUNIT_OCCURENCES, "The number of times TMTimerPoll found an expired TMCLOCK_VIRTUAL_SYNC queue.");
303 STAM_REG(pVM, &pVM->tm.s.StatPollMiss, STAMTYPE_COUNTER, "/TM/PollMiss", STAMUNIT_OCCURENCES, "TMTimerPoll calls where nothing had expired.");
304
305 STAM_REG(pVM, &pVM->tm.s.StatPostponedR3, STAMTYPE_COUNTER, "/TM/PostponedR3", STAMUNIT_OCCURENCES, "Postponed due to unschedulable state, in ring-3.");
306 STAM_REG(pVM, &pVM->tm.s.StatPostponedR0, STAMTYPE_COUNTER, "/TM/PostponedR0", STAMUNIT_OCCURENCES, "Postponed due to unschedulable state, in ring-0.");
307 STAM_REG(pVM, &pVM->tm.s.StatPostponedGC, STAMTYPE_COUNTER, "/TM/PostponedGC", STAMUNIT_OCCURENCES, "Postponed due to unschedulable state, in GC.");
308
309 STAM_REG(pVM, &pVM->tm.s.StatScheduleOneGC, STAMTYPE_PROFILE, "/TM/ScheduleOneGC", STAMUNIT_TICKS_PER_CALL, "Profiling the scheduling of one queue during a TMTimer* call in EMT.\n");
310 STAM_REG(pVM, &pVM->tm.s.StatScheduleOneR0, STAMTYPE_PROFILE, "/TM/ScheduleOneR0", STAMUNIT_TICKS_PER_CALL, "Profiling the scheduling of one queue during a TMTimer* call in EMT.\n");
311 STAM_REG(pVM, &pVM->tm.s.StatScheduleOneR3, STAMTYPE_PROFILE, "/TM/ScheduleOneR3", STAMUNIT_TICKS_PER_CALL, "Profiling the scheduling of one queue during a TMTimer* call in EMT.\n");
312 STAM_REG(pVM, &pVM->tm.s.StatScheduleSetFF, STAMTYPE_COUNTER, "/TM/ScheduleSetFF", STAMUNIT_OCCURENCES, "The number of times the timer FF was set instead of doing scheduling.");
313
314 STAM_REG(pVM, &pVM->tm.s.StatTimerSetGC, STAMTYPE_PROFILE, "/TM/TimerSetGC", STAMUNIT_TICKS_PER_CALL, "Profiling TMTimerSet calls made in GC.");
315 STAM_REG(pVM, &pVM->tm.s.StatTimerSetR0, STAMTYPE_PROFILE, "/TM/TimerSetR0", STAMUNIT_TICKS_PER_CALL, "Profiling TMTimerSet calls made in ring-0.");
316 STAM_REG(pVM, &pVM->tm.s.StatTimerSetR3, STAMTYPE_PROFILE, "/TM/TimerSetR3", STAMUNIT_TICKS_PER_CALL, "Profiling TMTimerSet calls made in ring-3.");
317
318 STAM_REG(pVM, &pVM->tm.s.StatTimerStopGC, STAMTYPE_PROFILE, "/TM/TimerStopGC", STAMUNIT_TICKS_PER_CALL, "Profiling TMTimerStop calls made in GC.");
319 STAM_REG(pVM, &pVM->tm.s.StatTimerStopR0, STAMTYPE_PROFILE, "/TM/TimerStopR0", STAMUNIT_TICKS_PER_CALL, "Profiling TMTimerStop calls made in ring-0.");
320 STAM_REG(pVM, &pVM->tm.s.StatTimerStopR3, STAMTYPE_PROFILE, "/TM/TimerStopR3", STAMUNIT_TICKS_PER_CALL, "Profiling TMTimerStop calls made in ring-3.");
321
322 STAM_REG(pVM, &pVM->tm.s.StatVirtualGet, STAMTYPE_COUNTER, "/TM/VirtualGet", STAMUNIT_OCCURENCES, "The number of times TMR3TimerGet was called when the clock was running.");
323 STAM_REG(pVM, &pVM->tm.s.StatVirtualGetSync, STAMTYPE_COUNTER, "/TM/VirtualGetSync", STAMUNIT_OCCURENCES, "The number of times TMR3TimerGetSync was called when the clock was running.");
324 STAM_REG(pVM, &pVM->tm.s.StatVirtualPause, STAMTYPE_COUNTER, "/TM/VirtualPause", STAMUNIT_OCCURENCES, "The number of times TMR3TimerPause was called.");
325 STAM_REG(pVM, &pVM->tm.s.StatVirtualResume, STAMTYPE_COUNTER, "/TM/VirtualResume", STAMUNIT_OCCURENCES, "The number of times TMR3TimerResume was called.");
326
327 STAM_REG(pVM, &pVM->tm.s.StatTimerCallbackSetFF,STAMTYPE_COUNTER, "/TM/CallbackSetFF", STAMUNIT_OCCURENCES, "The number of times the timer callback set FF.");
328#endif /* VBOX_WITH_STATISTICS */
329
330 /*
331 * Register info handlers.
332 */
333 DBGFR3InfoRegisterInternal(pVM, "timers", "Dumps all timers. No arguments.", tmR3TimerInfo);
334 DBGFR3InfoRegisterInternal(pVM, "activetimers", "Dumps active all timers. No arguments.", tmR3TimerInfoActive);
335 DBGFR3InfoRegisterInternal(pVM, "clocks", "Display the time of the various clocks.", tmR3InfoClocks);
336
337 return VINF_SUCCESS;
338}
339
340
341/**
342 * Calibrate the CPU tick.
343 *
344 * @returns Number of ticks per second.
345 */
346static uint64_t tmR3Calibrate(void)
347{
348 /*
349 * Use GIP when available present.
350 */
351 uint64_t u64Hz;
352 PCSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
353 if ( pGip
354 && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC)
355 {
356 unsigned iCpu = pGip->u32Mode != SUPGIPMODE_ASYNC_TSC ? 0 : ASMGetApicId();
357 if (iCpu >= RT_ELEMENTS(pGip->aCPUs))
358 AssertReleaseMsgFailed(("iCpu=%d - the ApicId is too high. send VBox.log and hardware specs!\n", iCpu));
359 else
360 {
361 RTThreadSleep(32); /* To preserve old behaviour and to get a good CpuHz at startup. */
362 pGip = g_pSUPGlobalInfoPage;
363 if ( pGip
364 && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC
365 && (u64Hz = pGip->aCPUs[iCpu].u64CpuHz)
366 && u64Hz != ~(uint64_t)0)
367 return u64Hz;
368 }
369 }
370
371 /* call this once first to make sure it's initialized. */
372 RTTimeNanoTS();
373
374 /*
375 * Yield the CPU to increase our chances of getting
376 * a correct value.
377 */
378 RTThreadYield(); /* Try avoid interruptions between TSC and NanoTS samplings. */
379 static const unsigned s_auSleep[5] = { 50, 30, 30, 40, 40 };
380 uint64_t au64Samples[5];
381 unsigned i;
382 for (i = 0; i < ELEMENTS(au64Samples); i++)
383 {
384 unsigned cMillies;
385 int cTries = 5;
386 uint64_t u64Start = ASMReadTSC();
387 uint64_t u64End;
388 uint64_t StartTS = RTTimeNanoTS();
389 uint64_t EndTS;
390 do
391 {
392 RTThreadSleep(s_auSleep[i]);
393 u64End = ASMReadTSC();
394 EndTS = RTTimeNanoTS();
395 cMillies = (unsigned)((EndTS - StartTS + 500000) / 1000000);
396 } while ( cMillies == 0 /* the sleep may be interrupted... */
397 || (cMillies < 20 && --cTries > 0));
398 uint64_t u64Diff = u64End - u64Start;
399
400 au64Samples[i] = (u64Diff * 1000) / cMillies;
401 AssertMsg(cTries > 0, ("cMillies=%d i=%d\n", cMillies, i));
402 }
403
404 /*
405 * Discard the highest and lowest results and calculate the average.
406 */
407 unsigned iHigh = 0;
408 unsigned iLow = 0;
409 for (i = 1; i < ELEMENTS(au64Samples); i++)
410 {
411 if (au64Samples[i] < au64Samples[iLow])
412 iLow = i;
413 if (au64Samples[i] > au64Samples[iHigh])
414 iHigh = i;
415 }
416 au64Samples[iLow] = 0;
417 au64Samples[iHigh] = 0;
418
419 u64Hz = au64Samples[0];
420 for (i = 1; i < ELEMENTS(au64Samples); i++)
421 u64Hz += au64Samples[i];
422 u64Hz /= ELEMENTS(au64Samples) - 2;
423
424 return u64Hz;
425}
426
427
428/**
429 * Applies relocations to data and code managed by this
430 * component. This function will be called at init and
431 * whenever the VMM need to relocate it self inside the GC.
432 *
433 * @param pVM The VM.
434 * @param offDelta Relocation delta relative to old location.
435 */
436TMR3DECL(void) TMR3Relocate(PVM pVM, RTGCINTPTR offDelta)
437{
438 LogFlow(("TMR3Relocate\n"));
439 pVM->tm.s.pvGIPGC = MMHyperR3ToGC(pVM, pVM->tm.s.pvGIPR3);
440 pVM->tm.s.paTimerQueuesGC = MMHyperR3ToGC(pVM, pVM->tm.s.paTimerQueuesR3);
441 pVM->tm.s.paTimerQueuesR0 = MMHyperR3ToR0(pVM, pVM->tm.s.paTimerQueuesR3);
442
443 /*
444 * Iterate the timers updating the pVMGC pointers.
445 */
446 for (PTMTIMER pTimer = pVM->tm.s.pCreated; pTimer; pTimer = pTimer->pBigNext)
447 {
448 pTimer->pVMGC = pVM->pVMGC;
449 pTimer->pVMR0 = (PVMR0)pVM->pVMHC; /// @todo pTimer->pVMR0 = pVM->pVMR0;
450 }
451}
452
453
454/**
455 * Terminates the TM.
456 *
457 * Termination means cleaning up and freeing all resources,
458 * the VM it self is at this point powered off or suspended.
459 *
460 * @returns VBox status code.
461 * @param pVM The VM to operate on.
462 */
463TMR3DECL(int) TMR3Term(PVM pVM)
464{
465 AssertMsg(pVM->tm.s.offVM, ("bad init order!\n"));
466 if (pVM->tm.s.pTimer)
467 {
468 int rc = RTTimerDestroy(pVM->tm.s.pTimer);
469 AssertRC(rc);
470 pVM->tm.s.pTimer = NULL;
471 }
472
473 return VINF_SUCCESS;
474}
475
476
477/**
478 * The VM is being reset.
479 *
480 * For the TM component this means that a rescheduling is preformed,
481 * the FF is cleared and but without running the queues. We'll have to
482 * check if this makes sense or not, but it seems like a good idea now....
483 *
484 * @param pVM VM handle.
485 */
486TMR3DECL(void) TMR3Reset(PVM pVM)
487{
488 LogFlow(("TMR3Reset:\n"));
489 VM_ASSERT_EMT(pVM);
490
491 /*
492 * Process the queues.
493 */
494 for (int i = 0; i < TMCLOCK_MAX; i++)
495 tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[i]);
496#ifdef VBOX_STRICT
497 tmTimerQueuesSanityChecks(pVM, "TMR3Reset");
498#endif
499 VM_FF_CLEAR(pVM, VM_FF_TIMER);
500}
501
502
503/**
504 * Resolve a builtin GC symbol.
505 * Called by PDM when loading or relocating GC modules.
506 *
507 * @returns VBox status
508 * @param pVM VM Handle.
509 * @param pszSymbol Symbol to resolv
510 * @param pGCPtrValue Where to store the symbol value.
511 * @remark This has to work before TMR3Relocate() is called.
512 */
513TMR3DECL(int) TMR3GetImportGC(PVM pVM, const char *pszSymbol, PRTGCPTR pGCPtrValue)
514{
515 if (!strcmp(pszSymbol, "g_pSUPGlobalInfoPage"))
516 *pGCPtrValue = MMHyperHC2GC(pVM, &pVM->tm.s.pvGIPGC);
517 //else if (..)
518 else
519 return VERR_SYMBOL_NOT_FOUND;
520 return VINF_SUCCESS;
521}
522
523
524/**
525 * Execute state save operation.
526 *
527 * @returns VBox status code.
528 * @param pVM VM Handle.
529 * @param pSSM SSM operation handle.
530 */
531static DECLCALLBACK(int) tmR3Save(PVM pVM, PSSMHANDLE pSSM)
532{
533 LogFlow(("tmR3Save:\n"));
534 Assert(!pVM->tm.s.fTSCTicking);
535 Assert(!pVM->tm.s.fVirtualTicking);
536 Assert(!pVM->tm.s.fVirtualSyncTicking);
537
538 /*
539 * Save the virtual clocks.
540 */
541 /* the virtual clock. */
542 SSMR3PutU64(pSSM, TMCLOCK_FREQ_VIRTUAL);
543 SSMR3PutU64(pSSM, pVM->tm.s.u64Virtual);
544
545 /* the virtual timer synchronous clock. */
546 SSMR3PutU64(pSSM, pVM->tm.s.u64VirtualSync);
547 SSMR3PutU64(pSSM, pVM->tm.s.u64VirtualSyncOffset);
548 SSMR3PutU64(pSSM, pVM->tm.s.u64VirtualSyncCatchUpPrev);
549 SSMR3PutBool(pSSM, pVM->tm.s.fVirtualSyncCatchUp);
550
551 /* real time clock */
552 SSMR3PutU64(pSSM, TMCLOCK_FREQ_REAL);
553
554 /* the cpu tick clock. */
555 SSMR3PutU64(pSSM, TMCpuTickGet(pVM));
556 return SSMR3PutU64(pSSM, pVM->tm.s.cTSCTicksPerSecond);
557}
558
559
560/**
561 * Execute state load operation.
562 *
563 * @returns VBox status code.
564 * @param pVM VM Handle.
565 * @param pSSM SSM operation handle.
566 * @param u32Version Data layout version.
567 */
568static DECLCALLBACK(int) tmR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version)
569{
570 LogFlow(("tmR3Load:\n"));
571 Assert(!pVM->tm.s.fTSCTicking);
572 Assert(!pVM->tm.s.fVirtualTicking);
573 Assert(!pVM->tm.s.fVirtualSyncTicking);
574
575 /*
576 * Validate version.
577 */
578 if (u32Version != TM_SAVED_STATE_VERSION)
579 {
580 Log(("tmR3Load: Invalid version u32Version=%d!\n", u32Version));
581 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
582 }
583
584 /*
585 * Load the virtual clock.
586 */
587 pVM->tm.s.fVirtualTicking = false;
588 /* the virtual clock. */
589 uint64_t u64Hz;
590 int rc = SSMR3GetU64(pSSM, &u64Hz);
591 if (VBOX_FAILURE(rc))
592 return rc;
593 if (u64Hz != TMCLOCK_FREQ_VIRTUAL)
594 {
595 AssertMsgFailed(("The virtual clock frequency differs! Saved: %RU64 Binary: %RU64\n",
596 u64Hz, TMCLOCK_FREQ_VIRTUAL));
597 return VERR_SSM_VIRTUAL_CLOCK_HZ;
598 }
599 SSMR3GetU64(pSSM, &pVM->tm.s.u64Virtual);
600 pVM->tm.s.u64VirtualOffset = 0;
601
602 /* the virtual timer synchronous clock. */
603 pVM->tm.s.fVirtualSyncTicking = false;
604 SSMR3GetU64(pSSM, &pVM->tm.s.u64VirtualSync);
605 uint64_t u64;
606 SSMR3GetU64(pSSM, &u64);
607 pVM->tm.s.u64VirtualSyncOffset = u64;
608 SSMR3GetU64(pSSM, &u64);
609 pVM->tm.s.u64VirtualSyncCatchUpPrev = u64;
610 bool f;
611 SSMR3GetBool(pSSM, &f);
612 pVM->tm.s.fVirtualSyncCatchUp = f;
613
614 /* the real clock */
615 rc = SSMR3GetU64(pSSM, &u64Hz);
616 if (VBOX_FAILURE(rc))
617 return rc;
618 if (u64Hz != TMCLOCK_FREQ_REAL)
619 {
620 AssertMsgFailed(("The real clock frequency differs! Saved: %RU64 Binary: %RU64\n",
621 u64Hz, TMCLOCK_FREQ_REAL));
622 return VERR_SSM_VIRTUAL_CLOCK_HZ; /* missleading... */
623 }
624
625 /* the cpu tick clock. */
626 pVM->tm.s.fTSCTicking = false;
627 SSMR3GetU64(pSSM, &pVM->tm.s.u64TSC);
628 rc = SSMR3GetU64(pSSM, &u64Hz);
629 if (VBOX_FAILURE(rc))
630 return rc;
631 if (pVM->tm.s.fTSCUseRealTSC)
632 pVM->tm.s.u64TSCOffset = 0; /** @todo TSC restore stuff and HWACC. */
633 else
634 pVM->tm.s.cTSCTicksPerSecond = u64Hz;
635 LogRel(("TM: cTSCTicksPerSecond=%#RX64 (%RU64) fTSCVirtualized=%RTbool fTSCUseRealTSC=%RTbool (state load)\n",
636 pVM->tm.s.cTSCTicksPerSecond, pVM->tm.s.cTSCTicksPerSecond, pVM->tm.s.fTSCVirtualized, pVM->tm.s.fTSCUseRealTSC));
637
638 /*
639 * Make sure timers get rescheduled immediately.
640 */
641 VM_FF_SET(pVM, VM_FF_TIMER);
642
643 return VINF_SUCCESS;
644}
645
646
647/** @todo doc */
648static int tmr3TimerCreate(PVM pVM, TMCLOCK enmClock, const char *pszDesc, PPTMTIMERHC ppTimer)
649{
650 VM_ASSERT_EMT(pVM);
651
652 /*
653 * Allocate the timer.
654 */
655 PTMTIMERHC pTimer = NULL;
656 if (pVM->tm.s.pFree && VM_IS_EMT(pVM))
657 {
658 pTimer = pVM->tm.s.pFree;
659 pVM->tm.s.pFree = pTimer->pBigNext;
660 Log3(("TM: Recycling timer %p, new free head %p.\n", pTimer, pTimer->pBigNext));
661 }
662
663 if (!pTimer)
664 {
665 int rc = MMHyperAlloc(pVM, sizeof(*pTimer), 0, MM_TAG_TM, (void **)&pTimer);
666 if (VBOX_FAILURE(rc))
667 return rc;
668 Log3(("TM: Allocated new timer %p\n", pTimer));
669 }
670
671 /*
672 * Initialize it.
673 */
674 pTimer->u64Expire = 0;
675 pTimer->enmClock = enmClock;
676 pTimer->pVMR3 = pVM;
677 pTimer->pVMR0 = (PVMR0)pVM->pVMHC; /// @todo pTimer->pVMR0 = pVM->pVMR0;
678 pTimer->pVMGC = pVM->pVMGC;
679 pTimer->enmState = TMTIMERSTATE_STOPPED;
680 pTimer->offScheduleNext = 0;
681 pTimer->offNext = 0;
682 pTimer->offPrev = 0;
683 pTimer->pszDesc = pszDesc;
684
685 /* insert into the list of created timers. */
686 pTimer->pBigPrev = NULL;
687 pTimer->pBigNext = pVM->tm.s.pCreated;
688 pVM->tm.s.pCreated = pTimer;
689 if (pTimer->pBigNext)
690 pTimer->pBigNext->pBigPrev = pTimer;
691#ifdef VBOX_STRICT
692 tmTimerQueuesSanityChecks(pVM, "tmR3TimerCreate");
693#endif
694
695 *ppTimer = pTimer;
696 return VINF_SUCCESS;
697}
698
699
700/**
701 * Creates a device timer.
702 *
703 * @returns VBox status.
704 * @param pVM The VM to create the timer in.
705 * @param pDevIns Device instance.
706 * @param enmClock The clock to use on this timer.
707 * @param pfnCallback Callback function.
708 * @param pszDesc Pointer to description string which must stay around
709 * until the timer is fully destroyed (i.e. a bit after TMTimerDestroy()).
710 * @param ppTimer Where to store the timer on success.
711 */
712TMR3DECL(int) TMR3TimerCreateDevice(PVM pVM, PPDMDEVINS pDevIns, TMCLOCK enmClock, PFNTMTIMERDEV pfnCallback, const char *pszDesc, PPTMTIMERHC ppTimer)
713{
714 /*
715 * Allocate and init stuff.
716 */
717 int rc = tmr3TimerCreate(pVM, enmClock, pszDesc, ppTimer);
718 if (VBOX_SUCCESS(rc))
719 {
720 (*ppTimer)->enmType = TMTIMERTYPE_DEV;
721 (*ppTimer)->u.Dev.pfnTimer = pfnCallback;
722 (*ppTimer)->u.Dev.pDevIns = pDevIns;
723 Log(("TM: Created device timer %p clock %d callback %p '%s'\n", (*ppTimer), enmClock, pfnCallback, pszDesc));
724 }
725
726 return rc;
727}
728
729
730/**
731 * Creates a driver timer.
732 *
733 * @returns VBox status.
734 * @param pVM The VM to create the timer in.
735 * @param pDrvIns Driver instance.
736 * @param enmClock The clock to use on this timer.
737 * @param pfnCallback Callback function.
738 * @param pszDesc Pointer to description string which must stay around
739 * until the timer is fully destroyed (i.e. a bit after TMTimerDestroy()).
740 * @param ppTimer Where to store the timer on success.
741 */
742TMR3DECL(int) TMR3TimerCreateDriver(PVM pVM, PPDMDRVINS pDrvIns, TMCLOCK enmClock, PFNTMTIMERDRV pfnCallback, const char *pszDesc, PPTMTIMERHC ppTimer)
743{
744 /*
745 * Allocate and init stuff.
746 */
747 int rc = tmr3TimerCreate(pVM, enmClock, pszDesc, ppTimer);
748 if (VBOX_SUCCESS(rc))
749 {
750 (*ppTimer)->enmType = TMTIMERTYPE_DRV;
751 (*ppTimer)->u.Drv.pfnTimer = pfnCallback;
752 (*ppTimer)->u.Drv.pDrvIns = pDrvIns;
753 Log(("TM: Created device timer %p clock %d callback %p '%s'\n", (*ppTimer), enmClock, pfnCallback, pszDesc));
754 }
755
756 return rc;
757}
758
759
760/**
761 * Creates an internal timer.
762 *
763 * @returns VBox status.
764 * @param pVM The VM to create the timer in.
765 * @param enmClock The clock to use on this timer.
766 * @param pfnCallback Callback function.
767 * @param pvUser User argument to be passed to the callback.
768 * @param pszDesc Pointer to description string which must stay around
769 * until the timer is fully destroyed (i.e. a bit after TMTimerDestroy()).
770 * @param ppTimer Where to store the timer on success.
771 */
772TMR3DECL(int) TMR3TimerCreateInternal(PVM pVM, TMCLOCK enmClock, PFNTMTIMERINT pfnCallback, void *pvUser, const char *pszDesc, PPTMTIMERHC ppTimer)
773{
774 /*
775 * Allocate and init stuff.
776 */
777 PTMTIMER pTimer;
778 int rc = tmr3TimerCreate(pVM, enmClock, pszDesc, &pTimer);
779 if (VBOX_SUCCESS(rc))
780 {
781 pTimer->enmType = TMTIMERTYPE_INTERNAL;
782 pTimer->u.Internal.pfnTimer = pfnCallback;
783 pTimer->u.Internal.pvUser = pvUser;
784 *ppTimer = pTimer;
785 Log(("TM: Created internal timer %p clock %d callback %p '%s'\n", pTimer, enmClock, pfnCallback, pszDesc));
786 }
787
788 return rc;
789}
790
791/**
792 * Creates an external timer.
793 *
794 * @returns Timer handle on success.
795 * @returns NULL on failure.
796 * @param pVM The VM to create the timer in.
797 * @param enmClock The clock to use on this timer.
798 * @param pfnCallback Callback function.
799 * @param pvUser User argument.
800 * @param pszDesc Pointer to description string which must stay around
801 * until the timer is fully destroyed (i.e. a bit after TMTimerDestroy()).
802 */
803TMR3DECL(PTMTIMERHC) TMR3TimerCreateExternal(PVM pVM, TMCLOCK enmClock, PFNTMTIMEREXT pfnCallback, void *pvUser, const char *pszDesc)
804{
805 /*
806 * Allocate and init stuff.
807 */
808 PTMTIMERHC pTimer;
809 int rc = tmr3TimerCreate(pVM, enmClock, pszDesc, &pTimer);
810 if (VBOX_SUCCESS(rc))
811 {
812 pTimer->enmType = TMTIMERTYPE_EXTERNAL;
813 pTimer->u.External.pfnTimer = pfnCallback;
814 pTimer->u.External.pvUser = pvUser;
815 Log(("TM: Created external timer %p clock %d callback %p '%s'\n", pTimer, enmClock, pfnCallback, pszDesc));
816 return pTimer;
817 }
818
819 return NULL;
820}
821
822
823/**
824 * Destroy all timers owned by a device.
825 *
826 * @returns VBox status.
827 * @param pVM VM handle.
828 * @param pDevIns Device which timers should be destroyed.
829 */
830TMR3DECL(int) TMR3TimerDestroyDevice(PVM pVM, PPDMDEVINS pDevIns)
831{
832 LogFlow(("TMR3TimerDestroyDevice: pDevIns=%p\n", pDevIns));
833 if (!pDevIns)
834 return VERR_INVALID_PARAMETER;
835
836 PTMTIMER pCur = pVM->tm.s.pCreated;
837 while (pCur)
838 {
839 PTMTIMER pDestroy = pCur;
840 pCur = pDestroy->pBigNext;
841 if ( pDestroy->enmType == TMTIMERTYPE_DEV
842 && pDestroy->u.Dev.pDevIns == pDevIns)
843 {
844 int rc = TMTimerDestroy(pDestroy);
845 AssertRC(rc);
846 }
847 }
848 LogFlow(("TMR3TimerDestroyDevice: returns VINF_SUCCESS\n"));
849 return VINF_SUCCESS;
850}
851
852
853/**
854 * Destroy all timers owned by a driver.
855 *
856 * @returns VBox status.
857 * @param pVM VM handle.
858 * @param pDrvIns Driver which timers should be destroyed.
859 */
860TMR3DECL(int) TMR3TimerDestroyDriver(PVM pVM, PPDMDRVINS pDrvIns)
861{
862 LogFlow(("TMR3TimerDestroyDriver: pDrvIns=%p\n", pDrvIns));
863 if (!pDrvIns)
864 return VERR_INVALID_PARAMETER;
865
866 PTMTIMER pCur = pVM->tm.s.pCreated;
867 while (pCur)
868 {
869 PTMTIMER pDestroy = pCur;
870 pCur = pDestroy->pBigNext;
871 if ( pDestroy->enmType == TMTIMERTYPE_DRV
872 && pDestroy->u.Drv.pDrvIns == pDrvIns)
873 {
874 int rc = TMTimerDestroy(pDestroy);
875 AssertRC(rc);
876 }
877 }
878 LogFlow(("TMR3TimerDestroyDriver: returns VINF_SUCCESS\n"));
879 return VINF_SUCCESS;
880}
881
882
883/**
884 * Checks if a queue has a pending timer.
885 *
886 * @returns true if it has a pending timer.
887 * @returns false is no pending timer.
888 *
889 * @param pVM The VM handle.
890 * @param enmClock The queue.
891 */
892DECLINLINE(bool) tmR3HasPending(PVM pVM, TMCLOCK enmClock)
893{
894 const uint64_t u64Expire = pVM->tm.s.CTXALLSUFF(paTimerQueues)[enmClock].u64Expire;
895 return u64Expire != INT64_MAX && u64Expire <= tmClock(pVM, enmClock);
896}
897
898
899/**
900 * Schedulation timer callback.
901 *
902 * @param pTimer Timer handle.
903 * @param pvUser VM handle.
904 * @remark We cannot do the scheduling and queues running from a timer handler
905 * since it's not executing in EMT, and even if it was it would be async
906 * and we wouldn't know the state of the affairs.
907 * So, we'll just raise the timer FF and force any REM execution to exit.
908 */
909static DECLCALLBACK(void) tmR3TimerCallback(PRTTIMER pTimer, void *pvUser)
910{
911 PVM pVM = (PVM)pvUser;
912 AssertCompile(TMCLOCK_MAX == 4);
913#ifdef DEBUG_Sander /* very annoying, keep it private. */
914 if (VM_FF_ISSET(pVM, VM_FF_TIMER))
915 Log(("tmR3TimerCallback: timer event still pending!!\n"));
916#endif
917 if ( !VM_FF_ISSET(pVM, VM_FF_TIMER)
918 && ( pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL_SYNC].offSchedule
919 || pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL].offSchedule
920 || pVM->tm.s.paTimerQueuesR3[TMCLOCK_REAL].offSchedule
921 || pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC].offSchedule
922 || tmR3HasPending(pVM, TMCLOCK_VIRTUAL_SYNC)
923 || tmR3HasPending(pVM, TMCLOCK_VIRTUAL)
924 || tmR3HasPending(pVM, TMCLOCK_REAL)
925 || tmR3HasPending(pVM, TMCLOCK_TSC)
926 )
927 && !VM_FF_ISSET(pVM, VM_FF_TIMER)
928 )
929 {
930 VM_FF_SET(pVM, VM_FF_TIMER);
931 REMR3NotifyTimerPending(pVM);
932 VMR3NotifyFF(pVM, true);
933 STAM_COUNTER_INC(&pVM->tm.s.StatTimerCallbackSetFF);
934 }
935}
936
937
938/**
939 * Schedules and runs any pending timers.
940 *
941 * This is normally called from a forced action handler in EMT.
942 *
943 * @param pVM The VM to run the timers for.
944 */
945TMR3DECL(void) TMR3TimerQueuesDo(PVM pVM)
946{
947 STAM_PROFILE_START(&pVM->tm.s.StatDoQueues, a);
948 Log2(("TMR3TimerQueuesDo:\n"));
949
950 /*
951 * Process the queues.
952 */
953 AssertCompile(TMCLOCK_MAX == 4);
954
955 /* TMCLOCK_VIRTUAL */
956 STAM_PROFILE_ADV_START(&pVM->tm.s.StatDoQueuesSchedule, s1);
957 tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL]);
958 STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesSchedule, s1);
959 STAM_PROFILE_ADV_START(&pVM->tm.s.StatDoQueuesRun, r1);
960 tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL]);
961 STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesRun, r1);
962
963 /* TMCLOCK_VIRTUAL_SYNC */
964 STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesSchedule, s1);
965 tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL_SYNC]);
966 STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesSchedule, s2);
967 STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesRun, r1);
968 tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_VIRTUAL_SYNC]);
969 STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesRun, r2);
970
971 /* TMCLOCK_REAL */
972 STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesSchedule, s2);
973 tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_REAL]);
974 STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesSchedule, s3);
975 STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesRun, r2);
976 tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_REAL]);
977 STAM_PROFILE_ADV_SUSPEND(&pVM->tm.s.StatDoQueuesRun, r3);
978
979 /* TMCLOCK_TSC */
980 STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesSchedule, s3);
981 tmTimerQueueSchedule(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC]);
982 STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatDoQueuesSchedule, s3);
983 STAM_PROFILE_ADV_RESUME(&pVM->tm.s.StatDoQueuesRun, r3);
984 tmR3TimerQueueRun(pVM, &pVM->tm.s.paTimerQueuesR3[TMCLOCK_TSC]);
985 STAM_PROFILE_ADV_STOP(&pVM->tm.s.StatDoQueuesRun, r3);
986
987 /* done. */
988 VM_FF_CLEAR(pVM, VM_FF_TIMER);
989
990#ifdef VBOX_STRICT
991 /* check that we didn't screwup. */
992 tmTimerQueuesSanityChecks(pVM, "TMR3TimerQueuesDo");
993#endif
994
995 Log2(("TMR3TimerQueuesDo: returns void\n"));
996 STAM_PROFILE_STOP(&pVM->tm.s.StatDoQueues, a);
997}
998
999
1000/**
1001 * Schedules and runs any pending times in the specified queue.
1002 *
1003 * This is normally called from a forced action handler in EMT.
1004 *
1005 * @param pVM The VM to run the timers for.
1006 * @param pQueue The queue to run.
1007 */
1008static void tmR3TimerQueueRun(PVM pVM, PTMTIMERQUEUE pQueue)
1009{
1010 VM_ASSERT_EMT(pVM);
1011
1012 /*
1013 * Run timers.
1014 *
1015 * We check the clock once and run all timers which are ACTIVE
1016 * and have an expire time less or equal to the time we read.
1017 *
1018 * N.B. A generic unlink must be applied since other threads
1019 * are allowed to mess with any active timer at any time.
1020 * However, we only allow EMT to handle EXPIRED_PENDING
1021 * timers, thus enabling the timer handler function to
1022 * arm the timer again.
1023 */
1024 PTMTIMER pNext = TMTIMER_GET_HEAD(pQueue);
1025 if (!pNext)
1026 return;
1027 /** @todo deal with the VIRTUAL_SYNC pausing and catch calcs ++ */
1028 uint64_t u64Now = tmClock(pVM, pQueue->enmClock);
1029 while (pNext && pNext->u64Expire <= u64Now)
1030 {
1031 PTMTIMER pTimer = pNext;
1032 pNext = TMTIMER_GET_NEXT(pTimer);
1033 Log2(("tmR3TimerQueueRun: pTimer=%p:{.enmState=%s, .enmClock=%d, .enmType=%d, u64Expire=%llx (now=%llx) .pszDesc=%s}\n",
1034 pTimer, tmTimerState(pTimer->enmState), pTimer->enmClock, pTimer->enmType, pTimer->u64Expire, u64Now, pTimer->pszDesc));
1035 bool fRc;
1036 TM_TRY_SET_STATE(pTimer, TMTIMERSTATE_EXPIRED, TMTIMERSTATE_ACTIVE, fRc);
1037 if (fRc)
1038 {
1039 Assert(!pTimer->offScheduleNext); /* this can trigger falsely */
1040
1041 /* unlink */
1042 const PTMTIMER pPrev = TMTIMER_GET_PREV(pTimer);
1043 if (pPrev)
1044 TMTIMER_SET_NEXT(pPrev, pNext);
1045 else
1046 {
1047 TMTIMER_SET_HEAD(pQueue, pNext);
1048 pQueue->u64Expire = pNext ? pNext->u64Expire : INT64_MAX;
1049 }
1050 if (pNext)
1051 TMTIMER_SET_PREV(pNext, pPrev);
1052 pTimer->offNext = 0;
1053 pTimer->offPrev = 0;
1054
1055
1056 /* fire */
1057 switch (pTimer->enmType)
1058 {
1059 case TMTIMERTYPE_DEV: pTimer->u.Dev.pfnTimer(pTimer->u.Dev.pDevIns, pTimer); break;
1060 case TMTIMERTYPE_DRV: pTimer->u.Drv.pfnTimer(pTimer->u.Drv.pDrvIns, pTimer); break;
1061 case TMTIMERTYPE_INTERNAL: pTimer->u.Internal.pfnTimer(pVM, pTimer, pTimer->u.Internal.pvUser); break;
1062 case TMTIMERTYPE_EXTERNAL: pTimer->u.External.pfnTimer(pTimer->u.External.pvUser); break;
1063 default:
1064 AssertMsgFailed(("Invalid timer type %d (%s)\n", pTimer->enmType, pTimer->pszDesc));
1065 break;
1066 }
1067
1068 /* change the state if it wasn't changed already in the handler. */
1069 TM_TRY_SET_STATE(pTimer, TMTIMERSTATE_STOPPED, TMTIMERSTATE_EXPIRED, fRc);
1070 Log2(("tmR3TimerQueueRun: new state %s\n", tmTimerState(pTimer->enmState)));
1071 }
1072 } /* run loop */
1073}
1074
1075
1076/**
1077 * Saves the state of a timer to a saved state.
1078 *
1079 * @returns VBox status.
1080 * @param pTimer Timer to save.
1081 * @param pSSM Save State Manager handle.
1082 */
1083TMR3DECL(int) TMR3TimerSave(PTMTIMERHC pTimer, PSSMHANDLE pSSM)
1084{
1085 LogFlow(("TMR3TimerSave: pTimer=%p:{enmState=%s, .pszDesc={%s}} pSSM=%p\n", pTimer, tmTimerState(pTimer->enmState), pTimer->pszDesc, pSSM));
1086 switch (pTimer->enmState)
1087 {
1088 case TMTIMERSTATE_STOPPED:
1089 case TMTIMERSTATE_PENDING_STOP:
1090 case TMTIMERSTATE_PENDING_STOP_SCHEDULE:
1091 return SSMR3PutU8(pSSM, (uint8_t)TMTIMERSTATE_PENDING_STOP);
1092
1093 case TMTIMERSTATE_PENDING_SCHEDULE_SET_EXPIRE:
1094 case TMTIMERSTATE_PENDING_RESCHEDULE_SET_EXPIRE:
1095 AssertMsgFailed(("u64Expire is being updated! (%s)\n", pTimer->pszDesc));
1096 if (!RTThreadYield())
1097 RTThreadSleep(1);
1098 /* fall thru */
1099 case TMTIMERSTATE_ACTIVE:
1100 case TMTIMERSTATE_PENDING_SCHEDULE:
1101 case TMTIMERSTATE_PENDING_RESCHEDULE:
1102 SSMR3PutU8(pSSM, (uint8_t)TMTIMERSTATE_PENDING_SCHEDULE);
1103 return SSMR3PutU64(pSSM, pTimer->u64Expire);
1104
1105 case TMTIMERSTATE_EXPIRED:
1106 case TMTIMERSTATE_PENDING_DESTROY:
1107 case TMTIMERSTATE_PENDING_STOP_DESTROY:
1108 case TMTIMERSTATE_FREE:
1109 AssertMsgFailed(("Invalid timer state %d %s (%s)\n", pTimer->enmState, tmTimerState(pTimer->enmState), pTimer->pszDesc));
1110 return SSMR3HandleSetStatus(pSSM, VERR_TM_INVALID_STATE);
1111 }
1112
1113 AssertMsgFailed(("Unknown timer state %d (%s)\n", pTimer->enmState, pTimer->pszDesc));
1114 return SSMR3HandleSetStatus(pSSM, VERR_TM_UNKNOWN_STATE);
1115}
1116
1117
1118/**
1119 * Loads the state of a timer from a saved state.
1120 *
1121 * @returns VBox status.
1122 * @param pTimer Timer to restore.
1123 * @param pSSM Save State Manager handle.
1124 */
1125TMR3DECL(int) TMR3TimerLoad(PTMTIMERHC pTimer, PSSMHANDLE pSSM)
1126{
1127 Assert(pTimer); Assert(pSSM); VM_ASSERT_EMT(pTimer->pVMR3);
1128 LogFlow(("TMR3TimerLoad: pTimer=%p:{enmState=%s, .pszDesc={%s}} pSSM=%p\n", pTimer, tmTimerState(pTimer->enmState), pTimer->pszDesc, pSSM));
1129
1130 /*
1131 * Load the state and validate it.
1132 */
1133 uint8_t u8State;
1134 int rc = SSMR3GetU8(pSSM, &u8State);
1135 if (VBOX_FAILURE(rc))
1136 return rc;
1137 TMTIMERSTATE enmState = (TMTIMERSTATE)u8State;
1138 if ( enmState != TMTIMERSTATE_PENDING_STOP
1139 && enmState != TMTIMERSTATE_PENDING_SCHEDULE
1140 && enmState != TMTIMERSTATE_PENDING_STOP_SCHEDULE)
1141 {
1142 AssertMsgFailed(("enmState=%d %s\n", enmState, tmTimerState(enmState)));
1143 return SSMR3HandleSetStatus(pSSM, VERR_TM_LOAD_STATE);
1144 }
1145
1146 if (enmState == TMTIMERSTATE_PENDING_SCHEDULE)
1147 {
1148 /*
1149 * Load the expire time.
1150 */
1151 uint64_t u64Expire;
1152 rc = SSMR3GetU64(pSSM, &u64Expire);
1153 if (VBOX_FAILURE(rc))
1154 return rc;
1155
1156 /*
1157 * Set it.
1158 */
1159 Log(("enmState=%d %s u64Expire=%llu\n", enmState, tmTimerState(enmState), u64Expire));
1160 rc = TMTimerSet(pTimer, u64Expire);
1161 }
1162 else
1163 {
1164 /*
1165 * Stop it.
1166 */
1167 Log(("enmState=%d %s\n", enmState, tmTimerState(enmState)));
1168 rc = TMTimerStop(pTimer);
1169 }
1170
1171 /*
1172 * On failure set SSM status.
1173 */
1174 if (VBOX_FAILURE(rc))
1175 rc = SSMR3HandleSetStatus(pSSM, rc);
1176 return rc;
1177}
1178
1179
1180/**
1181 * Display all timers.
1182 *
1183 * @param pVM VM Handle.
1184 * @param pHlp The info helpers.
1185 * @param pszArgs Arguments, ignored.
1186 */
1187static DECLCALLBACK(void) tmR3TimerInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
1188{
1189 NOREF(pszArgs);
1190 pHlp->pfnPrintf(pHlp,
1191 "Timers (pVM=%p)\n"
1192 "%.*s %.*s %.*s %.*s Clock %-18s %-18s %-25s Description\n",
1193 pVM,
1194 sizeof(RTR3PTR) * 2, "pTimerR3 ",
1195 sizeof(int32_t) * 2, "offNext ",
1196 sizeof(int32_t) * 2, "offPrev ",
1197 sizeof(int32_t) * 2, "offSched ",
1198 "Time",
1199 "Expire",
1200 "State");
1201 for (PTMTIMERHC pTimer = pVM->tm.s.pCreated; pTimer; pTimer = pTimer->pBigNext)
1202 {
1203 pHlp->pfnPrintf(pHlp,
1204 "%p %08RX32 %08RX32 %08RX32 %s %18RU64 %18RU64 %-25s %s\n",
1205 pTimer,
1206 pTimer->offNext,
1207 pTimer->offPrev,
1208 pTimer->offScheduleNext,
1209 pTimer->enmClock == TMCLOCK_REAL ? "Real " : "Virt ",
1210 TMTimerGet(pTimer),
1211 pTimer->u64Expire,
1212 tmTimerState(pTimer->enmState),
1213 pTimer->pszDesc);
1214 }
1215}
1216
1217
1218/**
1219 * Display all active timers.
1220 *
1221 * @param pVM VM Handle.
1222 * @param pHlp The info helpers.
1223 * @param pszArgs Arguments, ignored.
1224 */
1225static DECLCALLBACK(void) tmR3TimerInfoActive(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
1226{
1227 NOREF(pszArgs);
1228 pHlp->pfnPrintf(pHlp,
1229 "Active Timers (pVM=%p)\n"
1230 "%.*s %.*s %.*s %.*s Clock %-18s %-18s %-25s Description\n",
1231 pVM,
1232 sizeof(RTR3PTR) * 2, "pTimerR3 ",
1233 sizeof(int32_t) * 2, "offNext ",
1234 sizeof(int32_t) * 2, "offPrev ",
1235 sizeof(int32_t) * 2, "offSched ",
1236 "Time",
1237 "Expire",
1238 "State");
1239 for (unsigned iQueue = 0; iQueue < TMCLOCK_MAX; iQueue++)
1240 {
1241 for (PTMTIMERHC pTimer = TMTIMER_GET_HEAD(&pVM->tm.s.paTimerQueuesR3[iQueue]);
1242 pTimer;
1243 pTimer = TMTIMER_GET_NEXT(pTimer))
1244 {
1245 pHlp->pfnPrintf(pHlp,
1246 "%p %08RX32 %08RX32 %08RX32 %s %18RU64 %18RU64 %-25s %s\n",
1247 pTimer,
1248 pTimer->offNext,
1249 pTimer->offPrev,
1250 pTimer->offScheduleNext,
1251 pTimer->enmClock == TMCLOCK_REAL ? "Real " : "Virt ",
1252 TMTimerGet(pTimer),
1253 pTimer->u64Expire,
1254 tmTimerState(pTimer->enmState),
1255 pTimer->pszDesc);
1256 }
1257 }
1258}
1259
1260
1261/**
1262 * Display all clocks.
1263 *
1264 * @param pVM VM Handle.
1265 * @param pHlp The info helpers.
1266 * @param pszArgs Arguments, ignored.
1267 */
1268static DECLCALLBACK(void) tmR3InfoClocks(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
1269{
1270 NOREF(pszArgs);
1271
1272 /* TSC */
1273 uint64_t u64 = TMCpuTickGet(pVM);
1274 pHlp->pfnPrintf(pHlp,
1275 "Cpu Tick: %#RX64 (%RU64) %RU64Hz %s%s",
1276 u64, u64, TMCpuTicksPerSecond(pVM),
1277 pVM->tm.s.fTSCTicking ? "ticking" : "paused",
1278 pVM->tm.s.fTSCVirtualized ? " - virtualized" : "");
1279 if (pVM->tm.s.fTSCUseRealTSC)
1280 {
1281 pHlp->pfnPrintf(pHlp, " - real tsc");
1282 if (pVM->tm.s.u64TSCOffset)
1283 pHlp->pfnPrintf(pHlp, "\n offset %#RX64", pVM->tm.s.u64TSCOffset);
1284 }
1285 else
1286 pHlp->pfnPrintf(pHlp, " - virtual clock");
1287 pHlp->pfnPrintf(pHlp, "\n");
1288
1289 /* virtual */
1290 u64 = TMVirtualGet(pVM);
1291 pHlp->pfnPrintf(pHlp,
1292 " Virtual: %#RX64 (%RU64) %RU64Hz %s",
1293 u64, u64, TMVirtualGetFreq(pVM),
1294 pVM->tm.s.fVirtualTicking ? "ticking" : "paused");
1295 if (pVM->tm.s.fVirtualWarpDrive)
1296 pHlp->pfnPrintf(pHlp, " WarpDrive %RU32 %%", pVM->tm.s.u32VirtualWarpDrivePercentage);
1297 pHlp->pfnPrintf(pHlp, "\n");
1298
1299 /* virtual sync */
1300 u64 = TMVirtualGetSync(pVM);
1301 pHlp->pfnPrintf(pHlp,
1302 "VirtSync: %#RX64 (%RU64) %s%s",
1303 u64, u64,
1304 pVM->tm.s.fVirtualSyncTicking ? "ticking" : "paused",
1305 pVM->tm.s.fVirtualSyncCatchUp ? " - catchup" : "");
1306 if (pVM->tm.s.u64VirtualSyncOffset)
1307 pHlp->pfnPrintf(pHlp, "\n offset %#RX64", pVM->tm.s.u64VirtualSyncOffset);
1308 pHlp->pfnPrintf(pHlp, "\n");
1309
1310 /* real */
1311 u64 = TMRealGet(pVM);
1312 pHlp->pfnPrintf(pHlp,
1313 " Real: %#RX64 (%RU64) %RU64Hz\n",
1314 u64, u64, TMRealGetFreq(pVM));
1315}
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