VirtualBox

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

Last change on this file since 74168 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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