VirtualBox

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

Last change on this file since 4501 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

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