VirtualBox

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

Last change on this file since 93609 was 93554, checked in by vboxsync, 3 years ago

VMM: Changed PAGE_SIZE -> GUEST_PAGE_SIZE / HOST_PAGE_SIZE, PAGE_SHIFT -> GUEST_PAGE_SHIFT / HOST_PAGE_SHIFT, and PAGE_OFFSET_MASK -> GUEST_PAGE_OFFSET_MASK / HOST_PAGE_OFFSET_MASK. Also removed most usage of ASMMemIsZeroPage and ASMMemZeroPage since the host and guest page size doesn't need to be the same any more. Some work left to do in the page pool code. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.6 KB
Line 
1/* $Id: PGMSharedPage.cpp 93554 2022-02-02 22:57:02Z vboxsync $ */
2/** @file
3 * PGM - Page Manager and Monitor, Shared page handling
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 PGM_LOCK_VOID(pVM);
235 pgmR3PhysAssertSharedPageChecksums(pVM);
236 rc = GMMR3CheckSharedModules(pVM);
237 pgmR3PhysAssertSharedPageChecksums(pVM);
238 PGM_UNLOCK(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 PGMPTWALK Walk;
291
292 PGM_LOCK_VOID(pVM);
293
294 int rc = PGMGstGetPage(VMMGetCpu(pVM), GCPtrPage, &Walk);
295 switch (rc)
296 {
297 case VINF_SUCCESS:
298 {
299 PPGMPAGE pPage = pgmPhysGetPage(pVM, Walk.GCPhys);
300 if (pPage)
301 {
302 *pfShared = PGM_PAGE_IS_SHARED(pPage);
303 *pfPageFlags = Walk.fEffective;
304 }
305 else
306 rc = VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS;
307 break;
308 }
309
310 case VERR_PAGE_NOT_PRESENT:
311 case VERR_PAGE_TABLE_NOT_PRESENT:
312 case VERR_PAGE_MAP_LEVEL4_NOT_PRESENT:
313 case VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT:
314 *pfShared = false;
315 *pfPageFlags = 0;
316 rc = VINF_SUCCESS;
317 break;
318
319 default:
320 break;
321 }
322
323 PGM_UNLOCK(pVM);
324 return rc;
325}
326# endif /* DEBUG */
327
328# ifdef VBOX_STRICT
329
330/**
331 * @callback_method_impl{FNDBGCCMD, The '.pgmcheckduppages' command.}
332 */
333DECLCALLBACK(int) pgmR3CmdCheckDuplicatePages(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
334{
335 unsigned cBallooned = 0;
336 unsigned cShared = 0;
337 unsigned cZero = 0;
338 unsigned cUnique = 0;
339 unsigned cDuplicate = 0;
340 unsigned cAllocZero = 0;
341 unsigned cPages = 0;
342 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs);
343 PVM pVM = pUVM->pVM;
344 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
345
346 PGM_LOCK_VOID(pVM);
347
348 for (PPGMRAMRANGE pRam = pVM->pgm.s.pRamRangesXR3; pRam; pRam = pRam->pNextR3)
349 {
350 PPGMPAGE pPage = &pRam->aPages[0];
351 RTGCPHYS GCPhys = pRam->GCPhys;
352 uint32_t cLeft = pRam->cb >> GUEST_PAGE_SHIFT;
353 while (cLeft-- > 0)
354 {
355 if (PGM_PAGE_GET_TYPE(pPage) == PGMPAGETYPE_RAM)
356 {
357 switch (PGM_PAGE_GET_STATE(pPage))
358 {
359 case PGM_PAGE_STATE_ZERO:
360 cZero++;
361 break;
362
363 case PGM_PAGE_STATE_BALLOONED:
364 cBallooned++;
365 break;
366
367 case PGM_PAGE_STATE_SHARED:
368 cShared++;
369 break;
370
371 case PGM_PAGE_STATE_ALLOCATED:
372 case PGM_PAGE_STATE_WRITE_MONITORED:
373 {
374 /* Check if the page was allocated, but completely zero. */
375 PGMPAGEMAPLOCK PgMpLck;
376 const void *pvPage;
377 int rc = pgmPhysGCPhys2CCPtrInternalReadOnly(pVM, pPage, GCPhys, &pvPage, &PgMpLck);
378 if ( RT_SUCCESS(rc)
379 && ASMMemIsZero(pvPage, GUEST_PAGE_SIZE))
380 cAllocZero++;
381 else if (GMMR3IsDuplicatePage(pVM, PGM_PAGE_GET_PAGEID(pPage)))
382 cDuplicate++;
383 else
384 cUnique++;
385 if (RT_SUCCESS(rc))
386 pgmPhysReleaseInternalPageMappingLock(pVM, &PgMpLck);
387 break;
388 }
389
390 default:
391 AssertFailed();
392 break;
393 }
394 }
395
396 /* next */
397 pPage++;
398 GCPhys += GUEST_PAGE_SIZE;
399 cPages++;
400 /* Give some feedback for every processed megabyte. */
401 if ((cPages & 0x7f) == 0)
402 pCmdHlp->pfnPrintf(pCmdHlp, NULL, ".");
403 }
404 }
405 PGM_UNLOCK(pVM);
406
407 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "\nNumber of zero pages %08x (%d MB)\n", cZero, cZero / 256);
408 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of alloczero pages %08x (%d MB)\n", cAllocZero, cAllocZero / 256);
409 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of ballooned pages %08x (%d MB)\n", cBallooned, cBallooned / 256);
410 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of shared pages %08x (%d MB)\n", cShared, cShared / 256);
411 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of unique pages %08x (%d MB)\n", cUnique, cUnique / 256);
412 pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Number of duplicate pages %08x (%d MB)\n", cDuplicate, cDuplicate / 256);
413 return VINF_SUCCESS;
414}
415
416
417/**
418 * @callback_method_impl{FNDBGCCMD, The '.pgmsharedmodules' command.}
419 */
420DECLCALLBACK(int) pgmR3CmdShowSharedModules(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
421{
422 NOREF(pCmd); NOREF(paArgs); NOREF(cArgs);
423 PVM pVM = pUVM->pVM;
424 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
425
426 PGM_LOCK_VOID(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 PGM_UNLOCK(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