VirtualBox

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

Last change on this file since 19 was 1, checked in by vboxsync, 55 years ago

import

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