VirtualBox

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

最後變更 在這個檔案從38953是 38953,由 vboxsync 提交於 13 年 前

PGM: Attempt at fixing the VERR_MAP_FAILED during state save problem on 32-bit hosts when assigning lots of memory to the guest. PGM should lock down guest RAM pages before use and release them afterwards like everyone else. Still quite some stuff left to do there, so I've deviced a little hack for tracking unlocked mappings and using this as input when deciding to do async or sync chunk unmapping at save/load time. See xtracker #5912 and public ticket 7929.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette