VirtualBox

source: vbox/trunk/src/VBox/VMM/PGMPool.cpp@ 20330

Last change on this file since 20330 was 20058, checked in by vboxsync, 16 years ago

Deal with pool pages being modified while we wait for the pgm lock in access handlers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 29.2 KB
Line 
1/* $Id: PGMPool.cpp 20058 2009-05-27 08:29:57Z vboxsync $ */
2/** @file
3 * PGM Shadow Page Pool.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/** @page pg_pgm_pool PGM Shadow Page Pool
23 *
24 * Motivations:
25 * -# Relationship between shadow page tables and physical guest pages. This
26 * should allow us to skip most of the global flushes now following access
27 * handler changes. The main expense is flushing shadow pages.
28 * -# Limit the pool size if necessary (default is kind of limitless).
29 * -# Allocate shadow pages from RC. We use to only do this in SyncCR3.
30 * -# Required for 64-bit guests.
31 * -# Combining the PD cache and page pool in order to simplify caching.
32 *
33 *
34 * @section sec_pgm_pool_outline Design Outline
35 *
36 * The shadow page pool tracks pages used for shadowing paging structures (i.e.
37 * page tables, page directory, page directory pointer table and page map
38 * level-4). Each page in the pool has an unique identifier. This identifier is
39 * used to link a guest physical page to a shadow PT. The identifier is a
40 * non-zero value and has a relativly low max value - say 14 bits. This makes it
41 * possible to fit it into the upper bits of the of the aHCPhys entries in the
42 * ram range.
43 *
44 * By restricting host physical memory to the first 48 bits (which is the
45 * announced physical memory range of the K8L chip (scheduled for 2008)), we
46 * can safely use the upper 16 bits for shadow page ID and reference counting.
47 *
48 * Update: The 48 bit assumption will be lifted with the new physical memory
49 * management (PGMPAGE), so we won't have any trouble when someone stuffs 2TB
50 * into a box in some years.
51 *
52 * Now, it's possible for a page to be aliased, i.e. mapped by more than one PT
53 * or PD. This is solved by creating a list of physical cross reference extents
54 * when ever this happens. Each node in the list (extent) is can contain 3 page
55 * pool indexes. The list it self is chained using indexes into the paPhysExt
56 * array.
57 *
58 *
59 * @section sec_pgm_pool_life Life Cycle of a Shadow Page
60 *
61 * -# The SyncPT function requests a page from the pool.
62 * The request includes the kind of page it is (PT/PD, PAE/legacy), the
63 * address of the page it's shadowing, and more.
64 * -# The pool responds to the request by allocating a new page.
65 * When the cache is enabled, it will first check if it's in the cache.
66 * Should the pool be exhausted, one of two things can be done:
67 * -# Flush the whole pool and current CR3.
68 * -# Use the cache to find a page which can be flushed (~age).
69 * -# The SyncPT function will sync one or more pages and insert it into the
70 * shadow PD.
71 * -# The SyncPage function may sync more pages on a later \#PFs.
72 * -# The page is freed / flushed in SyncCR3 (perhaps) and some other cases.
73 * When caching is enabled, the page isn't flush but remains in the cache.
74 *
75 *
76 * @section sec_pgm_pool_impl Monitoring
77 *
78 * We always monitor PAGE_SIZE chunks of memory. When we've got multiple shadow
79 * pages for the same PAGE_SIZE of guest memory (PAE and mixed PD/PT) the pages
80 * sharing the monitor get linked using the iMonitoredNext/Prev. The head page
81 * is the pvUser to the access handlers.
82 *
83 *
84 * @section sec_pgm_pool_impl Implementation
85 *
86 * The pool will take pages from the MM page pool. The tracking data
87 * (attributes, bitmaps and so on) are allocated from the hypervisor heap. The
88 * pool content can be accessed both by using the page id and the physical
89 * address (HC). The former is managed by means of an array, the latter by an
90 * offset based AVL tree.
91 *
92 * Flushing of a pool page means that we iterate the content (we know what kind
93 * it is) and updates the link information in the ram range.
94 *
95 * ...
96 */
97
98
99/*******************************************************************************
100* Header Files *
101*******************************************************************************/
102#define LOG_GROUP LOG_GROUP_PGM_POOL
103#include <VBox/pgm.h>
104#include <VBox/mm.h>
105#include "PGMInternal.h"
106#include <VBox/vm.h>
107
108#include <VBox/log.h>
109#include <VBox/err.h>
110#include <iprt/asm.h>
111#include <iprt/string.h>
112
113
114/*******************************************************************************
115* Internal Functions *
116*******************************************************************************/
117#ifdef PGMPOOL_WITH_MONITORING
118static DECLCALLBACK(int) pgmR3PoolAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
119#endif /* PGMPOOL_WITH_MONITORING */
120
121
122/**
123 * Initalizes the pool
124 *
125 * @returns VBox status code.
126 * @param pVM The VM handle.
127 */
128int pgmR3PoolInit(PVM pVM)
129{
130 AssertCompile(NIL_PGMPOOL_IDX == 0);
131 /* pPage->cLocked is an unsigned byte. */
132 AssertCompile(VMM_MAX_CPU_COUNT <= 255);
133
134 /*
135 * Query Pool config.
136 */
137 PCFGMNODE pCfg = CFGMR3GetChild(CFGMR3GetRoot(pVM), "/PGM/Pool");
138
139 /** @cfgm{/PGM/Pool/MaxPages, uint16_t, #pages, 16, 0x3fff, 1024}
140 * The max size of the shadow page pool in pages. The pool will grow dynamically
141 * up to this limit.
142 */
143 uint16_t cMaxPages;
144 int rc = CFGMR3QueryU16Def(pCfg, "MaxPages", &cMaxPages, 4*_1M >> PAGE_SHIFT);
145 AssertLogRelRCReturn(rc, rc);
146 AssertLogRelMsgReturn(cMaxPages <= PGMPOOL_IDX_LAST && cMaxPages >= RT_ALIGN(PGMPOOL_IDX_FIRST, 16),
147 ("cMaxPages=%u (%#x)\n", cMaxPages, cMaxPages), VERR_INVALID_PARAMETER);
148 cMaxPages = RT_ALIGN(cMaxPages, 16);
149
150 /** @cfgm{/PGM/Pool/MaxUsers, uint16_t, #users, MaxUsers, 32K, MaxPages*2}
151 * The max number of shadow page user tracking records. Each shadow page has
152 * zero of other shadow pages (or CR3s) that references it, or uses it if you
153 * like. The structures describing these relationships are allocated from a
154 * fixed sized pool. This configuration variable defines the pool size.
155 */
156 uint16_t cMaxUsers;
157 rc = CFGMR3QueryU16Def(pCfg, "MaxUsers", &cMaxUsers, cMaxPages * 2);
158 AssertLogRelRCReturn(rc, rc);
159 AssertLogRelMsgReturn(cMaxUsers >= cMaxPages && cMaxPages <= _32K,
160 ("cMaxUsers=%u (%#x)\n", cMaxUsers, cMaxUsers), VERR_INVALID_PARAMETER);
161
162 /** @cfgm{/PGM/Pool/MaxPhysExts, uint16_t, #extents, 16, MaxPages * 2, MAX(MaxPages*2,0x3fff)}
163 * The max number of extents for tracking aliased guest pages.
164 */
165 uint16_t cMaxPhysExts;
166 rc = CFGMR3QueryU16Def(pCfg, "MaxPhysExts", &cMaxPhysExts, RT_MAX(cMaxPages * 2, PGMPOOL_IDX_LAST));
167 AssertLogRelRCReturn(rc, rc);
168 AssertLogRelMsgReturn(cMaxPhysExts >= 16 && cMaxPages <= PGMPOOL_IDX_LAST,
169 ("cMaxPhysExts=%u (%#x)\n", cMaxPhysExts, cMaxPhysExts), VERR_INVALID_PARAMETER);
170
171 /** @cfgm{/PGM/Pool/ChacheEnabled, bool, true}
172 * Enables or disabling caching of shadow pages. Chaching means that we will try
173 * reuse shadow pages instead of recreating them everything SyncCR3, SyncPT or
174 * SyncPage requests one. When reusing a shadow page, we can save time
175 * reconstructing it and it's children.
176 */
177 bool fCacheEnabled;
178 rc = CFGMR3QueryBoolDef(pCfg, "CacheEnabled", &fCacheEnabled, true);
179 AssertLogRelRCReturn(rc, rc);
180
181 Log(("pgmR3PoolInit: cMaxPages=%#RX16 cMaxUsers=%#RX16 cMaxPhysExts=%#RX16 fCacheEnable=%RTbool\n",
182 cMaxPages, cMaxUsers, cMaxPhysExts, fCacheEnabled));
183
184 /*
185 * Allocate the data structures.
186 */
187 uint32_t cb = RT_OFFSETOF(PGMPOOL, aPages[cMaxPages]);
188#ifdef PGMPOOL_WITH_USER_TRACKING
189 cb += cMaxUsers * sizeof(PGMPOOLUSER);
190#endif
191#ifdef PGMPOOL_WITH_GCPHYS_TRACKING
192 cb += cMaxPhysExts * sizeof(PGMPOOLPHYSEXT);
193#endif
194 PPGMPOOL pPool;
195 rc = MMR3HyperAllocOnceNoRel(pVM, cb, 0, MM_TAG_PGM_POOL, (void **)&pPool);
196 if (RT_FAILURE(rc))
197 return rc;
198 pVM->pgm.s.pPoolR3 = pPool;
199 pVM->pgm.s.pPoolR0 = MMHyperR3ToR0(pVM, pPool);
200 pVM->pgm.s.pPoolRC = MMHyperR3ToRC(pVM, pPool);
201
202 /*
203 * Initialize it.
204 */
205 pPool->pVMR3 = pVM;
206 pPool->pVMR0 = pVM->pVMR0;
207 pPool->pVMRC = pVM->pVMRC;
208 pPool->cMaxPages = cMaxPages;
209 pPool->cCurPages = PGMPOOL_IDX_FIRST;
210#ifdef PGMPOOL_WITH_USER_TRACKING
211 pPool->iUserFreeHead = 0;
212 pPool->cMaxUsers = cMaxUsers;
213 PPGMPOOLUSER paUsers = (PPGMPOOLUSER)&pPool->aPages[pPool->cMaxPages];
214 pPool->paUsersR3 = paUsers;
215 pPool->paUsersR0 = MMHyperR3ToR0(pVM, paUsers);
216 pPool->paUsersRC = MMHyperR3ToRC(pVM, paUsers);
217 for (unsigned i = 0; i < cMaxUsers; i++)
218 {
219 paUsers[i].iNext = i + 1;
220 paUsers[i].iUser = NIL_PGMPOOL_IDX;
221 paUsers[i].iUserTable = 0xfffffffe;
222 }
223 paUsers[cMaxUsers - 1].iNext = NIL_PGMPOOL_USER_INDEX;
224#endif
225#ifdef PGMPOOL_WITH_GCPHYS_TRACKING
226 pPool->iPhysExtFreeHead = 0;
227 pPool->cMaxPhysExts = cMaxPhysExts;
228 PPGMPOOLPHYSEXT paPhysExts = (PPGMPOOLPHYSEXT)&paUsers[cMaxUsers];
229 pPool->paPhysExtsR3 = paPhysExts;
230 pPool->paPhysExtsR0 = MMHyperR3ToR0(pVM, paPhysExts);
231 pPool->paPhysExtsRC = MMHyperR3ToRC(pVM, paPhysExts);
232 for (unsigned i = 0; i < cMaxPhysExts; i++)
233 {
234 paPhysExts[i].iNext = i + 1;
235 paPhysExts[i].aidx[0] = NIL_PGMPOOL_IDX;
236 paPhysExts[i].aidx[1] = NIL_PGMPOOL_IDX;
237 paPhysExts[i].aidx[2] = NIL_PGMPOOL_IDX;
238 }
239 paPhysExts[cMaxPhysExts - 1].iNext = NIL_PGMPOOL_PHYSEXT_INDEX;
240#endif
241#ifdef PGMPOOL_WITH_CACHE
242 for (unsigned i = 0; i < RT_ELEMENTS(pPool->aiHash); i++)
243 pPool->aiHash[i] = NIL_PGMPOOL_IDX;
244 pPool->iAgeHead = NIL_PGMPOOL_IDX;
245 pPool->iAgeTail = NIL_PGMPOOL_IDX;
246 pPool->fCacheEnabled = fCacheEnabled;
247#endif
248#ifdef PGMPOOL_WITH_MONITORING
249 pPool->pfnAccessHandlerR3 = pgmR3PoolAccessHandler;
250 pPool->pszAccessHandler = "Guest Paging Access Handler";
251#endif
252 pPool->HCPhysTree = 0;
253
254 /* The NIL entry. */
255 Assert(NIL_PGMPOOL_IDX == 0);
256 pPool->aPages[NIL_PGMPOOL_IDX].enmKind = PGMPOOLKIND_INVALID;
257
258 /* The Shadow 32-bit PD. (32 bits guest paging) */
259 pPool->aPages[PGMPOOL_IDX_PD].Core.Key = NIL_RTHCPHYS;
260 pPool->aPages[PGMPOOL_IDX_PD].GCPhys = NIL_RTGCPHYS;
261 pPool->aPages[PGMPOOL_IDX_PD].pvPageR3 = 0;
262 pPool->aPages[PGMPOOL_IDX_PD].enmKind = PGMPOOLKIND_32BIT_PD;
263 pPool->aPages[PGMPOOL_IDX_PD].idx = PGMPOOL_IDX_PD;
264
265 /* The Shadow PDPT. */
266 pPool->aPages[PGMPOOL_IDX_PDPT].Core.Key = NIL_RTHCPHYS;
267 pPool->aPages[PGMPOOL_IDX_PDPT].GCPhys = NIL_RTGCPHYS;
268 pPool->aPages[PGMPOOL_IDX_PDPT].pvPageR3 = 0;
269 pPool->aPages[PGMPOOL_IDX_PDPT].enmKind = PGMPOOLKIND_PAE_PDPT;
270 pPool->aPages[PGMPOOL_IDX_PDPT].idx = PGMPOOL_IDX_PDPT;
271
272 /* The Shadow AMD64 CR3. */
273 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].Core.Key = NIL_RTHCPHYS;
274 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].GCPhys = NIL_RTGCPHYS;
275 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].pvPageR3 = 0;
276 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].enmKind = PGMPOOLKIND_64BIT_PML4;
277 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].idx = PGMPOOL_IDX_AMD64_CR3;
278
279 /* The Nested Paging CR3. */
280 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].Core.Key = NIL_RTHCPHYS;
281 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].GCPhys = NIL_RTGCPHYS;
282 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].pvPageR3 = 0;
283 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].enmKind = PGMPOOLKIND_ROOT_NESTED;
284 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].idx = PGMPOOL_IDX_NESTED_ROOT;
285
286 /*
287 * Set common stuff.
288 */
289 for (unsigned iPage = 1; iPage < PGMPOOL_IDX_FIRST; iPage++)
290 {
291 pPool->aPages[iPage].iNext = NIL_PGMPOOL_IDX;
292#ifdef PGMPOOL_WITH_USER_TRACKING
293 pPool->aPages[iPage].iUserHead = NIL_PGMPOOL_USER_INDEX;
294#endif
295#ifdef PGMPOOL_WITH_MONITORING
296 pPool->aPages[iPage].iModifiedNext = NIL_PGMPOOL_IDX;
297 pPool->aPages[iPage].iModifiedPrev = NIL_PGMPOOL_IDX;
298 pPool->aPages[iPage].iMonitoredNext = NIL_PGMPOOL_IDX;
299 pPool->aPages[iPage].iMonitoredNext = NIL_PGMPOOL_IDX;
300#endif
301#ifdef PGMPOOL_WITH_CACHE
302 pPool->aPages[iPage].iAgeNext = NIL_PGMPOOL_IDX;
303 pPool->aPages[iPage].iAgePrev = NIL_PGMPOOL_IDX;
304#endif
305 Assert(pPool->aPages[iPage].idx == iPage);
306 Assert(pPool->aPages[iPage].GCPhys == NIL_RTGCPHYS);
307 Assert(!pPool->aPages[iPage].fSeenNonGlobal);
308 Assert(!pPool->aPages[iPage].fMonitored);
309 Assert(!pPool->aPages[iPage].fCached);
310 Assert(!pPool->aPages[iPage].fZeroed);
311 Assert(!pPool->aPages[iPage].fReusedFlushPending);
312 }
313
314#ifdef VBOX_WITH_STATISTICS
315 /*
316 * Register statistics.
317 */
318 STAM_REG(pVM, &pPool->cCurPages, STAMTYPE_U16, "/PGM/Pool/cCurPages", STAMUNIT_PAGES, "Current pool size.");
319 STAM_REG(pVM, &pPool->cMaxPages, STAMTYPE_U16, "/PGM/Pool/cMaxPages", STAMUNIT_PAGES, "Max pool size.");
320 STAM_REG(pVM, &pPool->cUsedPages, STAMTYPE_U16, "/PGM/Pool/cUsedPages", STAMUNIT_PAGES, "The number of pages currently in use.");
321 STAM_REG(pVM, &pPool->cUsedPagesHigh, STAMTYPE_U16_RESET, "/PGM/Pool/cUsedPagesHigh", STAMUNIT_PAGES, "The high watermark for cUsedPages.");
322 STAM_REG(pVM, &pPool->StatAlloc, STAMTYPE_PROFILE_ADV, "/PGM/Pool/Alloc", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolAlloc.");
323 STAM_REG(pVM, &pPool->StatClearAll, STAMTYPE_PROFILE, "/PGM/Pool/ClearAll", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolClearAll.");
324 STAM_REG(pVM, &pPool->StatFlushAllInt, STAMTYPE_PROFILE, "/PGM/Pool/FlushAllInt", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolFlushAllInt.");
325 STAM_REG(pVM, &pPool->StatFlushPage, STAMTYPE_PROFILE, "/PGM/Pool/FlushPage", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolFlushPage.");
326 STAM_REG(pVM, &pPool->StatFree, STAMTYPE_PROFILE, "/PGM/Pool/Free", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolFree.");
327 STAM_REG(pVM, &pPool->StatZeroPage, STAMTYPE_PROFILE, "/PGM/Pool/ZeroPage", STAMUNIT_TICKS_PER_CALL, "Profiling time spent zeroing pages. Overlaps with Alloc.");
328# ifdef PGMPOOL_WITH_USER_TRACKING
329 STAM_REG(pVM, &pPool->cMaxUsers, STAMTYPE_U16, "/PGM/Pool/Track/cMaxUsers", STAMUNIT_COUNT, "Max user tracking records.");
330 STAM_REG(pVM, &pPool->cPresent, STAMTYPE_U32, "/PGM/Pool/Track/cPresent", STAMUNIT_COUNT, "Number of present page table entries.");
331 STAM_REG(pVM, &pPool->StatTrackDeref, STAMTYPE_PROFILE, "/PGM/Pool/Track/Deref", STAMUNIT_OCCURENCES, "Profiling of pgmPoolTrackDeref.");
332 STAM_REG(pVM, &pPool->StatTrackFlushGCPhysPT, STAMTYPE_PROFILE, "/PGM/Pool/Track/FlushGCPhysPT", STAMUNIT_OCCURENCES, "Profiling of pgmPoolTrackFlushGCPhysPT.");
333 STAM_REG(pVM, &pPool->StatTrackFlushGCPhysPTs, STAMTYPE_PROFILE, "/PGM/Pool/Track/FlushGCPhysPTs", STAMUNIT_OCCURENCES, "Profiling of pgmPoolTrackFlushGCPhysPTs.");
334 STAM_REG(pVM, &pPool->StatTrackFlushGCPhysPTsSlow, STAMTYPE_PROFILE, "/PGM/Pool/Track/FlushGCPhysPTsSlow", STAMUNIT_OCCURENCES, "Profiling of pgmPoolTrackFlushGCPhysPTsSlow.");
335 STAM_REG(pVM, &pPool->StatTrackFreeUpOneUser, STAMTYPE_COUNTER, "/PGM/Pool/Track/FreeUpOneUser", STAMUNIT_OCCURENCES, "The number of times we were out of user tracking records.");
336# endif
337# ifdef PGMPOOL_WITH_GCPHYS_TRACKING
338 STAM_REG(pVM, &pPool->StatTrackDerefGCPhys, STAMTYPE_PROFILE, "/PGM/Pool/Track/DrefGCPhys", STAMUNIT_OCCURENCES, "Profiling deref activity related tracking GC physical pages.");
339 STAM_REG(pVM, &pPool->StatTrackLinearRamSearches, STAMTYPE_COUNTER, "/PGM/Pool/Track/LinearRamSearches", STAMUNIT_OCCURENCES, "The number of times we had to do linear ram searches.");
340 STAM_REG(pVM, &pPool->StamTrackPhysExtAllocFailures,STAMTYPE_COUNTER, "/PGM/Pool/Track/PhysExtAllocFailures", STAMUNIT_OCCURENCES, "The number of failing pgmPoolTrackPhysExtAlloc calls.");
341# endif
342# ifdef PGMPOOL_WITH_MONITORING
343 STAM_REG(pVM, &pPool->StatMonitorRZ, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/RZ", STAMUNIT_TICKS_PER_CALL, "Profiling the RC/R0 access handler.");
344 STAM_REG(pVM, &pPool->StatMonitorRZEmulateInstr, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/RZ/EmulateInstr", STAMUNIT_OCCURENCES, "Times we've failed interpreting the instruction.");
345 STAM_REG(pVM, &pPool->StatMonitorRZFlushPage, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/RZ/FlushPage", STAMUNIT_TICKS_PER_CALL, "Profiling the pgmPoolFlushPage calls made from the RC/R0 access handler.");
346 STAM_REG(pVM, &pPool->StatMonitorRZFork, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/RZ/Fork", STAMUNIT_OCCURENCES, "Times we've detected fork().");
347 STAM_REG(pVM, &pPool->StatMonitorRZHandled, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/RZ/Handled", STAMUNIT_TICKS_PER_CALL, "Profiling the RC/R0 access we've handled (except REP STOSD).");
348 STAM_REG(pVM, &pPool->StatMonitorRZIntrFailPatch1, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/RZ/IntrFailPatch1", STAMUNIT_OCCURENCES, "Times we've failed interpreting a patch code instruction.");
349 STAM_REG(pVM, &pPool->StatMonitorRZIntrFailPatch2, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/RZ/IntrFailPatch2", STAMUNIT_OCCURENCES, "Times we've failed interpreting a patch code instruction during flushing.");
350 STAM_REG(pVM, &pPool->StatMonitorRZRepPrefix, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/RZ/RepPrefix", STAMUNIT_OCCURENCES, "The number of times we've seen rep prefixes we can't handle.");
351 STAM_REG(pVM, &pPool->StatMonitorRZRepStosd, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/RZ/RepStosd", STAMUNIT_TICKS_PER_CALL, "Profiling the REP STOSD cases we've handled.");
352 STAM_REG(pVM, &pPool->StatMonitorR3, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/R3", STAMUNIT_TICKS_PER_CALL, "Profiling the R3 access handler.");
353 STAM_REG(pVM, &pPool->StatMonitorR3EmulateInstr, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/R3/EmulateInstr", STAMUNIT_OCCURENCES, "Times we've failed interpreting the instruction.");
354 STAM_REG(pVM, &pPool->StatMonitorR3FlushPage, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/R3/FlushPage", STAMUNIT_TICKS_PER_CALL, "Profiling the pgmPoolFlushPage calls made from the R3 access handler.");
355 STAM_REG(pVM, &pPool->StatMonitorR3Fork, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/R3/Fork", STAMUNIT_OCCURENCES, "Times we've detected fork().");
356 STAM_REG(pVM, &pPool->StatMonitorR3Handled, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/R3/Handled", STAMUNIT_TICKS_PER_CALL, "Profiling the R3 access we've handled (except REP STOSD).");
357 STAM_REG(pVM, &pPool->StatMonitorR3RepPrefix, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/R3/RepPrefix", STAMUNIT_OCCURENCES, "The number of times we've seen rep prefixes we can't handle.");
358 STAM_REG(pVM, &pPool->StatMonitorR3RepStosd, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/R3/RepStosd", STAMUNIT_TICKS_PER_CALL, "Profiling the REP STOSD cases we've handled.");
359 STAM_REG(pVM, &pPool->StatMonitorR3Async, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/R3/Async", STAMUNIT_OCCURENCES, "Times we're called in an async thread and need to flush.");
360 STAM_REG(pVM, &pPool->cModifiedPages, STAMTYPE_U16, "/PGM/Pool/Monitor/cModifiedPages", STAMUNIT_PAGES, "The current cModifiedPages value.");
361 STAM_REG(pVM, &pPool->cModifiedPagesHigh, STAMTYPE_U16_RESET, "/PGM/Pool/Monitor/cModifiedPagesHigh", STAMUNIT_PAGES, "The high watermark for cModifiedPages.");
362# endif
363# ifdef PGMPOOL_WITH_CACHE
364 STAM_REG(pVM, &pPool->StatCacheHits, STAMTYPE_COUNTER, "/PGM/Pool/Cache/Hits", STAMUNIT_OCCURENCES, "The number of pgmPoolAlloc calls satisfied by the cache.");
365 STAM_REG(pVM, &pPool->StatCacheMisses, STAMTYPE_COUNTER, "/PGM/Pool/Cache/Misses", STAMUNIT_OCCURENCES, "The number of pgmPoolAlloc calls not statisfied by the cache.");
366 STAM_REG(pVM, &pPool->StatCacheKindMismatches, STAMTYPE_COUNTER, "/PGM/Pool/Cache/KindMismatches", STAMUNIT_OCCURENCES, "The number of shadow page kind mismatches. (Better be low, preferably 0!)");
367 STAM_REG(pVM, &pPool->StatCacheFreeUpOne, STAMTYPE_COUNTER, "/PGM/Pool/Cache/FreeUpOne", STAMUNIT_OCCURENCES, "The number of times the cache was asked to free up a page.");
368 STAM_REG(pVM, &pPool->StatCacheCacheable, STAMTYPE_COUNTER, "/PGM/Pool/Cache/Cacheable", STAMUNIT_OCCURENCES, "The number of cacheable allocations.");
369 STAM_REG(pVM, &pPool->StatCacheUncacheable, STAMTYPE_COUNTER, "/PGM/Pool/Cache/Uncacheable", STAMUNIT_OCCURENCES, "The number of uncacheable allocations.");
370# endif
371#endif /* VBOX_WITH_STATISTICS */
372
373 return VINF_SUCCESS;
374}
375
376
377/**
378 * Relocate the page pool data.
379 *
380 * @param pVM The VM handle.
381 */
382void pgmR3PoolRelocate(PVM pVM)
383{
384 pVM->pgm.s.pPoolRC = MMHyperR3ToRC(pVM, pVM->pgm.s.pPoolR3);
385 pVM->pgm.s.pPoolR3->pVMRC = pVM->pVMRC;
386#ifdef PGMPOOL_WITH_USER_TRACKING
387 pVM->pgm.s.pPoolR3->paUsersRC = MMHyperR3ToRC(pVM, pVM->pgm.s.pPoolR3->paUsersR3);
388#endif
389#ifdef PGMPOOL_WITH_GCPHYS_TRACKING
390 pVM->pgm.s.pPoolR3->paPhysExtsRC = MMHyperR3ToRC(pVM, pVM->pgm.s.pPoolR3->paPhysExtsR3);
391#endif
392#ifdef PGMPOOL_WITH_MONITORING
393 int rc = PDMR3LdrGetSymbolRC(pVM, NULL, "pgmPoolAccessHandler", &pVM->pgm.s.pPoolR3->pfnAccessHandlerRC);
394 AssertReleaseRC(rc);
395 /* init order hack. */
396 if (!pVM->pgm.s.pPoolR3->pfnAccessHandlerR0)
397 {
398 rc = PDMR3LdrGetSymbolR0(pVM, NULL, "pgmPoolAccessHandler", &pVM->pgm.s.pPoolR3->pfnAccessHandlerR0);
399 AssertReleaseRC(rc);
400 }
401#endif
402}
403
404
405/**
406 * Grows the shadow page pool.
407 *
408 * I.e. adds more pages to it, assuming that hasn't reached cMaxPages yet.
409 *
410 * @returns VBox status code.
411 * @param pVM The VM handle.
412 */
413VMMR3DECL(int) PGMR3PoolGrow(PVM pVM)
414{
415 PPGMPOOL pPool = pVM->pgm.s.pPoolR3;
416 AssertReturn(pPool->cCurPages < pPool->cMaxPages, VERR_INTERNAL_ERROR);
417
418 pgmLock(pVM);
419
420 /*
421 * How much to grow it by?
422 */
423 uint32_t cPages = pPool->cMaxPages - pPool->cCurPages;
424 cPages = RT_MIN(PGMPOOL_CFG_MAX_GROW, cPages);
425 LogFlow(("PGMR3PoolGrow: Growing the pool by %d (%#x) pages.\n", cPages, cPages));
426
427 for (unsigned i = pPool->cCurPages; cPages-- > 0; i++)
428 {
429 PPGMPOOLPAGE pPage = &pPool->aPages[i];
430
431 /* Allocate all pages in low (below 4 GB) memory as 32 bits guests need a page table root in low memory. */
432 pPage->pvPageR3 = MMR3PageAllocLow(pVM);
433 if (!pPage->pvPageR3)
434 {
435 Log(("We're out of memory!! i=%d\n", i));
436 pgmUnlock(pVM);
437 return i ? VINF_SUCCESS : VERR_NO_PAGE_MEMORY;
438 }
439 pPage->Core.Key = MMPage2Phys(pVM, pPage->pvPageR3);
440 pPage->GCPhys = NIL_RTGCPHYS;
441 pPage->enmKind = PGMPOOLKIND_FREE;
442 pPage->idx = pPage - &pPool->aPages[0];
443 LogFlow(("PGMR3PoolGrow: insert page #%#x - %RHp\n", pPage->idx, pPage->Core.Key));
444 pPage->iNext = pPool->iFreeHead;
445#ifdef PGMPOOL_WITH_USER_TRACKING
446 pPage->iUserHead = NIL_PGMPOOL_USER_INDEX;
447#endif
448#ifdef PGMPOOL_WITH_MONITORING
449 pPage->iModifiedNext = NIL_PGMPOOL_IDX;
450 pPage->iModifiedPrev = NIL_PGMPOOL_IDX;
451 pPage->iMonitoredNext = NIL_PGMPOOL_IDX;
452 pPage->iMonitoredNext = NIL_PGMPOOL_IDX;
453#endif
454#ifdef PGMPOOL_WITH_CACHE
455 pPage->iAgeNext = NIL_PGMPOOL_IDX;
456 pPage->iAgePrev = NIL_PGMPOOL_IDX;
457#endif
458 /* commit it */
459 bool fRc = RTAvloHCPhysInsert(&pPool->HCPhysTree, &pPage->Core); Assert(fRc); NOREF(fRc);
460 pPool->iFreeHead = i;
461 pPool->cCurPages = i + 1;
462 }
463
464 pgmUnlock(pVM);
465 Assert(pPool->cCurPages <= pPool->cMaxPages);
466 return VINF_SUCCESS;
467}
468
469
470#ifdef PGMPOOL_WITH_MONITORING
471
472/**
473 * Worker used by pgmR3PoolAccessHandler when it's invoked by an async thread.
474 *
475 * @param pPool The pool.
476 * @param pPage The page.
477 */
478static DECLCALLBACK(void) pgmR3PoolFlushReusedPage(PPGMPOOL pPool, PPGMPOOLPAGE pPage)
479{
480 /* for the present this should be safe enough I think... */
481 pgmLock(pPool->pVMR3);
482 if ( pPage->fReusedFlushPending
483 && pPage->enmKind != PGMPOOLKIND_FREE)
484 pgmPoolFlushPage(pPool, pPage);
485 pgmUnlock(pPool->pVMR3);
486}
487
488
489/**
490 * \#PF Handler callback for PT write accesses.
491 *
492 * The handler can not raise any faults, it's mainly for monitoring write access
493 * to certain pages.
494 *
495 * @returns VINF_SUCCESS if the handler has carried out the operation.
496 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
497 * @param pVM VM Handle.
498 * @param GCPhys The physical address the guest is writing to.
499 * @param pvPhys The HC mapping of that address.
500 * @param pvBuf What the guest is reading/writing.
501 * @param cbBuf How much it's reading/writing.
502 * @param enmAccessType The access type.
503 * @param pvUser User argument.
504 */
505static DECLCALLBACK(int) pgmR3PoolAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
506{
507 STAM_PROFILE_START(&pVM->pgm.s.pPoolR3->StatMonitorR3, a);
508 PPGMPOOL pPool = pVM->pgm.s.pPoolR3;
509 PPGMPOOLPAGE pPage = (PPGMPOOLPAGE)pvUser;
510 LogFlow(("pgmR3PoolAccessHandler: GCPhys=%RGp %p:{.Core=%RHp, .idx=%d, .GCPhys=%RGp, .enmType=%d}\n",
511 GCPhys, pPage, pPage->Core.Key, pPage->idx, pPage->GCPhys, pPage->enmKind));
512
513 PVMCPU pVCpu = VMMGetCpu(pVM);
514
515 /*
516 * We don't have to be very sophisticated about this since there are relativly few calls here.
517 * However, we must try our best to detect any non-cpu accesses (disk / networking).
518 *
519 * Just to make life more interesting, we'll have to deal with the async threads too.
520 * We cannot flush a page if we're in an async thread because of REM notifications.
521 */
522 pgmLock(pVM);
523 if (PHYS_PAGE_ADDRESS(GCPhys) != PHYS_PAGE_ADDRESS(pPage->GCPhys))
524 {
525 /* Pool page changed while we were waiting for the lock; ignore. */
526 Log(("CPU%d: pgmR3PoolAccessHandler pgm pool page for %RGp changed (to %RGp) while waiting!\n", pVCpu->idCpu, PHYS_PAGE_ADDRESS(GCPhys), PHYS_PAGE_ADDRESS(pPage->GCPhys)));
527 pgmUnlock(pVM);
528 return VINF_PGM_HANDLER_DO_DEFAULT;
529 }
530
531 if (!pVCpu) /** @todo This shouldn't happen any longer, all access handlers will be called on an EMT. All ring-3 handlers, except MMIO, already own the PGM lock. @bugref{3170} */
532 {
533 Log(("pgmR3PoolAccessHandler: async thread, requesting EMT to flush the page: %p:{.Core=%RHp, .idx=%d, .GCPhys=%RGp, .enmType=%d}\n",
534 pPage, pPage->Core.Key, pPage->idx, pPage->GCPhys, pPage->enmKind));
535 STAM_COUNTER_INC(&pPool->StatMonitorR3Async);
536 if (!pPage->fReusedFlushPending)
537 {
538 pgmUnlock(pVM);
539 int rc = VMR3ReqCallEx(pPool->pVMR3, VMCPUID_ANY, NULL, 0, VMREQFLAGS_NO_WAIT | VMREQFLAGS_VOID, (PFNRT)pgmR3PoolFlushReusedPage, 2, pPool, pPage);
540 AssertRCReturn(rc, rc);
541 pgmLock(pVM);
542 pPage->fReusedFlushPending = true;
543 pPage->cModifications += 0x1000;
544 }
545
546 pgmPoolMonitorChainChanging(pVCpu, pPool, pPage, GCPhys, pvPhys, NULL);
547 /** @todo r=bird: making unsafe assumption about not crossing entries here! */
548 while (cbBuf > 4)
549 {
550 cbBuf -= 4;
551 pvPhys = (uint8_t *)pvPhys + 4;
552 GCPhys += 4;
553 pgmPoolMonitorChainChanging(pVCpu, pPool, pPage, GCPhys, pvPhys, NULL);
554 }
555 STAM_PROFILE_STOP(&pPool->StatMonitorR3, a);
556 }
557 else if ( ( pPage->cModifications < 96 /* it's cheaper here. */
558 || pgmPoolIsPageLocked(&pVM->pgm.s, pPage)
559 )
560 && cbBuf <= 4)
561 {
562 /* Clear the shadow entry. */
563 if (!pPage->cModifications++)
564 pgmPoolMonitorModifiedInsert(pPool, pPage);
565 /** @todo r=bird: making unsafe assumption about not crossing entries here! */
566 pgmPoolMonitorChainChanging(pVCpu, pPool, pPage, GCPhys, pvPhys, NULL);
567 STAM_PROFILE_STOP(&pPool->StatMonitorR3, a);
568 }
569 else
570 {
571 pgmPoolMonitorChainFlush(pPool, pPage); /* ASSUME that VERR_PGM_POOL_CLEARED can be ignored here and that FFs will deal with it in due time. */
572 STAM_PROFILE_STOP_EX(&pPool->StatMonitorR3, &pPool->StatMonitorR3FlushPage, a);
573 }
574 pgmUnlock(pVM);
575 return VINF_PGM_HANDLER_DO_DEFAULT;
576}
577
578#endif /* PGMPOOL_WITH_MONITORING */
579
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