VirtualBox

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

Last change on this file since 19167 was 19141, checked in by vboxsync, 16 years ago

Action flags breakup.
Fixed PGM saved state loading of 2.2.2 images.
Reduced hacks in PATM state loading (fixups).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 28.6 KB
Line 
1/* $Id: PGMPool.cpp 19141 2009-04-23 13:52:18Z 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
132 /*
133 * Query Pool config.
134 */
135 PCFGMNODE pCfg = CFGMR3GetChild(CFGMR3GetRoot(pVM), "/PGM/Pool");
136
137 /** @cfgm{/PGM/Pool/MaxPages, uint16_t, #pages, 16, 0x3fff, 1024}
138 * The max size of the shadow page pool in pages. The pool will grow dynamically
139 * up to this limit.
140 */
141 uint16_t cMaxPages;
142 int rc = CFGMR3QueryU16Def(pCfg, "MaxPages", &cMaxPages, 4*_1M >> PAGE_SHIFT);
143 AssertLogRelRCReturn(rc, rc);
144 AssertLogRelMsgReturn(cMaxPages <= PGMPOOL_IDX_LAST && cMaxPages >= RT_ALIGN(PGMPOOL_IDX_FIRST, 16),
145 ("cMaxPages=%u (%#x)\n", cMaxPages, cMaxPages), VERR_INVALID_PARAMETER);
146 cMaxPages = RT_ALIGN(cMaxPages, 16);
147
148 /** @cfgm{/PGM/Pool/MaxUsers, uint16_t, #users, MaxUsers, 32K, MaxPages*2}
149 * The max number of shadow page user tracking records. Each shadow page has
150 * zero of other shadow pages (or CR3s) that references it, or uses it if you
151 * like. The structures describing these relationships are allocated from a
152 * fixed sized pool. This configuration variable defines the pool size.
153 */
154 uint16_t cMaxUsers;
155 rc = CFGMR3QueryU16Def(pCfg, "MaxUsers", &cMaxUsers, cMaxPages * 2);
156 AssertLogRelRCReturn(rc, rc);
157 AssertLogRelMsgReturn(cMaxUsers >= cMaxPages && cMaxPages <= _32K,
158 ("cMaxUsers=%u (%#x)\n", cMaxUsers, cMaxUsers), VERR_INVALID_PARAMETER);
159
160 /** @cfgm{/PGM/Pool/MaxPhysExts, uint16_t, #extents, 16, MaxPages * 2, MAX(MaxPages*2,0x3fff)}
161 * The max number of extents for tracking aliased guest pages.
162 */
163 uint16_t cMaxPhysExts;
164 rc = CFGMR3QueryU16Def(pCfg, "MaxPhysExts", &cMaxPhysExts, RT_MAX(cMaxPages * 2, PGMPOOL_IDX_LAST));
165 AssertLogRelRCReturn(rc, rc);
166 AssertLogRelMsgReturn(cMaxPhysExts >= 16 && cMaxPages <= PGMPOOL_IDX_LAST,
167 ("cMaxPhysExts=%u (%#x)\n", cMaxPhysExts, cMaxPhysExts), VERR_INVALID_PARAMETER);
168
169 /** @cfgm{/PGM/Pool/ChacheEnabled, bool, true}
170 * Enables or disabling caching of shadow pages. Chaching means that we will try
171 * reuse shadow pages instead of recreating them everything SyncCR3, SyncPT or
172 * SyncPage requests one. When reusing a shadow page, we can save time
173 * reconstructing it and it's children.
174 */
175 bool fCacheEnabled;
176 rc = CFGMR3QueryBoolDef(pCfg, "CacheEnabled", &fCacheEnabled, true);
177 AssertLogRelRCReturn(rc, rc);
178
179 Log(("pgmR3PoolInit: cMaxPages=%#RX16 cMaxUsers=%#RX16 cMaxPhysExts=%#RX16 fCacheEnable=%RTbool\n",
180 cMaxPages, cMaxUsers, cMaxPhysExts, fCacheEnabled));
181
182 /*
183 * Allocate the data structures.
184 */
185 uint32_t cb = RT_OFFSETOF(PGMPOOL, aPages[cMaxPages]);
186#ifdef PGMPOOL_WITH_USER_TRACKING
187 cb += cMaxUsers * sizeof(PGMPOOLUSER);
188#endif
189#ifdef PGMPOOL_WITH_GCPHYS_TRACKING
190 cb += cMaxPhysExts * sizeof(PGMPOOLPHYSEXT);
191#endif
192 PPGMPOOL pPool;
193 rc = MMR3HyperAllocOnceNoRel(pVM, cb, 0, MM_TAG_PGM_POOL, (void **)&pPool);
194 if (RT_FAILURE(rc))
195 return rc;
196 pVM->pgm.s.pPoolR3 = pPool;
197 pVM->pgm.s.pPoolR0 = MMHyperR3ToR0(pVM, pPool);
198 pVM->pgm.s.pPoolRC = MMHyperR3ToRC(pVM, pPool);
199
200 /*
201 * Initialize it.
202 */
203 pPool->pVMR3 = pVM;
204 pPool->pVMR0 = pVM->pVMR0;
205 pPool->pVMRC = pVM->pVMRC;
206 pPool->cMaxPages = cMaxPages;
207 pPool->cCurPages = PGMPOOL_IDX_FIRST;
208#ifdef PGMPOOL_WITH_USER_TRACKING
209 pPool->iUserFreeHead = 0;
210 pPool->cMaxUsers = cMaxUsers;
211 PPGMPOOLUSER paUsers = (PPGMPOOLUSER)&pPool->aPages[pPool->cMaxPages];
212 pPool->paUsersR3 = paUsers;
213 pPool->paUsersR0 = MMHyperR3ToR0(pVM, paUsers);
214 pPool->paUsersRC = MMHyperR3ToRC(pVM, paUsers);
215 for (unsigned i = 0; i < cMaxUsers; i++)
216 {
217 paUsers[i].iNext = i + 1;
218 paUsers[i].iUser = NIL_PGMPOOL_IDX;
219 paUsers[i].iUserTable = 0xfffffffe;
220 }
221 paUsers[cMaxUsers - 1].iNext = NIL_PGMPOOL_USER_INDEX;
222#endif
223#ifdef PGMPOOL_WITH_GCPHYS_TRACKING
224 pPool->iPhysExtFreeHead = 0;
225 pPool->cMaxPhysExts = cMaxPhysExts;
226 PPGMPOOLPHYSEXT paPhysExts = (PPGMPOOLPHYSEXT)&paUsers[cMaxUsers];
227 pPool->paPhysExtsR3 = paPhysExts;
228 pPool->paPhysExtsR0 = MMHyperR3ToR0(pVM, paPhysExts);
229 pPool->paPhysExtsRC = MMHyperR3ToRC(pVM, paPhysExts);
230 for (unsigned i = 0; i < cMaxPhysExts; i++)
231 {
232 paPhysExts[i].iNext = i + 1;
233 paPhysExts[i].aidx[0] = NIL_PGMPOOL_IDX;
234 paPhysExts[i].aidx[1] = NIL_PGMPOOL_IDX;
235 paPhysExts[i].aidx[2] = NIL_PGMPOOL_IDX;
236 }
237 paPhysExts[cMaxPhysExts - 1].iNext = NIL_PGMPOOL_PHYSEXT_INDEX;
238#endif
239#ifdef PGMPOOL_WITH_CACHE
240 for (unsigned i = 0; i < RT_ELEMENTS(pPool->aiHash); i++)
241 pPool->aiHash[i] = NIL_PGMPOOL_IDX;
242 pPool->iAgeHead = NIL_PGMPOOL_IDX;
243 pPool->iAgeTail = NIL_PGMPOOL_IDX;
244 pPool->fCacheEnabled = fCacheEnabled;
245#endif
246#ifdef PGMPOOL_WITH_MONITORING
247 pPool->pfnAccessHandlerR3 = pgmR3PoolAccessHandler;
248 pPool->pszAccessHandler = "Guest Paging Access Handler";
249#endif
250 pPool->HCPhysTree = 0;
251
252 /* The NIL entry. */
253 Assert(NIL_PGMPOOL_IDX == 0);
254 pPool->aPages[NIL_PGMPOOL_IDX].enmKind = PGMPOOLKIND_INVALID;
255
256 /* The Shadow 32-bit PD. (32 bits guest paging) */
257 pPool->aPages[PGMPOOL_IDX_PD].Core.Key = NIL_RTHCPHYS;
258 pPool->aPages[PGMPOOL_IDX_PD].GCPhys = NIL_RTGCPHYS;
259 pPool->aPages[PGMPOOL_IDX_PD].pvPageR3 = 0;
260 pPool->aPages[PGMPOOL_IDX_PD].enmKind = PGMPOOLKIND_32BIT_PD;
261 pPool->aPages[PGMPOOL_IDX_PD].idx = PGMPOOL_IDX_PD;
262
263 /* The Shadow PDPT. */
264 pPool->aPages[PGMPOOL_IDX_PDPT].Core.Key = NIL_RTHCPHYS;
265 pPool->aPages[PGMPOOL_IDX_PDPT].GCPhys = NIL_RTGCPHYS;
266 pPool->aPages[PGMPOOL_IDX_PDPT].pvPageR3 = 0;
267 pPool->aPages[PGMPOOL_IDX_PDPT].enmKind = PGMPOOLKIND_PAE_PDPT;
268 pPool->aPages[PGMPOOL_IDX_PDPT].idx = PGMPOOL_IDX_PDPT;
269
270 /* The Shadow AMD64 CR3. */
271 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].Core.Key = NIL_RTHCPHYS;
272 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].GCPhys = NIL_RTGCPHYS;
273 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].pvPageR3 = 0;
274 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].enmKind = PGMPOOLKIND_64BIT_PML4;
275 pPool->aPages[PGMPOOL_IDX_AMD64_CR3].idx = PGMPOOL_IDX_AMD64_CR3;
276
277 /* The Nested Paging CR3. */
278 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].Core.Key = NIL_RTHCPHYS;
279 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].GCPhys = NIL_RTGCPHYS;
280 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].pvPageR3 = 0;
281 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].enmKind = PGMPOOLKIND_ROOT_NESTED;
282 pPool->aPages[PGMPOOL_IDX_NESTED_ROOT].idx = PGMPOOL_IDX_NESTED_ROOT;
283
284 /*
285 * Set common stuff.
286 */
287 for (unsigned iPage = 1; iPage < PGMPOOL_IDX_FIRST; iPage++)
288 {
289 pPool->aPages[iPage].iNext = NIL_PGMPOOL_IDX;
290#ifdef PGMPOOL_WITH_USER_TRACKING
291 pPool->aPages[iPage].iUserHead = NIL_PGMPOOL_USER_INDEX;
292#endif
293#ifdef PGMPOOL_WITH_MONITORING
294 pPool->aPages[iPage].iModifiedNext = NIL_PGMPOOL_IDX;
295 pPool->aPages[iPage].iModifiedPrev = NIL_PGMPOOL_IDX;
296 pPool->aPages[iPage].iMonitoredNext = NIL_PGMPOOL_IDX;
297 pPool->aPages[iPage].iMonitoredNext = NIL_PGMPOOL_IDX;
298#endif
299#ifdef PGMPOOL_WITH_CACHE
300 pPool->aPages[iPage].iAgeNext = NIL_PGMPOOL_IDX;
301 pPool->aPages[iPage].iAgePrev = NIL_PGMPOOL_IDX;
302#endif
303 Assert(pPool->aPages[iPage].idx == iPage);
304 Assert(pPool->aPages[iPage].GCPhys == NIL_RTGCPHYS);
305 Assert(!pPool->aPages[iPage].fSeenNonGlobal);
306 Assert(!pPool->aPages[iPage].fMonitored);
307 Assert(!pPool->aPages[iPage].fCached);
308 Assert(!pPool->aPages[iPage].fZeroed);
309 Assert(!pPool->aPages[iPage].fReusedFlushPending);
310 }
311
312#ifdef VBOX_WITH_STATISTICS
313 /*
314 * Register statistics.
315 */
316 STAM_REG(pVM, &pPool->cCurPages, STAMTYPE_U16, "/PGM/Pool/cCurPages", STAMUNIT_PAGES, "Current pool size.");
317 STAM_REG(pVM, &pPool->cMaxPages, STAMTYPE_U16, "/PGM/Pool/cMaxPages", STAMUNIT_PAGES, "Max pool size.");
318 STAM_REG(pVM, &pPool->cUsedPages, STAMTYPE_U16, "/PGM/Pool/cUsedPages", STAMUNIT_PAGES, "The number of pages currently in use.");
319 STAM_REG(pVM, &pPool->cUsedPagesHigh, STAMTYPE_U16_RESET, "/PGM/Pool/cUsedPagesHigh", STAMUNIT_PAGES, "The high watermark for cUsedPages.");
320 STAM_REG(pVM, &pPool->StatAlloc, STAMTYPE_PROFILE_ADV, "/PGM/Pool/Alloc", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolAlloc.");
321 STAM_REG(pVM, &pPool->StatClearAll, STAMTYPE_PROFILE, "/PGM/Pool/ClearAll", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolClearAll.");
322 STAM_REG(pVM, &pPool->StatFlushAllInt, STAMTYPE_PROFILE, "/PGM/Pool/FlushAllInt", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolFlushAllInt.");
323 STAM_REG(pVM, &pPool->StatFlushPage, STAMTYPE_PROFILE, "/PGM/Pool/FlushPage", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolFlushPage.");
324 STAM_REG(pVM, &pPool->StatFree, STAMTYPE_PROFILE, "/PGM/Pool/Free", STAMUNIT_TICKS_PER_CALL, "Profiling of pgmPoolFree.");
325 STAM_REG(pVM, &pPool->StatZeroPage, STAMTYPE_PROFILE, "/PGM/Pool/ZeroPage", STAMUNIT_TICKS_PER_CALL, "Profiling time spent zeroing pages. Overlaps with Alloc.");
326# ifdef PGMPOOL_WITH_USER_TRACKING
327 STAM_REG(pVM, &pPool->cMaxUsers, STAMTYPE_U16, "/PGM/Pool/Track/cMaxUsers", STAMUNIT_COUNT, "Max user tracking records.");
328 STAM_REG(pVM, &pPool->cPresent, STAMTYPE_U32, "/PGM/Pool/Track/cPresent", STAMUNIT_COUNT, "Number of present page table entries.");
329 STAM_REG(pVM, &pPool->StatTrackDeref, STAMTYPE_PROFILE, "/PGM/Pool/Track/Deref", STAMUNIT_OCCURENCES, "Profiling of pgmPoolTrackDeref.");
330 STAM_REG(pVM, &pPool->StatTrackFlushGCPhysPT, STAMTYPE_PROFILE, "/PGM/Pool/Track/FlushGCPhysPT", STAMUNIT_OCCURENCES, "Profiling of pgmPoolTrackFlushGCPhysPT.");
331 STAM_REG(pVM, &pPool->StatTrackFlushGCPhysPTs, STAMTYPE_PROFILE, "/PGM/Pool/Track/FlushGCPhysPTs", STAMUNIT_OCCURENCES, "Profiling of pgmPoolTrackFlushGCPhysPTs.");
332 STAM_REG(pVM, &pPool->StatTrackFlushGCPhysPTsSlow, STAMTYPE_PROFILE, "/PGM/Pool/Track/FlushGCPhysPTsSlow", STAMUNIT_OCCURENCES, "Profiling of pgmPoolTrackFlushGCPhysPTsSlow.");
333 STAM_REG(pVM, &pPool->StatTrackFreeUpOneUser, STAMTYPE_COUNTER, "/PGM/Pool/Track/FreeUpOneUser", STAMUNIT_OCCURENCES, "The number of times we were out of user tracking records.");
334# endif
335# ifdef PGMPOOL_WITH_GCPHYS_TRACKING
336 STAM_REG(pVM, &pPool->StatTrackDerefGCPhys, STAMTYPE_PROFILE, "/PGM/Pool/Track/DrefGCPhys", STAMUNIT_OCCURENCES, "Profiling deref activity related tracking GC physical pages.");
337 STAM_REG(pVM, &pPool->StatTrackLinearRamSearches, STAMTYPE_COUNTER, "/PGM/Pool/Track/LinearRamSearches", STAMUNIT_OCCURENCES, "The number of times we had to do linear ram searches.");
338 STAM_REG(pVM, &pPool->StamTrackPhysExtAllocFailures,STAMTYPE_COUNTER, "/PGM/Pool/Track/PhysExtAllocFailures", STAMUNIT_OCCURENCES, "The number of failing pgmPoolTrackPhysExtAlloc calls.");
339# endif
340# ifdef PGMPOOL_WITH_MONITORING
341 STAM_REG(pVM, &pPool->StatMonitorRZ, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/RZ", STAMUNIT_TICKS_PER_CALL, "Profiling the RC/R0 access handler.");
342 STAM_REG(pVM, &pPool->StatMonitorRZEmulateInstr, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/RZ/EmulateInstr", STAMUNIT_OCCURENCES, "Times we've failed interpreting the instruction.");
343 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.");
344 STAM_REG(pVM, &pPool->StatMonitorRZFork, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/RZ/Fork", STAMUNIT_OCCURENCES, "Times we've detected fork().");
345 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).");
346 STAM_REG(pVM, &pPool->StatMonitorRZIntrFailPatch1, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/RZ/IntrFailPatch1", STAMUNIT_OCCURENCES, "Times we've failed interpreting a patch code instruction.");
347 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.");
348 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.");
349 STAM_REG(pVM, &pPool->StatMonitorRZRepStosd, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/RZ/RepStosd", STAMUNIT_TICKS_PER_CALL, "Profiling the REP STOSD cases we've handled.");
350 STAM_REG(pVM, &pPool->StatMonitorR3, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/R3", STAMUNIT_TICKS_PER_CALL, "Profiling the R3 access handler.");
351 STAM_REG(pVM, &pPool->StatMonitorR3EmulateInstr, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/R3/EmulateInstr", STAMUNIT_OCCURENCES, "Times we've failed interpreting the instruction.");
352 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.");
353 STAM_REG(pVM, &pPool->StatMonitorR3Fork, STAMTYPE_COUNTER, "/PGM/Pool/Monitor/R3/Fork", STAMUNIT_OCCURENCES, "Times we've detected fork().");
354 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).");
355 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.");
356 STAM_REG(pVM, &pPool->StatMonitorR3RepStosd, STAMTYPE_PROFILE, "/PGM/Pool/Monitor/R3/RepStosd", STAMUNIT_TICKS_PER_CALL, "Profiling the REP STOSD cases we've handled.");
357 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.");
358 STAM_REG(pVM, &pPool->cModifiedPages, STAMTYPE_U16, "/PGM/Pool/Monitor/cModifiedPages", STAMUNIT_PAGES, "The current cModifiedPages value.");
359 STAM_REG(pVM, &pPool->cModifiedPagesHigh, STAMTYPE_U16_RESET, "/PGM/Pool/Monitor/cModifiedPagesHigh", STAMUNIT_PAGES, "The high watermark for cModifiedPages.");
360# endif
361# ifdef PGMPOOL_WITH_CACHE
362 STAM_REG(pVM, &pPool->StatCacheHits, STAMTYPE_COUNTER, "/PGM/Pool/Cache/Hits", STAMUNIT_OCCURENCES, "The number of pgmPoolAlloc calls satisfied by the cache.");
363 STAM_REG(pVM, &pPool->StatCacheMisses, STAMTYPE_COUNTER, "/PGM/Pool/Cache/Misses", STAMUNIT_OCCURENCES, "The number of pgmPoolAlloc calls not statisfied by the cache.");
364 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!)");
365 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.");
366 STAM_REG(pVM, &pPool->StatCacheCacheable, STAMTYPE_COUNTER, "/PGM/Pool/Cache/Cacheable", STAMUNIT_OCCURENCES, "The number of cacheable allocations.");
367 STAM_REG(pVM, &pPool->StatCacheUncacheable, STAMTYPE_COUNTER, "/PGM/Pool/Cache/Uncacheable", STAMUNIT_OCCURENCES, "The number of uncacheable allocations.");
368# endif
369#endif /* VBOX_WITH_STATISTICS */
370
371 return VINF_SUCCESS;
372}
373
374
375/**
376 * Relocate the page pool data.
377 *
378 * @param pVM The VM handle.
379 */
380void pgmR3PoolRelocate(PVM pVM)
381{
382 pVM->pgm.s.pPoolRC = MMHyperR3ToRC(pVM, pVM->pgm.s.pPoolR3);
383 pVM->pgm.s.pPoolR3->pVMRC = pVM->pVMRC;
384#ifdef PGMPOOL_WITH_USER_TRACKING
385 pVM->pgm.s.pPoolR3->paUsersRC = MMHyperR3ToRC(pVM, pVM->pgm.s.pPoolR3->paUsersR3);
386#endif
387#ifdef PGMPOOL_WITH_GCPHYS_TRACKING
388 pVM->pgm.s.pPoolR3->paPhysExtsRC = MMHyperR3ToRC(pVM, pVM->pgm.s.pPoolR3->paPhysExtsR3);
389#endif
390#ifdef PGMPOOL_WITH_MONITORING
391 int rc = PDMR3LdrGetSymbolRC(pVM, NULL, "pgmPoolAccessHandler", &pVM->pgm.s.pPoolR3->pfnAccessHandlerRC);
392 AssertReleaseRC(rc);
393 /* init order hack. */
394 if (!pVM->pgm.s.pPoolR3->pfnAccessHandlerR0)
395 {
396 rc = PDMR3LdrGetSymbolR0(pVM, NULL, "pgmPoolAccessHandler", &pVM->pgm.s.pPoolR3->pfnAccessHandlerR0);
397 AssertReleaseRC(rc);
398 }
399#endif
400}
401
402
403/**
404 * Reset notification.
405 *
406 * This will flush the pool.
407 * @param pVM The VM handle.
408 */
409void pgmR3PoolReset(PVM pVM)
410{
411 pgmPoolFlushAll(pVM);
412}
413
414
415/**
416 * Grows the shadow page pool.
417 *
418 * I.e. adds more pages to it, assuming that hasn't reached cMaxPages yet.
419 *
420 * @returns VBox status code.
421 * @param pVM The VM handle.
422 */
423VMMR3DECL(int) PGMR3PoolGrow(PVM pVM)
424{
425 PPGMPOOL pPool = pVM->pgm.s.pPoolR3;
426 AssertReturn(pPool->cCurPages < pPool->cMaxPages, VERR_INTERNAL_ERROR);
427
428 /*
429 * How much to grow it by?
430 */
431 uint32_t cPages = pPool->cMaxPages - pPool->cCurPages;
432 cPages = RT_MIN(PGMPOOL_CFG_MAX_GROW, cPages);
433 LogFlow(("PGMR3PoolGrow: Growing the pool by %d (%#x) pages.\n", cPages, cPages));
434
435 for (unsigned i = pPool->cCurPages; cPages-- > 0; i++)
436 {
437 PPGMPOOLPAGE pPage = &pPool->aPages[i];
438
439 /* Allocate all pages in low (below 4 GB) memory as 32 bits guests need a page table root in low memory. */
440 pPage->pvPageR3 = MMR3PageAllocLow(pVM);
441 if (!pPage->pvPageR3)
442 {
443 Log(("We're out of memory!! i=%d\n", i));
444 return i ? VINF_SUCCESS : VERR_NO_PAGE_MEMORY;
445 }
446 pPage->Core.Key = MMPage2Phys(pVM, pPage->pvPageR3);
447 pPage->GCPhys = NIL_RTGCPHYS;
448 pPage->enmKind = PGMPOOLKIND_FREE;
449 pPage->idx = pPage - &pPool->aPages[0];
450 LogFlow(("PGMR3PoolGrow: insert page #%#x - %RHp\n", pPage->idx, pPage->Core.Key));
451 pPage->iNext = pPool->iFreeHead;
452#ifdef PGMPOOL_WITH_USER_TRACKING
453 pPage->iUserHead = NIL_PGMPOOL_USER_INDEX;
454#endif
455#ifdef PGMPOOL_WITH_MONITORING
456 pPage->iModifiedNext = NIL_PGMPOOL_IDX;
457 pPage->iModifiedPrev = NIL_PGMPOOL_IDX;
458 pPage->iMonitoredNext = NIL_PGMPOOL_IDX;
459 pPage->iMonitoredNext = NIL_PGMPOOL_IDX;
460#endif
461#ifdef PGMPOOL_WITH_CACHE
462 pPage->iAgeNext = NIL_PGMPOOL_IDX;
463 pPage->iAgePrev = NIL_PGMPOOL_IDX;
464#endif
465 /* commit it */
466 bool fRc = RTAvloHCPhysInsert(&pPool->HCPhysTree, &pPage->Core); Assert(fRc); NOREF(fRc);
467 pPool->iFreeHead = i;
468 pPool->cCurPages = i + 1;
469 }
470
471 Assert(pPool->cCurPages <= pPool->cMaxPages);
472 return VINF_SUCCESS;
473}
474
475
476#ifdef PGMPOOL_WITH_MONITORING
477
478/**
479 * Worker used by pgmR3PoolAccessHandler when it's invoked by an async thread.
480 *
481 * @param pPool The pool.
482 * @param pPage The page.
483 */
484static DECLCALLBACK(void) pgmR3PoolFlushReusedPage(PPGMPOOL pPool, PPGMPOOLPAGE pPage)
485{
486 /* for the present this should be safe enough I think... */
487 pgmLock(pPool->pVMR3);
488 if ( pPage->fReusedFlushPending
489 && pPage->enmKind != PGMPOOLKIND_FREE)
490 pgmPoolFlushPage(pPool, pPage);
491 pgmUnlock(pPool->pVMR3);
492}
493
494
495/**
496 * \#PF Handler callback for PT write accesses.
497 *
498 * The handler can not raise any faults, it's mainly for monitoring write access
499 * to certain pages.
500 *
501 * @returns VINF_SUCCESS if the handler have carried out the operation.
502 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
503 * @param pVM VM Handle.
504 * @param GCPhys The physical address the guest is writing to.
505 * @param pvPhys The HC mapping of that address.
506 * @param pvBuf What the guest is reading/writing.
507 * @param cbBuf How much it's reading/writing.
508 * @param enmAccessType The access type.
509 * @param pvUser User argument.
510 */
511static DECLCALLBACK(int) pgmR3PoolAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
512{
513 STAM_PROFILE_START(&pVM->pgm.s.pPoolR3->StatMonitorR3, a);
514 PPGMPOOL pPool = pVM->pgm.s.pPoolR3;
515 PPGMPOOLPAGE pPage = (PPGMPOOLPAGE)pvUser;
516 LogFlow(("pgmR3PoolAccessHandler: GCPhys=%RGp %p:{.Core=%RHp, .idx=%d, .GCPhys=%RGp, .enmType=%d}\n",
517 GCPhys, pPage, pPage->Core.Key, pPage->idx, pPage->GCPhys, pPage->enmKind));
518
519 PVMCPU pVCpu = VMMGetCpu(pVM);
520
521 /*
522 * We don't have to be very sophisticated about this since there are relativly few calls here.
523 * However, we must try our best to detect any non-cpu accesses (disk / networking).
524 *
525 * Just to make life more interesting, we'll have to deal with the async threads too.
526 * We cannot flush a page if we're in an async thread because of REM notifications.
527 */
528 if (!VM_IS_EMT(pVM))
529 {
530 Log(("pgmR3PoolAccessHandler: async thread, requesting EMT to flush the page: %p:{.Core=%RHp, .idx=%d, .GCPhys=%RGp, .enmType=%d}\n",
531 pPage, pPage->Core.Key, pPage->idx, pPage->GCPhys, pPage->enmKind));
532 STAM_COUNTER_INC(&pPool->StatMonitorR3Async);
533 if (!pPage->fReusedFlushPending)
534 {
535 int rc = VMR3ReqCallEx(pPool->pVMR3, VMREQDEST_ANY, NULL, 0, VMREQFLAGS_NO_WAIT | VMREQFLAGS_VOID, (PFNRT)pgmR3PoolFlushReusedPage, 2, pPool, pPage);
536 AssertRCReturn(rc, rc);
537 pPage->fReusedFlushPending = true;
538 pPage->cModifications += 0x1000;
539 }
540 pgmPoolMonitorChainChanging(pVCpu, pPool, pPage, GCPhys, pvPhys, NULL);
541 /** @todo r=bird: making unsafe assumption about not crossing entries here! */
542 while (cbBuf > 4)
543 {
544 cbBuf -= 4;
545 pvPhys = (uint8_t *)pvPhys + 4;
546 GCPhys += 4;
547 pgmPoolMonitorChainChanging(pVCpu, pPool, pPage, GCPhys, pvPhys, NULL);
548 }
549 STAM_PROFILE_STOP(&pPool->StatMonitorR3, a);
550 }
551 else if ( ( pPage->cModifications < 96 /* it's cheaper here. */
552 || pgmPoolIsPageLocked(&pVM->pgm.s, pPage)
553 )
554 && cbBuf <= 4)
555 {
556 /* Clear the shadow entry. */
557 if (!pPage->cModifications++)
558 pgmPoolMonitorModifiedInsert(pPool, pPage);
559 /** @todo r=bird: making unsafe assumption about not crossing entries here! */
560 pgmPoolMonitorChainChanging(pVCpu, pPool, pPage, GCPhys, pvPhys, NULL);
561 STAM_PROFILE_STOP(&pPool->StatMonitorR3, a);
562 }
563 else
564 {
565 pgmPoolMonitorChainFlush(pPool, pPage); /* ASSUME that VERR_PGM_POOL_CLEARED can be ignored here and that FFs will deal with it in due time. */
566 STAM_PROFILE_STOP_EX(&pPool->StatMonitorR3, &pPool->StatMonitorR3FlushPage, a);
567 }
568
569 return VINF_PGM_HANDLER_DO_DEFAULT;
570}
571
572#endif /* PGMPOOL_WITH_MONITORING */
573
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