VirtualBox

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

Last change on this file since 84044 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.6 KB
RevLine 
[23]1/* $Id: MMUkHeap.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
[1]2/** @file
[18792]3 * MM - Memory Manager - Ring-3 Heap with kernel accessible mapping.
[1]4 */
5
6/*
[82968]7 * Copyright (C) 2006-2020 Oracle Corporation
[1]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
[5999]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.
[1]16 */
17
18
[57358]19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
[1]22#define LOG_GROUP LOG_GROUP_MM_HEAP
[35346]23#include <VBox/vmm/mm.h>
24#include <VBox/vmm/stam.h>
[1]25#include "MMInternal.h"
[35346]26#include <VBox/vmm/vm.h>
27#include <VBox/vmm/uvm.h>
[76454]28#include <iprt/errcore.h>
[1]29#include <VBox/param.h>
30#include <VBox/log.h>
31
32#include <iprt/assert.h>
33#include <iprt/string.h>
[18792]34#include <iprt/heap.h>
[1]35
36
[57358]37/*********************************************************************************************************************************
38* Internal Functions *
39*********************************************************************************************************************************/
[18792]40static void *mmR3UkHeapAlloc(PMMUKHEAP pHeap, MMTAG enmTag, size_t cb, bool fZero, PRTR0PTR pR0Ptr);
[1]41
42
43
44/**
[18792]45 * Create a User-kernel heap.
[1]46 *
[18792]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 *
[58170]50 * @returns VBox status code.
[58116]51 * @param pUVM Pointer to the user mode VM structure.
[1]52 * @param ppHeap Where to store the heap pointer.
53 */
[18792]54int mmR3UkHeapCreateU(PUVM pUVM, PMMUKHEAP *ppHeap)
[1]55{
[18792]56 PMMUKHEAP pHeap = (PMMUKHEAP)MMR3HeapAllocZU(pUVM, MM_TAG_MM, sizeof(MMUKHEAP));
[1]57 if (pHeap)
58 {
59 int rc = RTCritSectInit(&pHeap->Lock);
[13816]60 if (RT_SUCCESS(rc))
[1]61 {
62 /*
63 * Initialize the global stat record.
64 */
[6796]65 pHeap->pUVM = pUVM;
[18792]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.");
[6796]72 STAMR3RegisterU(pUVM, &pStat->cbCurAllocated, sizeof(pStat->cbCurAllocated) == sizeof(uint32_t) ? STAMTYPE_U32 : STAMTYPE_U64,
[18792]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.");
[1]76#endif
77 *ppHeap = pHeap;
78 return VINF_SUCCESS;
79 }
80 AssertRC(rc);
[18792]81 MMR3HeapFree(pHeap);
[1]82 }
83 AssertMsgFailed(("failed to allocate heap structure\n"));
84 return VERR_NO_MEMORY;
85}
86
87
88/**
[18792]89 * Destroy a User-kernel heap.
[1]90 *
91 * @param pHeap Heap handle.
92 */
[18792]93void mmR3UkHeapDestroy(PMMUKHEAP pHeap)
[1]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 /*
[18792]102 * Walk the sub-heaps and free them.
[1]103 */
[18792]104 while (pHeap->pSubHeapHead)
[1]105 {
[18792]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.
[1]110 }
[18792]111 //MMR3HeapFree(pHeap->stats);
112 //MMR3HeapFree(pHeap);
[1]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
[6796]121 * all the allocations together so they can be freed in one go.
[1]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.
[58122]127 * @param pVM The cross context VM structure.
[6796]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.
[18792]132 * @param pR0Ptr Where to return the ring-0 address of the memory.
[6796]133 */
[18792]134VMMR3DECL(void *) MMR3UkHeapAlloc(PVM pVM, MMTAG enmTag, size_t cbSize, PRTR0PTR pR0Ptr)
[6796]135{
[18792]136 return mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, false, pR0Ptr);
[6796]137}
138
139
140/**
[18792]141 * Same as MMR3UkHeapAlloc().
[6796]142 *
143 * @returns Pointer to allocated memory.
[58122]144 * @param pVM The cross context VM structure.
[1]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.
[6796]149 * @param ppv Where to store the pointer to the allocated memory on success.
[18792]150 * @param pR0Ptr Where to return the ring-0 address of the memory.
[6796]151 */
[18792]152VMMR3DECL(int) MMR3UkHeapAllocEx(PVM pVM, MMTAG enmTag, size_t cbSize, void **ppv, PRTR0PTR pR0Ptr)
[6796]153{
[18792]154 void *pv = mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, false, pR0Ptr);
[6796]155 if (pv)
[1]156 {
[6796]157 *ppv = pv;
158 return VINF_SUCCESS;
[1]159 }
[6796]160 return VERR_NO_MEMORY;
[1]161}
162
163
164/**
[18792]165 * Same as MMR3UkHeapAlloc() only the memory is zeroed.
[1]166 *
167 * @returns Pointer to allocated memory.
[58122]168 * @param pVM The cross context VM structure.
[1]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.
[18792]173 * @param pR0Ptr Where to return the ring-0 address of the memory.
[1]174 */
[18792]175VMMR3DECL(void *) MMR3UkHeapAllocZ(PVM pVM, MMTAG enmTag, size_t cbSize, PRTR0PTR pR0Ptr)
[1]176{
[18792]177 return mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, true, pR0Ptr);
[1]178}
179
180
181/**
[18792]182 * Same as MMR3UkHeapAllocZ().
[1]183 *
[6796]184 * @returns Pointer to allocated memory.
[58122]185 * @param pVM The cross context VM structure.
[1]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.
[6796]190 * @param ppv Where to store the pointer to the allocated memory on success.
[18792]191 * @param pR0Ptr Where to return the ring-0 address of the memory.
[6796]192 */
[18792]193VMMR3DECL(int) MMR3UkHeapAllocZEx(PVM pVM, MMTAG enmTag, size_t cbSize, void **ppv, PRTR0PTR pR0Ptr)
[6796]194{
[18792]195 void *pv = mmR3UkHeapAlloc(pVM->pUVM->mm.s.pUkHeap, enmTag, cbSize, true, pR0Ptr);
[6796]196 if (pv)
[1]197 {
[6796]198 *ppv = pv;
199 return VINF_SUCCESS;
[1]200 }
[6796]201 return VERR_NO_MEMORY;
[1]202}
203
204
[18792]205/***
206 * Worker for mmR3UkHeapAlloc that creates and adds a new sub-heap.
[1]207 *
[18792]208 * @returns Pointer to the new sub-heap.
209 * @param pHeap The heap
210 * @param cbSubHeap The size of the sub-heap.
[1]211 */
[18792]212static PMMUKHEAPSUB mmR3UkHeapAddSubHeap(PMMUKHEAP pHeap, size_t cbSubHeap)
[1]213{
[18792]214 PMMUKHEAPSUB pSubHeap = (PMMUKHEAPSUB)MMR3HeapAllocU(pHeap->pUVM, MM_TAG_MM/*_UK_HEAP*/, sizeof(*pSubHeap));
215 if (pSubHeap)
[1]216 {
[18792]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);
[1]233 }
[18792]234 return NULL;
[1]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.
[18792]246 * @param cb Size of the block.
[1]247 * @param fZero Whether or not to zero the memory block.
[18792]248 * @param pR0Ptr Where to return the ring-0 pointer.
[1]249 */
[18792]250static void *mmR3UkHeapAlloc(PMMUKHEAP pHeap, MMTAG enmTag, size_t cb, bool fZero, PRTR0PTR pR0Ptr)
[1]251{
[18792]252 if (pR0Ptr)
253 *pR0Ptr = NIL_RTR0PTR;
[1]254 RTCritSectEnter(&pHeap->Lock);
255
[18792]256#ifdef MMUKHEAP_WITH_STATISTICS
[1]257 /*
258 * Find/alloc statistics nodes.
259 */
260 pHeap->Stat.cAllocations++;
[18792]261 PMMUKHEAPSTAT pStat = (PMMUKHEAPSTAT)RTAvlULGet(&pHeap->pStatTree, (AVLULKEY)enmTag);
[1]262 if (pStat)
263 pStat->cAllocations++;
264 else
265 {
[18792]266 pStat = (PMMUKHEAPSTAT)MMR3HeapAllocZU(pHeap->pUVM, MM_TAG_MM, sizeof(MMUKHEAPSTAT));
[1]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 */
[6796]280 PUVM pUVM = pHeap->pUVM;
[20774]281 const char *pszTag = mmGetTagName(enmTag);
[18792]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);
[1]289 }
[62643]290#else
291 RT_NOREF_PV(enmTag);
[1]292#endif
293
294 /*
295 * Validate input.
296 */
[18792]297 if (cb == 0)
[1]298 {
[18792]299#ifdef MMUKHEAP_WITH_STATISTICS
[1]300 pStat->cFailures++;
301 pHeap->Stat.cFailures++;
[18792]302#endif
[1]303 RTCritSectLeave(&pHeap->Lock);
304 return NULL;
305 }
306
307 /*
308 * Allocate heap block.
309 */
[18792]310 cb = RT_ALIGN_Z(cb, MMUKHEAP_SIZE_ALIGNMENT);
311 void *pv = NULL;
312 PMMUKHEAPSUB pSubHeapPrev = NULL;
313 PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
314 while (pSubHeap)
[1]315 {
[18792]316 if (fZero)
317 pv = RTHeapSimpleAllocZ(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
318 else
319 pv = RTHeapSimpleAlloc(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
320 if (pv)
321 {
322 /* Move the sub-heap with free memory to the head. */
323 if (pSubHeapPrev)
324 {
325 pSubHeapPrev->pNext = pSubHeap->pNext;
326 pSubHeap->pNext = pHeap->pSubHeapHead;
327 pHeap->pSubHeapHead = pSubHeap;
328 }
329 break;
330 }
331 pSubHeapPrev = pSubHeap;
332 pSubHeap = pSubHeap->pNext;
333 }
334 if (RT_UNLIKELY(!pv))
335 {
336 /*
337 * Add another sub-heap.
338 */
339 pSubHeap = mmR3UkHeapAddSubHeap(pHeap, RT_MAX(RT_ALIGN_Z(cb, PAGE_SIZE) + PAGE_SIZE * 16, _256K));
340 if (pSubHeap)
341 {
342 if (fZero)
343 pv = RTHeapSimpleAllocZ(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
344 else
345 pv = RTHeapSimpleAlloc(pSubHeap->hSimple, cb, MMUKHEAP_SIZE_ALIGNMENT);
346 }
347 if (RT_UNLIKELY(!pv))
348 {
349 AssertMsgFailed(("Failed to allocate heap block %d, enmTag=%x(%.4s).\n", cb, enmTag, &enmTag));
350#ifdef MMUKHEAP_WITH_STATISTICS
351 pStat->cFailures++;
352 pHeap->Stat.cFailures++;
[1]353#endif
[18792]354 RTCritSectLeave(&pHeap->Lock);
355 return NULL;
356 }
[1]357 }
358
359 /*
360 * Update statistics
361 */
[18792]362#ifdef MMUKHEAP_WITH_STATISTICS
363 size_t cbActual = RTHeapSimpleSize(pSubHeap->hSimple, pv);
364 pStat->cbAllocated += cbActual;
365 pStat->cbCurAllocated += cbActual;
366 pHeap->Stat.cbAllocated += cbActual;
367 pHeap->Stat.cbCurAllocated += cbActual;
[1]368#endif
369
[18792]370 if (pR0Ptr)
371 *pR0Ptr = (uintptr_t)pv - (uintptr_t)pSubHeap->pv + pSubHeap->pvR0;
[1]372 RTCritSectLeave(&pHeap->Lock);
[18792]373 return pv;
[1]374}
375
376
377/**
[18792]378 * Releases memory allocated with MMR3UkHeapAlloc() and MMR3UkHeapAllocZ()
[1]379 *
[58122]380 * @param pVM The cross context VM structure.
[1]381 * @param pv Pointer to the memory block to free.
[58126]382 * @param enmTag The allocation accounting tag.
[1]383 */
[18792]384VMMR3DECL(void) MMR3UkHeapFree(PVM pVM, void *pv, MMTAG enmTag)
[1]385{
386 /* Ignore NULL pointers. */
387 if (!pv)
388 return;
389
[18792]390 PMMUKHEAP pHeap = pVM->pUVM->mm.s.pUkHeap;
391 RTCritSectEnter(&pHeap->Lock);
[1]392
393 /*
[18792]394 * Find the sub-heap and block
[1]395 */
[18792]396#ifdef MMUKHEAP_WITH_STATISTICS
397 size_t cbActual = 0;
[1]398#endif
[18792]399 PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
400 while (pSubHeap)
[1]401 {
[18792]402 if ((uintptr_t)pv - (uintptr_t)pSubHeap->pv < pSubHeap->cb)
403 {
404#ifdef MMUKHEAP_WITH_STATISTICS
405 cbActual = RTHeapSimpleSize(pSubHeap->hSimple, pv);
406 PMMUKHEAPSTAT pStat = (PMMUKHEAPSTAT)RTAvlULGet(&pHeap->pStatTree, (AVLULKEY)enmTag);
407 if (pStat)
408 {
409 pStat->cFrees++;
410 pStat->cbCurAllocated -= cbActual;
411 pStat->cbFreed += cbActual;
412 }
413 pHeap->Stat.cFrees++;
414 pHeap->Stat.cbFreed += cbActual;
415 pHeap->Stat.cbCurAllocated -= cbActual;
[62643]416#else
417 RT_NOREF_PV(enmTag);
[18792]418#endif
419 RTHeapSimpleFree(pSubHeap->hSimple, pv);
[1]420
[18792]421 RTCritSectLeave(&pHeap->Lock);
422 return;
[1]423 }
424 }
[18792]425 AssertMsgFailed(("pv=%p\n", pv));
[1]426}
427
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use