VirtualBox

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

Last change on this file since 43346 was 43045, checked in by vboxsync, 12 years ago

PGM: Employ the 4 unused PGMPHYSPAGE bits for checksumming shared pages in strict builds.

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