1
/* $Id: MMUkHeap.cpp $ */
3
* MM - Memory Manager - Ring-3 Heap with kernel accessible mapping.
7
* Copyright (C) 2006-2009 Oracle Corporation
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.
19
/*******************************************************************************
21
*******************************************************************************/
22
#define LOG_GROUP LOG_GROUP_MM_HEAP
24
#include <VBox/stam.h>
25
#include "MMInternal.h"
29
#include <VBox/param.h>
32
#include <iprt/assert.h>
33
#include <iprt/string.h>
34
#include <iprt/heap.h>
37
/*******************************************************************************
38
* Internal Functions *
39
*******************************************************************************/
40
static void *mmR3UkHeapAlloc(PMMUKHEAP pHeap, MMTAG enmTag, size_t cb, bool fZero, PRTR0PTR pR0Ptr);
45
* Create a User-kernel heap.
47
* This does not require SUPLib to be initialized as we'll lazily allocate the
48
* kernel accessible memory on the first alloc call.
50
* @returns VBox status.
51
* @param pVM The handle to the VM the heap should be associated with.
52
* @param ppHeap Where to store the heap pointer.
54
int mmR3UkHeapCreateU(PUVM pUVM, PMMUKHEAP *ppHeap)
56
PMMUKHEAP pHeap = (PMMUKHEAP)MMR3HeapAllocZU(pUVM, MM_TAG_MM, sizeof(MMUKHEAP));
59
int rc = RTCritSectInit(&pHeap->Lock);
63
* Initialize the global stat record.
66
#ifdef MMUKHEAP_WITH_STATISTICS
67
PMMUKHEAPSTAT pStat = &pHeap->Stat;
68
STAMR3RegisterU(pUVM, &pStat->cAllocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cAllocations", STAMUNIT_CALLS, "Number or MMR3UkHeapAlloc() calls.");
69
STAMR3RegisterU(pUVM, &pStat->cReallocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cReallocations", STAMUNIT_CALLS, "Number of MMR3UkHeapRealloc() calls.");
70
STAMR3RegisterU(pUVM, &pStat->cFrees, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cFrees", STAMUNIT_CALLS, "Number of MMR3UkHeapFree() calls.");
71
STAMR3RegisterU(pUVM, &pStat->cFailures, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cFailures", STAMUNIT_COUNT, "Number of failures.");
72
STAMR3RegisterU(pUVM, &pStat->cbCurAllocated, sizeof(pStat->cbCurAllocated) == sizeof(uint32_t) ? STAMTYPE_U32 : STAMTYPE_U64,
73
STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cbCurAllocated", STAMUNIT_BYTES, "Number of bytes currently allocated.");
74
STAMR3RegisterU(pUVM, &pStat->cbAllocated, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cbAllocated", STAMUNIT_BYTES, "Total number of bytes allocated.");
75
STAMR3RegisterU(pUVM, &pStat->cbFreed, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, "/MM/UkHeap/cbFreed", STAMUNIT_BYTES, "Total number of bytes freed.");
83
AssertMsgFailed(("failed to allocate heap structure\n"));
84
return VERR_NO_MEMORY;
89
* Destroy a User-kernel heap.
91
* @param pHeap Heap handle.
93
void mmR3UkHeapDestroy(PMMUKHEAP pHeap)
96
* Start by deleting the lock, that'll trap anyone
97
* attempting to use the heap.
99
RTCritSectDelete(&pHeap->Lock);
102
* Walk the sub-heaps and free them.
104
while (pHeap->pSubHeapHead)
106
PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
107
pHeap->pSubHeapHead = pSubHeap->pNext;
108
SUPR3PageFreeEx(pSubHeap->pv, pSubHeap->cb >> PAGE_SHIFT);
109
//MMR3HeapFree(pSubHeap); - rely on the automatic cleanup.
111
//MMR3HeapFree(pHeap->stats);
112
//MMR3HeapFree(pHeap);
117
* Allocate memory associating it with the VM for collective cleanup.
119
* The memory will be allocated from the default heap but a header
120
* is added in which we keep track of which VM it belongs to and chain
121
* all the allocations together so they can be freed in one go.
123
* This interface is typically used for memory block which will not be
124
* freed during the life of the VM.
126
* @returns Pointer to allocated memory.
127
* @param pVM VM handle.
128
* @param enmTag Statistics tag. Statistics are collected on a per tag
129
* basis in addition to a global one. Thus we can easily
130
* identify how memory is used by the VM.
131
* @param cbSize Size of the block.
132
* @param pR0Ptr Where to return the ring-0 address of the memory.
134
VMMR3DECL(void *) MMR3UkHeapAlloc(PVM pVM, MMTAG enmTag, size_t cbSize, PRTR0PTR pR0Ptr)
136
return mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, false, pR0Ptr);
141
* Same as MMR3UkHeapAlloc().
143
* @returns Pointer to allocated memory.
144
* @param pVM VM handle.
145
* @param enmTag Statistics tag. Statistics are collected on a per tag
146
* basis in addition to a global one. Thus we can easily
147
* identify how memory is used by the VM.
148
* @param cbSize Size of the block.
149
* @param ppv Where to store the pointer to the allocated memory on success.
150
* @param pR0Ptr Where to return the ring-0 address of the memory.
152
VMMR3DECL(int) MMR3UkHeapAllocEx(PVM pVM, MMTAG enmTag, size_t cbSize, void **ppv, PRTR0PTR pR0Ptr)
154
void *pv = mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, false, pR0Ptr);
160
return VERR_NO_MEMORY;
165
* Same as MMR3UkHeapAlloc() only the memory is zeroed.
167
* @returns Pointer to allocated memory.
168
* @param pVM VM handle.
169
* @param enmTag Statistics tag. Statistics are collected on a per tag
170
* basis in addition to a global one. Thus we can easily
171
* identify how memory is used by the VM.
172
* @param cbSize Size of the block.
173
* @param pR0Ptr Where to return the ring-0 address of the memory.
175
VMMR3DECL(void *) MMR3UkHeapAllocZ(PVM pVM, MMTAG enmTag, size_t cbSize, PRTR0PTR pR0Ptr)
177
return mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, true, pR0Ptr);
182
* Same as MMR3UkHeapAllocZ().
184
* @returns Pointer to allocated memory.
185
* @param pVM VM handle.
186
* @param enmTag Statistics tag. Statistics are collected on a per tag
187
* basis in addition to a global one. Thus we can easily
188
* identify how memory is used by the VM.
189
* @param cbSize Size of the block.
190
* @param ppv Where to store the pointer to the allocated memory on success.
191
* @param pR0Ptr Where to return the ring-0 address of the memory.
193
VMMR3DECL(int) MMR3UkHeapAllocZEx(PVM pVM, MMTAG enmTag, size_t cbSize, void **ppv, PRTR0PTR pR0Ptr)
195
void *pv = mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, true, pR0Ptr);
201
return VERR_NO_MEMORY;
206
* Worker for mmR3UkHeapAlloc that creates and adds a new sub-heap.
208
* @returns Pointer to the new sub-heap.
209
* @param pHeap The heap
210
* @param cbSubHeap The size of the sub-heap.
212
static PMMUKHEAPSUB mmR3UkHeapAddSubHeap(PMMUKHEAP pHeap, size_t cbSubHeap)
214
PMMUKHEAPSUB pSubHeap = (PMMUKHEAPSUB)MMR3HeapAllocU(pHeap->pUVM, MM_TAG_MM/*_UK_HEAP*/, sizeof(*pSubHeap));
217
pSubHeap->cb = cbSubHeap;
218
int rc = SUPR3PageAllocEx(pSubHeap->cb >> PAGE_SHIFT, 0, &pSubHeap->pv, &pSubHeap->pvR0, NULL);
221
rc = RTHeapSimpleInit(&pSubHeap->hSimple, pSubHeap->pv, pSubHeap->cb);
224
pSubHeap->pNext = pHeap->pSubHeapHead;
225
pHeap->pSubHeapHead = pSubHeap;
230
SUPR3PageFreeEx(pSubHeap->pv, pSubHeap->cb >> PAGE_SHIFT);
232
MMR3HeapFree(pSubHeap);
239
* Allocate memory from the heap.
241
* @returns Pointer to allocated memory.
242
* @param pHeap Heap handle.
243
* @param enmTag Statistics tag. Statistics are collected on a per tag
244
* basis in addition to a global one. Thus we can easily
245
* identify how memory is used by the VM.
246
* @param cb Size of the block.
247
* @param fZero Whether or not to zero the memory block.
248
* @param pR0Ptr Where to return the ring-0 pointer.
250
static void *mmR3UkHeapAlloc(PMMUKHEAP pHeap, MMTAG enmTag, size_t cb, bool fZero, PRTR0PTR pR0Ptr)
253
*pR0Ptr = NIL_RTR0PTR;
254
RTCritSectEnter(&pHeap->Lock);
256
#ifdef MMUKHEAP_WITH_STATISTICS
258
* Find/alloc statistics nodes.
260
pHeap->Stat.cAllocations++;
261
PMMUKHEAPSTAT pStat = (PMMUKHEAPSTAT)RTAvlULGet(&pHeap->pStatTree, (AVLULKEY)enmTag);
263
pStat->cAllocations++;
266
pStat = (PMMUKHEAPSTAT)MMR3HeapAllocZU(pHeap->pUVM, MM_TAG_MM, sizeof(MMUKHEAPSTAT));
269
pHeap->Stat.cFailures++;
270
AssertMsgFailed(("Failed to allocate heap stat record.\n"));
271
RTCritSectLeave(&pHeap->Lock);
274
pStat->Core.Key = (AVLULKEY)enmTag;
275
RTAvlULInsert(&pHeap->pStatTree, &pStat->Core);
277
pStat->cAllocations++;
279
/* register the statistics */
280
PUVM pUVM = pHeap->pUVM;
281
const char *pszTag = mmGetTagName(enmTag);
282
STAMR3RegisterFU(pUVM, &pStat->cbCurAllocated, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of bytes currently allocated.", "/MM/UkHeap/%s", pszTag);
283
STAMR3RegisterFU(pUVM, &pStat->cAllocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Number or MMR3UkHeapAlloc() calls.", "/MM/UkHeap/%s/cAllocations", pszTag);
284
STAMR3RegisterFU(pUVM, &pStat->cReallocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Number of MMR3UkHeapRealloc() calls.", "/MM/UkHeap/%s/cReallocations", pszTag);
285
STAMR3RegisterFU(pUVM, &pStat->cFrees, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_CALLS, "Number of MMR3UkHeapFree() calls.", "/MM/UkHeap/%s/cFrees", pszTag);
286
STAMR3RegisterFU(pUVM, &pStat->cFailures, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "Number of failures.", "/MM/UkHeap/%s/cFailures", pszTag);
287
STAMR3RegisterFU(pUVM, &pStat->cbAllocated, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total number of bytes allocated.", "/MM/UkHeap/%s/cbAllocated", pszTag);
288
STAMR3RegisterFU(pUVM, &pStat->cbFreed, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Total number of bytes freed.", "/MM/UkHeap/%s/cbFreed", pszTag);
297
#ifdef MMUKHEAP_WITH_STATISTICS
299
pHeap->Stat.cFailures++;
301
RTCritSectLeave(&pHeap->Lock);
306
* Allocate heap block.
308
cb = RT_ALIGN_Z(cb, MMUKHEAP_SIZE_ALIGNMENT);
310
PMMUKHEAPSUB pSubHeapPrev = NULL;
311
PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
315
pv = RTHeapSimpleAllocZ(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
317
pv = RTHeapSimpleAlloc(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
320
/* Move the sub-heap with free memory to the head. */
323
pSubHeapPrev->pNext = pSubHeap->pNext;
324
pSubHeap->pNext = pHeap->pSubHeapHead;
325
pHeap->pSubHeapHead = pSubHeap;
329
pSubHeapPrev = pSubHeap;
330
pSubHeap = pSubHeap->pNext;
332
if (RT_UNLIKELY(!pv))
335
* Add another sub-heap.
337
pSubHeap = mmR3UkHeapAddSubHeap(pHeap, RT_MAX(RT_ALIGN_Z(cb, PAGE_SIZE) + PAGE_SIZE * 16, _256K));
341
pv = RTHeapSimpleAllocZ(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
343
pv = RTHeapSimpleAlloc(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
345
if (RT_UNLIKELY(!pv))
347
AssertMsgFailed(("Failed to allocate heap block %d, enmTag=%x(%.4s).\n", cb, enmTag, &enmTag));
348
#ifdef MMUKHEAP_WITH_STATISTICS
350
pHeap->Stat.cFailures++;
352
RTCritSectLeave(&pHeap->Lock);
360
#ifdef MMUKHEAP_WITH_STATISTICS
361
size_t cbActual = RTHeapSimpleSize(pSubHeap->hSimple, pv);
362
pStat->cbAllocated += cbActual;
363
pStat->cbCurAllocated += cbActual;
364
pHeap->Stat.cbAllocated += cbActual;
365
pHeap->Stat.cbCurAllocated += cbActual;
369
*pR0Ptr = (uintptr_t)pv - (uintptr_t)pSubHeap->pv + pSubHeap->pvR0;
370
RTCritSectLeave(&pHeap->Lock);
376
* Releases memory allocated with MMR3UkHeapAlloc() and MMR3UkHeapAllocZ()
378
* @param pVM The VM handle.
379
* @param pv Pointer to the memory block to free.
381
VMMR3DECL(void) MMR3UkHeapFree(PVM pVM, void *pv, MMTAG enmTag)
383
/* Ignore NULL pointers. */
387
PMMUKHEAP pHeap = pVM->pUVM->mm.s.pUkHeap;
388
RTCritSectEnter(&pHeap->Lock);
391
* Find the sub-heap and block
393
#ifdef MMUKHEAP_WITH_STATISTICS
396
PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
399
if ((uintptr_t)pv - (uintptr_t)pSubHeap->pv < pSubHeap->cb)
401
#ifdef MMUKHEAP_WITH_STATISTICS
402
cbActual = RTHeapSimpleSize(pSubHeap->hSimple, pv);
403
PMMUKHEAPSTAT pStat = (PMMUKHEAPSTAT)RTAvlULGet(&pHeap->pStatTree, (AVLULKEY)enmTag);
407
pStat->cbCurAllocated -= cbActual;
408
pStat->cbFreed += cbActual;
410
pHeap->Stat.cFrees++;
411
pHeap->Stat.cbFreed += cbActual;
412
pHeap->Stat.cbCurAllocated -= cbActual;
414
RTHeapSimpleFree(pSubHeap->hSimple, pv);
416
RTCritSectLeave(&pHeap->Lock);
420
AssertMsgFailed(("pv=%p\n", pv));