VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PGMSharedPage.cpp@ 56255

Last change on this file since 56255 was 56255, checked in by vboxsync, 9 years ago

updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.0 KB
Line 
1/* $Id: PGMSharedPage.cpp 56255 2015-06-05 10:58:47Z vboxsync $ */
2/** @file
3 * PGM - Page Manager and Monitor, Shared page handling
4 */
5
6/*
7 * Copyright (C) 2006-2013 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/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_PGM_SHARED
23#include <VBox/vmm/pgm.h>
24#include <VBox/vmm/stam.h>
25#include <VBox/vmm/uvm.h>
26#include "PGMInternal.h"
27#include <VBox/vmm/vm.h>
28#include <VBox/sup.h>
29#include <VBox/param.h>
30#include <VBox/err.h>
31#include <VBox/log.h>
32#include <iprt/asm.h>
33#include <iprt/assert.h>
34#include <iprt/mem.h>
35#include <iprt/string.h>
36
37#include "PGMInline.h"
38
39
40#ifdef VBOX_WITH_PAGE_SHARING
41
42/*******************************************************************************
43* Global Variables *
44*******************************************************************************/
45# ifdef VBOX_STRICT
46/** Keep a copy of all registered shared modules for the .pgmcheckduppages debugger command. */
47static PGMMREGISTERSHAREDMODULEREQ g_apSharedModules[512] = {0};
48static unsigned g_cSharedModules = 0;
49# endif /* VBOX_STRICT */
50
51
52/**
53 * Registers a new shared module for the VM
54 *
55 * @returns VBox status code.
56 * @param pVM Pointer to the VM.
57 * @param enmGuestOS Guest OS type.
58 * @param pszModuleName Module name.
59 * @param pszVersion Module version.
60 * @param GCBaseAddr Module base address.
61 * @param cbModule Module size.
62 * @param cRegions Number of shared region descriptors.
63 * @param paRegions Shared region(s).
64 *
65 * @todo This should be a GMMR3 call. No need to involve GMM here.
66 */
67VMMR3DECL(int) PGMR3SharedModuleRegister(PVM pVM, VBOXOSFAMILY enmGuestOS, char *pszModuleName, char *pszVersion,
68 RTGCPTR GCBaseAddr, uint32_t cbModule, uint32_t cRegions,
69 VMMDEVSHAREDREGIONDESC const *paRegions)
70{
71 Log(("PGMR3SharedModuleRegister family=%d name=%s version=%s base=%RGv size=%x cRegions=%d\n",
72 enmGuestOS, pszModuleName, pszVersion, GCBaseAddr, cbModule, cRegions));
73
74 /*
75 * Sanity check.
76 */
77 AssertReturn(cRegions <= VMMDEVSHAREDREGIONDESC_MAX, VERR_INVALID_PARAMETER);
78 if (!pVM->pgm.s.fPageFusionAllowed)
79 return VERR_NOT_SUPPORTED;
80
81 /*
82 * Allocate and initialize a GMM request.
83 */
84 PGMMREGISTERSHAREDMODULEREQ pReq;
85 pReq = (PGMMREGISTERSHAREDMODULEREQ)RTMemAllocZ(RT_OFFSETOF(GMMREGISTERSHAREDMODULEREQ, aRegions[cRegions]));
86 AssertReturn(pReq, VERR_NO_MEMORY);
87
88 pReq->enmGuestOS = enmGuestOS;
89 pReq->GCBaseAddr = GCBaseAddr;
90 pReq->cbModule = cbModule;
91 pReq->cRegions = cRegions;
92 for (uint32_t i = 0; i < cRegions; i++)
93 pReq->aRegions[i] = paRegions[i];
94
95 int rc = RTStrCopy(pReq->szName, sizeof(pReq->szName), pszModuleName);
96 if (RT_SUCCESS(rc))
97 {
98 rc = RTStrCopy(pReq->szVersion, sizeof(pReq->szVersion), pszVersion);
99 if (RT_SUCCESS(rc))
100 {
101 /*
102 * Issue the request. In strict builds, do some local tracking.
103 */
104 pgmR3PhysAssertSharedPageChecksums(pVM);
105 rc = GMMR3RegisterSharedModule(pVM, pReq);
106 if (RT_SUCCESS(rc))
107 rc = pReq->rc;
108 AssertMsg(rc == VINF_SUCCESS || rc == VINF_GMM_SHARED_MODULE_ALREADY_REGISTERED, ("%Rrc\n", rc));
109
110# ifdef VBOX_STRICT
111 if ( rc == VINF_SUCCESS
112 && g_cSharedModules < RT_ELEMENTS(g_apSharedModules))
113 {
114 unsigned i;
115 for (i = 0; i < RT_ELEMENTS(g_apSharedModules); i++)
116 if (g_apSharedModules[i] == NULL)
117 {
118
119 size_t const cbSharedModule = RT_OFFSETOF(GMMREGISTERSHAREDMODULEREQ, aRegions[cRegions]);
120 g_apSharedModules[i] = (PGMMREGISTERSHAREDMODULEREQ)RTMemDup(pReq, cbSharedModule);
121 g_cSharedModules++;
122 break;
123 }
124 Assert(i < RT_ELEMENTS(g_apSharedModules));
125 }
126# endif /* VBOX_STRICT */
127 if (RT_SUCCESS(rc))
128 rc = VINF_SUCCESS;
129 }
130 }
131
132 RTMemFree(pReq);
133 return rc;
134}
135
136
137/**
138 * Unregisters a shared module for the VM
139 *
140 * @returns VBox status code.
141 * @param pVM Pointer to the VM.
142 * @param pszModuleName Module name.
143 * @param pszVersion Module version.
144 * @param GCBaseAddr Module base address.
145 * @param cbModule Module size.
146 *
147 * @todo This should be a GMMR3 call. No need to involve GMM here.
148 */
149VMMR3DECL(int) PGMR3SharedModuleUnregister(PVM pVM, char *pszModuleName, char *pszVersion, RTGCPTR GCBaseAddr, uint32_t cbModule)
150{
151 Log(("PGMR3SharedModuleUnregister name=%s version=%s base=%RGv size=%x\n", pszModuleName, pszVersion, GCBaseAddr, cbModule));
152
153 AssertMsgReturn(cbModule > 0 && cbModule < _1G, ("%u\n", cbModule), VERR_OUT_OF_RANGE);
154 if (!pVM->pgm.s.fPageFusionAllowed)
155 return VERR_NOT_SUPPORTED;
156
157 /*
158 * Forward the request to GMM (ring-0).
159 */
160 PGMMUNREGISTERSHAREDMODULEREQ pReq = (PGMMUNREGISTERSHAREDMODULEREQ)RTMemAlloc(sizeof(*pReq));
161 AssertReturn(pReq, VERR_NO_MEMORY);
162
163 pReq->GCBaseAddr = GCBaseAddr;
164 pReq->u32Alignment = 0;
165 pReq->cbModule = cbModule;
166
167 int rc = RTStrCopy(pReq->szName, sizeof(pReq->szName), pszModuleName);
168 if (RT_SUCCESS(rc))
169 {
170 rc = RTStrCopy(pReq->szVersion, sizeof(pReq->szVersion), pszVersion);
171 if (RT_SUCCESS(rc))
172 {
173 pgmR3PhysAssertSharedPageChecksums(pVM);
174 rc = GMMR3UnregisterSharedModule(pVM, pReq);
175 pgmR3PhysAssertSharedPageChecksums(pVM);
176
177# ifdef VBOX_STRICT
178 /*
179 * Update our local tracking.
180 */
181 for (unsigned i = 0; i < g_cSharedModules; i++)
182 {
183 if ( g_apSharedModules[i]
184 && !strcmp(g_apSharedModules[i]->szName, pszModuleName)
185 && !strcmp(g_apSharedModules[i]->szVersion, pszVersion))
186 {
187 RTMemFree(g_apSharedModules[i]);
188 g_apSharedModules[i] = NULL;
189 g_cSharedModules--;
190 break;
191 }
192 }
193# endif /* VBOX_STRICT */
194 }
195 }
196
197 RTMemFree(pReq);
198 return rc;
199}
200
201
202/**
203 * Rendezvous callback that will be called once.
204 *
205 * @returns VBox strict status code.
206 * @param pVM Pointer to the VM.
207 * @param pVCpu Pointer to the VMCPU of the calling EMT.
208 * @param pvUser Pointer to a VMCPUID with the requester's ID.
209 */
210static DECLCALLBACK(VBOXSTRICTRC) pgmR3SharedModuleRegRendezvous(PVM pVM, PVMCPU pVCpu, void *pvUser)
211{
212 VMCPUID idCpu = *(VMCPUID *)pvUser;
213
214 /* Execute on the VCPU that issued the original request to make sure we're in the right cr3 context. */
215 if (pVCpu->idCpu != idCpu)
216 {
217 Assert(pVM->cCpus > 1);
218 return VINF_SUCCESS;
219 }
220
221
222 /* Flush all pending handy page operations before changing any shared page assignments. */
223 int rc = PGMR3PhysAllocateHandyPages(pVM);
224 AssertRC(rc);
225
226 /*
227 * Lock it here as we can't deal with busy locks in this ring-0 path.
228 */
229 LogFlow(("pgmR3SharedModuleRegRendezvous: start (%d)\n", pVM->pgm.s.cSharedPages));
230
231 pgmLock(pVM);
232 pgmR3PhysAssertSharedPageChecksums(pVM);
233 rc = GMMR3CheckSharedModules(pVM);
234 pgmR3PhysAssertSharedPageChecksums(pVM);
235 pgmUnlock(pVM);
236 AssertLogRelRC(rc);
237
238 LogFlow(("pgmR3SharedModuleRegRendezvous: done (%d)\n", pVM->pgm.s.cSharedPages));
239 return rc;
240}
241
242/**
243 * Shared module check helper (called on the way out).
244 *
245 * @param pVM Pointer to the VM.
246 * @param VMCPUID VCPU id
247 */
248static DECLCALLBACK(void) pgmR3CheckSharedModulesHelper(PVM pVM, VMCPUID idCpu)
249{
250 /* We must stall other VCPUs as we'd otherwise have to send IPI flush commands for every single change we make. */
251 STAM_REL_PROFILE_START(&pVM->pgm.s.StatShModCheck, a);
252 int rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, pgmR3SharedModuleRegRendezvous, &idCpu);
253 AssertRCSuccess(rc);
254 STAM_REL_PROFILE_STOP(&pVM->pgm.s.StatShModCheck, a);
255}
256
257
258/**
259 * Check all registered modules for changes.
260 *
261 * @returns VBox status code.
262 * @param pVM Pointer to the VM
263 */
264VMMR3DECL(int) PGMR3SharedModuleCheckAll(PVM pVM)
265{
266 if (!pVM->pgm.s.fPageFusionAllowed)
267 return VERR_NOT_SUPPORTED;
268
269 /* Queue the actual registration as we are under the IOM lock right now. Perform this operation on the way out. */
270 return VMR3ReqCallNoWait(pVM, VMCPUID_ANY_QUEUE, (PFNRT)pgmR3CheckSharedModulesHelper, 2, pVM, VMMGetCpuId(pVM));
271}
272
273
274# ifdef DEBUG
275/**
276 * Query the state of a page in a shared module
277 *
278 * @returns VBox status code.
279 * @param pVM Pointer to the VM.
280 * @param GCPtrPage Page address.
281 * @param pfShared Shared status (out).
282 * @param pfPageFlags Page flags (out).
283 */
284VMMR3DECL(int) PGMR3SharedModuleGetPageState(PVM pVM, RTGCPTR GCPtrPage, bool *pfShared, uint64_t *pfPageFlags)
285{
286 /* Debug only API for the page fusion testcase. */
287 RTGCPHYS GCPhys;
288 uint64_t fFlags;
289
290 pgmLock(pVM);
291
292 int rc = PGMGstGetPage(VMMGetCpu(pVM), GCPtrPage, &fFlags, &GCPhys);
293 switch (rc)
294 {
295 case VINF_SUCCESS:
296 {
297 PPGMPAGE pPage = pgmPhysGetPage(pVM, GCPhys);
298 if (pPage)
299 {
300 *pfShared = PGM_PAGE_IS_SHARED(pPage);
301 *pfPageFlags = fFlags;
302 }
303 else
304 rc = VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
305 break;
306 }
307
308 case VERR_PAGE_NOT_PRESENT:
309 case VERR_PAGE_TABLE_NOT_PRESENT:
310 case VERR_PAGE_MAP_LEVEL4_NOT_PRESENT:
311 case VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT:
312 *pfShared = false;
313 *pfPageFlags = 0;
314 rc = VINF_SUCCESS;
315 break;
316
317 default:
318 break;
319 }
320
321 pgmUnlock(pVM);
322 return rc;
323}
324# endif /* DEBUG */
325
326# ifdef VBOX_STRICT
327
328/**
329 * @callback_method_impl{FNDBGCCMD, The '.pgmcheckduppages' command.}
330 */
331DECLCALLBACK(int) pgmR3CmdCheckDuplicatePages(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
332{
333 unsigned cBallooned = 0;
334 unsigned cShared = 0;
335 unsigned cZero = 0;
336 unsigned cUnique = 0;
337 unsigned cDuplicate = 0;
338 unsigned cAllocZero = 0;
339 unsigned cPages = 0;
340 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs);
341 PVM pVM = pUVM->pVM;
342 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
343
344 pgmLock(pVM);
345
346 for (PPGMRAMRANGE pRam = pVM->pgm.s.pRamRangesXR3; pRam; pRam = pRam->pNextR3)
347 {
348 PPGMPAGE pPage = &pRam->aPages[0];
349 RTGCPHYS GCPhys = pRam->GCPhys;
350 uint32_t cLeft = pRam->cb >> PAGE_SHIFT;
351 while (cLeft-- > 0)
352 {
353 if (PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_RAM)
354 {
355 switch (PGM_PAGE_GET_STATE(pPage))
356 {
357 case PGM_PAGE_STATE_ZERO:
358 cZero++;
359 break;
360
361 case PGM_PAGE_STATE_BALLOONED:
362 cBallooned++;
363 break;
364
365 case PGM_PAGE_STATE_SHARED:
366 cShared++;
367 break;
368
369 case PGM_PAGE_STATE_ALLOCATED:
370 case PGM_PAGE_STATE_WRITE_MONITORED:
371 {
372 /* Check if the page was allocated, but completely zero. */
373 PGMPAGEMAPLOCK PgMpLck;
374 const void *pvPage;
375 int rc = pgmPhysGCPhys2CCPtrInternalReadOnly(pVM, pPage, GCPhys, &pvPage, &PgMpLck);
376 if ( RT_SUCCESS(rc)
377 && ASMMemIsZeroPage(pvPage))
378 cAllocZero++;
379 else if (GMMR3IsDuplicatePage(pVM, PGM_PAGE_GET_PAGEID(pPage)))
380 cDuplicate++;
381 else
382 cUnique++;
383 if (RT_SUCCESS(rc))
384 pgmPhysReleaseInternalPageMappingLock(pVM, &PgMpLck);
385 break;
386 }
387
388 default:
389 AssertFailed();
390 break;
391 }
392 }
393
394 /* next */
395 pPage++;
396 GCPhys += PAGE_SIZE;
397 cPages++;
398 /* Give some feedback for every processed megabyte. */
399 if ((cPages & 0x7f) == 0)
400 pCmdHlp->pfnPrintf(pCmdHlp, NULL, ".");
401 }
402 }
403 pgmUnlock(pVM);
404
405 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\nNumber of zero pages %08x (%d MB)\n", cZero, cZero / 256);
406 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of alloczero pages %08x (%d MB)\n", cAllocZero, cAllocZero / 256);
407 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of ballooned pages %08x (%d MB)\n", cBallooned, cBallooned / 256);
408 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of shared pages %08x (%d MB)\n", cShared, cShared / 256);
409 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of unique pages %08x (%d MB)\n", cUnique, cUnique / 256);
410 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of duplicate pages %08x (%d MB)\n", cDuplicate, cDuplicate / 256);
411 return VINF_SUCCESS;
412}
413
414
415/**
416 * @callback_method_impl{FNDBGCCMD, The '.pgmsharedmodules' command.}
417 */
418DECLCALLBACK(int) pgmR3CmdShowSharedModules(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
419{
420 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs);
421 PVM pVM = pUVM->pVM;
422 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
423
424 pgmLock(pVM);
425 for (unsigned i = 0; i < RT_ELEMENTS(g_apSharedModules); i++)
426 {
427 if (g_apSharedModules[i])
428 {
429 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Shared module %s (%s):\n", g_apSharedModules[i]->szName, g_apSharedModules[i]->szVersion);
430 for (unsigned j = 0; j < g_apSharedModules[i]->cRegions; j++)
431 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "--- Region %d: base %RGv size %x\n", j, g_apSharedModules[i]->aRegions[j].GCRegionAddr, g_apSharedModules[i]->aRegions[j].cbRegion);
432 }
433 }
434 pgmUnlock(pVM);
435
436 return VINF_SUCCESS;
437}
438
439# endif /* VBOX_STRICT*/
440#endif /* VBOX_WITH_PAGE_SHARING */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use