VirtualBox

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

Last change on this file since 1714 was 1480, checked in by vboxsync, 18 years ago

No longer require contiguous memory for the VM structure.
Did long overdue IOCtl cleanup wrt R3/R0 pointers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.0 KB
Line 
1/* $Id: MMPagePool.cpp 1480 2007-03-14 18:27:47Z vboxsync $ */
2/** @file
3 * MM - Memory Monitor(/Manager) - Page Pool.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung 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);
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);
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 << PAGE_SHIFT, paPhysPages);
245 if (VBOX_FAILURE(rc))
246 {
247 SUPPageFree(pSub->pvPages);
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