VirtualBox

source: vbox/trunk/src/VBox/VMM/VMEmt.cpp@ 2283

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

Working TMCLOCK_VIRTUAL_SYNC.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.0 KB
Line 
1/* $Id: VMEmt.cpp 2283 2007-04-20 22:27:52Z vboxsync $ */
2/** @file
3 * VM - Virtual Machine, The Emulation Thread.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_VM
27#include <VBox/tm.h>
28#include <VBox/dbgf.h>
29#include <VBox/em.h>
30#include <VBox/pdm.h>
31#include <VBox/rem.h>
32#include "VMInternal.h"
33#include <VBox/vm.h>
34
35#include <VBox/err.h>
36#include <VBox/log.h>
37#include <iprt/assert.h>
38#include <iprt/asm.h>
39#include <iprt/semaphore.h>
40#include <iprt/thread.h>
41#include <iprt/time.h>
42
43
44
45/**
46 * The emulation thread.
47 *
48 * @returns Thread exit code.
49 * @param ThreadSelf The handle to the executing thread.
50 * @param pvArgs Pointer to a VMEMULATIONTHREADARGS structure.
51 */
52DECLCALLBACK(int) vmR3EmulationThread(RTTHREAD ThreadSelf, void *pvArgs)
53{
54 PVMEMULATIONTHREADARGS pArgs = (PVMEMULATIONTHREADARGS)pvArgs;
55 AssertReleaseMsg(pArgs && pArgs->pVM, ("Invalid arguments to the emulation thread!\n"));
56
57 /*
58 * Init the native thread member.
59 */
60 PVM pVM = pArgs->pVM;
61 pVM->NativeThreadEMT = RTThreadGetNative(ThreadSelf);
62
63 /*
64 * The request loop.
65 */
66 VMSTATE enmBefore;
67 int rc;
68 Log(("vmR3EmulationThread: Emulation thread starting the days work... Thread=%#x pVM=%p\n", ThreadSelf, pVM));
69 for (;;)
70 {
71 /* Requested to exit the EMT thread out of sync? (currently only VMR3WaitForResume) */
72 if (setjmp(pVM->vm.s.emtJumpEnv) != 0)
73 {
74 rc = VINF_SUCCESS;
75 break;
76 }
77
78 /*
79 * Pending requests which needs servicing?
80 *
81 * We check for state changes in addition to status codes when
82 * servicing requests. (Look after the ifs.)
83 */
84 enmBefore = pVM->enmVMState;
85 if (VM_FF_ISSET(pVM, VM_FF_TERMINATE))
86 {
87 rc = VINF_EM_TERMINATE;
88 break;
89 }
90 else if (pVM->vm.s.pReqs)
91 {
92 /*
93 * Service execute in EMT request.
94 */
95 rc = VMR3ReqProcess(pVM);
96 Log(("vmR3EmulationThread: Req rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
97 }
98 else if (VM_FF_ISSET(pVM, VM_FF_DBGF))
99 {
100 /*
101 * Service the debugger request.
102 */
103 rc = DBGFR3VMMForcedAction(pVM);
104 Log(("vmR3EmulationThread: Dbg rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
105 }
106 else if (VM_FF_ISSET(pVM, VM_FF_RESET))
107 {
108 /*
109 * Service a delay reset request.
110 */
111 rc = VMR3Reset(pVM);
112 VM_FF_CLEAR(pVM, VM_FF_RESET);
113 Log(("vmR3EmulationThread: Reset rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
114 }
115 else
116 {
117 /*
118 * Nothing important is pending, so wait for something.
119 */
120 rc = VMR3Wait(pVM);
121 if (VBOX_FAILURE(rc))
122 break;
123 }
124
125 /*
126 * Check for termination requests, these are extremely high priority.
127 */
128 if ( rc == VINF_EM_TERMINATE
129 || VM_FF_ISSET(pVM, VM_FF_TERMINATE))
130 break;
131
132 /*
133 * Some requests (both VMR3Req* and the DBGF) can potentially
134 * resume or start the VM, in that case we'll get a change in
135 * VM status indicating that we're now running.
136 */
137 if ( VBOX_SUCCESS(rc)
138 && enmBefore != pVM->enmVMState
139 && (pVM->enmVMState == VMSTATE_RUNNING))
140 {
141 rc = EMR3ExecuteVM(pVM);
142 Log(("vmR3EmulationThread: EMR3ExecuteVM() -> rc=%Vrc, enmVMState=%d\n", rc, pVM->enmVMState));
143 }
144
145 } /* forever */
146
147
148 /*
149 * Exiting.
150 */
151 Log(("vmR3EmulationThread: Terminating emulation thread! Thread=%#x pVM=%p rc=%Vrc enmBefore=%d enmVMState=%d\n",
152 ThreadSelf, pVM, rc, enmBefore, pVM->enmVMState));
153 if (pVM->vm.s.fEMTDoesTheCleanup)
154 {
155 Log(("vmR3EmulationThread: executing delayed Destroy\n"));
156 vmR3Destroy(pVM);
157 vmR3DestroyFinalBit(pVM);
158 Log(("vmR3EmulationThread: EMT is terminated.\n"));
159 }
160 else
161 {
162 /* we don't reset ThreadEMT here because it's used in waiting. */
163 pVM->NativeThreadEMT = NIL_RTNATIVETHREAD;
164 }
165 return rc;
166}
167
168/**
169 * Wait for VM to be resumed. Handle events like vmR3EmulationThread does.
170 * In case the VM is stopped, clean up and long jump to the main EMT loop.
171 *
172 * @returns VINF_SUCCESS or doesn't return
173 * @param pVM VM handle.
174 */
175VMR3DECL(int) VMR3WaitForResume(PVM pVM)
176{
177 /*
178 * The request loop.
179 */
180 VMSTATE enmBefore;
181 int rc;
182 for (;;)
183 {
184
185 /*
186 * Pending requests which needs servicing?
187 *
188 * We check for state changes in addition to status codes when
189 * servicing requests. (Look after the ifs.)
190 */
191 enmBefore = pVM->enmVMState;
192 if (VM_FF_ISSET(pVM, VM_FF_TERMINATE))
193 {
194 rc = VINF_EM_TERMINATE;
195 break;
196 }
197 else if (pVM->vm.s.pReqs)
198 {
199 /*
200 * Service execute in EMT request.
201 */
202 rc = VMR3ReqProcess(pVM);
203 Log(("vmR3EmulationThread: Req rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
204 }
205 else if (VM_FF_ISSET(pVM, VM_FF_DBGF))
206 {
207 /*
208 * Service the debugger request.
209 */
210 rc = DBGFR3VMMForcedAction(pVM);
211 Log(("vmR3EmulationThread: Dbg rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
212 }
213 else if (VM_FF_ISSET(pVM, VM_FF_RESET))
214 {
215 /*
216 * Service a delay reset request.
217 */
218 rc = VMR3Reset(pVM);
219 VM_FF_CLEAR(pVM, VM_FF_RESET);
220 Log(("vmR3EmulationThread: Reset rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
221 }
222 else
223 {
224 /*
225 * Nothing important is pending, so wait for something.
226 */
227 rc = VMR3Wait(pVM);
228 if (VBOX_FAILURE(rc))
229 break;
230 }
231
232 /*
233 * Check for termination requests, these are extremely high priority.
234 */
235 if ( rc == VINF_EM_TERMINATE
236 || VM_FF_ISSET(pVM, VM_FF_TERMINATE))
237 break;
238
239 /*
240 * Some requests (both VMR3Req* and the DBGF) can potentially
241 * resume or start the VM, in that case we'll get a change in
242 * VM status indicating that we're now running.
243 */
244 if ( VBOX_SUCCESS(rc)
245 && enmBefore != pVM->enmVMState
246 && (pVM->enmVMState == VMSTATE_RUNNING))
247 {
248 /* Only valid exit reason. */
249 return VINF_SUCCESS;
250 }
251
252 } /* forever */
253
254 /* Return to the main loop in vmR3EmulationThread, which will clean up for us. */
255 longjmp(pVM->vm.s.emtJumpEnv, 1);
256}
257
258/**
259 * Notify the emulation thread (EMT) about pending Forced Action (FF).
260 *
261 * This function is called by thread other than EMT to make
262 * sure EMT wakes up and promptly service an FF request.
263 *
264 * @param pVM VM handle.
265 * @param fNotifiedREM Set if REM have already been notified. If clear the
266 * generic REMR3NotifyFF() method is called.
267 */
268VMR3DECL(void) VMR3NotifyFF(PVM pVM, bool fNotifiedREM)
269{
270 LogFlow(("VMR3NotifyFF:\n"));
271 if (pVM->vm.s.fWait)
272 {
273 int rc = RTSemEventSignal(pVM->vm.s.EventSemWait);
274 AssertRC(rc);
275 }
276 else if (!fNotifiedREM)
277 REMR3NotifyFF(pVM);
278}
279
280
281/**
282 * Halted VM Wait.
283 * Any external event will unblock the thread.
284 *
285 * @returns VINF_SUCCESS unless a fatal error occured. In the latter
286 * case an appropriate status code is returned.
287 * @param pVM VM handle.
288 * @param fIgnoreInterrupts If set the VM_FF_INTERRUPT flags is ignored.
289 * @thread The emulation thread.
290 */
291VMR3DECL(int) VMR3WaitHalted(PVM pVM, bool fIgnoreInterrupts)
292{
293 LogFlow(("VMR3WaitHalted: fIgnoreInterrupts=%d\n", fIgnoreInterrupts));
294
295 /*
296 * Check Relevant FFs.
297 */
298 const uint32_t fMask = !fIgnoreInterrupts
299 ? VM_FF_EXTERNAL_HALTED_MASK
300 : VM_FF_EXTERNAL_HALTED_MASK & ~(VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC);
301 if (VM_FF_ISPENDING(pVM, fMask))
302 {
303 LogFlow(("VMR3WaitHalted: returns VINF_SUCCESS (FF %#x)\n", pVM->fForcedActions));
304 return VINF_SUCCESS;
305 }
306
307 /*
308 * The CPU TSC is running while halted,
309 * and the yielder is suspended.
310 */
311 VMMR3YieldSuspend(pVM);
312
313 /*
314 * Halt loop.
315 */
316 int rc = VINF_SUCCESS;
317 ASMAtomicXchgU32(&pVM->vm.s.fWait, 1);
318 unsigned cLoops = 0;
319 for (;;)
320 {
321 /*
322 * Work the timers and check if we can exit.
323 * The poll call gives us the ticks left to the next event in
324 * addition to perhaps set an FF.
325 */
326 STAM_PROFILE_ADV_START(&pVM->vm.s.StatHaltPoll, a);
327 PDMR3Poll(pVM);
328 STAM_PROFILE_ADV_STOP(&pVM->vm.s.StatHaltPoll, a);
329 STAM_PROFILE_ADV_START(&pVM->vm.s.StatHaltTimers, b);
330 TMR3TimerQueuesDo(pVM);
331 STAM_PROFILE_ADV_STOP(&pVM->vm.s.StatHaltTimers, b);
332 if (VM_FF_ISPENDING(pVM, fMask))
333 break;
334 uint64_t u64NanoTS = TMVirtualToNano(pVM, TMTimerPoll(pVM));
335 if (VM_FF_ISPENDING(pVM, fMask))
336 break;
337
338 /*
339 * Wait for a while. Someone will wake us up or interrupt the call if
340 * anything needs our attention.
341 */
342 if (u64NanoTS < 50000)
343 {
344 RTLogPrintf("u64NanoTS=%RI64 cLoops=%d spin\n", u64NanoTS, cLoops++);
345 /* spin */;
346 }
347 else
348 {
349 VMMR3YieldStop(pVM);
350 uint64_t u64Start = RTTimeNanoTS();
351 if (u64NanoTS < 870000) /* this is a bit speculative... works fine on linux. */
352 {
353 RTLogPrintf("u64NanoTS=%RI64 cLoops=%d yield", u64NanoTS, cLoops++);
354 STAM_PROFILE_ADV_START(&pVM->vm.s.StatHaltYield, a);
355 RTThreadYield(); /* this is the best we can do here */
356 STAM_PROFILE_ADV_STOP(&pVM->vm.s.StatHaltYield, a);
357 }
358 else if (u64NanoTS < 2000000)
359 {
360 RTLogPrintf("u64NanoTS=%RI64 cLoops=%d sleep 1ms", u64NanoTS, cLoops++);
361 STAM_PROFILE_ADV_START(&pVM->vm.s.StatHaltBlock, a);
362 rc = RTSemEventWait(pVM->vm.s.EventSemWait, 1);
363 STAM_PROFILE_ADV_STOP(&pVM->vm.s.StatHaltBlock, a);
364 }
365 else
366 {
367 RTLogPrintf("u64NanoTS=%RI64 cLoops=%d sleep %dms", u64NanoTS, cLoops++, (uint32_t)RT_MIN((u64NanoTS - 500000) / 1000000, 15));
368 STAM_PROFILE_ADV_START(&pVM->vm.s.StatHaltBlock, a);
369 rc = RTSemEventWait(pVM->vm.s.EventSemWait, RT_MIN((u64NanoTS - 1000000) / 1000000, 15));
370 STAM_PROFILE_ADV_STOP(&pVM->vm.s.StatHaltBlock, a);
371 }
372 uint64_t u64Slept = RTTimeNanoTS() - u64Start;
373 RTLogPrintf(" -> rc=%Vrc in %RU64 ns / %RI64 ns delta\n", rc, u64Slept, u64NanoTS - u64Slept);
374 }
375 if (rc == VERR_TIMEOUT)
376 rc = VINF_SUCCESS;
377 else if (VBOX_FAILURE(rc))
378 {
379 AssertRC(rc != VERR_INTERRUPTED);
380 AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
381 VM_FF_SET(pVM, VM_FF_TERMINATE);
382 rc = VERR_INTERNAL_ERROR;
383 break;
384 }
385 }
386 ASMAtomicXchgU32(&pVM->vm.s.fWait, 0);
387
388 /*
389 *
390 * Pause the TSC, it's restarted when we start executing,
391 * and resume the yielder.
392 */
393 VMMR3YieldResume(pVM);
394
395
396 LogFlow(("VMR3WaitHalted: returns %Vrc (FF %#x)\n", rc, pVM->fForcedActions ));
397 return rc;
398}
399
400
401/**
402 * Suspended VM Wait.
403 * Only a handful of forced actions will cause the function to
404 * return to the caller.
405 *
406 * @returns VINF_SUCCESS unless a fatal error occured. In the latter
407 * case an appropriate status code is returned.
408 * @param pVM VM handle.
409 * @thread The emulation thread.
410 */
411VMR3DECL(int) VMR3Wait(PVM pVM)
412{
413 LogFlow(("VMR3Wait:\n"));
414
415 /*
416 * Check Relevant FFs.
417 */
418 if (VM_FF_ISPENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK))
419 {
420 LogFlow(("VMR3Wait: returns VINF_SUCCESS (FF %#x)\n", pVM->fForcedActions));
421 return VINF_SUCCESS;
422 }
423
424 int rc = VINF_SUCCESS;
425 ASMAtomicXchgU32(&pVM->vm.s.fWait, 1);
426 for (;;)
427 {
428 /*
429 * Check Relevant FFs.
430 */
431 if (VM_FF_ISPENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK))
432 break;
433
434 /*
435 * Wait for a while. Someone will wake us up or interrupt the call if
436 * anything needs our attention.
437 */
438 rc = RTSemEventWait(pVM->vm.s.EventSemWait, 1000);
439 if (rc == VERR_TIMEOUT)
440 rc = VINF_SUCCESS;
441 else if (VBOX_FAILURE(rc))
442 {
443 AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
444 VM_FF_SET(pVM, VM_FF_TERMINATE);
445 rc = VERR_INTERNAL_ERROR;
446 break;
447 }
448
449 }
450 ASMAtomicXchgU32(&pVM->vm.s.fWait, 0);
451
452 LogFlow(("VMR3Wait: returns %Vrc (FF %#x)\n", rc, pVM->fForcedActions));
453 return rc;
454}
455
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