~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/VBox/VMM/VMEmt.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: VMEmt.cpp 4295 2007-08-22 20:17:19Z vboxsync $ */
 
2
/** @file
 
3
 * VM - Virtual Machine, The Emulation Thread.
 
4
 */
 
5
 
 
6
/*
 
7
 * Copyright (C) 2006-2007 innotek 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
 
 
18
 
 
19
/*******************************************************************************
 
20
*   Header Files                                                               *
 
21
*******************************************************************************/
 
22
#define LOG_GROUP LOG_GROUP_VM
 
23
#include <VBox/tm.h>
 
24
#include <VBox/dbgf.h>
 
25
#include <VBox/em.h>
 
26
#include <VBox/pdmapi.h>
 
27
#include <VBox/rem.h>
 
28
#include "VMInternal.h"
 
29
#include <VBox/vm.h>
 
30
 
 
31
#include <VBox/err.h>
 
32
#include <VBox/log.h>
 
33
#include <iprt/assert.h>
 
34
#include <iprt/asm.h>
 
35
#include <iprt/semaphore.h>
 
36
#include <iprt/string.h>
 
37
#include <iprt/thread.h>
 
38
#include <iprt/time.h>
 
39
 
 
40
 
 
41
 
 
42
 
 
43
/**
 
44
 * The emulation thread.
 
45
 *
 
46
 * @returns Thread exit code.
 
47
 * @param   ThreadSelf  The handle to the executing thread.
 
48
 * @param   pvArgs      Pointer to a VMEMULATIONTHREADARGS structure.
 
49
 */
 
50
DECLCALLBACK(int) vmR3EmulationThread(RTTHREAD ThreadSelf, void *pvArgs)
 
51
{
 
52
    PVMEMULATIONTHREADARGS pArgs = (PVMEMULATIONTHREADARGS)pvArgs;
 
53
    AssertReleaseMsg(pArgs && pArgs->pVM, ("Invalid arguments to the emulation thread!\n"));
 
54
 
 
55
    /*
 
56
     * Init the native thread member.
 
57
     */
 
58
    PVM pVM = pArgs->pVM;
 
59
    pVM->NativeThreadEMT = RTThreadGetNative(ThreadSelf);
 
60
 
 
61
    /*
 
62
     * The request loop.
 
63
     */
 
64
    VMSTATE enmBefore;
 
65
    int     rc;
 
66
    Log(("vmR3EmulationThread: Emulation thread starting the days work... Thread=%#x pVM=%p\n", ThreadSelf, pVM));
 
67
    for (;;)
 
68
    {
 
69
        /* Requested to exit the EMT thread out of sync? (currently only VMR3WaitForResume) */
 
70
        if (setjmp(pVM->vm.s.emtJumpEnv) != 0)
 
71
        {
 
72
            rc = VINF_SUCCESS;
 
73
            break;
 
74
        }
 
75
 
 
76
        /*
 
77
         * Pending requests which needs servicing?
 
78
         *
 
79
         * We check for state changes in addition to status codes when
 
80
         * servicing requests. (Look after the ifs.)
 
81
         */
 
82
        enmBefore = pVM->enmVMState;
 
83
        if (VM_FF_ISSET(pVM, VM_FF_TERMINATE))
 
84
        {
 
85
            rc = VINF_EM_TERMINATE;
 
86
            break;
 
87
        }
 
88
        else if (pVM->vm.s.pReqs)
 
89
        {
 
90
            /*
 
91
             * Service execute in EMT request.
 
92
             */
 
93
            rc = VMR3ReqProcess(pVM);
 
94
            Log(("vmR3EmulationThread: Req rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
 
95
        }
 
96
        else if (VM_FF_ISSET(pVM, VM_FF_DBGF))
 
97
        {
 
98
            /*
 
99
             * Service the debugger request.
 
100
             */
 
101
            rc = DBGFR3VMMForcedAction(pVM);
 
102
            Log(("vmR3EmulationThread: Dbg rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
 
103
        }
 
104
        else if (VM_FF_ISSET(pVM, VM_FF_RESET))
 
105
        {
 
106
            /*
 
107
             * Service a delay reset request.
 
108
             */
 
109
            rc = VMR3Reset(pVM);
 
110
            VM_FF_CLEAR(pVM, VM_FF_RESET);
 
111
            Log(("vmR3EmulationThread: Reset rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
 
112
        }
 
113
        else
 
114
        {
 
115
            /*
 
116
             * Nothing important is pending, so wait for something.
 
117
             */
 
118
            rc = VMR3Wait(pVM);
 
119
            if (VBOX_FAILURE(rc))
 
120
                break;
 
121
        }
 
122
 
 
123
        /*
 
124
         * Check for termination requests, these are extremely high priority.
 
125
         */
 
126
        if (    rc == VINF_EM_TERMINATE
 
127
            ||  VM_FF_ISSET(pVM, VM_FF_TERMINATE))
 
128
            break;
 
129
 
 
130
        /*
 
131
         * Some requests (both VMR3Req* and the DBGF) can potentially
 
132
         * resume or start the VM, in that case we'll get a change in
 
133
         * VM status indicating that we're now running.
 
134
         */
 
135
        if (    VBOX_SUCCESS(rc)
 
136
            &&  enmBefore != pVM->enmVMState
 
137
            &&  (pVM->enmVMState == VMSTATE_RUNNING))
 
138
        {
 
139
            rc = EMR3ExecuteVM(pVM);
 
140
            Log(("vmR3EmulationThread: EMR3ExecuteVM() -> rc=%Vrc, enmVMState=%d\n", rc, pVM->enmVMState));
 
141
            if (EMGetState(pVM) == EMSTATE_GURU_MEDITATION)
 
142
                vmR3SetState(pVM, VMSTATE_GURU_MEDITATION);
 
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
/**
 
170
 * Wait for VM to be resumed. Handle events like vmR3EmulationThread does.
 
171
 * In case the VM is stopped, clean up and long jump to the main EMT loop.
 
172
 *
 
173
 * @returns VINF_SUCCESS or doesn't return
 
174
 * @param   pVM             VM handle.
 
175
 */
 
176
VMR3DECL(int) VMR3WaitForResume(PVM pVM)
 
177
{
 
178
    /*
 
179
     * The request loop.
 
180
     */
 
181
    VMSTATE enmBefore;
 
182
    int     rc;
 
183
    for (;;)
 
184
    {
 
185
 
 
186
        /*
 
187
         * Pending requests which needs servicing?
 
188
         *
 
189
         * We check for state changes in addition to status codes when
 
190
         * servicing requests. (Look after the ifs.)
 
191
         */
 
192
        enmBefore = pVM->enmVMState;
 
193
        if (VM_FF_ISSET(pVM, VM_FF_TERMINATE))
 
194
        {
 
195
            rc = VINF_EM_TERMINATE;
 
196
            break;
 
197
        }
 
198
        else if (pVM->vm.s.pReqs)
 
199
        {
 
200
            /*
 
201
             * Service execute in EMT request.
 
202
             */
 
203
            rc = VMR3ReqProcess(pVM);
 
204
            Log(("vmR3EmulationThread: Req rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
 
205
        }
 
206
        else if (VM_FF_ISSET(pVM, VM_FF_DBGF))
 
207
        {
 
208
            /*
 
209
             * Service the debugger request.
 
210
             */
 
211
            rc = DBGFR3VMMForcedAction(pVM);
 
212
            Log(("vmR3EmulationThread: Dbg rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
 
213
        }
 
214
        else if (VM_FF_ISSET(pVM, VM_FF_RESET))
 
215
        {
 
216
            /*
 
217
             * Service a delay reset request.
 
218
             */
 
219
            rc = VMR3Reset(pVM);
 
220
            VM_FF_CLEAR(pVM, VM_FF_RESET);
 
221
            Log(("vmR3EmulationThread: Reset rc=%Vrc, VM state %d -> %d\n", rc, enmBefore, pVM->enmVMState));
 
222
        }
 
223
        else
 
224
        {
 
225
            /*
 
226
             * Nothing important is pending, so wait for something.
 
227
             */
 
228
            rc = VMR3Wait(pVM);
 
229
            if (VBOX_FAILURE(rc))
 
230
                break;
 
231
        }
 
232
 
 
233
        /*
 
234
         * Check for termination requests, these are extremely high priority.
 
235
         */
 
236
        if (    rc == VINF_EM_TERMINATE
 
237
            ||  VM_FF_ISSET(pVM, VM_FF_TERMINATE))
 
238
            break;
 
239
 
 
240
        /*
 
241
         * Some requests (both VMR3Req* and the DBGF) can potentially
 
242
         * resume or start the VM, in that case we'll get a change in
 
243
         * VM status indicating that we're now running.
 
244
         */
 
245
        if (    VBOX_SUCCESS(rc)
 
246
            &&  enmBefore != pVM->enmVMState
 
247
            &&  (pVM->enmVMState == VMSTATE_RUNNING))
 
248
        {
 
249
            /* Only valid exit reason. */
 
250
            return VINF_SUCCESS;
 
251
        }
 
252
 
 
253
    } /* forever */
 
254
 
 
255
    /* Return to the main loop in vmR3EmulationThread, which will clean up for us. */
 
256
    longjmp(pVM->vm.s.emtJumpEnv, 1);
 
257
}
 
258
 
 
259
 
 
260
/**
 
261
 * The old halt loop.
 
262
 */
 
263
static DECLCALLBACK(int) vmR3HaltOldDoHalt(PVM pVM, const uint32_t fMask, uint64_t /* u64Now*/)
 
264
{
 
265
    /*
 
266
     * Halt loop.
 
267
     */
 
268
    int rc = VINF_SUCCESS;
 
269
    ASMAtomicXchgU32(&pVM->vm.s.fWait, 1);
 
270
    //unsigned cLoops = 0;
 
271
    for (;;)
 
272
    {
 
273
        /*
 
274
         * Work the timers and check if we can exit.
 
275
         * The poll call gives us the ticks left to the next event in
 
276
         * addition to perhaps set an FF.
 
277
         */
 
278
        STAM_REL_PROFILE_START(&pVM->vm.s.StatHaltPoll, a);
 
279
        PDMR3Poll(pVM);
 
280
        STAM_REL_PROFILE_STOP(&pVM->vm.s.StatHaltPoll, a);
 
281
        STAM_REL_PROFILE_START(&pVM->vm.s.StatHaltTimers, b);
 
282
        TMR3TimerQueuesDo(pVM);
 
283
        STAM_REL_PROFILE_STOP(&pVM->vm.s.StatHaltTimers, b);
 
284
        if (VM_FF_ISPENDING(pVM, fMask))
 
285
            break;
 
286
        uint64_t u64NanoTS = TMVirtualToNano(pVM, TMTimerPoll(pVM));
 
287
        if (VM_FF_ISPENDING(pVM, fMask))
 
288
            break;
 
289
 
 
290
        /*
 
291
         * Wait for a while. Someone will wake us up or interrupt the call if
 
292
         * anything needs our attention.
 
293
         */
 
294
        if (u64NanoTS < 50000)
 
295
        {
 
296
            //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d spin\n", u64NanoTS, cLoops++);
 
297
            /* spin */;
 
298
        }
 
299
        else
 
300
        {
 
301
            VMMR3YieldStop(pVM);
 
302
            //uint64_t u64Start = RTTimeNanoTS();
 
303
            if (u64NanoTS <  870000) /* this is a bit speculative... works fine on linux. */
 
304
            {
 
305
                //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d yield", u64NanoTS, cLoops++);
 
306
                STAM_REL_PROFILE_START(&pVM->vm.s.StatHaltYield, a);
 
307
                RTThreadYield(); /* this is the best we can do here */
 
308
                STAM_REL_PROFILE_STOP(&pVM->vm.s.StatHaltYield, a);
 
309
            }
 
310
            else if (u64NanoTS < 2000000)
 
311
            {
 
312
                //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d sleep 1ms", u64NanoTS, cLoops++);
 
313
                STAM_REL_PROFILE_START(&pVM->vm.s.StatHaltBlock, a);
 
314
                rc = RTSemEventWait(pVM->vm.s.EventSemWait, 1);
 
315
                STAM_REL_PROFILE_STOP(&pVM->vm.s.StatHaltBlock, a);
 
316
            }
 
317
            else
 
318
            {
 
319
                //RTLogPrintf("u64NanoTS=%RI64 cLoops=%d sleep %dms", u64NanoTS, cLoops++, (uint32_t)RT_MIN((u64NanoTS - 500000) / 1000000, 15));
 
320
                STAM_REL_PROFILE_START(&pVM->vm.s.StatHaltBlock, a);
 
321
                rc = RTSemEventWait(pVM->vm.s.EventSemWait, RT_MIN((u64NanoTS - 1000000) / 1000000, 15));
 
322
                STAM_REL_PROFILE_STOP(&pVM->vm.s.StatHaltBlock, a);
 
323
            }
 
324
            //uint64_t u64Slept = RTTimeNanoTS() - u64Start;
 
325
            //RTLogPrintf(" -> rc=%Vrc in %RU64 ns / %RI64 ns delta\n", rc, u64Slept, u64NanoTS - u64Slept);
 
326
        }
 
327
        if (rc == VERR_TIMEOUT)
 
328
            rc = VINF_SUCCESS;
 
329
        else if (VBOX_FAILURE(rc))
 
330
        {
 
331
            AssertRC(rc != VERR_INTERRUPTED);
 
332
            AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
 
333
            VM_FF_SET(pVM, VM_FF_TERMINATE);
 
334
            rc = VERR_INTERNAL_ERROR;
 
335
            break;
 
336
        }
 
337
    }
 
338
 
 
339
    return rc;
 
340
}
 
341
 
 
342
 
 
343
/**
 
344
 * Initialize the configuration of halt method 1 & 2.
 
345
 *
 
346
 * @return VBox status code. Failure on invalid CFGM data.
 
347
 * @param   pVM     The VM handle.
 
348
 */
 
349
static int vmR3HaltMethod12ReadConfig(PVM pVM)
 
350
{
 
351
    /*
 
352
     * The defaults.
 
353
     */
 
354
    pVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg = 4;
 
355
    pVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg =   5*1000000;
 
356
    pVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg = 200*1000000;
 
357
    pVM->vm.s.Halt.Method12.u32StartSpinningCfg    =  20*1000000;
 
358
    pVM->vm.s.Halt.Method12.u32StopSpinningCfg     =   2*1000000;
 
359
 
 
360
    /*
 
361
     * Query overrides.
 
362
     */
 
363
    PCFGMNODE pCfg = CFGMR3GetChild(CFGMR3GetRoot(pVM), "/VMM/HaltedMethod1");
 
364
    if (pCfg)
 
365
    {
 
366
 
 
367
    }
 
368
 
 
369
    return VINF_SUCCESS;
 
370
}
 
371
 
 
372
 
 
373
/**
 
374
 * Initialize halt method 1.
 
375
 *
 
376
 * @return VBox status code.
 
377
 * @param   pVM     The VM handle.
 
378
 */
 
379
static DECLCALLBACK(int) vmR3HaltMethod1Init(PVM pVM)
 
380
{
 
381
    return vmR3HaltMethod12ReadConfig(pVM);
 
382
}
 
383
 
 
384
 
 
385
/**
 
386
 * Method 1 - Block whenever possible, and when lagging behind
 
387
 * switch to spinning for 10-30ms with occational blocking until
 
388
 * the lag has been eliminated.
 
389
 */
 
390
static DECLCALLBACK(int) vmR3HaltMethod1DoHalt(PVM pVM, const uint32_t fMask, uint64_t u64Now)
 
391
{
 
392
    /*
 
393
     * To simplify things, we decide up-front whether we should switch to spinning or
 
394
     * not. This makes some ASSUMPTIONS about the cause of the spinning (PIT/RTC/PCNet)
 
395
     * and that it will generate interrupts or other events that will cause us to exit
 
396
     * the halt loop.
 
397
     */
 
398
    bool fBlockOnce = false;
 
399
    bool fSpinning = false;
 
400
    uint32_t u32CatchUpPct = TMVirtualSyncGetCatchUpPct(pVM);
 
401
    if (u32CatchUpPct /* non-zero if catching up */)
 
402
    {
 
403
        if (pVM->vm.s.Halt.Method12.u64StartSpinTS)
 
404
        {
 
405
            fSpinning = TMVirtualSyncGetLag(pVM) >= pVM->vm.s.Halt.Method12.u32StopSpinningCfg;
 
406
            if (fSpinning)
 
407
            {
 
408
                uint64_t u64Lag = TMVirtualSyncGetLag(pVM);
 
409
                fBlockOnce = u64Now - pVM->vm.s.Halt.Method12.u64LastBlockTS
 
410
                           > RT_MAX(pVM->vm.s.Halt.Method12.u32MinBlockIntervalCfg,
 
411
                                    RT_MIN(u64Lag / pVM->vm.s.Halt.Method12.u32LagBlockIntervalDivisorCfg,
 
412
                                           pVM->vm.s.Halt.Method12.u32MaxBlockIntervalCfg));
 
413
            }
 
414
            else
 
415
            {
 
416
                //RTLogRelPrintf("Stopped spinning (%u ms)\n", (u64Now - pVM->vm.s.Halt.Method12.u64StartSpinTS) / 1000000);
 
417
                pVM->vm.s.Halt.Method12.u64StartSpinTS = 0;
 
418
            }
 
419
        }
 
420
        else
 
421
        {
 
422
            fSpinning = TMVirtualSyncGetLag(pVM) >= pVM->vm.s.Halt.Method12.u32StartSpinningCfg;
 
423
            if (fSpinning)
 
424
                pVM->vm.s.Halt.Method12.u64StartSpinTS = u64Now;
 
425
        }
 
426
    }
 
427
    else if (pVM->vm.s.Halt.Method12.u64StartSpinTS)
 
428
    {
 
429
        //RTLogRelPrintf("Stopped spinning (%u ms)\n", (u64Now - pVM->vm.s.Halt.Method12.u64StartSpinTS) / 1000000);
 
430
        pVM->vm.s.Halt.Method12.u64StartSpinTS = 0;
 
431
    }
 
432
 
 
433
    /*
 
434
     * Halt loop.
 
435
     */
 
436
    int rc = VINF_SUCCESS;
 
437
    ASMAtomicXchgU32(&pVM->vm.s.fWait, 1);
 
438
    unsigned cLoops = 0;
 
439
    for (;; cLoops++)
 
440
    {
 
441
        /*
 
442
         * Work the timers and check if we can exit.
 
443
         */
 
444
        STAM_REL_PROFILE_START(&pVM->vm.s.StatHaltPoll, a);
 
445
        PDMR3Poll(pVM);
 
446
        STAM_REL_PROFILE_STOP(&pVM->vm.s.StatHaltPoll, a);
 
447
        STAM_REL_PROFILE_START(&pVM->vm.s.StatHaltTimers, b);
 
448
        TMR3TimerQueuesDo(pVM);
 
449
        STAM_REL_PROFILE_STOP(&pVM->vm.s.StatHaltTimers, b);
 
450
        if (VM_FF_ISPENDING(pVM, fMask))
 
451
            break;
 
452
 
 
453
        /*
 
454
         * Estimate time left to the next event.
 
455
         */
 
456
        uint64_t u64NanoTS = TMVirtualToNano(pVM, TMTimerPoll(pVM));
 
457
        if (VM_FF_ISPENDING(pVM, fMask))
 
458
            break;
 
459
 
 
460
        /*
 
461
         * Block if we're not spinning and the interval isn't all that small.
 
462
         */
 
463
        if (    (   !fSpinning
 
464
                 || fBlockOnce)
 
465
            &&  u64NanoTS >= 250000) /* 0.250 ms */
 
466
        {
 
467
            const uint64_t Start = pVM->vm.s.Halt.Method12.u64LastBlockTS = RTTimeNanoTS();
 
468
            VMMR3YieldStop(pVM);
 
469
 
 
470
            uint32_t cMilliSecs = RT_MIN(u64NanoTS / 1000000, 15);
 
471
            if (cMilliSecs <= pVM->vm.s.Halt.Method12.cNSBlockedTooLongAvg)
 
472
                cMilliSecs = 1;
 
473
            else
 
474
                cMilliSecs -= pVM->vm.s.Halt.Method12.cNSBlockedTooLongAvg;
 
475
            //RTLogRelPrintf("u64NanoTS=%RI64 cLoops=%3d sleep %02dms (%7RU64) ", u64NanoTS, cLoops, cMilliSecs, u64NanoTS);
 
476
            STAM_REL_PROFILE_START(&pVM->vm.s.StatHaltBlock, a);
 
477
            rc = RTSemEventWait(pVM->vm.s.EventSemWait, cMilliSecs);
 
478
            STAM_REL_PROFILE_STOP(&pVM->vm.s.StatHaltBlock, a);
 
479
            if (rc == VERR_TIMEOUT)
 
480
                rc = VINF_SUCCESS;
 
481
            else if (VBOX_FAILURE(rc))
 
482
            {
 
483
                AssertRC(rc != VERR_INTERRUPTED);
 
484
                AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
 
485
                VM_FF_SET(pVM, VM_FF_TERMINATE);
 
486
                rc = VERR_INTERNAL_ERROR;
 
487
                break;
 
488
            }
 
489
 
 
490
            /*
 
491
             * Calc the statistics.
 
492
             * Update averages every 16th time, and flush parts of the history every 64th time.
 
493
             */
 
494
            const uint64_t Elapsed = RTTimeNanoTS() - Start;
 
495
            pVM->vm.s.Halt.Method12.cNSBlocked += Elapsed;
 
496
            if (Elapsed > u64NanoTS)
 
497
                pVM->vm.s.Halt.Method12.cNSBlockedTooLong += Elapsed - u64NanoTS;
 
498
            pVM->vm.s.Halt.Method12.cBlocks++;
 
499
            if (!(pVM->vm.s.Halt.Method12.cBlocks & 0xf))
 
500
            {
 
501
                pVM->vm.s.Halt.Method12.cNSBlockedTooLongAvg = pVM->vm.s.Halt.Method12.cNSBlockedTooLong / pVM->vm.s.Halt.Method12.cBlocks;
 
502
                if (!(pVM->vm.s.Halt.Method12.cBlocks & 0x3f))
 
503
                {
 
504
                    pVM->vm.s.Halt.Method12.cNSBlockedTooLong = pVM->vm.s.Halt.Method12.cNSBlockedTooLongAvg * 0x40;
 
505
                    pVM->vm.s.Halt.Method12.cBlocks = 0x40;
 
506
                }
 
507
            }
 
508
            //RTLogRelPrintf(" -> %7RU64 ns / %7RI64 ns delta%s\n", Elapsed, Elapsed - u64NanoTS, fBlockOnce ? " (block once)" : "");
 
509
 
 
510
            /*
 
511
             * Clear the block once flag if we actually blocked.
 
512
             */
 
513
            if (    fBlockOnce
 
514
                &&  Elapsed > 100000 /* 0.1 ms */)
 
515
                fBlockOnce = false;
 
516
        }
 
517
    }
 
518
    //if (fSpinning) RTLogRelPrintf("spun for %RU64 ns %u loops; lag=%RU64 pct=%d\n", RTTimeNanoTS() - u64Now, cLoops, TMVirtualSyncGetLag(pVM), u32CatchUpPct);
 
519
 
 
520
    return rc;
 
521
}
 
522
 
 
523
 
 
524
/**
 
525
 * Default VMR3Wait() worker.
 
526
 *
 
527
 * @returns VBox status code.
 
528
 * @param   pVM     The VM handle.
 
529
 */
 
530
static DECLCALLBACK(int) vmR3DefaultWait(PVM pVM)
 
531
{
 
532
    int rc = VINF_SUCCESS;
 
533
    ASMAtomicXchgU32(&pVM->vm.s.fWait, 1);
 
534
    for (;;)
 
535
    {
 
536
        /*
 
537
         * Check Relevant FFs.
 
538
         */
 
539
        if (VM_FF_ISPENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK))
 
540
            break;
 
541
 
 
542
        /*
 
543
         * Wait for a while. Someone will wake us up or interrupt the call if
 
544
         * anything needs our attention.
 
545
         */
 
546
        rc = RTSemEventWait(pVM->vm.s.EventSemWait, 1000);
 
547
        if (rc == VERR_TIMEOUT)
 
548
            rc = VINF_SUCCESS;
 
549
        else if (VBOX_FAILURE(rc))
 
550
        {
 
551
            AssertMsgFailed(("RTSemEventWait->%Vrc\n", rc));
 
552
            VM_FF_SET(pVM, VM_FF_TERMINATE);
 
553
            rc = VERR_INTERNAL_ERROR;
 
554
            break;
 
555
        }
 
556
 
 
557
    }
 
558
    ASMAtomicXchgU32(&pVM->vm.s.fWait, 0);
 
559
    return rc;
 
560
}
 
561
 
 
562
 
 
563
/**
 
564
 * Default VMR3NotifyFF() worker.
 
565
 *
 
566
 * @param   pVM             The VM handle.
 
567
 * @param   fNotifiedREM    Se VMR3NotifyFF().
 
568
 */
 
569
static DECLCALLBACK(void) vmR3DefaultNotifyFF(PVM pVM, bool fNotifiedREM)
 
570
{
 
571
    if (pVM->vm.s.fWait)
 
572
    {
 
573
        int rc = RTSemEventSignal(pVM->vm.s.EventSemWait);
 
574
        AssertRC(rc);
 
575
    }
 
576
    else if (!fNotifiedREM)
 
577
        REMR3NotifyFF(pVM);
 
578
}
 
579
 
 
580
 
 
581
/**
 
582
 * Array with halt method descriptors.
 
583
 * VMINT::iHaltMethod contains an index into this array.
 
584
 */
 
585
static const struct VMHALTMETHODDESC
 
586
{
 
587
    /** The halt method id. */
 
588
    VMHALTMETHOD enmHaltMethod;
 
589
    /** The init function for loading config and initialize variables. */
 
590
    DECLR3CALLBACKMEMBER(int,  pfnInit,(PVM pVM));
 
591
    /** The term function. */
 
592
    DECLR3CALLBACKMEMBER(void, pfnTerm,(PVM pVM));
 
593
    /** The halt function. */
 
594
    DECLR3CALLBACKMEMBER(int,  pfnHalt,(PVM pVM, const uint32_t fMask, uint64_t u64Now));
 
595
    /** The wait function. */
 
596
    DECLR3CALLBACKMEMBER(int,  pfnWait,(PVM pVM));
 
597
    /** The notifyFF function. */
 
598
    DECLR3CALLBACKMEMBER(void, pfnNotifyFF,(PVM pVM, bool fNotifiedREM));
 
599
} g_aHaltMethods[] =
 
600
{
 
601
    { VMHALTMETHOD_OLD,     NULL,                   NULL,                   vmR3HaltOldDoHalt,      vmR3DefaultWait,        vmR3DefaultNotifyFF },
 
602
    { VMHALTMETHOD_1,       vmR3HaltMethod1Init,    NULL,                   vmR3HaltMethod1DoHalt,  vmR3DefaultWait,        vmR3DefaultNotifyFF },
 
603
  //{ VMHALTMETHOD_2,       vmR3HaltMethod2Init,    vmR3HaltMethod2Term,    vmR3HaltMethod2DoWait,  vmR3HaltMethod2Wait,    vmR3HaltMethod2NotifyFF },
 
604
};
 
605
 
 
606
 
 
607
/**
 
608
 * Notify the emulation thread (EMT) about pending Forced Action (FF).
 
609
 *
 
610
 * This function is called by thread other than EMT to make
 
611
 * sure EMT wakes up and promptly service an FF request.
 
612
 *
 
613
 * @param   pVM             VM handle.
 
614
 * @param   fNotifiedREM    Set if REM have already been notified. If clear the
 
615
 *                          generic REMR3NotifyFF() method is called.
 
616
 */
 
617
VMR3DECL(void) VMR3NotifyFF(PVM pVM, bool fNotifiedREM)
 
618
{
 
619
    LogFlow(("VMR3NotifyFF:\n"));
 
620
    g_aHaltMethods[pVM->vm.s.iHaltMethod].pfnNotifyFF(pVM, fNotifiedREM);
 
621
}
 
622
 
 
623
 
 
624
/**
 
625
 * Halted VM Wait.
 
626
 * Any external event will unblock the thread.
 
627
 *
 
628
 * @returns VINF_SUCCESS unless a fatal error occured. In the latter
 
629
 *          case an appropriate status code is returned.
 
630
 * @param   pVM         VM handle.
 
631
 * @param   fIgnoreInterrupts   If set the VM_FF_INTERRUPT flags is ignored.
 
632
 * @thread  The emulation thread.
 
633
 */
 
634
VMR3DECL(int) VMR3WaitHalted(PVM pVM, bool fIgnoreInterrupts)
 
635
{
 
636
    LogFlow(("VMR3WaitHalted: fIgnoreInterrupts=%d\n", fIgnoreInterrupts));
 
637
 
 
638
    /*
 
639
     * Check Relevant FFs.
 
640
     */
 
641
    const uint32_t fMask = !fIgnoreInterrupts
 
642
        ? VM_FF_EXTERNAL_HALTED_MASK
 
643
        : VM_FF_EXTERNAL_HALTED_MASK & ~(VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC);
 
644
    if (VM_FF_ISPENDING(pVM, fMask))
 
645
    {
 
646
        LogFlow(("VMR3WaitHalted: returns VINF_SUCCESS (FF %#x)\n", pVM->fForcedActions));
 
647
        return VINF_SUCCESS;
 
648
    }
 
649
 
 
650
    /*
 
651
     * The yielder is suspended while we're halting.
 
652
     */
 
653
    VMMR3YieldSuspend(pVM);
 
654
 
 
655
    /*
 
656
     * Record halt averages for the last second.
 
657
     */
 
658
    uint64_t u64Now = RTTimeNanoTS();
 
659
    int64_t off = u64Now - pVM->vm.s.u64HaltsStartTS;
 
660
    if (off > 1000000000)
 
661
    {
 
662
        if (off > _4G || !pVM->vm.s.cHalts)
 
663
        {
 
664
            pVM->vm.s.HaltInterval = 1000000000 /* 1 sec */;
 
665
            pVM->vm.s.HaltFrequency = 1;
 
666
        }
 
667
        else
 
668
        {
 
669
            pVM->vm.s.HaltInterval = (uint32_t)off / pVM->vm.s.cHalts;
 
670
            pVM->vm.s.HaltFrequency = ASMMultU64ByU32DivByU32(pVM->vm.s.cHalts, 1000000000, (uint32_t)off);
 
671
        }
 
672
        pVM->vm.s.u64HaltsStartTS = u64Now;
 
673
        pVM->vm.s.cHalts = 0;
 
674
    }
 
675
    pVM->vm.s.cHalts++;
 
676
 
 
677
    /*
 
678
     * Do the halt.
 
679
     */
 
680
    int rc = g_aHaltMethods[pVM->vm.s.iHaltMethod].pfnHalt(pVM, fMask, u64Now);
 
681
 
 
682
    /*
 
683
     * Resume the yielder and tell the world we're not blocking.
 
684
     */
 
685
    ASMAtomicXchgU32(&pVM->vm.s.fWait, 0);
 
686
    VMMR3YieldResume(pVM);
 
687
 
 
688
    LogFlow(("VMR3WaitHalted: returns %Vrc (FF %#x)\n", rc, pVM->fForcedActions));
 
689
    return rc;
 
690
}
 
691
 
 
692
 
 
693
/**
 
694
 * Suspended VM Wait.
 
695
 * Only a handful of forced actions will cause the function to
 
696
 * return to the caller.
 
697
 *
 
698
 * @returns VINF_SUCCESS unless a fatal error occured. In the latter
 
699
 *          case an appropriate status code is returned.
 
700
 * @param   pVM         VM handle.
 
701
 * @thread  The emulation thread.
 
702
 */
 
703
VMR3DECL(int) VMR3Wait(PVM pVM)
 
704
{
 
705
    LogFlow(("VMR3Wait:\n"));
 
706
 
 
707
    /*
 
708
     * Check Relevant FFs.
 
709
     */
 
710
    if (VM_FF_ISPENDING(pVM, VM_FF_EXTERNAL_SUSPENDED_MASK))
 
711
    {
 
712
        LogFlow(("VMR3Wait: returns VINF_SUCCESS (FF %#x)\n", pVM->fForcedActions));
 
713
        return VINF_SUCCESS;
 
714
    }
 
715
 
 
716
    /*
 
717
     * Do waiting according to the halt method (so VMR3NotifyFF
 
718
     * doesn't have to special case anything).
 
719
     */
 
720
    int rc = g_aHaltMethods[pVM->vm.s.iHaltMethod].pfnWait(pVM);
 
721
    LogFlow(("VMR3Wait: returns %Vrc (FF %#x)\n", rc, pVM->fForcedActions));
 
722
    return rc;
 
723
}
 
724
 
 
725
 
 
726
/**
 
727
 * Changes the halt method.
 
728
 *
 
729
 * @returns VBox status code.
 
730
 * @param   pVM             The VM handle.
 
731
 * @param   enmHaltMethod   The new halt method.
 
732
 * @thread  EMT.
 
733
 */
 
734
int vmR3SetHaltMethod(PVM pVM, VMHALTMETHOD enmHaltMethod)
 
735
{
 
736
    VM_ASSERT_EMT(pVM);
 
737
    AssertReturn(enmHaltMethod > VMHALTMETHOD_INVALID && enmHaltMethod < VMHALTMETHOD_END, VERR_INVALID_PARAMETER);
 
738
 
 
739
    /*
 
740
     * Resolve default (can be overridden in the configuration).
 
741
     */
 
742
    if (enmHaltMethod == VMHALTMETHOD_DEFAULT)
 
743
    {
 
744
        uint32_t u32;
 
745
        int rc = CFGMR3QueryU32(CFGMR3GetChild(CFGMR3GetRoot(pVM), "VM"), "HaltMethod", &u32);
 
746
        if (VBOX_SUCCESS(rc))
 
747
        {
 
748
            enmHaltMethod = (VMHALTMETHOD)u32;
 
749
            if (enmHaltMethod <= VMHALTMETHOD_INVALID || enmHaltMethod >= VMHALTMETHOD_END)
 
750
                return VMSetError(pVM, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("Invalid VM/HaltMethod value %d."), enmHaltMethod);
 
751
        }
 
752
        else if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_CHILD_NOT_FOUND)
 
753
            return VMSetError(pVM, rc, RT_SRC_POS, N_("Failed to Query VM/HaltMethod as uint32_t."));
 
754
        else
 
755
            enmHaltMethod = VMHALTMETHOD_1;
 
756
            //enmHaltMethod = VMHALTMETHOD_OLD;
 
757
    }
 
758
 
 
759
    /*
 
760
     * Find the descriptor.
 
761
     */
 
762
    unsigned i = 0;
 
763
    while (     i < RT_ELEMENTS(g_aHaltMethods)
 
764
           &&   g_aHaltMethods[i].enmHaltMethod != enmHaltMethod)
 
765
        i++;
 
766
    AssertReturn(i < RT_ELEMENTS(g_aHaltMethods), VERR_INVALID_PARAMETER);
 
767
 
 
768
    /*
 
769
     * Terminate the old one.
 
770
     */
 
771
    if (    pVM->vm.s.enmHaltMethod != VMHALTMETHOD_INVALID
 
772
        &&  g_aHaltMethods[pVM->vm.s.iHaltMethod].pfnTerm)
 
773
    {
 
774
        g_aHaltMethods[pVM->vm.s.iHaltMethod].pfnTerm(pVM);
 
775
        pVM->vm.s.enmHaltMethod = VMHALTMETHOD_INVALID;
 
776
    }
 
777
 
 
778
    /*
 
779
     * Init the new one.
 
780
     */
 
781
    memset(&pVM->vm.s.Halt, 0, sizeof(pVM->vm.s.Halt));
 
782
    if (g_aHaltMethods[i].pfnInit)
 
783
    {
 
784
        int rc = g_aHaltMethods[i].pfnInit(pVM);
 
785
        AssertRCReturn(rc, rc);
 
786
    }
 
787
    pVM->vm.s.enmHaltMethod = enmHaltMethod;
 
788
    ASMAtomicXchgU32(&pVM->vm.s.iHaltMethod, i);
 
789
    return VINF_SUCCESS;
 
790
}
 
791