VirtualBox

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

Last change on this file since 86473 was 86473, checked in by vboxsync, 4 years ago

VMM/PGM: Working on eliminating page table bitfield use. bugref:9841 bugref:9746

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