VirtualBox

source: vbox/trunk/src/VBox/VMM/MMPagePool.cpp@ 10700

Last change on this file since 10700 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.0 KB
Line 
1/* $Id: MMPagePool.cpp 8155 2008-04-18 15:16:47Z vboxsync $ */
2/** @file
3 * MM - Memory Monitor(/Manager) - 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/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_MM_POOL
26#include <VBox/mm.h>
27#include <VBox/pgm.h>
28#include <VBox/stam.h>
29#include "MMInternal.h"
30#include <VBox/vm.h>
31#include <VBox/param.h>
32#include <VBox/err.h>
33#include <VBox/log.h>
34#include <iprt/alloc.h>
35#include <iprt/assert.h>
36#define USE_INLINE_ASM_BIT_OPS
37#ifdef USE_INLINE_ASM_BIT_OPS
38# include <iprt/asm.h>
39#endif
40#include <iprt/string.h>
41
42
43
44/*******************************************************************************
45* Internal Functions *
46*******************************************************************************/
47#ifdef IN_RING3
48static void * mmR3PagePoolAlloc(PMMPAGEPOOL pPool);
49static void mmR3PagePoolFree(PMMPAGEPOOL pPool, void *pv);
50#endif
51
52
53/**
54 * Initializes the page pool
55 *
56 * @return VBox status.
57 * @param pVM VM handle.
58 * @thread The Emulation Thread.
59 */
60int mmR3PagePoolInit(PVM pVM)
61{
62 AssertMsg(!pVM->mm.s.pPagePool, ("Already initialized!\n"));
63
64 /*
65 * Allocate the pool structures.
66 */
67 AssertRelease(sizeof(*pVM->mm.s.pPagePool) + sizeof(*pVM->mm.s.pPagePoolLow) < PAGE_SIZE);
68 int rc = SUPPageAllocLocked(1, (void **)&pVM->mm.s.pPagePool);
69 if (VBOX_FAILURE(rc))
70 return rc;
71 memset(pVM->mm.s.pPagePool, 0, PAGE_SIZE);
72 pVM->mm.s.pPagePool->pVM = pVM;
73 STAM_REG(pVM, &pVM->mm.s.pPagePool->cPages, STAMTYPE_U32, "/MM/Page/Def/cPages", STAMUNIT_PAGES, "Number of pages in the default pool.");
74 STAM_REG(pVM, &pVM->mm.s.pPagePool->cFreePages, STAMTYPE_U32, "/MM/Page/Def/cFreePages", STAMUNIT_PAGES, "Number of free pages in the default pool.");
75 STAM_REG(pVM, &pVM->mm.s.pPagePool->cSubPools, STAMTYPE_U32, "/MM/Page/Def/cSubPools", STAMUNIT_COUNT, "Number of sub pools in the default pool.");
76 STAM_REG(pVM, &pVM->mm.s.pPagePool->cAllocCalls, STAMTYPE_COUNTER, "/MM/Page/Def/cAllocCalls", STAMUNIT_CALLS, "Number of MMR3PageAlloc() calls for the default pool.");
77 STAM_REG(pVM, &pVM->mm.s.pPagePool->cFreeCalls, STAMTYPE_COUNTER, "/MM/Page/Def/cFreeCalls", STAMUNIT_CALLS, "Number of MMR3PageFree()+MMR3PageFreeByPhys() calls for the default pool.");
78 STAM_REG(pVM, &pVM->mm.s.pPagePool->cToPhysCalls, STAMTYPE_COUNTER, "/MM/Page/Def/cToPhysCalls", STAMUNIT_CALLS, "Number of MMR3Page2Phys() calls for this pool.");
79 STAM_REG(pVM, &pVM->mm.s.pPagePool->cToVirtCalls, STAMTYPE_COUNTER, "/MM/Page/Def/cToVirtCalls", STAMUNIT_CALLS, "Number of MMR3PagePhys2Page()+MMR3PageFreeByPhys() calls for the default pool.");
80 STAM_REG(pVM, &pVM->mm.s.pPagePool->cErrors, STAMTYPE_COUNTER, "/MM/Page/Def/cErrors", STAMUNIT_ERRORS,"Number of errors for the default pool.");
81
82 pVM->mm.s.pPagePoolLow = pVM->mm.s.pPagePool + 1;
83 pVM->mm.s.pPagePoolLow->pVM = pVM;
84 pVM->mm.s.pPagePoolLow->fLow = true;
85 STAM_REG(pVM, &pVM->mm.s.pPagePoolLow->cPages, STAMTYPE_U32, "/MM/Page/Low/cPages", STAMUNIT_PAGES, "Number of pages in the <4GB pool.");
86 STAM_REG(pVM, &pVM->mm.s.pPagePoolLow->cFreePages, STAMTYPE_U32, "/MM/Page/Low/cFreePages", STAMUNIT_PAGES, "Number of free pages in the <4GB pool.");
87 STAM_REG(pVM, &pVM->mm.s.pPagePoolLow->cSubPools, STAMTYPE_U32, "/MM/Page/Low/cSubPools", STAMUNIT_COUNT, "Number of sub pools in the <4GB pool.");
88 STAM_REG(pVM, &pVM->mm.s.pPagePoolLow->cAllocCalls, STAMTYPE_COUNTER, "/MM/Page/Low/cAllocCalls", STAMUNIT_CALLS, "Number of MMR3PageAllocLow() calls for the <4GB pool.");
89 STAM_REG(pVM, &pVM->mm.s.pPagePoolLow->cFreeCalls, STAMTYPE_COUNTER, "/MM/Page/Low/cFreeCalls", STAMUNIT_CALLS, "Number of MMR3PageFreeLow()+MMR3PageFreeByPhys() calls for the <4GB pool.");
90 STAM_REG(pVM, &pVM->mm.s.pPagePoolLow->cToPhysCalls,STAMTYPE_COUNTER, "/MM/Page/Low/cToPhysCalls", STAMUNIT_CALLS, "Number of MMR3Page2Phys() calls for the <4GB pool.");
91 STAM_REG(pVM, &pVM->mm.s.pPagePoolLow->cToVirtCalls,STAMTYPE_COUNTER, "/MM/Page/Low/cToVirtCalls", STAMUNIT_CALLS, "Number of MMR3PagePhys2Page()+MMR3PageFreeByPhys() calls for the <4GB pool.");
92 STAM_REG(pVM, &pVM->mm.s.pPagePoolLow->cErrors, STAMTYPE_COUNTER, "/MM/Page/Low/cErrors", STAMUNIT_ERRORS,"Number of errors for the <4GB pool.");
93
94 /** @todo init a mutex? */
95 return VINF_SUCCESS;
96}
97
98
99/**
100 * Release all locks and free the allocated memory.
101 *
102 * @param pVM VM handle.
103 * @thread The Emulation Thread.
104 */
105void mmR3PagePoolTerm(PVM pVM)
106{
107 if (pVM->mm.s.pPagePool)
108 {
109 /*
110 * Unlock all memory held by subpools and free the memory.
111 * (The MM Heap will free the memory used for internal stuff.)
112 */
113 Assert(!pVM->mm.s.pPagePool->fLow);
114 PMMPAGESUBPOOL pSubPool = pVM->mm.s.pPagePool->pHead;
115 while (pSubPool)
116 {
117 int rc = SUPPageUnlock(pSubPool->pvPages);
118 AssertMsgRC(rc, ("SUPPageUnlock(%p) failed with rc=%d\n", pSubPool->pvPages, rc));
119 rc = SUPPageFree(pSubPool->pvPages, pSubPool->cPages);
120 AssertMsgRC(rc, ("SUPPageFree(%p) failed with rc=%d\n", pSubPool->pvPages, rc));
121 pSubPool->pvPages = NULL;
122
123 /* next */
124 pSubPool = pSubPool->pNext;
125 }
126 pVM->mm.s.pPagePool = NULL;
127 }
128
129 if (pVM->mm.s.pPagePoolLow)
130 {
131 /*
132 * Free the memory.
133 */
134 Assert(pVM->mm.s.pPagePoolLow->fLow);
135 PMMPAGESUBPOOL pSubPool = pVM->mm.s.pPagePoolLow->pHead;
136 while (pSubPool)
137 {
138 int rc = SUPLowFree(pSubPool->pvPages, pSubPool->cPages);
139 AssertMsgRC(rc, ("SUPPageFree(%p) failed with rc=%d\n", pSubPool->pvPages, rc));
140 pSubPool->pvPages = NULL;
141
142 /* next */
143 pSubPool = pSubPool->pNext;
144 }
145 pVM->mm.s.pPagePoolLow = NULL;
146 }
147}
148
149
150/**
151 * Allocates a page from the page pool.
152 *
153 * @returns Pointer to allocated page(s).
154 * @returns NULL on failure.
155 * @param pPool Pointer to the page pool.
156 * @thread The Emulation Thread.
157 */
158DECLINLINE(void *) mmR3PagePoolAlloc(PMMPAGEPOOL pPool)
159{
160 VM_ASSERT_EMT(pPool->pVM);
161 STAM_COUNTER_INC(&pPool->cAllocCalls);
162
163 /*
164 * Walk free list.
165 */
166 if (pPool->pHeadFree)
167 {
168 PMMPAGESUBPOOL pSub = pPool->pHeadFree;
169 /* decrement free count and unlink if no more free entries. */
170 if (!--pSub->cPagesFree)
171 pPool->pHeadFree = pSub->pNextFree;
172#ifdef VBOX_WITH_STATISTICS
173 pPool->cFreePages--;
174#endif
175
176 /* find free spot in bitmap. */
177#ifdef USE_INLINE_ASM_BIT_OPS
178 const int iPage = ASMBitFirstClear(pSub->auBitmap, pSub->cPages);
179 if (iPage >= 0)
180 {
181 Assert(!ASMBitTest(pSub->auBitmap, iPage));
182 ASMBitSet(pSub->auBitmap, iPage);
183 return (char *)pSub->pvPages + PAGE_SIZE * iPage;
184 }
185#else
186 unsigned *pu = &pSub->auBitmap[0];
187 unsigned *puEnd = &pSub->auBitmap[pSub->cPages / (sizeof(pSub->auBitmap) * 8)];
188 while (pu < puEnd)
189 {
190 unsigned u;
191 if ((u = *pu) != ~0U)
192 {
193 unsigned iBit = 0;
194 unsigned uMask = 1;
195 while (iBit < sizeof(pSub->auBitmap[0]) * 8)
196 {
197 if (!(u & uMask))
198 {
199 *pu |= uMask;
200 return (char *)pSub->pvPages
201 + PAGE_SIZE * (iBit + ((char *)pu - (char *)&pSub->auBitmap[0]) * 8);
202 }
203 iBit++;
204 uMask <<= 1;
205 }
206 STAM_COUNTER_INC(&pPool->cErrors);
207 AssertMsgFailed(("how odd, expected to find a free bit in %#x, but didn't\n", u));
208 }
209 /* next */
210 pu++;
211 }
212#endif
213 STAM_COUNTER_INC(&pPool->cErrors);
214#ifdef VBOX_WITH_STATISTICS
215 pPool->cFreePages++;
216#endif
217 AssertMsgFailed(("how strange, expected to find a free bit in %p, but didn't (%d pages supposed to be free!)\n", pSub, pSub->cPagesFree + 1));
218 }
219
220 /*
221 * Allocate new subpool.
222 */
223 unsigned cPages = !pPool->fLow ? 128 : 32;
224 PMMPAGESUBPOOL pSub;
225 int rc = MMHyperAlloc(pPool->pVM,
226 RT_OFFSETOF(MMPAGESUBPOOL, auBitmap[cPages / (sizeof(pSub->auBitmap[0] * 8))])
227 + (sizeof(SUPPAGE) + sizeof(MMPPLOOKUPHCPHYS)) * cPages
228 + sizeof(MMPPLOOKUPHCPTR),
229 0,
230 MM_TAG_MM_PAGE,
231 (void **)&pSub);
232 if (VBOX_FAILURE(rc))
233 return NULL;
234
235 PSUPPAGE paPhysPages = (PSUPPAGE)&pSub->auBitmap[cPages / (sizeof(pSub->auBitmap[0]) * 8)];
236 Assert((uintptr_t)paPhysPages >= (uintptr_t)&pSub->auBitmap[1]);
237 if (!pPool->fLow)
238 {
239 /*
240 * Allocate and lock the pages.
241 */
242 rc = SUPPageAlloc(cPages, &pSub->pvPages);
243 if (VBOX_SUCCESS(rc))
244 {
245 rc = SUPPageLock(pSub->pvPages, cPages, paPhysPages);
246 if (VBOX_FAILURE(rc))
247 {
248 SUPPageFree(pSub->pvPages, cPages);
249 rc = VMSetError(pPool->pVM, rc, RT_SRC_POS,
250 N_("Failed to lock host %zd bytes of memory (out of memory)"), (size_t)cPages << PAGE_SHIFT);
251 }
252 }
253 }
254 else
255 rc = SUPLowAlloc(cPages, &pSub->pvPages, NULL, paPhysPages);
256 if (VBOX_SUCCESS(rc))
257 {
258 /*
259 * Setup the sub structure and allocate the requested page.
260 */
261 pSub->cPages = cPages;
262 pSub->cPagesFree= cPages - 1;
263 pSub->paPhysPages = paPhysPages;
264 memset(pSub->auBitmap, 0, cPages / 8);
265 /* allocate first page. */
266 pSub->auBitmap[0] |= 1;
267 /* link into free chain. */
268 pSub->pNextFree = pPool->pHeadFree;
269 pPool->pHeadFree= pSub;
270 /* link into main chain. */
271 pSub->pNext = pPool->pHead;
272 pPool->pHead = pSub;
273 /* update pool statistics. */
274 pPool->cSubPools++;
275 pPool->cPages += cPages;
276#ifdef VBOX_WITH_STATISTICS
277 pPool->cFreePages += cPages - 1;
278#endif
279
280 /*
281 * Initialize the physical pages with backpointer to subpool.
282 */
283 unsigned i = cPages;
284 while (i-- > 0)
285 {
286 AssertMsg(paPhysPages[i].Phys && !(paPhysPages[i].Phys & PAGE_OFFSET_MASK),
287 ("i=%d Phys=%d\n", i, paPhysPages[i].Phys));
288 paPhysPages[i].uReserved = (RTHCUINTPTR)pSub;
289 }
290
291 /*
292 * Initialize the physical lookup record with backpointers to the physical pages.
293 */
294 PMMPPLOOKUPHCPHYS paLookupPhys = (PMMPPLOOKUPHCPHYS)&paPhysPages[cPages];
295 i = cPages;
296 while (i-- > 0)
297 {
298 paLookupPhys[i].pPhysPage = &paPhysPages[i];
299 paLookupPhys[i].Core.Key = paPhysPages[i].Phys;
300 RTAvlHCPhysInsert(&pPool->pLookupPhys, &paLookupPhys[i].Core);
301 }
302
303 /*
304 * And the one record for virtual memory lookup.
305 */
306 PMMPPLOOKUPHCPTR pLookupVirt = (PMMPPLOOKUPHCPTR)&paLookupPhys[cPages];
307 pLookupVirt->pSubPool = pSub;
308 pLookupVirt->Core.Key = pSub->pvPages;
309 RTAvlPVInsert(&pPool->pLookupVirt, &pLookupVirt->Core);
310
311 /* return allocated page (first). */
312 return pSub->pvPages;
313 }
314
315 MMR3HeapFree(pSub);
316 STAM_COUNTER_INC(&pPool->cErrors);
317 if (pPool->fLow)
318 VMSetError(pPool->pVM, rc, RT_SRC_POS,
319 N_("Failed to expand page pool for memory below 4GB. current size: %d pages"),
320 pPool->cPages);
321 AssertMsgFailed(("Failed to expand pool%s. rc=%Vrc poolsize=%d\n",
322 pPool->fLow ? " (<4GB)" : "", rc, pPool->cPages));
323 return NULL;
324}
325
326
327/**
328 * Frees a page from the page pool.
329 *
330 * @param pPool Pointer to the page pool.
331 * @param pv Pointer to the page to free.
332 * I.e. pointer returned by mmR3PagePoolAlloc().
333 * @thread The Emulation Thread.
334 */
335DECLINLINE(void) mmR3PagePoolFree(PMMPAGEPOOL pPool, void *pv)
336{
337 VM_ASSERT_EMT(pPool->pVM);
338 STAM_COUNTER_INC(&pPool->cFreeCalls);
339
340 /*
341 * Lookup the virtual address.
342 */
343 PMMPPLOOKUPHCPTR pLookup = (PMMPPLOOKUPHCPTR)RTAvlPVGetBestFit(&pPool->pLookupVirt, pv, false);
344 if ( !pLookup
345 || (char *)pv >= (char *)pLookup->pSubPool->pvPages + (pLookup->pSubPool->cPages << PAGE_SHIFT)
346 )
347 {
348 STAM_COUNTER_INC(&pPool->cErrors);
349 AssertMsgFailed(("invalid pointer %p\n", pv));
350 return;
351 }
352
353 /*
354 * Free the page.
355 */
356 PMMPAGESUBPOOL pSubPool = pLookup->pSubPool;
357 /* clear bitmap bit */
358 const unsigned iPage = ((char *)pv - (char *)pSubPool->pvPages) >> PAGE_SHIFT;
359#ifdef USE_INLINE_ASM_BIT_OPS
360 Assert(ASMBitTest(pSubPool->auBitmap, iPage));
361 ASMBitClear(pSubPool->auBitmap, iPage);
362#else
363 unsigned iBit = iPage % (sizeof(pSubPool->auBitmap[0]) * 8);
364 unsigned iIndex = iPage / (sizeof(pSubPool->auBitmap[0]) * 8);
365 pSubPool->auBitmap[iIndex] &= ~(1 << iBit);
366#endif
367 /* update stats. */
368 pSubPool->cPagesFree++;
369#ifdef VBOX_WITH_STATISTICS
370 pPool->cFreePages++;
371#endif
372 if (pSubPool->cPagesFree == 1)
373 {
374 pSubPool->pNextFree = pPool->pHeadFree;
375 pPool->pHeadFree = pSubPool;
376 }
377}
378
379
380/**
381 * Allocates a page from the page pool.
382 *
383 * This function may returns pages which has physical addresses any
384 * where. If you require a page to be within the first 4GB of physical
385 * memory, use MMR3PageAllocLow().
386 *
387 * @returns Pointer to the allocated page page.
388 * @returns NULL on failure.
389 * @param pVM VM handle.
390 * @thread The Emulation Thread.
391 */
392MMR3DECL(void *) MMR3PageAlloc(PVM pVM)
393{
394 return mmR3PagePoolAlloc(pVM->mm.s.pPagePool);
395}
396
397
398/**
399 * Allocates a page from the page pool and return its physical address.
400 *
401 * This function may returns pages which has physical addresses any
402 * where. If you require a page to be within the first 4GB of physical
403 * memory, use MMR3PageAllocLow().
404 *
405 * @returns Pointer to the allocated page page.
406 * @returns NIL_RTHCPHYS on failure.
407 * @param pVM VM handle.
408 * @thread The Emulation Thread.
409 */
410MMR3DECL(RTHCPHYS) MMR3PageAllocPhys(PVM pVM)
411{
412 /** @todo optimize this, it's the most common case now. */
413 void *pv = mmR3PagePoolAlloc(pVM->mm.s.pPagePool);
414 if (pv)
415 return mmPagePoolPtr2Phys(pVM->mm.s.pPagePool, pv);
416 return NIL_RTHCPHYS;
417}
418
419
420/**
421 * Frees a page allocated from the page pool by MMR3PageAlloc() or
422 * MMR3PageAllocPhys().
423 *
424 * @param pVM VM handle.
425 * @param pvPage Pointer to the page.
426 * @thread The Emulation Thread.
427 */
428MMR3DECL(void) MMR3PageFree(PVM pVM, void *pvPage)
429{
430 mmR3PagePoolFree(pVM->mm.s.pPagePool, pvPage);
431}
432
433
434/**
435 * Allocates a page from the low page pool.
436 *
437 * @returns Pointer to the allocated page.
438 * @returns NULL on failure.
439 * @param pVM VM handle.
440 * @thread The Emulation Thread.
441 */
442MMR3DECL(void *) MMR3PageAllocLow(PVM pVM)
443{
444 return mmR3PagePoolAlloc(pVM->mm.s.pPagePoolLow);
445}
446
447
448/**
449 * Frees a page allocated from the page pool by MMR3PageAllocLow().
450 *
451 * @param pVM VM handle.
452 * @param pvPage Pointer to the page.
453 * @thread The Emulation Thread.
454 */
455MMR3DECL(void) MMR3PageFreeLow(PVM pVM, void *pvPage)
456{
457 mmR3PagePoolFree(pVM->mm.s.pPagePoolLow, pvPage);
458}
459
460
461/**
462 * Free a page allocated from the page pool by physical address.
463 * This works for pages allocated by MMR3PageAlloc(), MMR3PageAllocPhys()
464 * and MMR3PageAllocLow().
465 *
466 * @param pVM VM handle.
467 * @param HCPhysPage The physical address of the page to be freed.
468 * @thread The Emulation Thread.
469 */
470MMR3DECL(void) MMR3PageFreeByPhys(PVM pVM, RTHCPHYS HCPhysPage)
471{
472 void *pvPage = mmPagePoolPhys2Ptr(pVM->mm.s.pPagePool, HCPhysPage);
473 if (!pvPage)
474 pvPage = mmPagePoolPhys2Ptr(pVM->mm.s.pPagePoolLow, HCPhysPage);
475 if (pvPage)
476 mmR3PagePoolFree(pVM->mm.s.pPagePool, pvPage);
477 else
478 AssertMsgFailed(("Invalid address HCPhysPT=%#x\n", HCPhysPage));
479}
480
481
482/**
483 * Gets the HC pointer to the dummy page.
484 *
485 * The dummy page is used as a place holder to prevent potential bugs
486 * from doing really bad things to the system.
487 *
488 * @returns Pointer to the dummy page.
489 * @param pVM VM handle.
490 * @thread The Emulation Thread.
491 */
492MMR3DECL(void *) MMR3PageDummyHCPtr(PVM pVM)
493{
494 VM_ASSERT_EMT(pVM);
495 if (!pVM->mm.s.pvDummyPage)
496 {
497 pVM->mm.s.pvDummyPage = mmR3PagePoolAlloc(pVM->mm.s.pPagePool);
498 AssertRelease(pVM->mm.s.pvDummyPage);
499 pVM->mm.s.HCPhysDummyPage = mmPagePoolPtr2Phys(pVM->mm.s.pPagePool, pVM->mm.s.pvDummyPage);
500 AssertRelease(!(pVM->mm.s.HCPhysDummyPage & ~X86_PTE_PAE_PG_MASK));
501 }
502 return pVM->mm.s.pvDummyPage;
503}
504
505
506/**
507 * Gets the HC Phys to the dummy page.
508 *
509 * The dummy page is used as a place holder to prevent potential bugs
510 * from doing really bad things to the system.
511 *
512 * @returns Pointer to the dummy page.
513 * @param pVM VM handle.
514 * @thread The Emulation Thread.
515 */
516MMR3DECL(RTHCPHYS) MMR3PageDummyHCPhys(PVM pVM)
517{
518 VM_ASSERT_EMT(pVM);
519 if (!pVM->mm.s.pvDummyPage)
520 MMR3PageDummyHCPtr(pVM);
521 return pVM->mm.s.HCPhysDummyPage;
522}
523
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