VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp@ 32815

Last change on this file since 32815 was 32815, checked in by vboxsync, 14 years ago

GVMMR0SchedHalt: limit the timeout to 1 second

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 83.9 KB
Line 
1/* $Id: GVMMR0.cpp 32815 2010-09-29 14:02:18Z vboxsync $ */
2/** @file
3 * GVMM - Global VM Manager.
4 */
5
6/*
7 * Copyright (C) 2007-2010 Oracle Corporation
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 (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/** @page pg_gvmm GVMM - The Global VM Manager
20 *
21 * The Global VM Manager lives in ring-0. Its main function at the moment is
22 * to manage a list of all running VMs, keep a ring-0 only structure (GVM) for
23 * each of them, and assign them unique identifiers (so GMM can track page
24 * owners). The GVMM also manage some of the host CPU resources, like the the
25 * periodic preemption timer.
26 *
27 * The GVMM will create a ring-0 object for each VM when it is registered, this
28 * is both for session cleanup purposes and for having a point where it is
29 * possible to implement usage polices later (in SUPR0ObjRegister).
30 *
31 *
32 * @section sec_gvmm_ppt Periodic Preemption Timer (PPT)
33 *
34 * On system that sports a high resolution kernel timer API, we use per-cpu
35 * timers to generate interrupts that preempts VT-x, AMD-V and raw-mode guest
36 * execution. The timer frequency is calculating by taking the max
37 * TMCalcHostTimerFrequency for all VMs running on a CPU for the last ~160 ms
38 * (RT_ELEMENTS((PGVMMHOSTCPU)0, Ppt.aHzHistory) *
39 * GVMMHOSTCPU_PPT_HIST_INTERVAL_NS).
40 *
41 * The TMCalcHostTimerFrequency() part of the things gets its takes the max
42 * TMTimerSetFrequencyHint() value and adjusts by the current catch-up percent,
43 * warp drive percent and some fudge factors. VMMR0.cpp reports the result via
44 * GVMMR0SchedUpdatePeriodicPreemptionTimer() before switching to the VT-x,
45 * AMD-V and raw-mode execution environments.
46 */
47
48
49/*******************************************************************************
50* Header Files *
51*******************************************************************************/
52#define LOG_GROUP LOG_GROUP_GVMM
53#include <VBox/gvmm.h>
54#include <VBox/gmm.h>
55#include "GVMMR0Internal.h"
56#include <VBox/gvm.h>
57#include <VBox/vm.h>
58#include <VBox/vmm.h>
59#include <VBox/param.h>
60#include <VBox/err.h>
61#include <iprt/asm.h>
62#include <iprt/asm-amd64-x86.h>
63#include <iprt/mem.h>
64#include <iprt/semaphore.h>
65#include <iprt/time.h>
66#include <VBox/log.h>
67#include <iprt/thread.h>
68#include <iprt/process.h>
69#include <iprt/param.h>
70#include <iprt/string.h>
71#include <iprt/assert.h>
72#include <iprt/mem.h>
73#include <iprt/memobj.h>
74#include <iprt/mp.h>
75#include <iprt/cpuset.h>
76#include <iprt/spinlock.h>
77#include <iprt/timer.h>
78
79
80/*******************************************************************************
81* Defined Constants And Macros *
82*******************************************************************************/
83#if defined(RT_OS_LINUX) || defined(DOXYGEN_RUNNING)
84/** Define this to enable the periodic preemption timer. */
85# define GVMM_SCHED_WITH_PPT
86#endif
87
88
89/*******************************************************************************
90* Structures and Typedefs *
91*******************************************************************************/
92
93/**
94 * Global VM handle.
95 */
96typedef struct GVMHANDLE
97{
98 /** The index of the next handle in the list (free or used). (0 is nil.) */
99 uint16_t volatile iNext;
100 /** Our own index / handle value. */
101 uint16_t iSelf;
102 /** The pointer to the ring-0 only (aka global) VM structure. */
103 PGVM pGVM;
104 /** The ring-0 mapping of the shared VM instance data. */
105 PVM pVM;
106 /** The virtual machine object. */
107 void *pvObj;
108 /** The session this VM is associated with. */
109 PSUPDRVSESSION pSession;
110 /** The ring-0 handle of the EMT0 thread.
111 * This is used for ownership checks as well as looking up a VM handle by thread
112 * at times like assertions. */
113 RTNATIVETHREAD hEMT0;
114 /** The process ID of the handle owner.
115 * This is used for access checks. */
116 RTPROCESS ProcId;
117} GVMHANDLE;
118/** Pointer to a global VM handle. */
119typedef GVMHANDLE *PGVMHANDLE;
120
121/** Number of GVM handles (including the NIL handle). */
122#if HC_ARCH_BITS == 64
123# define GVMM_MAX_HANDLES 1024
124#else
125# define GVMM_MAX_HANDLES 128
126#endif
127
128/**
129 * Per host CPU GVMM data.
130 */
131typedef struct GVMMHOSTCPU
132{
133 /** Magic number (GVMMHOSTCPU_MAGIC). */
134 uint32_t volatile u32Magic;
135 /** The CPU ID. */
136 RTCPUID idCpu;
137 /** The CPU set index. */
138 uint32_t idxCpuSet;
139
140#ifdef GVMM_SCHED_WITH_PPT
141 /** Periodic preemption timer data. */
142 struct
143 {
144 /** The handle to the periodic preemption timer. */
145 PRTTIMER pTimer;
146 /** Spinlock protecting the data below. */
147 RTSPINLOCK hSpinlock;
148 /** The smalles Hz that we need to care about. (static) */
149 uint32_t uMinHz;
150 /** The number of ticks between each historization. */
151 uint32_t cTicksHistoriziationInterval;
152 /** The current historization tick (counting up to
153 * cTicksHistoriziationInterval and then resetting). */
154 uint32_t iTickHistorization;
155 /** The current timer interval. This is set to 0 when inactive. */
156 uint32_t cNsInterval;
157 /** The current timer frequency. This is set to 0 when inactive. */
158 uint32_t uTimerHz;
159 /** The current max frequency reported by the EMTs.
160 * This gets historicize and reset by the timer callback. This is
161 * read without holding the spinlock, so needs atomic updating. */
162 uint32_t volatile uDesiredHz;
163 /** Whether the timer was started or not. */
164 bool volatile fStarted;
165 /** Set if we're starting timer. */
166 bool volatile fStarting;
167 /** The index of the next history entry (mod it). */
168 uint32_t iHzHistory;
169 /** Hitoricized uDesiredHz values. The array wraps around, new entries
170 * are added at iHzHistory. This is updated approximately every
171 * GVMMHOSTCPU_PPT_HIST_INTERVAL_NS by the timer callback. */
172 uint32_t aHzHistory[8];
173 /** Statistics counter for recording the number of interval changes. */
174 uint32_t cChanges;
175 /** Statistics counter for recording the number of timer starts. */
176 uint32_t cStarts;
177 } Ppt;
178#endif /* GVMM_SCHED_WITH_PPT */
179
180} GVMMHOSTCPU;
181/** Pointer to the per host CPU GVMM data. */
182typedef GVMMHOSTCPU *PGVMMHOSTCPU;
183/** The GVMMHOSTCPU::u32Magic value (Petra, Tanya & Rachel Haden). */
184#define GVMMHOSTCPU_MAGIC UINT32_C(0x19711011)
185/** The interval on history entry should cover (approximately) give in
186 * nanoseconds. */
187#define GVMMHOSTCPU_PPT_HIST_INTERVAL_NS UINT32_C(20000000)
188
189
190/**
191 * The GVMM instance data.
192 */
193typedef struct GVMM
194{
195 /** Eyecatcher / magic. */
196 uint32_t u32Magic;
197 /** The index of the head of the free handle chain. (0 is nil.) */
198 uint16_t volatile iFreeHead;
199 /** The index of the head of the active handle chain. (0 is nil.) */
200 uint16_t volatile iUsedHead;
201 /** The number of VMs. */
202 uint16_t volatile cVMs;
203// /** The number of halted EMT threads. */
204// uint16_t volatile cHaltedEMTs;
205 /** The number of EMTs. */
206 uint32_t volatile cEMTs;
207 /** The lock used to serialize VM creation, destruction and associated events that
208 * isn't performance critical. Owners may acquire the list lock. */
209 RTSEMFASTMUTEX CreateDestroyLock;
210 /** The lock used to serialize used list updates and accesses.
211 * This indirectly includes scheduling since the scheduler will have to walk the
212 * used list to examin running VMs. Owners may not acquire any other locks. */
213 RTSEMFASTMUTEX UsedLock;
214 /** The handle array.
215 * The size of this array defines the maximum number of currently running VMs.
216 * The first entry is unused as it represents the NIL handle. */
217 GVMHANDLE aHandles[GVMM_MAX_HANDLES];
218
219 /** @gcfgm{/GVMM/cEMTsMeansCompany, 32-bit, 0, UINT32_MAX, 1}
220 * The number of EMTs that means we no longer consider ourselves alone on a
221 * CPU/Core.
222 */
223 uint32_t cEMTsMeansCompany;
224 /** @gcfgm{/GVMM/MinSleepAlone,32-bit, 0, 100000000, 750000, ns}
225 * The minimum sleep time for when we're alone, in nano seconds.
226 */
227 uint32_t nsMinSleepAlone;
228 /** @gcfgm{/GVMM/MinSleepCompany,32-bit,0, 100000000, 15000, ns}
229 * The minimum sleep time for when we've got company, in nano seconds.
230 */
231 uint32_t nsMinSleepCompany;
232 /** @gcfgm{/GVMM/EarlyWakeUp1, 32-bit, 0, 100000000, 25000, ns}
233 * The limit for the first round of early wakeups, given in nano seconds.
234 */
235 uint32_t nsEarlyWakeUp1;
236 /** @gcfgm{/GVMM/EarlyWakeUp2, 32-bit, 0, 100000000, 50000, ns}
237 * The limit for the second round of early wakeups, given in nano seconds.
238 */
239 uint32_t nsEarlyWakeUp2;
240
241 /** The number of entries in the host CPU array (aHostCpus). */
242 uint32_t cHostCpus;
243 /** Per host CPU data (variable length). */
244 GVMMHOSTCPU aHostCpus[1];
245} GVMM;
246/** Pointer to the GVMM instance data. */
247typedef GVMM *PGVMM;
248
249/** The GVMM::u32Magic value (Charlie Haden). */
250#define GVMM_MAGIC 0x19370806
251
252
253
254/*******************************************************************************
255* Global Variables *
256*******************************************************************************/
257/** Pointer to the GVMM instance data.
258 * (Just my general dislike for global variables.) */
259static PGVMM g_pGVMM = NULL;
260
261/** Macro for obtaining and validating the g_pGVMM pointer.
262 * On failure it will return from the invoking function with the specified return value.
263 *
264 * @param pGVMM The name of the pGVMM variable.
265 * @param rc The return value on failure. Use VERR_INTERNAL_ERROR for
266 * VBox status codes.
267 */
268#define GVMM_GET_VALID_INSTANCE(pGVMM, rc) \
269 do { \
270 (pGVMM) = g_pGVMM;\
271 AssertPtrReturn((pGVMM), (rc)); \
272 AssertMsgReturn((pGVMM)->u32Magic == GVMM_MAGIC, ("%p - %#x\n", (pGVMM), (pGVMM)->u32Magic), (rc)); \
273 } while (0)
274
275/** Macro for obtaining and validating the g_pGVMM pointer, void function variant.
276 * On failure it will return from the invoking function.
277 *
278 * @param pGVMM The name of the pGVMM variable.
279 */
280#define GVMM_GET_VALID_INSTANCE_VOID(pGVMM) \
281 do { \
282 (pGVMM) = g_pGVMM;\
283 AssertPtrReturnVoid((pGVMM)); \
284 AssertMsgReturnVoid((pGVMM)->u32Magic == GVMM_MAGIC, ("%p - %#x\n", (pGVMM), (pGVMM)->u32Magic)); \
285 } while (0)
286
287
288/*******************************************************************************
289* Internal Functions *
290*******************************************************************************/
291static void gvmmR0InitPerVMData(PGVM pGVM);
292static DECLCALLBACK(void) gvmmR0HandleObjDestructor(void *pvObj, void *pvGVMM, void *pvHandle);
293static int gvmmR0ByVM(PVM pVM, PGVM *ppGVM, PGVMM *ppGVMM, bool fTakeUsedLock);
294static int gvmmR0ByVMAndEMT(PVM pVM, VMCPUID idCpu, PGVM *ppGVM, PGVMM *ppGVMM);
295#ifdef GVMM_SCHED_WITH_PPT
296static DECLCALLBACK(void) gvmmR0SchedPeriodicPreemptionTimerCallback(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
297#endif
298
299
300/**
301 * Initializes the GVMM.
302 *
303 * This is called while owninng the loader sempahore (see supdrvIOCtl_LdrLoad()).
304 *
305 * @returns VBox status code.
306 */
307GVMMR0DECL(int) GVMMR0Init(void)
308{
309 LogFlow(("GVMMR0Init:\n"));
310
311 /*
312 * Allocate and initialize the instance data.
313 */
314 uint32_t cHostCpus = RTMpGetArraySize();
315 AssertMsgReturn(cHostCpus > 0 && cHostCpus < _64K, ("%d", (int)cHostCpus), VERR_INTERNAL_ERROR_2);
316
317 PGVMM pGVMM = (PGVMM)RTMemAllocZ(RT_UOFFSETOF(GVMM, aHostCpus[cHostCpus]));
318 if (!pGVMM)
319 return VERR_NO_MEMORY;
320 int rc = RTSemFastMutexCreate(&pGVMM->CreateDestroyLock);
321 if (RT_SUCCESS(rc))
322 {
323 rc = RTSemFastMutexCreate(&pGVMM->UsedLock);
324 if (RT_SUCCESS(rc))
325 {
326 pGVMM->u32Magic = GVMM_MAGIC;
327 pGVMM->iUsedHead = 0;
328 pGVMM->iFreeHead = 1;
329
330 /* the nil handle */
331 pGVMM->aHandles[0].iSelf = 0;
332 pGVMM->aHandles[0].iNext = 0;
333
334 /* the tail */
335 unsigned i = RT_ELEMENTS(pGVMM->aHandles) - 1;
336 pGVMM->aHandles[i].iSelf = i;
337 pGVMM->aHandles[i].iNext = 0; /* nil */
338
339 /* the rest */
340 while (i-- > 1)
341 {
342 pGVMM->aHandles[i].iSelf = i;
343 pGVMM->aHandles[i].iNext = i + 1;
344 }
345
346 /* The default configuration values. */
347 pGVMM->cEMTsMeansCompany = 1; /** @todo should be adjusted to relative to the cpu count or something... */
348 pGVMM->nsMinSleepAlone = 750000 /* ns (0.750 ms) */; /** @todo this should be adjusted to be 75% (or something) of the scheduler granularity... */
349 pGVMM->nsMinSleepCompany = 15000 /* ns (0.015 ms) */;
350 pGVMM->nsEarlyWakeUp1 = 25000 /* ns (0.025 ms) */;
351 pGVMM->nsEarlyWakeUp2 = 50000 /* ns (0.050 ms) */;
352
353 /* The host CPU data. */
354 pGVMM->cHostCpus = cHostCpus;
355 uint32_t iCpu = cHostCpus;
356 RTCPUSET PossibleSet;
357 RTMpGetSet(&PossibleSet);
358 while (iCpu-- > 0)
359 {
360 pGVMM->aHostCpus[iCpu].idxCpuSet = iCpu;
361#ifdef GVMM_SCHED_WITH_PPT
362 pGVMM->aHostCpus[iCpu].Ppt.pTimer = NULL;
363 pGVMM->aHostCpus[iCpu].Ppt.hSpinlock = NIL_RTSPINLOCK;
364 pGVMM->aHostCpus[iCpu].Ppt.uMinHz = 5; /** @todo Add some API which figures this one out. (not *that* important) */
365 pGVMM->aHostCpus[iCpu].Ppt.cTicksHistoriziationInterval = 1;
366 //pGVMM->aHostCpus[iCpu].Ppt.iTickHistorization = 0;
367 //pGVMM->aHostCpus[iCpu].Ppt.cNsInterval = 0;
368 //pGVMM->aHostCpus[iCpu].Ppt.uTimerHz = 0;
369 //pGVMM->aHostCpus[iCpu].Ppt.uDesiredHz = 0;
370 //pGVMM->aHostCpus[iCpu].Ppt.fStarted = false;
371 //pGVMM->aHostCpus[iCpu].Ppt.fStarting = false;
372 //pGVMM->aHostCpus[iCpu].Ppt.iHzHistory = 0;
373 //pGVMM->aHostCpus[iCpu].Ppt.aHzHistory = {0};
374#endif
375
376 if (RTCpuSetIsMember(&PossibleSet, iCpu))
377 {
378 pGVMM->aHostCpus[iCpu].idCpu = RTMpCpuIdFromSetIndex(iCpu);
379 pGVMM->aHostCpus[iCpu].u32Magic = GVMMHOSTCPU_MAGIC;
380
381#ifdef GVMM_SCHED_WITH_PPT
382 rc = RTTimerCreateEx(&pGVMM->aHostCpus[iCpu].Ppt.pTimer,
383 50*1000*1000 /* whatever */,
384 RTTIMER_FLAGS_CPU(iCpu) | RTTIMER_FLAGS_HIGH_RES,
385 gvmmR0SchedPeriodicPreemptionTimerCallback,
386 &pGVMM->aHostCpus[iCpu]);
387 if (RT_SUCCESS(rc))
388 rc = RTSpinlockCreate(&pGVMM->aHostCpus[iCpu].Ppt.hSpinlock);
389 if (RT_FAILURE(rc))
390 {
391 while (iCpu < cHostCpus)
392 {
393 RTTimerDestroy(pGVMM->aHostCpus[iCpu].Ppt.pTimer);
394 RTSpinlockDestroy(pGVMM->aHostCpus[iCpu].Ppt.hSpinlock);
395 pGVMM->aHostCpus[iCpu].Ppt.hSpinlock = NIL_RTSPINLOCK;
396 iCpu++;
397 }
398 break;
399 }
400#endif
401 }
402 else
403 {
404 pGVMM->aHostCpus[iCpu].idCpu = NIL_RTCPUID;
405 pGVMM->aHostCpus[iCpu].u32Magic = 0;
406 }
407 }
408 if (RT_SUCCESS(rc))
409 {
410 g_pGVMM = pGVMM;
411 LogFlow(("GVMMR0Init: pGVMM=%p cHostCpus=%u\n", pGVMM, cHostCpus));
412 return VINF_SUCCESS;
413 }
414
415 /* bail out. */
416 RTSemFastMutexDestroy(pGVMM->UsedLock);
417 pGVMM->UsedLock = NIL_RTSEMFASTMUTEX;
418 }
419 RTSemFastMutexDestroy(pGVMM->CreateDestroyLock);
420 pGVMM->CreateDestroyLock = NIL_RTSEMFASTMUTEX;
421 }
422
423 RTMemFree(pGVMM);
424 return rc;
425}
426
427
428/**
429 * Terminates the GVM.
430 *
431 * This is called while owning the loader semaphore (see supdrvLdrFree()).
432 * And unless something is wrong, there should be absolutely no VMs
433 * registered at this point.
434 */
435GVMMR0DECL(void) GVMMR0Term(void)
436{
437 LogFlow(("GVMMR0Term:\n"));
438
439 PGVMM pGVMM = g_pGVMM;
440 g_pGVMM = NULL;
441 if (RT_UNLIKELY(!VALID_PTR(pGVMM)))
442 {
443 SUPR0Printf("GVMMR0Term: pGVMM=%p\n", pGVMM);
444 return;
445 }
446
447 /*
448 * First of all, stop all active timers.
449 */
450 uint32_t cActiveTimers = 0;
451 uint32_t iCpu = pGVMM->cHostCpus;
452 while (iCpu-- > 0)
453 {
454 ASMAtomicWriteU32(&pGVMM->aHostCpus[iCpu].u32Magic, ~GVMMHOSTCPU_MAGIC);
455#ifdef GVMM_SCHED_WITH_PPT
456 if ( pGVMM->aHostCpus[iCpu].Ppt.pTimer != NULL
457 && RT_SUCCESS(RTTimerStop(pGVMM->aHostCpus[iCpu].Ppt.pTimer)))
458 cActiveTimers++;
459#endif
460 }
461 if (cActiveTimers)
462 RTThreadSleep(1); /* fudge */
463
464 /*
465 * Invalidate the and free resources.
466 */
467 pGVMM->u32Magic = ~GVMM_MAGIC;
468 RTSemFastMutexDestroy(pGVMM->UsedLock);
469 pGVMM->UsedLock = NIL_RTSEMFASTMUTEX;
470 RTSemFastMutexDestroy(pGVMM->CreateDestroyLock);
471 pGVMM->CreateDestroyLock = NIL_RTSEMFASTMUTEX;
472
473 pGVMM->iFreeHead = 0;
474 if (pGVMM->iUsedHead)
475 {
476 SUPR0Printf("GVMMR0Term: iUsedHead=%#x! (cVMs=%#x cEMTs=%#x)\n", pGVMM->iUsedHead, pGVMM->cVMs, pGVMM->cEMTs);
477 pGVMM->iUsedHead = 0;
478 }
479
480#ifdef GVMM_SCHED_WITH_PPT
481 iCpu = pGVMM->cHostCpus;
482 while (iCpu-- > 0)
483 {
484 RTTimerDestroy(pGVMM->aHostCpus[iCpu].Ppt.pTimer);
485 pGVMM->aHostCpus[iCpu].Ppt.pTimer = NULL;
486 RTSpinlockDestroy(pGVMM->aHostCpus[iCpu].Ppt.hSpinlock);
487 pGVMM->aHostCpus[iCpu].Ppt.hSpinlock = NIL_RTSPINLOCK;
488 }
489#endif
490
491 RTMemFree(pGVMM);
492}
493
494
495/**
496 * A quick hack for setting global config values.
497 *
498 * @returns VBox status code.
499 *
500 * @param pSession The session handle. Used for authentication.
501 * @param pszName The variable name.
502 * @param u64Value The new value.
503 */
504GVMMR0DECL(int) GVMMR0SetConfig(PSUPDRVSESSION pSession, const char *pszName, uint64_t u64Value)
505{
506 /*
507 * Validate input.
508 */
509 PGVMM pGVMM;
510 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
511 AssertPtrReturn(pSession, VERR_INVALID_HANDLE);
512 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
513
514 /*
515 * String switch time!
516 */
517 if (strncmp(pszName, "/GVMM/", sizeof("/GVMM/") - 1))
518 return VERR_CFGM_VALUE_NOT_FOUND; /* borrow status codes from CFGM... */
519 int rc = VINF_SUCCESS;
520 pszName += sizeof("/GVMM/") - 1;
521 if (!strcmp(pszName, "cEMTsMeansCompany"))
522 {
523 if (u64Value <= UINT32_MAX)
524 pGVMM->cEMTsMeansCompany = u64Value;
525 else
526 rc = VERR_OUT_OF_RANGE;
527 }
528 else if (!strcmp(pszName, "MinSleepAlone"))
529 {
530 if (u64Value <= 100000000)
531 pGVMM->nsMinSleepAlone = u64Value;
532 else
533 rc = VERR_OUT_OF_RANGE;
534 }
535 else if (!strcmp(pszName, "MinSleepCompany"))
536 {
537 if (u64Value <= 100000000)
538 pGVMM->nsMinSleepCompany = u64Value;
539 else
540 rc = VERR_OUT_OF_RANGE;
541 }
542 else if (!strcmp(pszName, "EarlyWakeUp1"))
543 {
544 if (u64Value <= 100000000)
545 pGVMM->nsEarlyWakeUp1 = u64Value;
546 else
547 rc = VERR_OUT_OF_RANGE;
548 }
549 else if (!strcmp(pszName, "EarlyWakeUp2"))
550 {
551 if (u64Value <= 100000000)
552 pGVMM->nsEarlyWakeUp2 = u64Value;
553 else
554 rc = VERR_OUT_OF_RANGE;
555 }
556 else
557 rc = VERR_CFGM_VALUE_NOT_FOUND;
558 return rc;
559}
560
561
562/**
563 * A quick hack for getting global config values.
564 *
565 * @returns VBox status code.
566 *
567 * @param pSession The session handle. Used for authentication.
568 * @param pszName The variable name.
569 * @param u64Value The new value.
570 */
571GVMMR0DECL(int) GVMMR0QueryConfig(PSUPDRVSESSION pSession, const char *pszName, uint64_t *pu64Value)
572{
573 /*
574 * Validate input.
575 */
576 PGVMM pGVMM;
577 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
578 AssertPtrReturn(pSession, VERR_INVALID_HANDLE);
579 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
580 AssertPtrReturn(pu64Value, VERR_INVALID_POINTER);
581
582 /*
583 * String switch time!
584 */
585 if (strncmp(pszName, "/GVMM/", sizeof("/GVMM/") - 1))
586 return VERR_CFGM_VALUE_NOT_FOUND; /* borrow status codes from CFGM... */
587 int rc = VINF_SUCCESS;
588 pszName += sizeof("/GVMM/") - 1;
589 if (!strcmp(pszName, "cEMTsMeansCompany"))
590 *pu64Value = pGVMM->cEMTsMeansCompany;
591 else if (!strcmp(pszName, "MinSleepAlone"))
592 *pu64Value = pGVMM->nsMinSleepAlone;
593 else if (!strcmp(pszName, "MinSleepCompany"))
594 *pu64Value = pGVMM->nsMinSleepCompany;
595 else if (!strcmp(pszName, "EarlyWakeUp1"))
596 *pu64Value = pGVMM->nsEarlyWakeUp1;
597 else if (!strcmp(pszName, "EarlyWakeUp2"))
598 *pu64Value = pGVMM->nsEarlyWakeUp2;
599 else
600 rc = VERR_CFGM_VALUE_NOT_FOUND;
601 return rc;
602}
603
604
605/**
606 * Try acquire the 'used' lock.
607 *
608 * @returns IPRT status code, see RTSemFastMutexRequest.
609 * @param pGVMM The GVMM instance data.
610 */
611DECLINLINE(int) gvmmR0UsedLock(PGVMM pGVMM)
612{
613 LogFlow(("++gvmmR0UsedLock(%p)\n", pGVMM));
614 int rc = RTSemFastMutexRequest(pGVMM->UsedLock);
615 LogFlow(("gvmmR0UsedLock(%p)->%Rrc\n", pGVMM, rc));
616 return rc;
617}
618
619
620/**
621 * Release the 'used' lock.
622 *
623 * @returns IPRT status code, see RTSemFastMutexRelease.
624 * @param pGVMM The GVMM instance data.
625 */
626DECLINLINE(int) gvmmR0UsedUnlock(PGVMM pGVMM)
627{
628 LogFlow(("--gvmmR0UsedUnlock(%p)\n", pGVMM));
629 int rc = RTSemFastMutexRelease(pGVMM->UsedLock);
630 AssertRC(rc);
631 return rc;
632}
633
634
635/**
636 * Try acquire the 'create & destroy' lock.
637 *
638 * @returns IPRT status code, see RTSemFastMutexRequest.
639 * @param pGVMM The GVMM instance data.
640 */
641DECLINLINE(int) gvmmR0CreateDestroyLock(PGVMM pGVMM)
642{
643 LogFlow(("++gvmmR0CreateDestroyLock(%p)\n", pGVMM));
644 int rc = RTSemFastMutexRequest(pGVMM->CreateDestroyLock);
645 LogFlow(("gvmmR0CreateDestroyLock(%p)->%Rrc\n", pGVMM, rc));
646 return rc;
647}
648
649
650/**
651 * Release the 'create & destroy' lock.
652 *
653 * @returns IPRT status code, see RTSemFastMutexRequest.
654 * @param pGVMM The GVMM instance data.
655 */
656DECLINLINE(int) gvmmR0CreateDestroyUnlock(PGVMM pGVMM)
657{
658 LogFlow(("--gvmmR0CreateDestroyUnlock(%p)\n", pGVMM));
659 int rc = RTSemFastMutexRelease(pGVMM->CreateDestroyLock);
660 AssertRC(rc);
661 return rc;
662}
663
664
665/**
666 * Request wrapper for the GVMMR0CreateVM API.
667 *
668 * @returns VBox status code.
669 * @param pReq The request buffer.
670 */
671GVMMR0DECL(int) GVMMR0CreateVMReq(PGVMMCREATEVMREQ pReq)
672{
673 /*
674 * Validate the request.
675 */
676 if (!VALID_PTR(pReq))
677 return VERR_INVALID_POINTER;
678 if (pReq->Hdr.cbReq != sizeof(*pReq))
679 return VERR_INVALID_PARAMETER;
680 if (!VALID_PTR(pReq->pSession))
681 return VERR_INVALID_POINTER;
682
683 /*
684 * Execute it.
685 */
686 PVM pVM;
687 pReq->pVMR0 = NULL;
688 pReq->pVMR3 = NIL_RTR3PTR;
689 int rc = GVMMR0CreateVM(pReq->pSession, pReq->cCpus, &pVM);
690 if (RT_SUCCESS(rc))
691 {
692 pReq->pVMR0 = pVM;
693 pReq->pVMR3 = pVM->pVMR3;
694 }
695 return rc;
696}
697
698
699/**
700 * Allocates the VM structure and registers it with GVM.
701 *
702 * The caller will become the VM owner and there by the EMT.
703 *
704 * @returns VBox status code.
705 * @param pSession The support driver session.
706 * @param cCpus Number of virtual CPUs for the new VM.
707 * @param ppVM Where to store the pointer to the VM structure.
708 *
709 * @thread EMT.
710 */
711GVMMR0DECL(int) GVMMR0CreateVM(PSUPDRVSESSION pSession, uint32_t cCpus, PVM *ppVM)
712{
713 LogFlow(("GVMMR0CreateVM: pSession=%p\n", pSession));
714 PGVMM pGVMM;
715 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
716
717 AssertPtrReturn(ppVM, VERR_INVALID_POINTER);
718 *ppVM = NULL;
719
720 if ( cCpus == 0
721 || cCpus > VMM_MAX_CPU_COUNT)
722 return VERR_INVALID_PARAMETER;
723
724 RTNATIVETHREAD hEMT0 = RTThreadNativeSelf();
725 AssertReturn(hEMT0 != NIL_RTNATIVETHREAD, VERR_INTERNAL_ERROR);
726 RTNATIVETHREAD ProcId = RTProcSelf();
727 AssertReturn(ProcId != NIL_RTPROCESS, VERR_INTERNAL_ERROR);
728
729 /*
730 * The whole allocation process is protected by the lock.
731 */
732 int rc = gvmmR0CreateDestroyLock(pGVMM);
733 AssertRCReturn(rc, rc);
734
735 /*
736 * Allocate a handle first so we don't waste resources unnecessarily.
737 */
738 uint16_t iHandle = pGVMM->iFreeHead;
739 if (iHandle)
740 {
741 PGVMHANDLE pHandle = &pGVMM->aHandles[iHandle];
742
743 /* consistency checks, a bit paranoid as always. */
744 if ( !pHandle->pVM
745 && !pHandle->pGVM
746 && !pHandle->pvObj
747 && pHandle->iSelf == iHandle)
748 {
749 pHandle->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_VM, gvmmR0HandleObjDestructor, pGVMM, pHandle);
750 if (pHandle->pvObj)
751 {
752 /*
753 * Move the handle from the free to used list and perform permission checks.
754 */
755 rc = gvmmR0UsedLock(pGVMM);
756 AssertRC(rc);
757
758 pGVMM->iFreeHead = pHandle->iNext;
759 pHandle->iNext = pGVMM->iUsedHead;
760 pGVMM->iUsedHead = iHandle;
761 pGVMM->cVMs++;
762
763 pHandle->pVM = NULL;
764 pHandle->pGVM = NULL;
765 pHandle->pSession = pSession;
766 pHandle->hEMT0 = NIL_RTNATIVETHREAD;
767 pHandle->ProcId = NIL_RTPROCESS;
768
769 gvmmR0UsedUnlock(pGVMM);
770
771 rc = SUPR0ObjVerifyAccess(pHandle->pvObj, pSession, NULL);
772 if (RT_SUCCESS(rc))
773 {
774 /*
775 * Allocate the global VM structure (GVM) and initialize it.
776 */
777 PGVM pGVM = (PGVM)RTMemAllocZ(RT_UOFFSETOF(GVM, aCpus[cCpus]));
778 if (pGVM)
779 {
780 pGVM->u32Magic = GVM_MAGIC;
781 pGVM->hSelf = iHandle;
782 pGVM->pVM = NULL;
783 pGVM->cCpus = cCpus;
784
785 gvmmR0InitPerVMData(pGVM);
786 GMMR0InitPerVMData(pGVM);
787
788 /*
789 * Allocate the shared VM structure and associated page array.
790 */
791 const uint32_t cbVM = RT_UOFFSETOF(VM, aCpus[cCpus]);
792 const uint32_t cPages = RT_ALIGN_32(cbVM, PAGE_SIZE) >> PAGE_SHIFT;
793#ifdef RT_OS_DARWIN /** @todo Figure out why this is broken. Is it only on snow leopard? */
794 rc = RTR0MemObjAllocLow(&pGVM->gvmm.s.VMMemObj, (cPages + 1) << PAGE_SHIFT, false /* fExecutable */);
795#else
796 rc = RTR0MemObjAllocLow(&pGVM->gvmm.s.VMMemObj, cPages << PAGE_SHIFT, false /* fExecutable */);
797#endif
798 if (RT_SUCCESS(rc))
799 {
800 PVM pVM = (PVM)RTR0MemObjAddress(pGVM->gvmm.s.VMMemObj); AssertPtr(pVM);
801 memset(pVM, 0, cPages << PAGE_SHIFT);
802 pVM->enmVMState = VMSTATE_CREATING;
803 pVM->pVMR0 = pVM;
804 pVM->pSession = pSession;
805 pVM->hSelf = iHandle;
806 pVM->cbSelf = cbVM;
807 pVM->cCpus = cCpus;
808 pVM->uCpuPriority = 100; /* default is maximum priority. */
809 pVM->offVMCPU = RT_UOFFSETOF(VM, aCpus);
810
811 rc = RTR0MemObjAllocPage(&pGVM->gvmm.s.VMPagesMemObj, cPages * sizeof(SUPPAGE), false /* fExecutable */);
812 if (RT_SUCCESS(rc))
813 {
814 PSUPPAGE paPages = (PSUPPAGE)RTR0MemObjAddress(pGVM->gvmm.s.VMPagesMemObj); AssertPtr(paPages);
815 for (uint32_t iPage = 0; iPage < cPages; iPage++)
816 {
817 paPages[iPage].uReserved = 0;
818 paPages[iPage].Phys = RTR0MemObjGetPagePhysAddr(pGVM->gvmm.s.VMMemObj, iPage);
819 Assert(paPages[iPage].Phys != NIL_RTHCPHYS);
820 }
821
822 /*
823 * Map them into ring-3.
824 */
825 rc = RTR0MemObjMapUser(&pGVM->gvmm.s.VMMapObj, pGVM->gvmm.s.VMMemObj, (RTR3PTR)-1, 0,
826 RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
827 if (RT_SUCCESS(rc))
828 {
829 pVM->pVMR3 = RTR0MemObjAddressR3(pGVM->gvmm.s.VMMapObj);
830 AssertPtr((void *)pVM->pVMR3);
831
832 /* Initialize all the VM pointers. */
833 for (uint32_t i = 0; i < cCpus; i++)
834 {
835 pVM->aCpus[i].pVMR0 = pVM;
836 pVM->aCpus[i].pVMR3 = pVM->pVMR3;
837 pVM->aCpus[i].idHostCpu = NIL_RTCPUID;
838 pVM->aCpus[i].hNativeThreadR0 = NIL_RTNATIVETHREAD;
839 }
840
841 rc = RTR0MemObjMapUser(&pGVM->gvmm.s.VMPagesMapObj, pGVM->gvmm.s.VMPagesMemObj, (RTR3PTR)-1, 0,
842 RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
843 if (RT_SUCCESS(rc))
844 {
845 pVM->paVMPagesR3 = RTR0MemObjAddressR3(pGVM->gvmm.s.VMPagesMapObj);
846 AssertPtr((void *)pVM->paVMPagesR3);
847
848 /* complete the handle - take the UsedLock sem just to be careful. */
849 rc = gvmmR0UsedLock(pGVMM);
850 AssertRC(rc);
851
852 pHandle->pVM = pVM;
853 pHandle->pGVM = pGVM;
854 pHandle->hEMT0 = hEMT0;
855 pHandle->ProcId = ProcId;
856 pGVM->pVM = pVM;
857 pGVM->aCpus[0].hEMT = hEMT0;
858 pVM->aCpus[0].hNativeThreadR0 = hEMT0;
859 pGVMM->cEMTs += cCpus;
860
861 gvmmR0UsedUnlock(pGVMM);
862 gvmmR0CreateDestroyUnlock(pGVMM);
863
864 *ppVM = pVM;
865 Log(("GVMMR0CreateVM: pVM=%p pVMR3=%p pGVM=%p hGVM=%d\n", pVM, pVM->pVMR3, pGVM, iHandle));
866 return VINF_SUCCESS;
867 }
868
869 RTR0MemObjFree(pGVM->gvmm.s.VMMapObj, false /* fFreeMappings */);
870 pGVM->gvmm.s.VMMapObj = NIL_RTR0MEMOBJ;
871 }
872 RTR0MemObjFree(pGVM->gvmm.s.VMPagesMemObj, false /* fFreeMappings */);
873 pGVM->gvmm.s.VMPagesMemObj = NIL_RTR0MEMOBJ;
874 }
875 RTR0MemObjFree(pGVM->gvmm.s.VMMemObj, false /* fFreeMappings */);
876 pGVM->gvmm.s.VMMemObj = NIL_RTR0MEMOBJ;
877 }
878 }
879 }
880 /* else: The user wasn't permitted to create this VM. */
881
882 /*
883 * The handle will be freed by gvmmR0HandleObjDestructor as we release the
884 * object reference here. A little extra mess because of non-recursive lock.
885 */
886 void *pvObj = pHandle->pvObj;
887 pHandle->pvObj = NULL;
888 gvmmR0CreateDestroyUnlock(pGVMM);
889
890 SUPR0ObjRelease(pvObj, pSession);
891
892 SUPR0Printf("GVMMR0CreateVM: failed, rc=%d\n", rc);
893 return rc;
894 }
895
896 rc = VERR_NO_MEMORY;
897 }
898 else
899 rc = VERR_INTERNAL_ERROR;
900 }
901 else
902 rc = VERR_GVM_TOO_MANY_VMS;
903
904 gvmmR0CreateDestroyUnlock(pGVMM);
905 return rc;
906}
907
908
909/**
910 * Initializes the per VM data belonging to GVMM.
911 *
912 * @param pGVM Pointer to the global VM structure.
913 */
914static void gvmmR0InitPerVMData(PGVM pGVM)
915{
916 AssertCompile(RT_SIZEOFMEMB(GVM,gvmm.s) <= RT_SIZEOFMEMB(GVM,gvmm.padding));
917 AssertCompile(RT_SIZEOFMEMB(GVMCPU,gvmm.s) <= RT_SIZEOFMEMB(GVMCPU,gvmm.padding));
918 pGVM->gvmm.s.VMMemObj = NIL_RTR0MEMOBJ;
919 pGVM->gvmm.s.VMMapObj = NIL_RTR0MEMOBJ;
920 pGVM->gvmm.s.VMPagesMemObj = NIL_RTR0MEMOBJ;
921 pGVM->gvmm.s.VMPagesMapObj = NIL_RTR0MEMOBJ;
922 pGVM->gvmm.s.fDoneVMMR0Init = false;
923 pGVM->gvmm.s.fDoneVMMR0Term = false;
924
925 for (VMCPUID i = 0; i < pGVM->cCpus; i++)
926 {
927 pGVM->aCpus[i].gvmm.s.HaltEventMulti = NIL_RTSEMEVENTMULTI;
928 pGVM->aCpus[i].hEMT = NIL_RTNATIVETHREAD;
929 }
930}
931
932
933/**
934 * Does the VM initialization.
935 *
936 * @returns VBox status code.
937 * @param pVM Pointer to the shared VM structure.
938 */
939GVMMR0DECL(int) GVMMR0InitVM(PVM pVM)
940{
941 LogFlow(("GVMMR0InitVM: pVM=%p\n", pVM));
942
943 /*
944 * Validate the VM structure, state and handle.
945 */
946 PGVM pGVM;
947 PGVMM pGVMM;
948 int rc = gvmmR0ByVMAndEMT(pVM, 0 /* idCpu */, &pGVM, &pGVMM);
949 if (RT_SUCCESS(rc))
950 {
951 if ( !pGVM->gvmm.s.fDoneVMMR0Init
952 && pGVM->aCpus[0].gvmm.s.HaltEventMulti == NIL_RTSEMEVENTMULTI)
953 {
954 for (VMCPUID i = 0; i < pGVM->cCpus; i++)
955 {
956 rc = RTSemEventMultiCreate(&pGVM->aCpus[i].gvmm.s.HaltEventMulti);
957 if (RT_FAILURE(rc))
958 {
959 pGVM->aCpus[i].gvmm.s.HaltEventMulti = NIL_RTSEMEVENTMULTI;
960 break;
961 }
962 }
963 }
964 else
965 rc = VERR_WRONG_ORDER;
966 }
967
968 LogFlow(("GVMMR0InitVM: returns %Rrc\n", rc));
969 return rc;
970}
971
972
973/**
974 * Indicates that we're done with the ring-0 initialization
975 * of the VM.
976 *
977 * @param pVM Pointer to the shared VM structure.
978 * @thread EMT(0)
979 */
980GVMMR0DECL(void) GVMMR0DoneInitVM(PVM pVM)
981{
982 /* Validate the VM structure, state and handle. */
983 PGVM pGVM;
984 PGVMM pGVMM;
985 int rc = gvmmR0ByVMAndEMT(pVM, 0 /* idCpu */, &pGVM, &pGVMM);
986 AssertRCReturnVoid(rc);
987
988 /* Set the indicator. */
989 pGVM->gvmm.s.fDoneVMMR0Init = true;
990}
991
992
993/**
994 * Indicates that we're doing the ring-0 termination of the VM.
995 *
996 * @returns true if termination hasn't been done already, false if it has.
997 * @param pVM Pointer to the shared VM structure.
998 * @param pGVM Pointer to the global VM structure. Optional.
999 * @thread EMT(0)
1000 */
1001GVMMR0DECL(bool) GVMMR0DoingTermVM(PVM pVM, PGVM pGVM)
1002{
1003 /* Validate the VM structure, state and handle. */
1004 AssertPtrNullReturn(pGVM, false);
1005 AssertReturn(!pGVM || pGVM->u32Magic == GVM_MAGIC, false);
1006 if (!pGVM)
1007 {
1008 PGVMM pGVMM;
1009 int rc = gvmmR0ByVMAndEMT(pVM, 0 /* idCpu */, &pGVM, &pGVMM);
1010 AssertRCReturn(rc, false);
1011 }
1012
1013 /* Set the indicator. */
1014 if (pGVM->gvmm.s.fDoneVMMR0Term)
1015 return false;
1016 pGVM->gvmm.s.fDoneVMMR0Term = true;
1017 return true;
1018}
1019
1020
1021/**
1022 * Destroys the VM, freeing all associated resources (the ring-0 ones anyway).
1023 *
1024 * This is call from the vmR3DestroyFinalBit and from a error path in VMR3Create,
1025 * and the caller is not the EMT thread, unfortunately. For security reasons, it
1026 * would've been nice if the caller was actually the EMT thread or that we somehow
1027 * could've associated the calling thread with the VM up front.
1028 *
1029 * @returns VBox status code.
1030 * @param pVM Where to store the pointer to the VM structure.
1031 *
1032 * @thread EMT(0) if it's associated with the VM, otherwise any thread.
1033 */
1034GVMMR0DECL(int) GVMMR0DestroyVM(PVM pVM)
1035{
1036 LogFlow(("GVMMR0DestroyVM: pVM=%p\n", pVM));
1037 PGVMM pGVMM;
1038 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
1039
1040
1041 /*
1042 * Validate the VM structure, state and caller.
1043 */
1044 AssertPtrReturn(pVM, VERR_INVALID_POINTER);
1045 AssertReturn(!((uintptr_t)pVM & PAGE_OFFSET_MASK), VERR_INVALID_POINTER);
1046 AssertMsgReturn(pVM->enmVMState >= VMSTATE_CREATING && pVM->enmVMState <= VMSTATE_TERMINATED, ("%d\n", pVM->enmVMState), VERR_WRONG_ORDER);
1047
1048 uint32_t hGVM = pVM->hSelf;
1049 AssertReturn(hGVM != NIL_GVM_HANDLE, VERR_INVALID_HANDLE);
1050 AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), VERR_INVALID_HANDLE);
1051
1052 PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
1053 AssertReturn(pHandle->pVM == pVM, VERR_NOT_OWNER);
1054
1055 RTPROCESS ProcId = RTProcSelf();
1056 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
1057 AssertReturn( ( pHandle->hEMT0 == hSelf
1058 && pHandle->ProcId == ProcId)
1059 || pHandle->hEMT0 == NIL_RTNATIVETHREAD, VERR_NOT_OWNER);
1060
1061 /*
1062 * Lookup the handle and destroy the object.
1063 * Since the lock isn't recursive and we'll have to leave it before dereferencing the
1064 * object, we take some precautions against racing callers just in case...
1065 */
1066 int rc = gvmmR0CreateDestroyLock(pGVMM);
1067 AssertRC(rc);
1068
1069 /* be careful here because we might theoretically be racing someone else cleaning up. */
1070 if ( pHandle->pVM == pVM
1071 && ( ( pHandle->hEMT0 == hSelf
1072 && pHandle->ProcId == ProcId)
1073 || pHandle->hEMT0 == NIL_RTNATIVETHREAD)
1074 && VALID_PTR(pHandle->pvObj)
1075 && VALID_PTR(pHandle->pSession)
1076 && VALID_PTR(pHandle->pGVM)
1077 && pHandle->pGVM->u32Magic == GVM_MAGIC)
1078 {
1079 void *pvObj = pHandle->pvObj;
1080 pHandle->pvObj = NULL;
1081 gvmmR0CreateDestroyUnlock(pGVMM);
1082
1083 SUPR0ObjRelease(pvObj, pHandle->pSession);
1084 }
1085 else
1086 {
1087 SUPR0Printf("GVMMR0DestroyVM: pHandle=%p:{.pVM=%p, .hEMT0=%p, .ProcId=%u, .pvObj=%p} pVM=%p hSelf=%p\n",
1088 pHandle, pHandle->pVM, pHandle->hEMT0, pHandle->ProcId, pHandle->pvObj, pVM, hSelf);
1089 gvmmR0CreateDestroyUnlock(pGVMM);
1090 rc = VERR_INTERNAL_ERROR;
1091 }
1092
1093 return rc;
1094}
1095
1096
1097/**
1098 * Performs VM cleanup task as part of object destruction.
1099 *
1100 * @param pGVM The GVM pointer.
1101 */
1102static void gvmmR0CleanupVM(PGVM pGVM)
1103{
1104 if ( pGVM->gvmm.s.fDoneVMMR0Init
1105 && !pGVM->gvmm.s.fDoneVMMR0Term)
1106 {
1107 if ( pGVM->gvmm.s.VMMemObj != NIL_RTR0MEMOBJ
1108 && RTR0MemObjAddress(pGVM->gvmm.s.VMMemObj) == pGVM->pVM)
1109 {
1110 LogFlow(("gvmmR0CleanupVM: Calling VMMR0TermVM\n"));
1111 VMMR0TermVM(pGVM->pVM, pGVM);
1112 }
1113 else
1114 AssertMsgFailed(("gvmmR0CleanupVM: VMMemObj=%p pVM=%p\n", pGVM->gvmm.s.VMMemObj, pGVM->pVM));
1115 }
1116
1117 GMMR0CleanupVM(pGVM);
1118}
1119
1120
1121/**
1122 * Handle destructor.
1123 *
1124 * @param pvGVMM The GVM instance pointer.
1125 * @param pvHandle The handle pointer.
1126 */
1127static DECLCALLBACK(void) gvmmR0HandleObjDestructor(void *pvObj, void *pvGVMM, void *pvHandle)
1128{
1129 LogFlow(("gvmmR0HandleObjDestructor: %p %p %p\n", pvObj, pvGVMM, pvHandle));
1130
1131 /*
1132 * Some quick, paranoid, input validation.
1133 */
1134 PGVMHANDLE pHandle = (PGVMHANDLE)pvHandle;
1135 AssertPtr(pHandle);
1136 PGVMM pGVMM = (PGVMM)pvGVMM;
1137 Assert(pGVMM == g_pGVMM);
1138 const uint16_t iHandle = pHandle - &pGVMM->aHandles[0];
1139 if ( !iHandle
1140 || iHandle >= RT_ELEMENTS(pGVMM->aHandles)
1141 || iHandle != pHandle->iSelf)
1142 {
1143 SUPR0Printf("GVM: handle %d is out of range or corrupt (iSelf=%d)!\n", iHandle, pHandle->iSelf);
1144 return;
1145 }
1146
1147 int rc = gvmmR0CreateDestroyLock(pGVMM);
1148 AssertRC(rc);
1149 rc = gvmmR0UsedLock(pGVMM);
1150 AssertRC(rc);
1151
1152 /*
1153 * This is a tad slow but a doubly linked list is too much hazzle.
1154 */
1155 if (RT_UNLIKELY(pHandle->iNext >= RT_ELEMENTS(pGVMM->aHandles)))
1156 {
1157 SUPR0Printf("GVM: used list index %d is out of range!\n", pHandle->iNext);
1158 gvmmR0UsedUnlock(pGVMM);
1159 gvmmR0CreateDestroyUnlock(pGVMM);
1160 return;
1161 }
1162
1163 if (pGVMM->iUsedHead == iHandle)
1164 pGVMM->iUsedHead = pHandle->iNext;
1165 else
1166 {
1167 uint16_t iPrev = pGVMM->iUsedHead;
1168 int c = RT_ELEMENTS(pGVMM->aHandles) + 2;
1169 while (iPrev)
1170 {
1171 if (RT_UNLIKELY(iPrev >= RT_ELEMENTS(pGVMM->aHandles)))
1172 {
1173 SUPR0Printf("GVM: used list index %d is out of range!\n", iPrev);
1174 gvmmR0UsedUnlock(pGVMM);
1175 gvmmR0CreateDestroyUnlock(pGVMM);
1176 return;
1177 }
1178 if (RT_UNLIKELY(c-- <= 0))
1179 {
1180 iPrev = 0;
1181 break;
1182 }
1183
1184 if (pGVMM->aHandles[iPrev].iNext == iHandle)
1185 break;
1186 iPrev = pGVMM->aHandles[iPrev].iNext;
1187 }
1188 if (!iPrev)
1189 {
1190 SUPR0Printf("GVM: can't find the handle previous previous of %d!\n", pHandle->iSelf);
1191 gvmmR0UsedUnlock(pGVMM);
1192 gvmmR0CreateDestroyUnlock(pGVMM);
1193 return;
1194 }
1195
1196 Assert(pGVMM->aHandles[iPrev].iNext == iHandle);
1197 pGVMM->aHandles[iPrev].iNext = pHandle->iNext;
1198 }
1199 pHandle->iNext = 0;
1200 pGVMM->cVMs--;
1201
1202 /*
1203 * Do the global cleanup round.
1204 */
1205 PGVM pGVM = pHandle->pGVM;
1206 if ( VALID_PTR(pGVM)
1207 && pGVM->u32Magic == GVM_MAGIC)
1208 {
1209 pGVMM->cEMTs -= pGVM->cCpus;
1210 gvmmR0UsedUnlock(pGVMM);
1211
1212 gvmmR0CleanupVM(pGVM);
1213
1214 /*
1215 * Do the GVMM cleanup - must be done last.
1216 */
1217 /* The VM and VM pages mappings/allocations. */
1218 if (pGVM->gvmm.s.VMPagesMapObj != NIL_RTR0MEMOBJ)
1219 {
1220 rc = RTR0MemObjFree(pGVM->gvmm.s.VMPagesMapObj, false /* fFreeMappings */); AssertRC(rc);
1221 pGVM->gvmm.s.VMPagesMapObj = NIL_RTR0MEMOBJ;
1222 }
1223
1224 if (pGVM->gvmm.s.VMMapObj != NIL_RTR0MEMOBJ)
1225 {
1226 rc = RTR0MemObjFree(pGVM->gvmm.s.VMMapObj, false /* fFreeMappings */); AssertRC(rc);
1227 pGVM->gvmm.s.VMMapObj = NIL_RTR0MEMOBJ;
1228 }
1229
1230 if (pGVM->gvmm.s.VMPagesMemObj != NIL_RTR0MEMOBJ)
1231 {
1232 rc = RTR0MemObjFree(pGVM->gvmm.s.VMPagesMemObj, false /* fFreeMappings */); AssertRC(rc);
1233 pGVM->gvmm.s.VMPagesMemObj = NIL_RTR0MEMOBJ;
1234 }
1235
1236 if (pGVM->gvmm.s.VMMemObj != NIL_RTR0MEMOBJ)
1237 {
1238 rc = RTR0MemObjFree(pGVM->gvmm.s.VMMemObj, false /* fFreeMappings */); AssertRC(rc);
1239 pGVM->gvmm.s.VMMemObj = NIL_RTR0MEMOBJ;
1240 }
1241
1242 for (VMCPUID i = 0; i < pGVM->cCpus; i++)
1243 {
1244 if (pGVM->aCpus[i].gvmm.s.HaltEventMulti != NIL_RTSEMEVENTMULTI)
1245 {
1246 rc = RTSemEventMultiDestroy(pGVM->aCpus[i].gvmm.s.HaltEventMulti); AssertRC(rc);
1247 pGVM->aCpus[i].gvmm.s.HaltEventMulti = NIL_RTSEMEVENTMULTI;
1248 }
1249 }
1250
1251 /* the GVM structure itself. */
1252 pGVM->u32Magic |= UINT32_C(0x80000000);
1253 RTMemFree(pGVM);
1254
1255 /* Re-acquire the UsedLock before freeing the handle since we're updating handle fields. */
1256 rc = gvmmR0UsedLock(pGVMM);
1257 AssertRC(rc);
1258 }
1259 /* else: GVMMR0CreateVM cleanup. */
1260
1261 /*
1262 * Free the handle.
1263 */
1264 pHandle->iNext = pGVMM->iFreeHead;
1265 pGVMM->iFreeHead = iHandle;
1266 ASMAtomicWriteNullPtr(&pHandle->pGVM);
1267 ASMAtomicWriteNullPtr(&pHandle->pVM);
1268 ASMAtomicWriteNullPtr(&pHandle->pvObj);
1269 ASMAtomicWriteNullPtr(&pHandle->pSession);
1270 ASMAtomicWriteSize(&pHandle->hEMT0, NIL_RTNATIVETHREAD);
1271 ASMAtomicWriteSize(&pHandle->ProcId, NIL_RTPROCESS);
1272
1273 gvmmR0UsedUnlock(pGVMM);
1274 gvmmR0CreateDestroyUnlock(pGVMM);
1275 LogFlow(("gvmmR0HandleObjDestructor: returns\n"));
1276}
1277
1278
1279/**
1280 * Registers the calling thread as the EMT of a Virtual CPU.
1281 *
1282 * Note that VCPU 0 is automatically registered during VM creation.
1283 *
1284 * @returns VBox status code
1285 * @param pVM The shared VM structure (the ring-0 mapping).
1286 * @param idCpu VCPU id.
1287 */
1288GVMMR0DECL(int) GVMMR0RegisterVCpu(PVM pVM, VMCPUID idCpu)
1289{
1290 AssertReturn(idCpu != 0, VERR_NOT_OWNER);
1291
1292 /*
1293 * Validate the VM structure, state and handle.
1294 */
1295 PGVM pGVM;
1296 PGVMM pGVMM;
1297 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, false /* fTakeUsedLock */);
1298 if (RT_FAILURE(rc))
1299 return rc;
1300
1301 AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID);
1302 AssertReturn(pGVM->aCpus[idCpu].hEMT == NIL_RTNATIVETHREAD, VERR_ACCESS_DENIED);
1303 Assert(pGVM->cCpus == pVM->cCpus);
1304 Assert(pVM->aCpus[idCpu].hNativeThreadR0 == NIL_RTNATIVETHREAD);
1305
1306 pVM->aCpus[idCpu].hNativeThreadR0 = pGVM->aCpus[idCpu].hEMT = RTThreadNativeSelf();
1307
1308 return VINF_SUCCESS;
1309}
1310
1311
1312/**
1313 * Lookup a GVM structure by its handle.
1314 *
1315 * @returns The GVM pointer on success, NULL on failure.
1316 * @param hGVM The global VM handle. Asserts on bad handle.
1317 */
1318GVMMR0DECL(PGVM) GVMMR0ByHandle(uint32_t hGVM)
1319{
1320 PGVMM pGVMM;
1321 GVMM_GET_VALID_INSTANCE(pGVMM, NULL);
1322
1323 /*
1324 * Validate.
1325 */
1326 AssertReturn(hGVM != NIL_GVM_HANDLE, NULL);
1327 AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), NULL);
1328
1329 /*
1330 * Look it up.
1331 */
1332 PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
1333 AssertPtrReturn(pHandle->pVM, NULL);
1334 AssertPtrReturn(pHandle->pvObj, NULL);
1335 PGVM pGVM = pHandle->pGVM;
1336 AssertPtrReturn(pGVM, NULL);
1337 AssertReturn(pGVM->pVM == pHandle->pVM, NULL);
1338
1339 return pHandle->pGVM;
1340}
1341
1342
1343/**
1344 * Lookup a GVM structure by the shared VM structure.
1345 *
1346 * The calling thread must be in the same process as the VM. All current lookups
1347 * are by threads inside the same process, so this will not be an issue.
1348 *
1349 * @returns VBox status code.
1350 * @param pVM The shared VM structure (the ring-0 mapping).
1351 * @param ppGVM Where to store the GVM pointer.
1352 * @param ppGVMM Where to store the pointer to the GVMM instance data.
1353 * @param fTakeUsedLock Whether to take the used lock or not.
1354 * Be very careful if not taking the lock as it's possible that
1355 * the VM will disappear then.
1356 *
1357 * @remark This will not assert on an invalid pVM but try return sliently.
1358 */
1359static int gvmmR0ByVM(PVM pVM, PGVM *ppGVM, PGVMM *ppGVMM, bool fTakeUsedLock)
1360{
1361 RTPROCESS ProcId = RTProcSelf();
1362 PGVMM pGVMM;
1363 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
1364
1365 /*
1366 * Validate.
1367 */
1368 if (RT_UNLIKELY( !VALID_PTR(pVM)
1369 || ((uintptr_t)pVM & PAGE_OFFSET_MASK)))
1370 return VERR_INVALID_POINTER;
1371 if (RT_UNLIKELY( pVM->enmVMState < VMSTATE_CREATING
1372 || pVM->enmVMState >= VMSTATE_TERMINATED))
1373 return VERR_INVALID_POINTER;
1374
1375 uint16_t hGVM = pVM->hSelf;
1376 if (RT_UNLIKELY( hGVM == NIL_GVM_HANDLE
1377 || hGVM >= RT_ELEMENTS(pGVMM->aHandles)))
1378 return VERR_INVALID_HANDLE;
1379
1380 /*
1381 * Look it up.
1382 */
1383 PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
1384 PGVM pGVM;
1385 if (fTakeUsedLock)
1386 {
1387 int rc = gvmmR0UsedLock(pGVMM);
1388 AssertRCReturn(rc, rc);
1389
1390 pGVM = pHandle->pGVM;
1391 if (RT_UNLIKELY( pHandle->pVM != pVM
1392 || pHandle->ProcId != ProcId
1393 || !VALID_PTR(pHandle->pvObj)
1394 || !VALID_PTR(pGVM)
1395 || pGVM->pVM != pVM))
1396 {
1397 gvmmR0UsedUnlock(pGVMM);
1398 return VERR_INVALID_HANDLE;
1399 }
1400 }
1401 else
1402 {
1403 if (RT_UNLIKELY(pHandle->pVM != pVM))
1404 return VERR_INVALID_HANDLE;
1405 if (RT_UNLIKELY(pHandle->ProcId != ProcId))
1406 return VERR_INVALID_HANDLE;
1407 if (RT_UNLIKELY(!VALID_PTR(pHandle->pvObj)))
1408 return VERR_INVALID_HANDLE;
1409
1410 pGVM = pHandle->pGVM;
1411 if (RT_UNLIKELY(!VALID_PTR(pGVM)))
1412 return VERR_INVALID_HANDLE;
1413 if (RT_UNLIKELY(pGVM->pVM != pVM))
1414 return VERR_INVALID_HANDLE;
1415 }
1416
1417 *ppGVM = pGVM;
1418 *ppGVMM = pGVMM;
1419 return VINF_SUCCESS;
1420}
1421
1422
1423/**
1424 * Lookup a GVM structure by the shared VM structure.
1425 *
1426 * @returns VBox status code.
1427 * @param pVM The shared VM structure (the ring-0 mapping).
1428 * @param ppGVM Where to store the GVM pointer.
1429 *
1430 * @remark This will not take the 'used'-lock because it doesn't do
1431 * nesting and this function will be used from under the lock.
1432 */
1433GVMMR0DECL(int) GVMMR0ByVM(PVM pVM, PGVM *ppGVM)
1434{
1435 PGVMM pGVMM;
1436 return gvmmR0ByVM(pVM, ppGVM, &pGVMM, false /* fTakeUsedLock */);
1437}
1438
1439
1440/**
1441 * Lookup a GVM structure by the shared VM structure and ensuring that the
1442 * caller is an EMT thread.
1443 *
1444 * @returns VBox status code.
1445 * @param pVM The shared VM structure (the ring-0 mapping).
1446 * @param idCpu The Virtual CPU ID of the calling EMT.
1447 * @param ppGVM Where to store the GVM pointer.
1448 * @param ppGVMM Where to store the pointer to the GVMM instance data.
1449 * @thread EMT
1450 *
1451 * @remark This will assert in all failure paths.
1452 */
1453static int gvmmR0ByVMAndEMT(PVM pVM, VMCPUID idCpu, PGVM *ppGVM, PGVMM *ppGVMM)
1454{
1455 PGVMM pGVMM;
1456 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
1457
1458 /*
1459 * Validate.
1460 */
1461 AssertPtrReturn(pVM, VERR_INVALID_POINTER);
1462 AssertReturn(!((uintptr_t)pVM & PAGE_OFFSET_MASK), VERR_INVALID_POINTER);
1463
1464 uint16_t hGVM = pVM->hSelf;
1465 AssertReturn(hGVM != NIL_GVM_HANDLE, VERR_INVALID_HANDLE);
1466 AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), VERR_INVALID_HANDLE);
1467
1468 /*
1469 * Look it up.
1470 */
1471 PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
1472 AssertReturn(pHandle->pVM == pVM, VERR_NOT_OWNER);
1473 RTPROCESS ProcId = RTProcSelf();
1474 AssertReturn(pHandle->ProcId == ProcId, VERR_NOT_OWNER);
1475 AssertPtrReturn(pHandle->pvObj, VERR_INTERNAL_ERROR);
1476
1477 PGVM pGVM = pHandle->pGVM;
1478 AssertPtrReturn(pGVM, VERR_INTERNAL_ERROR);
1479 AssertReturn(pGVM->pVM == pVM, VERR_INTERNAL_ERROR);
1480 RTNATIVETHREAD hAllegedEMT = RTThreadNativeSelf();
1481 AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID);
1482 AssertReturn(pGVM->aCpus[idCpu].hEMT == hAllegedEMT, VERR_INTERNAL_ERROR);
1483
1484 *ppGVM = pGVM;
1485 *ppGVMM = pGVMM;
1486 return VINF_SUCCESS;
1487}
1488
1489
1490/**
1491 * Lookup a GVM structure by the shared VM structure
1492 * and ensuring that the caller is the EMT thread.
1493 *
1494 * @returns VBox status code.
1495 * @param pVM The shared VM structure (the ring-0 mapping).
1496 * @param idCpu The Virtual CPU ID of the calling EMT.
1497 * @param ppGVM Where to store the GVM pointer.
1498 * @thread EMT
1499 */
1500GVMMR0DECL(int) GVMMR0ByVMAndEMT(PVM pVM, VMCPUID idCpu, PGVM *ppGVM)
1501{
1502 AssertPtrReturn(ppGVM, VERR_INVALID_POINTER);
1503 PGVMM pGVMM;
1504 return gvmmR0ByVMAndEMT(pVM, idCpu, ppGVM, &pGVMM);
1505}
1506
1507
1508/**
1509 * Lookup a VM by its global handle.
1510 *
1511 * @returns The VM handle on success, NULL on failure.
1512 * @param hGVM The global VM handle. Asserts on bad handle.
1513 */
1514GVMMR0DECL(PVM) GVMMR0GetVMByHandle(uint32_t hGVM)
1515{
1516 PGVM pGVM = GVMMR0ByHandle(hGVM);
1517 return pGVM ? pGVM->pVM : NULL;
1518}
1519
1520
1521/**
1522 * Looks up the VM belonging to the specified EMT thread.
1523 *
1524 * This is used by the assertion machinery in VMMR0.cpp to avoid causing
1525 * unnecessary kernel panics when the EMT thread hits an assertion. The
1526 * call may or not be an EMT thread.
1527 *
1528 * @returns The VM handle on success, NULL on failure.
1529 * @param hEMT The native thread handle of the EMT.
1530 * NIL_RTNATIVETHREAD means the current thread
1531 */
1532GVMMR0DECL(PVM) GVMMR0GetVMByEMT(RTNATIVETHREAD hEMT)
1533{
1534 /*
1535 * No Assertions here as we're usually called in a AssertMsgN or
1536 * RTAssert* context.
1537 */
1538 PGVMM pGVMM = g_pGVMM;
1539 if ( !VALID_PTR(pGVMM)
1540 || pGVMM->u32Magic != GVMM_MAGIC)
1541 return NULL;
1542
1543 if (hEMT == NIL_RTNATIVETHREAD)
1544 hEMT = RTThreadNativeSelf();
1545 RTPROCESS ProcId = RTProcSelf();
1546
1547 /*
1548 * Search the handles in a linear fashion as we don't dare to take the lock (assert).
1549 */
1550 for (unsigned i = 1; i < RT_ELEMENTS(pGVMM->aHandles); i++)
1551 {
1552 if ( pGVMM->aHandles[i].iSelf == i
1553 && pGVMM->aHandles[i].ProcId == ProcId
1554 && VALID_PTR(pGVMM->aHandles[i].pvObj)
1555 && VALID_PTR(pGVMM->aHandles[i].pVM)
1556 && VALID_PTR(pGVMM->aHandles[i].pGVM))
1557 {
1558 if (pGVMM->aHandles[i].hEMT0 == hEMT)
1559 return pGVMM->aHandles[i].pVM;
1560
1561 /* This is fearly safe with the current process per VM approach. */
1562 PGVM pGVM = pGVMM->aHandles[i].pGVM;
1563 VMCPUID const cCpus = pGVM->cCpus;
1564 if ( cCpus < 1
1565 || cCpus > VMM_MAX_CPU_COUNT)
1566 continue;
1567 for (VMCPUID idCpu = 1; idCpu < cCpus; idCpu++)
1568 if (pGVM->aCpus[idCpu].hEMT == hEMT)
1569 return pGVMM->aHandles[i].pVM;
1570 }
1571 }
1572 return NULL;
1573}
1574
1575
1576/**
1577 * This is will wake up expired and soon-to-be expired VMs.
1578 *
1579 * @returns Number of VMs that has been woken up.
1580 * @param pGVMM Pointer to the GVMM instance data.
1581 * @param u64Now The current time.
1582 */
1583static unsigned gvmmR0SchedDoWakeUps(PGVMM pGVMM, uint64_t u64Now)
1584{
1585/** @todo Rewrite this algorithm. See performance defect XYZ. */
1586
1587 /*
1588 * The first pass will wake up VMs which have actually expired
1589 * and look for VMs that should be woken up in the 2nd and 3rd passes.
1590 */
1591 unsigned cWoken = 0;
1592 unsigned cHalted = 0;
1593 unsigned cTodo2nd = 0;
1594 unsigned cTodo3rd = 0;
1595 for (unsigned i = pGVMM->iUsedHead, cGuard = 0;
1596 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
1597 i = pGVMM->aHandles[i].iNext)
1598 {
1599 PGVM pCurGVM = pGVMM->aHandles[i].pGVM;
1600 if ( VALID_PTR(pCurGVM)
1601 && pCurGVM->u32Magic == GVM_MAGIC)
1602 {
1603 for (VMCPUID idCpu = 0; idCpu < pCurGVM->cCpus; idCpu++)
1604 {
1605 PGVMCPU pCurGVCpu = &pCurGVM->aCpus[idCpu];
1606
1607 uint64_t u64 = pCurGVCpu->gvmm.s.u64HaltExpire;
1608 if (u64)
1609 {
1610 if (u64 <= u64Now)
1611 {
1612 if (ASMAtomicXchgU64(&pCurGVCpu->gvmm.s.u64HaltExpire, 0))
1613 {
1614 int rc = RTSemEventMultiSignal(pCurGVCpu->gvmm.s.HaltEventMulti);
1615 AssertRC(rc);
1616 cWoken++;
1617 }
1618 }
1619 else
1620 {
1621 cHalted++;
1622 if (u64 <= u64Now + pGVMM->nsEarlyWakeUp1)
1623 cTodo2nd++;
1624 else if (u64 <= u64Now + pGVMM->nsEarlyWakeUp2)
1625 cTodo3rd++;
1626 }
1627 }
1628 }
1629 }
1630 AssertLogRelBreak(cGuard++ < RT_ELEMENTS(pGVMM->aHandles));
1631 }
1632
1633 if (cTodo2nd)
1634 {
1635 for (unsigned i = pGVMM->iUsedHead, cGuard = 0;
1636 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
1637 i = pGVMM->aHandles[i].iNext)
1638 {
1639 PGVM pCurGVM = pGVMM->aHandles[i].pGVM;
1640 if ( VALID_PTR(pCurGVM)
1641 && pCurGVM->u32Magic == GVM_MAGIC)
1642 {
1643 for (VMCPUID idCpu = 0; idCpu < pCurGVM->cCpus; idCpu++)
1644 {
1645 PGVMCPU pCurGVCpu = &pCurGVM->aCpus[idCpu];
1646
1647 if ( pCurGVCpu->gvmm.s.u64HaltExpire
1648 && pCurGVCpu->gvmm.s.u64HaltExpire <= u64Now + pGVMM->nsEarlyWakeUp1)
1649 {
1650 if (ASMAtomicXchgU64(&pCurGVCpu->gvmm.s.u64HaltExpire, 0))
1651 {
1652 int rc = RTSemEventMultiSignal(pCurGVCpu->gvmm.s.HaltEventMulti);
1653 AssertRC(rc);
1654 cWoken++;
1655 }
1656 }
1657 }
1658 }
1659 AssertLogRelBreak(cGuard++ < RT_ELEMENTS(pGVMM->aHandles));
1660 }
1661 }
1662
1663 if (cTodo3rd)
1664 {
1665 for (unsigned i = pGVMM->iUsedHead, cGuard = 0;
1666 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
1667 i = pGVMM->aHandles[i].iNext)
1668 {
1669 PGVM pCurGVM = pGVMM->aHandles[i].pGVM;
1670 if ( VALID_PTR(pCurGVM)
1671 && pCurGVM->u32Magic == GVM_MAGIC)
1672 {
1673 for (VMCPUID idCpu = 0; idCpu < pCurGVM->cCpus; idCpu++)
1674 {
1675 PGVMCPU pCurGVCpu = &pCurGVM->aCpus[idCpu];
1676
1677 if ( pCurGVCpu->gvmm.s.u64HaltExpire
1678 && pCurGVCpu->gvmm.s.u64HaltExpire <= u64Now + pGVMM->nsEarlyWakeUp2)
1679 {
1680 if (ASMAtomicXchgU64(&pCurGVCpu->gvmm.s.u64HaltExpire, 0))
1681 {
1682 int rc = RTSemEventMultiSignal(pCurGVCpu->gvmm.s.HaltEventMulti);
1683 AssertRC(rc);
1684 cWoken++;
1685 }
1686 }
1687 }
1688 }
1689 AssertLogRelBreak(cGuard++ < RT_ELEMENTS(pGVMM->aHandles));
1690 }
1691 }
1692
1693 return cWoken;
1694}
1695
1696
1697/**
1698 * Halt the EMT thread.
1699 *
1700 * @returns VINF_SUCCESS normal wakeup (timeout or kicked by other thread).
1701 * VERR_INTERRUPTED if a signal was scheduled for the thread.
1702 * @param pVM Pointer to the shared VM structure.
1703 * @param idCpu The Virtual CPU ID of the calling EMT.
1704 * @param u64ExpireGipTime The time for the sleep to expire expressed as GIP time.
1705 * @thread EMT(idCpu).
1706 */
1707GVMMR0DECL(int) GVMMR0SchedHalt(PVM pVM, VMCPUID idCpu, uint64_t u64ExpireGipTime)
1708{
1709 LogFlow(("GVMMR0SchedHalt: pVM=%p\n", pVM));
1710
1711 /*
1712 * Validate the VM structure, state and handle.
1713 */
1714 PGVM pGVM;
1715 PGVMM pGVMM;
1716 int rc = gvmmR0ByVMAndEMT(pVM, idCpu, &pGVM, &pGVMM);
1717 if (RT_FAILURE(rc))
1718 return rc;
1719 pGVM->gvmm.s.StatsSched.cHaltCalls++;
1720
1721 PGVMCPU pCurGVCpu = &pGVM->aCpus[idCpu];
1722 Assert(!pCurGVCpu->gvmm.s.u64HaltExpire);
1723
1724 /*
1725 * Take the UsedList semaphore, get the current time
1726 * and check if anyone needs waking up.
1727 * Interrupts must NOT be disabled at this point because we ask for GIP time!
1728 */
1729 rc = gvmmR0UsedLock(pGVMM);
1730 AssertRC(rc);
1731
1732 pCurGVCpu->gvmm.s.iCpuEmt = ASMGetApicId();
1733
1734 Assert(ASMGetFlags() & X86_EFL_IF);
1735 const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
1736 pGVM->gvmm.s.StatsSched.cHaltWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
1737
1738 /*
1739 * Go to sleep if we must...
1740 */
1741 if ( u64Now < u64ExpireGipTime
1742 && u64ExpireGipTime - u64Now > (pGVMM->cEMTs > pGVMM->cEMTsMeansCompany
1743 ? pGVMM->nsMinSleepCompany
1744 : pGVMM->nsMinSleepAlone))
1745 {
1746 pGVM->gvmm.s.StatsSched.cHaltBlocking++;
1747 ASMAtomicXchgU64(&pCurGVCpu->gvmm.s.u64HaltExpire, u64ExpireGipTime);
1748 gvmmR0UsedUnlock(pGVMM);
1749
1750 uint32_t cMillies = (u64ExpireGipTime - u64Now) / 1000000;
1751 /* Cap the timeout to one second. */
1752 cMillies = RT_MIN(1000, cMillies);
1753 rc = RTSemEventMultiWaitNoResume(pCurGVCpu->gvmm.s.HaltEventMulti, cMillies ? cMillies : 1);
1754 ASMAtomicXchgU64(&pCurGVCpu->gvmm.s.u64HaltExpire, 0);
1755 if (rc == VERR_TIMEOUT)
1756 {
1757 pGVM->gvmm.s.StatsSched.cHaltTimeouts++;
1758 rc = VINF_SUCCESS;
1759 }
1760 }
1761 else
1762 {
1763 pGVM->gvmm.s.StatsSched.cHaltNotBlocking++;
1764 gvmmR0UsedUnlock(pGVMM);
1765 }
1766
1767 /* Make sure false wake up calls (gvmmR0SchedDoWakeUps) cause us to spin. */
1768 RTSemEventMultiReset(pCurGVCpu->gvmm.s.HaltEventMulti);
1769
1770 return rc;
1771}
1772
1773
1774/**
1775 * Worker for GVMMR0SchedWakeUp and GVMMR0SchedWakeUpAndPokeCpus that wakes up
1776 * the a sleeping EMT.
1777 *
1778 * @retval VINF_SUCCESS if successfully woken up.
1779 * @retval VINF_GVM_NOT_BLOCKED if the EMT wasn't blocked.
1780 *
1781 * @param pGVM The global (ring-0) VM structure.
1782 * @param pGVCpu The global (ring-0) VCPU structure.
1783 */
1784DECLINLINE(int) gvmmR0SchedWakeUpOne(PGVM pGVM, PGVMCPU pGVCpu)
1785{
1786 pGVM->gvmm.s.StatsSched.cWakeUpCalls++;
1787
1788 /*
1789 * Signal the semaphore regardless of whether it's current blocked on it.
1790 *
1791 * The reason for this is that there is absolutely no way we can be 100%
1792 * certain that it isn't *about* go to go to sleep on it and just got
1793 * delayed a bit en route. So, we will always signal the semaphore when
1794 * the it is flagged as halted in the VMM.
1795 */
1796/** @todo we can optimize some of that by means of the pVCpu->enmState now. */
1797 int rc;
1798 if (pGVCpu->gvmm.s.u64HaltExpire)
1799 {
1800 rc = VINF_SUCCESS;
1801 ASMAtomicXchgU64(&pGVCpu->gvmm.s.u64HaltExpire, 0);
1802 }
1803 else
1804 {
1805 rc = VINF_GVM_NOT_BLOCKED;
1806 pGVM->gvmm.s.StatsSched.cWakeUpNotHalted++;
1807 }
1808
1809 int rc2 = RTSemEventMultiSignal(pGVCpu->gvmm.s.HaltEventMulti);
1810 AssertRC(rc2);
1811
1812 return rc;
1813}
1814
1815
1816/**
1817 * Wakes up the halted EMT thread so it can service a pending request.
1818 *
1819 * @returns VBox status code.
1820 * @retval VINF_SUCCESS if successfully woken up.
1821 * @retval VINF_GVM_NOT_BLOCKED if the EMT wasn't blocked.
1822 *
1823 * @param pVM Pointer to the shared VM structure.
1824 * @param idCpu The Virtual CPU ID of the EMT to wake up.
1825 * @param fTakeUsedLock Take the used lock or not
1826 * @thread Any but EMT.
1827 */
1828GVMMR0DECL(int) GVMMR0SchedWakeUpEx(PVM pVM, VMCPUID idCpu, bool fTakeUsedLock)
1829{
1830 /*
1831 * Validate input and take the UsedLock.
1832 */
1833 PGVM pGVM;
1834 PGVMM pGVMM;
1835 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, fTakeUsedLock);
1836 if (RT_SUCCESS(rc))
1837 {
1838 if (idCpu < pGVM->cCpus)
1839 {
1840 /*
1841 * Do the actual job.
1842 */
1843 rc = gvmmR0SchedWakeUpOne(pGVM, &pGVM->aCpus[idCpu]);
1844
1845 if (fTakeUsedLock)
1846 {
1847 /*
1848 * While we're here, do a round of scheduling.
1849 */
1850 Assert(ASMGetFlags() & X86_EFL_IF);
1851 const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
1852 pGVM->gvmm.s.StatsSched.cWakeUpWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
1853 }
1854 }
1855 else
1856 rc = VERR_INVALID_CPU_ID;
1857
1858 if (fTakeUsedLock)
1859 {
1860 int rc2 = gvmmR0UsedUnlock(pGVMM);
1861 AssertRC(rc2);
1862 }
1863 }
1864
1865 LogFlow(("GVMMR0SchedWakeUp: returns %Rrc\n", rc));
1866 return rc;
1867}
1868
1869
1870/**
1871 * Wakes up the halted EMT thread so it can service a pending request.
1872 *
1873 * @returns VBox status code.
1874 * @retval VINF_SUCCESS if successfully woken up.
1875 * @retval VINF_GVM_NOT_BLOCKED if the EMT wasn't blocked.
1876 *
1877 * @param pVM Pointer to the shared VM structure.
1878 * @param idCpu The Virtual CPU ID of the EMT to wake up.
1879 * @thread Any but EMT.
1880 */
1881GVMMR0DECL(int) GVMMR0SchedWakeUp(PVM pVM, VMCPUID idCpu)
1882{
1883 return GVMMR0SchedWakeUpEx(pVM, idCpu, true /* fTakeUsedLock */);
1884}
1885
1886/**
1887 * Worker common to GVMMR0SchedPoke and GVMMR0SchedWakeUpAndPokeCpus that pokes
1888 * the Virtual CPU if it's still busy executing guest code.
1889 *
1890 * @returns VBox status code.
1891 * @retval VINF_SUCCESS if poked successfully.
1892 * @retval VINF_GVM_NOT_BUSY_IN_GC if the EMT wasn't busy in GC.
1893 *
1894 * @param pGVM The global (ring-0) VM structure.
1895 * @param pVCpu The Virtual CPU handle.
1896 */
1897DECLINLINE(int) gvmmR0SchedPokeOne(PGVM pGVM, PVMCPU pVCpu)
1898{
1899 pGVM->gvmm.s.StatsSched.cPokeCalls++;
1900
1901 RTCPUID idHostCpu = pVCpu->idHostCpu;
1902 if ( idHostCpu == NIL_RTCPUID
1903 || VMCPU_GET_STATE(pVCpu) != VMCPUSTATE_STARTED_EXEC)
1904 {
1905 pGVM->gvmm.s.StatsSched.cPokeNotBusy++;
1906 return VINF_GVM_NOT_BUSY_IN_GC;
1907 }
1908
1909 /* Note: this function is not implemented on Darwin and Linux (kernel < 2.6.19) */
1910 RTMpPokeCpu(idHostCpu);
1911 return VINF_SUCCESS;
1912}
1913
1914/**
1915 * Pokes an EMT if it's still busy running guest code.
1916 *
1917 * @returns VBox status code.
1918 * @retval VINF_SUCCESS if poked successfully.
1919 * @retval VINF_GVM_NOT_BUSY_IN_GC if the EMT wasn't busy in GC.
1920 *
1921 * @param pVM Pointer to the shared VM structure.
1922 * @param idCpu The ID of the virtual CPU to poke.
1923 * @param fTakeUsedLock Take the used lock or not
1924 */
1925GVMMR0DECL(int) GVMMR0SchedPokeEx(PVM pVM, VMCPUID idCpu, bool fTakeUsedLock)
1926{
1927 /*
1928 * Validate input and take the UsedLock.
1929 */
1930 PGVM pGVM;
1931 PGVMM pGVMM;
1932 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, fTakeUsedLock);
1933 if (RT_SUCCESS(rc))
1934 {
1935 if (idCpu < pGVM->cCpus)
1936 rc = gvmmR0SchedPokeOne(pGVM, &pVM->aCpus[idCpu]);
1937 else
1938 rc = VERR_INVALID_CPU_ID;
1939
1940 if (fTakeUsedLock)
1941 {
1942 int rc2 = gvmmR0UsedUnlock(pGVMM);
1943 AssertRC(rc2);
1944 }
1945 }
1946
1947 LogFlow(("GVMMR0SchedWakeUpAndPokeCpus: returns %Rrc\n", rc));
1948 return rc;
1949}
1950
1951
1952/**
1953 * Pokes an EMT if it's still busy running guest code.
1954 *
1955 * @returns VBox status code.
1956 * @retval VINF_SUCCESS if poked successfully.
1957 * @retval VINF_GVM_NOT_BUSY_IN_GC if the EMT wasn't busy in GC.
1958 *
1959 * @param pVM Pointer to the shared VM structure.
1960 * @param idCpu The ID of the virtual CPU to poke.
1961 */
1962GVMMR0DECL(int) GVMMR0SchedPoke(PVM pVM, VMCPUID idCpu)
1963{
1964 return GVMMR0SchedPokeEx(pVM, idCpu, true /* fTakeUsedLock */);
1965}
1966
1967
1968/**
1969 * Wakes up a set of halted EMT threads so they can service pending request.
1970 *
1971 * @returns VBox status code, no informational stuff.
1972 *
1973 * @param pVM Pointer to the shared VM structure.
1974 * @param pSleepSet The set of sleepers to wake up.
1975 * @param pPokeSet The set of CPUs to poke.
1976 */
1977GVMMR0DECL(int) GVMMR0SchedWakeUpAndPokeCpus(PVM pVM, PCVMCPUSET pSleepSet, PCVMCPUSET pPokeSet)
1978{
1979 AssertPtrReturn(pSleepSet, VERR_INVALID_POINTER);
1980 AssertPtrReturn(pPokeSet, VERR_INVALID_POINTER);
1981 RTNATIVETHREAD hSelf = RTThreadNativeSelf();
1982
1983 /*
1984 * Validate input and take the UsedLock.
1985 */
1986 PGVM pGVM;
1987 PGVMM pGVMM;
1988 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, true /* fTakeUsedLock */);
1989 if (RT_SUCCESS(rc))
1990 {
1991 rc = VINF_SUCCESS;
1992 VMCPUID idCpu = pGVM->cCpus;
1993 while (idCpu-- > 0)
1994 {
1995 /* Don't try poke or wake up ourselves. */
1996 if (pGVM->aCpus[idCpu].hEMT == hSelf)
1997 continue;
1998
1999 /* just ignore errors for now. */
2000 if (VMCPUSET_IS_PRESENT(pSleepSet, idCpu))
2001 gvmmR0SchedWakeUpOne(pGVM, &pGVM->aCpus[idCpu]);
2002 else if (VMCPUSET_IS_PRESENT(pPokeSet, idCpu))
2003 gvmmR0SchedPokeOne(pGVM, &pVM->aCpus[idCpu]);
2004 }
2005
2006 int rc2 = gvmmR0UsedUnlock(pGVMM);
2007 AssertRC(rc2);
2008 }
2009
2010 LogFlow(("GVMMR0SchedWakeUpAndPokeCpus: returns %Rrc\n", rc));
2011 return rc;
2012}
2013
2014
2015/**
2016 * VMMR0 request wrapper for GVMMR0SchedWakeUpAndPokeCpus.
2017 *
2018 * @returns see GVMMR0SchedWakeUpAndPokeCpus.
2019 * @param pVM Pointer to the shared VM structure.
2020 * @param pReq The request packet.
2021 */
2022GVMMR0DECL(int) GVMMR0SchedWakeUpAndPokeCpusReq(PVM pVM, PGVMMSCHEDWAKEUPANDPOKECPUSREQ pReq)
2023{
2024 /*
2025 * Validate input and pass it on.
2026 */
2027 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2028 AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
2029
2030 return GVMMR0SchedWakeUpAndPokeCpus(pVM, &pReq->SleepSet, &pReq->PokeSet);
2031}
2032
2033
2034
2035/**
2036 * Poll the schedule to see if someone else should get a chance to run.
2037 *
2038 * This is a bit hackish and will not work too well if the machine is
2039 * under heavy load from non-VM processes.
2040 *
2041 * @returns VINF_SUCCESS if not yielded.
2042 * VINF_GVM_YIELDED if an attempt to switch to a different VM task was made.
2043 * @param pVM Pointer to the shared VM structure.
2044 * @param idCpu The Virtual CPU ID of the calling EMT.
2045 * @param u64ExpireGipTime The time for the sleep to expire expressed as GIP time.
2046 * @param fYield Whether to yield or not.
2047 * This is for when we're spinning in the halt loop.
2048 * @thread EMT(idCpu).
2049 */
2050GVMMR0DECL(int) GVMMR0SchedPoll(PVM pVM, VMCPUID idCpu, bool fYield)
2051{
2052 /*
2053 * Validate input.
2054 */
2055 PGVM pGVM;
2056 PGVMM pGVMM;
2057 int rc = gvmmR0ByVMAndEMT(pVM, idCpu, &pGVM, &pGVMM);
2058 if (RT_SUCCESS(rc))
2059 {
2060 rc = gvmmR0UsedLock(pGVMM);
2061 AssertRC(rc);
2062 pGVM->gvmm.s.StatsSched.cPollCalls++;
2063
2064 Assert(ASMGetFlags() & X86_EFL_IF);
2065 const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
2066
2067 if (!fYield)
2068 pGVM->gvmm.s.StatsSched.cPollWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
2069 else
2070 {
2071 /** @todo implement this... */
2072 rc = VERR_NOT_IMPLEMENTED;
2073 }
2074
2075 gvmmR0UsedUnlock(pGVMM);
2076 }
2077
2078 LogFlow(("GVMMR0SchedWakeUp: returns %Rrc\n", rc));
2079 return rc;
2080}
2081
2082
2083#ifdef GVMM_SCHED_WITH_PPT
2084/**
2085 * Timer callback for the periodic preemption timer.
2086 *
2087 * @param pTimer The timer handle.
2088 * @param pvUser Pointer to the per cpu structure.
2089 * @param iTick The current tick.
2090 */
2091static DECLCALLBACK(void) gvmmR0SchedPeriodicPreemptionTimerCallback(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
2092{
2093 PGVMMHOSTCPU pCpu = (PGVMMHOSTCPU)pvUser;
2094
2095 /*
2096 * Termination check
2097 */
2098 if (pCpu->u32Magic != GVMMHOSTCPU_MAGIC)
2099 return;
2100
2101 /*
2102 * Do the house keeping.
2103 */
2104 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
2105 RTSpinlockAcquireNoInts(pCpu->Ppt.hSpinlock, &Tmp);
2106
2107 if (++pCpu->Ppt.iTickHistorization >= pCpu->Ppt.cTicksHistoriziationInterval)
2108 {
2109 /*
2110 * Historicize the max frequency.
2111 */
2112 uint32_t iHzHistory = ++pCpu->Ppt.iHzHistory % RT_ELEMENTS(pCpu->Ppt.aHzHistory);
2113 pCpu->Ppt.aHzHistory[iHzHistory] = pCpu->Ppt.uDesiredHz;
2114 pCpu->Ppt.iTickHistorization = 0;
2115 pCpu->Ppt.uDesiredHz = 0;
2116
2117 /*
2118 * Check if the current timer frequency.
2119 */
2120 uint32_t uHistMaxHz = 0;
2121 for (uint32_t i = 0; i < RT_ELEMENTS(pCpu->Ppt.aHzHistory); i++)
2122 if (pCpu->Ppt.aHzHistory[i] > uHistMaxHz)
2123 uHistMaxHz = pCpu->Ppt.aHzHistory[i];
2124 if (uHistMaxHz == pCpu->Ppt.uTimerHz)
2125 RTSpinlockReleaseNoInts(pCpu->Ppt.hSpinlock, &Tmp);
2126 else if (uHistMaxHz)
2127 {
2128 /*
2129 * Reprogram it.
2130 */
2131 pCpu->Ppt.cChanges++;
2132 pCpu->Ppt.iTickHistorization = 0;
2133 pCpu->Ppt.uTimerHz = uHistMaxHz;
2134 uint32_t const cNsInterval = UINT32_C(1000000000) / uHistMaxHz;
2135 pCpu->Ppt.cNsInterval = cNsInterval;
2136 if (cNsInterval < GVMMHOSTCPU_PPT_HIST_INTERVAL_NS)
2137 pCpu->Ppt.cTicksHistoriziationInterval = ( GVMMHOSTCPU_PPT_HIST_INTERVAL_NS
2138 + GVMMHOSTCPU_PPT_HIST_INTERVAL_NS / 2 - 1)
2139 / cNsInterval;
2140 else
2141 pCpu->Ppt.cTicksHistoriziationInterval = 1;
2142 RTSpinlockReleaseNoInts(pCpu->Ppt.hSpinlock, &Tmp);
2143
2144 /*SUPR0Printf("Cpu%u: change to %u Hz / %u ns\n", pCpu->idxCpuSet, uHistMaxHz, cNsInterval);*/
2145 RTTimerChangeInterval(pTimer, cNsInterval);
2146 }
2147 else
2148 {
2149 /*
2150 * Stop it.
2151 */
2152 pCpu->Ppt.fStarted = false;
2153 pCpu->Ppt.uTimerHz = 0;
2154 pCpu->Ppt.cNsInterval = 0;
2155 RTSpinlockReleaseNoInts(pCpu->Ppt.hSpinlock, &Tmp);
2156
2157 /*SUPR0Printf("Cpu%u: stopping (%u Hz)\n", pCpu->idxCpuSet, uHistMaxHz);*/
2158 RTTimerStop(pTimer);
2159 }
2160 }
2161 else
2162 RTSpinlockReleaseNoInts(pCpu->Ppt.hSpinlock, &Tmp);
2163}
2164#endif /* GVMM_SCHED_WITH_PPT */
2165
2166
2167/**
2168 * Updates the periodic preemption timer for the calling CPU.
2169 *
2170 * The caller must have disabled preemption!
2171 * The caller must check that the host can do high resolution timers.
2172 *
2173 * @param pVM The VM handle.
2174 * @param idHostCpu The current host CPU id.
2175 * @param uHz The desired frequency.
2176 */
2177GVMMR0DECL(void) GVMMR0SchedUpdatePeriodicPreemptionTimer(PVM pVM, RTCPUID idHostCpu, uint32_t uHz)
2178{
2179#ifdef GVMM_SCHED_WITH_PPT
2180 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
2181 Assert(RTTimerCanDoHighResolution());
2182
2183 /*
2184 * Resolve the per CPU data.
2185 */
2186 uint32_t iCpu = RTMpCpuIdToSetIndex(idHostCpu);
2187 PGVMM pGVMM = g_pGVMM;
2188 if ( !VALID_PTR(pGVMM)
2189 || pGVMM->u32Magic != GVMM_MAGIC)
2190 return;
2191 AssertMsgReturnVoid(iCpu < pGVMM->cHostCpus, ("iCpu=%d cHostCpus=%d\n", iCpu, pGVMM->cHostCpus));
2192 PGVMMHOSTCPU pCpu = &pGVMM->aHostCpus[iCpu];
2193 AssertMsgReturnVoid( pCpu->u32Magic == GVMMHOSTCPU_MAGIC
2194 && pCpu->idCpu == idHostCpu,
2195 ("u32Magic=%#x idCpu=% idHostCpu=%d\n", pCpu->u32Magic, pCpu->idCpu, idHostCpu));
2196
2197 /*
2198 * Check whether we need to do anything about the timer.
2199 * We have to be a little bit careful since we might be race the timer
2200 * callback here.
2201 */
2202 if (uHz > 16384)
2203 uHz = 16384; /** @todo add a query method for this! */
2204 if (RT_UNLIKELY( uHz > ASMAtomicReadU32(&pCpu->Ppt.uDesiredHz)
2205 && uHz >= pCpu->Ppt.uMinHz
2206 && !pCpu->Ppt.fStarting /* solaris paranoia */))
2207 {
2208 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
2209 RTSpinlockAcquireNoInts(pCpu->Ppt.hSpinlock, &Tmp);
2210
2211 pCpu->Ppt.uDesiredHz = uHz;
2212 uint32_t cNsInterval = 0;
2213 if (!pCpu->Ppt.fStarted)
2214 {
2215 pCpu->Ppt.cStarts++;
2216 pCpu->Ppt.fStarted = true;
2217 pCpu->Ppt.fStarting = true;
2218 pCpu->Ppt.iTickHistorization = 0;
2219 pCpu->Ppt.uTimerHz = uHz;
2220 pCpu->Ppt.cNsInterval = cNsInterval = UINT32_C(1000000000) / uHz;
2221 if (cNsInterval < GVMMHOSTCPU_PPT_HIST_INTERVAL_NS)
2222 pCpu->Ppt.cTicksHistoriziationInterval = ( GVMMHOSTCPU_PPT_HIST_INTERVAL_NS
2223 + GVMMHOSTCPU_PPT_HIST_INTERVAL_NS / 2 - 1)
2224 / cNsInterval;
2225 else
2226 pCpu->Ppt.cTicksHistoriziationInterval = 1;
2227 }
2228
2229 RTSpinlockReleaseNoInts(pCpu->Ppt.hSpinlock, &Tmp);
2230
2231 if (cNsInterval)
2232 {
2233 RTTimerChangeInterval(pCpu->Ppt.pTimer, cNsInterval);
2234 int rc = RTTimerStart(pCpu->Ppt.pTimer, cNsInterval);
2235 AssertRC(rc);
2236
2237 RTSpinlockAcquireNoInts(pCpu->Ppt.hSpinlock, &Tmp);
2238 if (RT_FAILURE(rc))
2239 pCpu->Ppt.fStarted = false;
2240 pCpu->Ppt.fStarting = false;
2241 RTSpinlockReleaseNoInts(pCpu->Ppt.hSpinlock, &Tmp);
2242 }
2243 }
2244#endif /* GVMM_SCHED_WITH_PPT */
2245}
2246
2247
2248/**
2249 * Retrieves the GVMM statistics visible to the caller.
2250 *
2251 * @returns VBox status code.
2252 *
2253 * @param pStats Where to put the statistics.
2254 * @param pSession The current session.
2255 * @param pVM The VM to obtain statistics for. Optional.
2256 */
2257GVMMR0DECL(int) GVMMR0QueryStatistics(PGVMMSTATS pStats, PSUPDRVSESSION pSession, PVM pVM)
2258{
2259 LogFlow(("GVMMR0QueryStatistics: pStats=%p pSession=%p pVM=%p\n", pStats, pSession, pVM));
2260
2261 /*
2262 * Validate input.
2263 */
2264 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2265 AssertPtrReturn(pStats, VERR_INVALID_POINTER);
2266 pStats->cVMs = 0; /* (crash before taking the sem...) */
2267
2268 /*
2269 * Take the lock and get the VM statistics.
2270 */
2271 PGVMM pGVMM;
2272 if (pVM)
2273 {
2274 PGVM pGVM;
2275 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, true /*fTakeUsedLock*/);
2276 if (RT_FAILURE(rc))
2277 return rc;
2278 pStats->SchedVM = pGVM->gvmm.s.StatsSched;
2279 }
2280 else
2281 {
2282 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
2283 memset(&pStats->SchedVM, 0, sizeof(pStats->SchedVM));
2284
2285 int rc = gvmmR0UsedLock(pGVMM);
2286 AssertRCReturn(rc, rc);
2287 }
2288
2289 /*
2290 * Enumerate the VMs and add the ones visibile to the statistics.
2291 */
2292 pStats->cVMs = 0;
2293 pStats->cEMTs = 0;
2294 memset(&pStats->SchedSum, 0, sizeof(pStats->SchedSum));
2295
2296 for (unsigned i = pGVMM->iUsedHead;
2297 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
2298 i = pGVMM->aHandles[i].iNext)
2299 {
2300 PGVM pGVM = pGVMM->aHandles[i].pGVM;
2301 void *pvObj = pGVMM->aHandles[i].pvObj;
2302 if ( VALID_PTR(pvObj)
2303 && VALID_PTR(pGVM)
2304 && pGVM->u32Magic == GVM_MAGIC
2305 && RT_SUCCESS(SUPR0ObjVerifyAccess(pvObj, pSession, NULL)))
2306 {
2307 pStats->cVMs++;
2308 pStats->cEMTs += pGVM->cCpus;
2309
2310 pStats->SchedSum.cHaltCalls += pGVM->gvmm.s.StatsSched.cHaltCalls;
2311 pStats->SchedSum.cHaltBlocking += pGVM->gvmm.s.StatsSched.cHaltBlocking;
2312 pStats->SchedSum.cHaltTimeouts += pGVM->gvmm.s.StatsSched.cHaltTimeouts;
2313 pStats->SchedSum.cHaltNotBlocking += pGVM->gvmm.s.StatsSched.cHaltNotBlocking;
2314 pStats->SchedSum.cHaltWakeUps += pGVM->gvmm.s.StatsSched.cHaltWakeUps;
2315
2316 pStats->SchedSum.cWakeUpCalls += pGVM->gvmm.s.StatsSched.cWakeUpCalls;
2317 pStats->SchedSum.cWakeUpNotHalted += pGVM->gvmm.s.StatsSched.cWakeUpNotHalted;
2318 pStats->SchedSum.cWakeUpWakeUps += pGVM->gvmm.s.StatsSched.cWakeUpWakeUps;
2319
2320 pStats->SchedSum.cPokeCalls += pGVM->gvmm.s.StatsSched.cPokeCalls;
2321 pStats->SchedSum.cPokeNotBusy += pGVM->gvmm.s.StatsSched.cPokeNotBusy;
2322
2323 pStats->SchedSum.cPollCalls += pGVM->gvmm.s.StatsSched.cPollCalls;
2324 pStats->SchedSum.cPollHalts += pGVM->gvmm.s.StatsSched.cPollHalts;
2325 pStats->SchedSum.cPollWakeUps += pGVM->gvmm.s.StatsSched.cPollWakeUps;
2326 }
2327 }
2328
2329 /*
2330 * Copy out the per host CPU statistics.
2331 */
2332 uint32_t iDstCpu = 0;
2333 uint32_t cSrcCpus = pGVMM->cHostCpus;
2334 for (uint32_t iSrcCpu = 0; iSrcCpu < cSrcCpus; iSrcCpu++)
2335 {
2336 if (pGVMM->aHostCpus[iSrcCpu].idCpu != NIL_RTCPUID)
2337 {
2338 pStats->aHostCpus[iDstCpu].idCpu = pGVMM->aHostCpus[iSrcCpu].idCpu;
2339 pStats->aHostCpus[iDstCpu].idxCpuSet = pGVMM->aHostCpus[iSrcCpu].idxCpuSet;
2340#ifdef GVMM_SCHED_WITH_PPT
2341 pStats->aHostCpus[iDstCpu].uDesiredHz = pGVMM->aHostCpus[iSrcCpu].Ppt.uDesiredHz;
2342 pStats->aHostCpus[iDstCpu].uTimerHz = pGVMM->aHostCpus[iSrcCpu].Ppt.uTimerHz;
2343 pStats->aHostCpus[iDstCpu].cChanges = pGVMM->aHostCpus[iSrcCpu].Ppt.cChanges;
2344 pStats->aHostCpus[iDstCpu].cStarts = pGVMM->aHostCpus[iSrcCpu].Ppt.cStarts;
2345#else
2346 pStats->aHostCpus[iDstCpu].uDesiredHz = 0;
2347 pStats->aHostCpus[iDstCpu].uTimerHz = 0;
2348 pStats->aHostCpus[iDstCpu].cChanges = 0;
2349 pStats->aHostCpus[iDstCpu].cStarts = 0;
2350#endif
2351 iDstCpu++;
2352 }
2353 }
2354 pStats->cHostCpus = iDstCpu;
2355
2356 gvmmR0UsedUnlock(pGVMM);
2357
2358 return VINF_SUCCESS;
2359}
2360
2361
2362/**
2363 * VMMR0 request wrapper for GVMMR0QueryStatistics.
2364 *
2365 * @returns see GVMMR0QueryStatistics.
2366 * @param pVM Pointer to the shared VM structure. Optional.
2367 * @param pReq The request packet.
2368 */
2369GVMMR0DECL(int) GVMMR0QueryStatisticsReq(PVM pVM, PGVMMQUERYSTATISTICSSREQ pReq)
2370{
2371 /*
2372 * Validate input and pass it on.
2373 */
2374 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2375 AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
2376
2377 return GVMMR0QueryStatistics(&pReq->Stats, pReq->pSession, pVM);
2378}
2379
2380
2381/**
2382 * Resets the specified GVMM statistics.
2383 *
2384 * @returns VBox status code.
2385 *
2386 * @param pStats Which statistics to reset, that is, non-zero fields indicates which to reset.
2387 * @param pSession The current session.
2388 * @param pVM The VM to reset statistics for. Optional.
2389 */
2390GVMMR0DECL(int) GVMMR0ResetStatistics(PCGVMMSTATS pStats, PSUPDRVSESSION pSession, PVM pVM)
2391{
2392 LogFlow(("GVMMR0ResetStatistics: pStats=%p pSession=%p pVM=%p\n", pStats, pSession, pVM));
2393
2394 /*
2395 * Validate input.
2396 */
2397 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
2398 AssertPtrReturn(pStats, VERR_INVALID_POINTER);
2399
2400 /*
2401 * Take the lock and get the VM statistics.
2402 */
2403 PGVMM pGVMM;
2404 if (pVM)
2405 {
2406 PGVM pGVM;
2407 int rc = gvmmR0ByVM(pVM, &pGVM, &pGVMM, true /*fTakeUsedLock*/);
2408 if (RT_FAILURE(rc))
2409 return rc;
2410# define MAYBE_RESET_FIELD(field) \
2411 do { if (pStats->SchedVM. field ) { pGVM->gvmm.s.StatsSched. field = 0; } } while (0)
2412 MAYBE_RESET_FIELD(cHaltCalls);
2413 MAYBE_RESET_FIELD(cHaltBlocking);
2414 MAYBE_RESET_FIELD(cHaltTimeouts);
2415 MAYBE_RESET_FIELD(cHaltNotBlocking);
2416 MAYBE_RESET_FIELD(cHaltWakeUps);
2417 MAYBE_RESET_FIELD(cWakeUpCalls);
2418 MAYBE_RESET_FIELD(cWakeUpNotHalted);
2419 MAYBE_RESET_FIELD(cWakeUpWakeUps);
2420 MAYBE_RESET_FIELD(cPokeCalls);
2421 MAYBE_RESET_FIELD(cPokeNotBusy);
2422 MAYBE_RESET_FIELD(cPollCalls);
2423 MAYBE_RESET_FIELD(cPollHalts);
2424 MAYBE_RESET_FIELD(cPollWakeUps);
2425# undef MAYBE_RESET_FIELD
2426 }
2427 else
2428 {
2429 GVMM_GET_VALID_INSTANCE(pGVMM, VERR_INTERNAL_ERROR);
2430
2431 int rc = gvmmR0UsedLock(pGVMM);
2432 AssertRCReturn(rc, rc);
2433 }
2434
2435 /*
2436 * Enumerate the VMs and add the ones visibile to the statistics.
2437 */
2438 if (ASMMemIsAll8(&pStats->SchedSum, sizeof(pStats->SchedSum), 0))
2439 {
2440 for (unsigned i = pGVMM->iUsedHead;
2441 i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
2442 i = pGVMM->aHandles[i].iNext)
2443 {
2444 PGVM pGVM = pGVMM->aHandles[i].pGVM;
2445 void *pvObj = pGVMM->aHandles[i].pvObj;
2446 if ( VALID_PTR(pvObj)
2447 && VALID_PTR(pGVM)
2448 && pGVM->u32Magic == GVM_MAGIC
2449 && RT_SUCCESS(SUPR0ObjVerifyAccess(pvObj, pSession, NULL)))
2450 {
2451# define MAYBE_RESET_FIELD(field) \
2452 do { if (pStats->SchedSum. field ) { pGVM->gvmm.s.StatsSched. field = 0; } } while (0)
2453 MAYBE_RESET_FIELD(cHaltCalls);
2454 MAYBE_RESET_FIELD(cHaltBlocking);
2455 MAYBE_RESET_FIELD(cHaltTimeouts);
2456 MAYBE_RESET_FIELD(cHaltNotBlocking);
2457 MAYBE_RESET_FIELD(cHaltWakeUps);
2458 MAYBE_RESET_FIELD(cWakeUpCalls);
2459 MAYBE_RESET_FIELD(cWakeUpNotHalted);
2460 MAYBE_RESET_FIELD(cWakeUpWakeUps);
2461 MAYBE_RESET_FIELD(cPokeCalls);
2462 MAYBE_RESET_FIELD(cPokeNotBusy);
2463 MAYBE_RESET_FIELD(cPollCalls);
2464 MAYBE_RESET_FIELD(cPollHalts);
2465 MAYBE_RESET_FIELD(cPollWakeUps);
2466# undef MAYBE_RESET_FIELD
2467 }
2468 }
2469 }
2470
2471 gvmmR0UsedUnlock(pGVMM);
2472
2473 return VINF_SUCCESS;
2474}
2475
2476
2477/**
2478 * VMMR0 request wrapper for GVMMR0ResetStatistics.
2479 *
2480 * @returns see GVMMR0ResetStatistics.
2481 * @param pVM Pointer to the shared VM structure. Optional.
2482 * @param pReq The request packet.
2483 */
2484GVMMR0DECL(int) GVMMR0ResetStatisticsReq(PVM pVM, PGVMMRESETSTATISTICSSREQ pReq)
2485{
2486 /*
2487 * Validate input and pass it on.
2488 */
2489 AssertPtrReturn(pReq, VERR_INVALID_POINTER);
2490 AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
2491
2492 return GVMMR0ResetStatistics(&pReq->Stats, pReq->pSession, pVM);
2493}
2494
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