VirtualBox

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

Last change on this file since 43858 was 41800, checked in by vboxsync, 13 years ago

Doxygen.

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