VirtualBox

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

Last change on this file since 30798 was 30784, checked in by vboxsync, 15 years ago

Better check

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