VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 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
Line 
1/* $Id: MMUkHeap.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * MM - Memory Manager - Ring-3 Heap with kernel accessible mapping.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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 <iprt/errcore.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 code.
51 * @param pUVM Pointer to the user mode VM structure.
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 The cross context VM structure.
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 The cross context VM structure.
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 The cross context VM structure.
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 The cross context VM structure.
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#else
291 RT_NOREF_PV(enmTag);
292#endif
293
294 /*
295 * Validate input.
296 */
297 if (cb == 0)
298 {
299#ifdef MMUKHEAP_WITH_STATISTICS
300 pStat->cFailures++;
301 pHeap->Stat.cFailures++;
302#endif
303 RTCritSectLeave(&pHeap->Lock);
304 return NULL;
305 }
306
307 /*
308 * Allocate heap block.
309 */
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)
315 {
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++;
353#endif
354 RTCritSectLeave(&pHeap->Lock);
355 return NULL;
356 }
357 }
358
359 /*
360 * Update statistics
361 */
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;
368#endif
369
370 if (pR0Ptr)
371 *pR0Ptr = (uintptr_t)pv - (uintptr_t)pSubHeap->pv + pSubHeap->pvR0;
372 RTCritSectLeave(&pHeap->Lock);
373 return pv;
374}
375
376
377/**
378 * Releases memory allocated with MMR3UkHeapAlloc() and MMR3UkHeapAllocZ()
379 *
380 * @param pVM The cross context VM structure.
381 * @param pv Pointer to the memory block to free.
382 * @param enmTag The allocation accounting tag.
383 */
384VMMR3DECL(void) MMR3UkHeapFree(PVM pVM, void *pv, MMTAG enmTag)
385{
386 /* Ignore NULL pointers. */
387 if (!pv)
388 return;
389
390 PMMUKHEAP pHeap = pVM->pUVM->mm.s.pUkHeap;
391 RTCritSectEnter(&pHeap->Lock);
392
393 /*
394 * Find the sub-heap and block
395 */
396#ifdef MMUKHEAP_WITH_STATISTICS
397 size_t cbActual = 0;
398#endif
399 PMMUKHEAPSUB pSubHeap = pHeap->pSubHeapHead;
400 while (pSubHeap)
401 {
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;
416#else
417 RT_NOREF_PV(enmTag);
418#endif
419 RTHeapSimpleFree(pSubHeap->hSimple, pv);
420
421 RTCritSectLeave(&pHeap->Lock);
422 return;
423 }
424 }
425 AssertMsgFailed(("pv=%p\n", pv));
426}
427
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