VirtualBox

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

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

Backed out 18570 & 18569

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