VirtualBox

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

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