VirtualBox

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

Last change on this file since 45480 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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