VirtualBox

source: vbox/trunk/src/VBox/VMM/MMUkHeap.cpp@ 20374

Last change on this file since 20374 was 18792, checked in by vboxsync, 16 years ago

MM,PGM: New User-kernel heap (aka MMUkHeap), use it for the PGMCHUNKR3MAP instead of the hyper heap.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
Line 
1/* $Id: MMUkHeap.cpp 18792 2009-04-06 18:40:52Z vboxsync $ */
2/** @file
3 * MM - Memory Manager - Ring-3 Heap with kernel accessible mapping.
4 */
5
6/*
7 * Copyright (C) 2006-2009 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/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_MM_HEAP
27#include <VBox/mm.h>
28#include <VBox/stam.h>
29#include "MMInternal.h"
30#include <VBox/vm.h>
31#include <VBox/uvm.h>
32#include <VBox/err.h>
33#include <VBox/param.h>
34#include <VBox/log.h>
35
36#include <iprt/assert.h>
37#include <iprt/string.h>
38#include <iprt/heap.h>
39
40
41/*******************************************************************************
42* Internal Functions *
43*******************************************************************************/
44static void *mmR3UkHeapAlloc(PMMUKHEAP pHeap, MMTAG enmTag, size_t cb, bool fZero, PRTR0PTR pR0Ptr);
45
46
47
48/**
49 * Create a User-kernel heap.
50 *
51 * This does not require SUPLib to be initialized as we'll lazily allocate the
52 * kernel accessible memory on the first alloc call.
53 *
54 * @returns VBox status.
55 * @param pVM The handle to the VM the heap should be associated with.
56 * @param ppHeap Where to store the heap pointer.
57 */
58int mmR3UkHeapCreateU(PUVM pUVM, PMMUKHEAP *ppHeap)
59{
60 PMMUKHEAP pHeap = (PMMUKHEAP)MMR3HeapAllocZU(pUVM, MM_TAG_MM, sizeof(MMUKHEAP));
61 if (pHeap)
62 {
63 int rc = RTCritSectInit(&pHeap->Lock);
64 if (RT_SUCCESS(rc))
65 {
66 /*
67 * Initialize the global stat record.
68 */
69 pHeap->pUVM = pUVM;
70#ifdef MMUKHEAP_WITH_STATISTICS
71 PMMUKHEAPSTAT pStat = &pHeap->Stat;
72 STAMR3RegisterU(pUVM, &pStat->cAllocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cAllocations", STAMUNIT_CALLS, "Number or MMR3UkHeapAlloc() calls.");
73 STAMR3RegisterU(pUVM, &pStat->cReallocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cReallocations", STAMUNIT_CALLS, "Number of MMR3UkHeapRealloc() calls.");
74 STAMR3RegisterU(pUVM, &pStat->cFrees, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cFrees", STAMUNIT_CALLS, "Number of MMR3UkHeapFree() calls.");
75 STAMR3RegisterU(pUVM, &pStat->cFailures, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cFailures", STAMUNIT_COUNT, "Number of failures.");
76 STAMR3RegisterU(pUVM, &pStat->cbCurAllocated, sizeof(pStat->cbCurAllocated) == sizeof(uint32_t) ? STAMTYPE_U32 : STAMTYPE_U64,
77 STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cbCurAllocated", STAMUNIT_BYTES, "Number of bytes currently allocated.");
78 STAMR3RegisterU(pUVM, &pStat->cbAllocated, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cbAllocated", STAMUNIT_BYTES, "Total number of bytes allocated.");
79 STAMR3RegisterU(pUVM, &pStat->cbFreed, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cbFreed", STAMUNIT_BYTES, "Total number of bytes freed.");
80#endif
81 *ppHeap = pHeap;
82 return VINF_SUCCESS;
83 }
84 AssertRC(rc);
85 MMR3HeapFree(pHeap);
86 }
87 AssertMsgFailed(("failed to allocate heap structure\n"));
88 return VERR_NO_MEMORY;
89}
90
91
92/**
93 * Destroy a User-kernel heap.
94 *
95 * @param pHeap Heap handle.
96 */
97void mmR3UkHeapDestroy(PMMUKHEAP pHeap)
98{
99 /*
100 * Start by deleting the lock, that'll trap anyone
101 * attempting to use the heap.
102 */
103 RTCritSectDelete(&pHeap->Lock);
104
105 /*
106 * Walk the sub-heaps and free them.
107 */
108 while (pHeap->pSubHeapHead)
109 {
110 PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
111 pHeap->pSubHeapHead = pSubHeap->pNext;
112 SUPR3PageFreeEx(pSubHeap->pv, pSubHeap->cb >> PAGE_SHIFT);
113 //MMR3HeapFree(pSubHeap); - rely on the automatic cleanup.
114 }
115 //MMR3HeapFree(pHeap->stats);
116 //MMR3HeapFree(pHeap);
117}
118
119
120/**
121 * Allocate memory associating it with the VM for collective cleanup.
122 *
123 * The memory will be allocated from the default heap but a header
124 * is added in which we keep track of which VM it belongs to and chain
125 * all the allocations together so they can be freed in one go.
126 *
127 * This interface is typically used for memory block which will not be
128 * freed during the life of the VM.
129 *
130 * @returns Pointer to allocated memory.
131 * @param pVM VM handle.
132 * @param enmTag Statistics tag. Statistics are collected on a per tag
133 * basis in addition to a global one. Thus we can easily
134 * identify how memory is used by the VM.
135 * @param cbSize Size of the block.
136 * @param pR0Ptr Where to return the ring-0 address of the memory.
137 */
138VMMR3DECL(void *) MMR3UkHeapAlloc(PVM pVM, MMTAG enmTag, size_t cbSize, PRTR0PTR pR0Ptr)
139{
140 return mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, false, pR0Ptr);
141}
142
143
144/**
145 * Same as MMR3UkHeapAlloc().
146 *
147 * @returns Pointer to allocated memory.
148 * @param pVM VM handle.
149 * @param enmTag Statistics tag. Statistics are collected on a per tag
150 * basis in addition to a global one. Thus we can easily
151 * identify how memory is used by the VM.
152 * @param cbSize Size of the block.
153 * @param ppv Where to store the pointer to the allocated memory on success.
154 * @param pR0Ptr Where to return the ring-0 address of the memory.
155 */
156VMMR3DECL(int) MMR3UkHeapAllocEx(PVM pVM, MMTAG enmTag, size_t cbSize, void **ppv, PRTR0PTR pR0Ptr)
157{
158 void *pv = mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, false, pR0Ptr);
159 if (pv)
160 {
161 *ppv = pv;
162 return VINF_SUCCESS;
163 }
164 return VERR_NO_MEMORY;
165}
166
167
168/**
169 * Same as MMR3UkHeapAlloc() only the memory is zeroed.
170 *
171 * @returns Pointer to allocated memory.
172 * @param pVM VM handle.
173 * @param enmTag Statistics tag. Statistics are collected on a per tag
174 * basis in addition to a global one. Thus we can easily
175 * identify how memory is used by the VM.
176 * @param cbSize Size of the block.
177 * @param pR0Ptr Where to return the ring-0 address of the memory.
178 */
179VMMR3DECL(void *) MMR3UkHeapAllocZ(PVM pVM, MMTAG enmTag, size_t cbSize, PRTR0PTR pR0Ptr)
180{
181 return mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, true, pR0Ptr);
182}
183
184
185/**
186 * Same as MMR3UkHeapAllocZ().
187 *
188 * @returns Pointer to allocated memory.
189 * @param pVM VM handle.
190 * @param enmTag Statistics tag. Statistics are collected on a per tag
191 * basis in addition to a global one. Thus we can easily
192 * identify how memory is used by the VM.
193 * @param cbSize Size of the block.
194 * @param ppv Where to store the pointer to the allocated memory on success.
195 * @param pR0Ptr Where to return the ring-0 address of the memory.
196 */
197VMMR3DECL(int) MMR3UkHeapAllocZEx(PVM pVM, MMTAG enmTag, size_t cbSize, void **ppv, PRTR0PTR pR0Ptr)
198{
199 void *pv = mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, true, pR0Ptr);
200 if (pv)
201 {
202 *ppv = pv;
203 return VINF_SUCCESS;
204 }
205 return VERR_NO_MEMORY;
206}
207
208
209/***
210 * Worker for mmR3UkHeapAlloc that creates and adds a new sub-heap.
211 *
212 * @returns Pointer to the new sub-heap.
213 * @param pHeap The heap
214 * @param cbSubHeap The size of the sub-heap.
215 */
216static PMMUKHEAPSUB mmR3UkHeapAddSubHeap(PMMUKHEAP pHeap, size_t cbSubHeap)
217{
218 PMMUKHEAPSUB pSubHeap = (PMMUKHEAPSUB)MMR3HeapAllocU(pHeap->pUVM, MM_TAG_MM/*_UK_HEAP*/, sizeof(*pSubHeap));
219 if (pSubHeap)
220 {
221 pSubHeap->cb = cbSubHeap;
222 int rc = SUPR3PageAllocEx(pSubHeap->cb >> PAGE_SHIFT, 0, &pSubHeap->pv, &pSubHeap->pvR0, NULL);
223 if (RT_SUCCESS(rc))
224 {
225 rc = RTHeapSimpleInit(&pSubHeap->hSimple, pSubHeap->pv, pSubHeap->cb);
226 if (RT_SUCCESS(rc))
227 {
228 pSubHeap->pNext = pHeap->pSubHeapHead;
229 pHeap->pSubHeapHead = pSubHeap;
230 return pSubHeap;
231 }
232
233 /* bail out */
234 SUPR3PageFreeEx(pSubHeap->pv, pSubHeap->cb >> PAGE_SHIFT);
235 }
236 MMR3HeapFree(pSubHeap);
237 }
238 return NULL;
239}
240
241
242/**
243 * Allocate memory from the heap.
244 *
245 * @returns Pointer to allocated memory.
246 * @param pHeap Heap handle.
247 * @param enmTag Statistics tag. Statistics are collected on a per tag
248 * basis in addition to a global one. Thus we can easily
249 * identify how memory is used by the VM.
250 * @param cb Size of the block.
251 * @param fZero Whether or not to zero the memory block.
252 * @param pR0Ptr Where to return the ring-0 pointer.
253 */
254static void *mmR3UkHeapAlloc(PMMUKHEAP pHeap, MMTAG enmTag, size_t cb, bool fZero, PRTR0PTR pR0Ptr)
255{
256 if (pR0Ptr)
257 *pR0Ptr = NIL_RTR0PTR;
258 RTCritSectEnter(&pHeap->Lock);
259
260#ifdef MMUKHEAP_WITH_STATISTICS
261 /*
262 * Find/alloc statistics nodes.
263 */
264 pHeap->Stat.cAllocations++;
265 PMMUKHEAPSTAT pStat = (PMMUKHEAPSTAT)RTAvlULGet(&pHeap->pStatTree, (AVLULKEY)enmTag);
266 if (pStat)
267 pStat->cAllocations++;
268 else
269 {
270 pStat = (PMMUKHEAPSTAT)MMR3HeapAllocZU(pHeap->pUVM, MM_TAG_MM, sizeof(MMUKHEAPSTAT));
271 if (!pStat)
272 {
273 pHeap->Stat.cFailures++;
274 AssertMsgFailed(("Failed to allocate heap stat record.\n"));
275 RTCritSectLeave(&pHeap->Lock);
276 return NULL;
277 }
278 pStat->Core.Key = (AVLULKEY)enmTag;
279 RTAvlULInsert(&pHeap->pStatTree, &pStat->Core);
280
281 pStat->cAllocations++;
282
283 /* register the statistics */
284 PUVM pUVM = pHeap->pUVM;
285 const char *pszTag = mmR3GetTagName(enmTag);
286 STAMR3RegisterFU(pUVM, &pStat->cbCurAllocated, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes currently allocated.", "/MM/UkHeap/%s", pszTag);
287 STAMR3RegisterFU(pUVM, &pStat->cAllocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Number or MMR3UkHeapAlloc() calls.", "/MM/UkHeap/%s/cAllocations", pszTag);
288 STAMR3RegisterFU(pUVM, &pStat->cReallocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Number of MMR3UkHeapRealloc() calls.", "/MM/UkHeap/%s/cReallocations", pszTag);
289 STAMR3RegisterFU(pUVM, &pStat->cFrees, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Number of MMR3UkHeapFree() calls.", "/MM/UkHeap/%s/cFrees", pszTag);
290 STAMR3RegisterFU(pUVM, &pStat->cFailures, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failures.", "/MM/UkHeap/%s/cFailures", pszTag);
291 STAMR3RegisterFU(pUVM, &pStat->cbAllocated, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total number of bytes allocated.", "/MM/UkHeap/%s/cbAllocated", pszTag);
292 STAMR3RegisterFU(pUVM, &pStat->cbFreed, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total number of bytes freed.", "/MM/UkHeap/%s/cbFreed", pszTag);
293 }
294#endif
295
296 /*
297 * Validate input.
298 */
299 if (cb == 0)
300 {
301#ifdef MMUKHEAP_WITH_STATISTICS
302 pStat->cFailures++;
303 pHeap->Stat.cFailures++;
304#endif
305 RTCritSectLeave(&pHeap->Lock);
306 return NULL;
307 }
308
309 /*
310 * Allocate heap block.
311 */
312 cb = RT_ALIGN_Z(cb, MMUKHEAP_SIZE_ALIGNMENT);
313 void *pv = NULL;
314 PMMUKHEAPSUB pSubHeapPrev = NULL;
315 PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
316 while (pSubHeap)
317 {
318 if (fZero)
319 pv = RTHeapSimpleAllocZ(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
320 else
321 pv = RTHeapSimpleAlloc(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
322 if (pv)
323 {
324 /* Move the sub-heap with free memory to the head. */
325 if (pSubHeapPrev)
326 {
327 pSubHeapPrev->pNext = pSubHeap->pNext;
328 pSubHeap->pNext = pHeap->pSubHeapHead;
329 pHeap->pSubHeapHead = pSubHeap;
330 }
331 break;
332 }
333 pSubHeapPrev = pSubHeap;
334 pSubHeap = pSubHeap->pNext;
335 }
336 if (RT_UNLIKELY(!pv))
337 {
338 /*
339 * Add another sub-heap.
340 */
341 pSubHeap = mmR3UkHeapAddSubHeap(pHeap, RT_MAX(RT_ALIGN_Z(cb, PAGE_SIZE) + PAGE_SIZE * 16, _256K));
342 if (pSubHeap)
343 {
344 if (fZero)
345 pv = RTHeapSimpleAllocZ(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
346 else
347 pv = RTHeapSimpleAlloc(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
348 }
349 if (RT_UNLIKELY(!pv))
350 {
351 AssertMsgFailed(("Failed to allocate heap block %d, enmTag=%x(%.4s).\n", cb, enmTag, &enmTag));
352#ifdef MMUKHEAP_WITH_STATISTICS
353 pStat->cFailures++;
354 pHeap->Stat.cFailures++;
355#endif
356 RTCritSectLeave(&pHeap->Lock);
357 return NULL;
358 }
359 }
360
361 /*
362 * Update statistics
363 */
364#ifdef MMUKHEAP_WITH_STATISTICS
365 size_t cbActual = RTHeapSimpleSize(pSubHeap->hSimple, pv);
366 pStat->cbAllocated += cbActual;
367 pStat->cbCurAllocated += cbActual;
368 pHeap->Stat.cbAllocated += cbActual;
369 pHeap->Stat.cbCurAllocated += cbActual;
370#endif
371
372 if (pR0Ptr)
373 *pR0Ptr = (uintptr_t)pv - (uintptr_t)pSubHeap->pv + pSubHeap->pvR0;
374 RTCritSectLeave(&pHeap->Lock);
375 return pv;
376}
377
378
379/**
380 * Releases memory allocated with MMR3UkHeapAlloc() and MMR3UkHeapAllocZ()
381 *
382 * @param pVM The VM handle.
383 * @param pv Pointer to the memory block to free.
384 */
385VMMR3DECL(void) MMR3UkHeapFree(PVM pVM, void *pv, MMTAG enmTag)
386{
387 /* Ignore NULL pointers. */
388 if (!pv)
389 return;
390
391 PMMUKHEAP pHeap = pVM->pUVM->mm.s.pUkHeap;
392 RTCritSectEnter(&pHeap->Lock);
393
394 /*
395 * Find the sub-heap and block
396 */
397#ifdef MMUKHEAP_WITH_STATISTICS
398 size_t cbActual = 0;
399#endif
400 PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
401 while (pSubHeap)
402 {
403 if ((uintptr_t)pv - (uintptr_t)pSubHeap->pv < pSubHeap->cb)
404 {
405#ifdef MMUKHEAP_WITH_STATISTICS
406 cbActual = RTHeapSimpleSize(pSubHeap->hSimple, pv);
407 PMMUKHEAPSTAT pStat = (PMMUKHEAPSTAT)RTAvlULGet(&pHeap->pStatTree, (AVLULKEY)enmTag);
408 if (pStat)
409 {
410 pStat->cFrees++;
411 pStat->cbCurAllocated -= cbActual;
412 pStat->cbFreed += cbActual;
413 }
414 pHeap->Stat.cFrees++;
415 pHeap->Stat.cbFreed += cbActual;
416 pHeap->Stat.cbCurAllocated -= cbActual;
417#endif
418 RTHeapSimpleFree(pSubHeap->hSimple, pv);
419
420 RTCritSectLeave(&pHeap->Lock);
421 return;
422 }
423 }
424 AssertMsgFailed(("pv=%p\n", pv));
425}
426
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