VirtualBox

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

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

GVMMR0,TM,STAM: Periodic preemption timer fixes, adjustments and statistics. (still disabled)

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