VirtualBox

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

Last change on this file since 77807 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.4 KB
Line 
1/* $Id: PGMSharedPage.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * PGM - Page Manager and Monitor, Shared page handling
4 */
5
6/*
7 * Copyright (C) 2006-2019 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 <VBox/VMMDev.h>
33#include <iprt/asm.h>
34#include <iprt/assert.h>
35#include <iprt/mem.h>
36#include <iprt/string.h>
37
38#include "PGMInline.h"
39
40
41#ifdef VBOX_WITH_PAGE_SHARING
42
43
44/*********************************************************************************************************************************
45* Global Variables *
46*********************************************************************************************************************************/
47# ifdef VBOX_STRICT
48/** Keep a copy of all registered shared modules for the .pgmcheckduppages debugger command. */
49static PGMMREGISTERSHAREDMODULEREQ g_apSharedModules[512] = {0};
50static unsigned g_cSharedModules = 0;
51# endif /* VBOX_STRICT */
52
53
54/**
55 * Registers a new shared module for the VM
56 *
57 * @returns VBox status code.
58 * @param pVM The cross context VM structure.
59 * @param enmGuestOS Guest OS type.
60 * @param pszModuleName Module name.
61 * @param pszVersion Module version.
62 * @param GCBaseAddr Module base address.
63 * @param cbModule Module size.
64 * @param cRegions Number of shared region descriptors.
65 * @param paRegions Shared region(s).
66 *
67 * @todo This should be a GMMR3 call. No need to involve GMM here.
68 */
69VMMR3DECL(int) PGMR3SharedModuleRegister(PVM pVM, VBOXOSFAMILY enmGuestOS, char *pszModuleName, char *pszVersion,
70 RTGCPTR GCBaseAddr, uint32_t cbModule, uint32_t cRegions,
71 VMMDEVSHAREDREGIONDESC const *paRegions)
72{
73 Log(("PGMR3SharedModuleRegister family=%d name=%s version=%s base=%RGv size=%x cRegions=%d\n",
74 enmGuestOS, pszModuleName, pszVersion, GCBaseAddr, cbModule, cRegions));
75
76 /*
77 * Sanity check.
78 */
79 AssertReturn(cRegions <= VMMDEVSHAREDREGIONDESC_MAX, VERR_INVALID_PARAMETER);
80 if (!pVM->pgm.s.fPageFusionAllowed)
81 return VERR_NOT_SUPPORTED;
82
83 /*
84 * Allocate and initialize a GMM request.
85 */
86 PGMMREGISTERSHAREDMODULEREQ pReq;
87 pReq = (PGMMREGISTERSHAREDMODULEREQ)RTMemAllocZ(RT_UOFFSETOF_DYN(GMMREGISTERSHAREDMODULEREQ, aRegions[cRegions]));
88 AssertReturn(pReq, VERR_NO_MEMORY);
89
90 pReq->enmGuestOS = enmGuestOS;
91 pReq->GCBaseAddr = GCBaseAddr;
92 pReq->cbModule = cbModule;
93 pReq->cRegions = cRegions;
94 for (uint32_t i = 0; i < cRegions; i++)
95 pReq->aRegions[i] = paRegions[i];
96
97 int rc = RTStrCopy(pReq->szName, sizeof(pReq->szName), pszModuleName);
98 if (RT_SUCCESS(rc))
99 {
100 rc = RTStrCopy(pReq->szVersion, sizeof(pReq->szVersion), pszVersion);
101 if (RT_SUCCESS(rc))
102 {
103 /*
104 * Issue the request. In strict builds, do some local tracking.
105 */
106 pgmR3PhysAssertSharedPageChecksums(pVM);
107 rc = GMMR3RegisterSharedModule(pVM, pReq);
108 if (RT_SUCCESS(rc))
109 rc = pReq->rc;
110 AssertMsg(rc == VINF_SUCCESS || rc == VINF_GMM_SHARED_MODULE_ALREADY_REGISTERED, ("%Rrc\n", rc));
111
112# ifdef VBOX_STRICT
113 if ( rc == VINF_SUCCESS
114 && g_cSharedModules < RT_ELEMENTS(g_apSharedModules))
115 {
116 unsigned i;
117 for (i = 0; i < RT_ELEMENTS(g_apSharedModules); i++)
118 if (g_apSharedModules[i] == NULL)
119 {
120
121 size_t const cbSharedModule = RT_UOFFSETOF_DYN(GMMREGISTERSHAREDMODULEREQ, aRegions[cRegions]);
122 g_apSharedModules[i] = (PGMMREGISTERSHAREDMODULEREQ)RTMemDup(pReq, cbSharedModule);
123 g_cSharedModules++;
124 break;
125 }
126 Assert(i < RT_ELEMENTS(g_apSharedModules));
127 }
128# endif /* VBOX_STRICT */
129 if (RT_SUCCESS(rc))
130 rc = VINF_SUCCESS;
131 }
132 }
133
134 RTMemFree(pReq);
135 return rc;
136}
137
138
139/**
140 * Unregisters a shared module for the VM
141 *
142 * @returns VBox status code.
143 * @param pVM The cross context VM structure.
144 * @param pszModuleName Module name.
145 * @param pszVersion Module version.
146 * @param GCBaseAddr Module base address.
147 * @param cbModule Module size.
148 *
149 * @todo This should be a GMMR3 call. No need to involve GMM here.
150 */
151VMMR3DECL(int) PGMR3SharedModuleUnregister(PVM pVM, char *pszModuleName, char *pszVersion, RTGCPTR GCBaseAddr, uint32_t cbModule)
152{
153 Log(("PGMR3SharedModuleUnregister name=%s version=%s base=%RGv size=%x\n", pszModuleName, pszVersion, GCBaseAddr, cbModule));
154
155 AssertMsgReturn(cbModule > 0 && cbModule < _1G, ("%u\n", cbModule), VERR_OUT_OF_RANGE);
156 if (!pVM->pgm.s.fPageFusionAllowed)
157 return VERR_NOT_SUPPORTED;
158
159 /*
160 * Forward the request to GMM (ring-0).
161 */
162 PGMMUNREGISTERSHAREDMODULEREQ pReq = (PGMMUNREGISTERSHAREDMODULEREQ)RTMemAlloc(sizeof(*pReq));
163 AssertReturn(pReq, VERR_NO_MEMORY);
164
165 pReq->GCBaseAddr = GCBaseAddr;
166 pReq->u32Alignment = 0;
167 pReq->cbModule = cbModule;
168
169 int rc = RTStrCopy(pReq->szName, sizeof(pReq->szName), pszModuleName);
170 if (RT_SUCCESS(rc))
171 {
172 rc = RTStrCopy(pReq->szVersion, sizeof(pReq->szVersion), pszVersion);
173 if (RT_SUCCESS(rc))
174 {
175 pgmR3PhysAssertSharedPageChecksums(pVM);
176 rc = GMMR3UnregisterSharedModule(pVM, pReq);
177 pgmR3PhysAssertSharedPageChecksums(pVM);
178
179# ifdef VBOX_STRICT
180 /*
181 * Update our local tracking.
182 */
183 for (unsigned i = 0; i < g_cSharedModules; i++)
184 {
185 if ( g_apSharedModules[i]
186 && !strcmp(g_apSharedModules[i]->szName, pszModuleName)
187 && !strcmp(g_apSharedModules[i]->szVersion, pszVersion))
188 {
189 RTMemFree(g_apSharedModules[i]);
190 g_apSharedModules[i] = NULL;
191 g_cSharedModules--;
192 break;
193 }
194 }
195# endif /* VBOX_STRICT */
196 }
197 }
198
199 RTMemFree(pReq);
200 return rc;
201}
202
203
204/**
205 * Rendezvous callback that will be called once.
206 *
207 * @returns VBox strict status code.
208 * @param pVM The cross context VM structure.
209 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
210 * @param pvUser Pointer to a VMCPUID with the requester's ID.
211 */
212static DECLCALLBACK(VBOXSTRICTRC) pgmR3SharedModuleRegRendezvous(PVM pVM, PVMCPU pVCpu, void *pvUser)
213{
214 VMCPUID idCpu = *(VMCPUID *)pvUser;
215
216 /* Execute on the VCPU that issued the original request to make sure we're in the right cr3 context. */
217 if (pVCpu->idCpu != idCpu)
218 {
219 Assert(pVM->cCpus > 1);
220 return VINF_SUCCESS;
221 }
222
223
224 /* Flush all pending handy page operations before changing any shared page assignments. */
225 int rc = PGMR3PhysAllocateHandyPages(pVM);
226 AssertRC(rc);
227
228 /*
229 * Lock it here as we can't deal with busy locks in this ring-0 path.
230 */
231 LogFlow(("pgmR3SharedModuleRegRendezvous: start (%d)\n", pVM->pgm.s.cSharedPages));
232
233 pgmLock(pVM);
234 pgmR3PhysAssertSharedPageChecksums(pVM);
235 rc = GMMR3CheckSharedModules(pVM);
236 pgmR3PhysAssertSharedPageChecksums(pVM);
237 pgmUnlock(pVM);
238 AssertLogRelRC(rc);
239
240 LogFlow(("pgmR3SharedModuleRegRendezvous: done (%d)\n", pVM->pgm.s.cSharedPages));
241 return rc;
242}
243
244/**
245 * Shared module check helper (called on the way out).
246 *
247 * @param pVM The cross context VM structure.
248 * @param idCpu VCPU id.
249 */
250static DECLCALLBACK(void) pgmR3CheckSharedModulesHelper(PVM pVM, VMCPUID idCpu)
251{
252 /* We must stall other VCPUs as we'd otherwise have to send IPI flush commands for every single change we make. */
253 STAM_REL_PROFILE_START(&pVM->pgm.s.StatShModCheck, a);
254 int rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, pgmR3SharedModuleRegRendezvous, &idCpu);
255 AssertRCSuccess(rc);
256 STAM_REL_PROFILE_STOP(&pVM->pgm.s.StatShModCheck, a);
257}
258
259
260/**
261 * Check all registered modules for changes.
262 *
263 * @returns VBox status code.
264 * @param pVM The cross context VM structure.
265 */
266VMMR3DECL(int) PGMR3SharedModuleCheckAll(PVM pVM)
267{
268 if (!pVM->pgm.s.fPageFusionAllowed)
269 return VERR_NOT_SUPPORTED;
270
271 /* Queue the actual registration as we are under the IOM lock right now. Perform this operation on the way out. */
272 return VMR3ReqCallNoWait(pVM, VMCPUID_ANY_QUEUE, (PFNRT)pgmR3CheckSharedModulesHelper, 2, pVM, VMMGetCpuId(pVM));
273}
274
275
276# ifdef DEBUG
277/**
278 * Query the state of a page in a shared module
279 *
280 * @returns VBox status code.
281 * @param pVM The cross context VM structure.
282 * @param GCPtrPage Page address.
283 * @param pfShared Shared status (out).
284 * @param pfPageFlags Page flags (out).
285 */
286VMMR3DECL(int) PGMR3SharedModuleGetPageState(PVM pVM, RTGCPTR GCPtrPage, bool *pfShared, uint64_t *pfPageFlags)
287{
288 /* Debug only API for the page fusion testcase. */
289 RTGCPHYS GCPhys;
290 uint64_t fFlags;
291
292 pgmLock(pVM);
293
294 int rc = PGMGstGetPage(VMMGetCpu(pVM), GCPtrPage, &fFlags, &GCPhys);
295 switch (rc)
296 {
297 case VINF_SUCCESS:
298 {
299 PPGMPAGE pPage = pgmPhysGetPage(pVM, GCPhys);
300 if (pPage)
301 {
302 *pfShared = PGM_PAGE_IS_SHARED(pPage);
303 *pfPageFlags = fFlags;
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 pgmUnlock(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 pgmLock(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 >> 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 && ASMMemIsZeroPage(pvPage))
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 += 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 pgmUnlock(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 pgmLock(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 pgmUnlock(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