VirtualBox

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

Last change on this file since 3723 was 2981, checked in by vboxsync, 17 years ago

InnoTek -> innotek: all the headers and comments.

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