VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/MMAllHyper.cpp@ 4403

Last change on this file since 4403 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 43.2 KB
Line 
1/* $Id: MMAllHyper.cpp 4071 2007-08-07 17:07:59Z vboxsync $ */
2/** @file
3 * MM - Memory Monitor(/Manager) - Hypervisor Memory Area, All Contexts.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_MM_HYPER_HEAP
23#include <VBox/mm.h>
24#include <VBox/stam.h>
25#include "MMInternal.h"
26#include <VBox/vm.h>
27
28#include <VBox/err.h>
29#include <VBox/param.h>
30#include <iprt/assert.h>
31#include <VBox/log.h>
32#include <iprt/asm.h>
33#include <iprt/string.h>
34
35
36/*******************************************************************************
37* Defined Constants And Macros *
38*******************************************************************************/
39#ifdef DEBUG
40# define MMHYPER_HEAP_STRICT 1
41#endif
42
43#define ASSERT_L(u1, u2) AssertMsg((u1) < (u2), ("u1=%#x u2=%#x\n", u1, u2))
44#define ASSERT_LE(u1, u2) AssertMsg((u1) <= (u2), ("u1=%#x u2=%#x\n", u1, u2))
45#define ASSERT_GE(u1, u2) AssertMsg((u1) >= (u2), ("u1=%#x u2=%#x\n", u1, u2))
46#define ASSERT_ALIGN(u1) AssertMsg(!((u1) & (MMHYPER_HEAP_ALIGN_MIN - 1)), ("u1=%#x (%d)\n", u1, u1))
47
48#define ASSERT_OFFPREV(pHeap, pChunk) \
49 do { Assert(MMHYPERCHUNK_GET_OFFPREV(pChunk) <= 0); \
50 Assert(MMHYPERCHUNK_GET_OFFPREV(pChunk) >= (intptr_t)CTXSUFF((pHeap)->pbHeap) - (intptr_t)(pChunk)); \
51 AssertMsg( MMHYPERCHUNK_GET_OFFPREV(pChunk) != 0 \
52 || (uint8_t *)(pChunk) == CTXSUFF((pHeap)->pbHeap), \
53 ("pChunk=%p pvHyperHeap=%p\n", (pChunk), CTXSUFF((pHeap)->pbHeap))); \
54 } while (0)
55
56#define ASSERT_OFFNEXT(pHeap, pChunk) \
57 do { ASSERT_ALIGN((pChunk)->offNext); \
58 ASSERT_L((pChunk)->offNext, (uintptr_t)CTXSUFF((pHeap)->pbHeap) + (pHeap)->offPageAligned - (uintptr_t)(pChunk)); \
59 } while (0)
60
61#define ASSERT_OFFHEAP(pHeap, pChunk) \
62 do { Assert((pChunk)->offHeap); \
63 AssertMsg((PMMHYPERHEAP)((pChunk)->offHeap + (uintptr_t)pChunk) == (pHeap), \
64 ("offHeap=%RX32 pChunk=%p pHeap=%p\n", (pChunk)->offHeap, (pChunk), (pHeap))); \
65 Assert((pHeap)->u32Magic == MMHYPERHEAP_MAGIC); \
66 } while (0)
67
68#ifdef VBOX_WITH_STATISTICS
69#define ASSERT_OFFSTAT(pHeap, pChunk) \
70 do { if (MMHYPERCHUNK_ISFREE(pChunk)) \
71 Assert(!(pChunk)->offStat); \
72 else if ((pChunk)->offStat) \
73 { \
74 Assert((pChunk)->offStat); \
75 AssertMsg(!((pChunk)->offStat & (MMHYPER_HEAP_ALIGN_MIN - 1)), ("offStat=%RX32\n", (pChunk)->offStat)); \
76 uintptr_t uPtr = (uintptr_t)(pChunk)->offStat + (uintptr_t)pChunk; NOREF(uPtr); \
77 AssertMsg(uPtr - (uintptr_t)CTXSUFF((pHeap)->pbHeap) < (pHeap)->offPageAligned, \
78 ("%p - %p < %RX32\n", uPtr, CTXSUFF((pHeap)->pbHeap), (pHeap)->offPageAligned)); \
79 } \
80 } while (0)
81#else
82#define ASSERT_OFFSTAT(pHeap, pChunk) \
83 do { Assert(!(pChunk)->offStat); \
84 } while (0)
85#endif
86
87#define ASSERT_CHUNK(pHeap, pChunk) \
88 do { ASSERT_OFFNEXT(pHeap, pChunk); \
89 ASSERT_OFFPREV(pHeap, pChunk); \
90 ASSERT_OFFHEAP(pHeap, pChunk); \
91 ASSERT_OFFSTAT(pHeap, pChunk); \
92 } while (0)
93#define ASSERT_CHUNK_USED(pHeap, pChunk) \
94 do { ASSERT_OFFNEXT(pHeap, pChunk); \
95 ASSERT_OFFPREV(pHeap, pChunk); \
96 Assert(MMHYPERCHUNK_ISUSED(pChunk)); \
97 } while (0)
98
99#define ASSERT_FREE_OFFPREV(pHeap, pChunk) \
100 do { ASSERT_ALIGN((pChunk)->offPrev); \
101 ASSERT_GE(((pChunk)->offPrev & (MMHYPER_HEAP_ALIGN_MIN - 1)), (intptr_t)CTXSUFF((pHeap)->pbHeap) - (intptr_t)(pChunk)); \
102 Assert((pChunk)->offPrev != MMHYPERCHUNK_GET_OFFPREV(&(pChunk)->core) || !(pChunk)->offPrev); \
103 AssertMsg( (pChunk)->offPrev \
104 || (uintptr_t)(pChunk) - (uintptr_t)CTXSUFF((pHeap)->pbHeap) == (pHeap)->offFreeHead, \
105 ("pChunk=%p offChunk=%#x offFreeHead=%#x\n", (pChunk), (uintptr_t)(pChunk) - (uintptr_t)CTXSUFF((pHeap)->pbHeap),\
106 (pHeap)->offFreeHead)); \
107 } while (0)
108
109#define ASSERT_FREE_OFFNEXT(pHeap, pChunk) \
110 do { ASSERT_ALIGN((pChunk)->offNext); \
111 ASSERT_L((pChunk)->offNext, (uintptr_t)CTXSUFF((pHeap)->pbHeap) + (pHeap)->offPageAligned - (uintptr_t)(pChunk)); \
112 Assert((pChunk)->offNext != (pChunk)->core.offNext || !(pChunk)->offNext); \
113 AssertMsg( (pChunk)->offNext \
114 || (uintptr_t)(pChunk) - (uintptr_t)CTXSUFF((pHeap)->pbHeap) == (pHeap)->offFreeTail, \
115 ("pChunk=%p offChunk=%#x offFreeTail=%#x\n", (pChunk), (uintptr_t)(pChunk) - (uintptr_t)CTXSUFF((pHeap)->pbHeap), \
116 (pHeap)->offFreeTail)); \
117 } while (0)
118
119#define ASSERT_FREE_CB(pHeap, pChunk) \
120 do { ASSERT_ALIGN((pChunk)->cb); \
121 Assert((pChunk)->cb > 0); \
122 if ((pChunk)->core.offNext) \
123 AssertMsg((pChunk)->cb == ((pChunk)->core.offNext - sizeof(MMHYPERCHUNK)), \
124 ("cb=%d offNext=%d\n", (pChunk)->cb, (pChunk)->core.offNext)); \
125 else \
126 ASSERT_LE((pChunk)->cb, (uintptr_t)CTXSUFF((pHeap)->pbHeap) + (pHeap)->offPageAligned - (uintptr_t)(pChunk)); \
127 } while (0)
128
129#define ASSERT_CHUNK_FREE(pHeap, pChunk) \
130 do { ASSERT_CHUNK(pHeap, &(pChunk)->core); \
131 Assert(MMHYPERCHUNK_ISFREE(pChunk)); \
132 ASSERT_FREE_OFFNEXT(pHeap, pChunk); \
133 ASSERT_FREE_OFFPREV(pHeap, pChunk); \
134 ASSERT_FREE_CB(pHeap, pChunk); \
135 } while (0)
136
137
138/*******************************************************************************
139* Internal Functions *
140*******************************************************************************/
141static PMMHYPERCHUNK mmHyperAllocChunk(PMMHYPERHEAP pHeap, uint32_t cb, unsigned uAlignment);
142static void *mmHyperAllocPages(PMMHYPERHEAP pHeap, uint32_t cb);
143#ifdef VBOX_WITH_STATISTICS
144static PMMHYPERSTAT mmHyperStat(PMMHYPERHEAP pHeap, MMTAG enmTag);
145#ifdef IN_RING3
146static void mmR3HyperStatRegisterOne(PVM pVM, PMMHYPERSTAT pStat);
147#endif
148#endif
149static int mmHyperFree(PMMHYPERHEAP pHeap, PMMHYPERCHUNK pChunk);
150#ifdef MMHYPER_HEAP_STRICT
151static void mmr3HyperHeapCheck(PMMHYPERHEAP pHeap);
152#endif
153
154
155/**
156 * Allocates memory in the Hypervisor (GC VMM) area.
157 * The returned memory is of course zeroed.
158 *
159 * @returns VBox status code.
160 * @param pVM The VM to operate on.
161 * @param cb Number of bytes to allocate.
162 * @param uAlignment Required memory alignment in bytes.
163 * Values are 0,8,16,32 and PAGE_SIZE.
164 * 0 -> default alignment, i.e. 8 bytes.
165 * @param enmTag The statistics tag.
166 * @param ppv Where to store the address to the allocated
167 * memory.
168 * @remark This is assumed not to be used at times when serialization is required.
169 */
170MMDECL(int) MMHyperAlloc(PVM pVM, size_t cb, unsigned uAlignment, MMTAG enmTag, void **ppv)
171{
172 AssertMsg(cb >= 8, ("Hey! Do you really mean to allocate less than 8 bytes?! cb=%d\n", cb));
173
174 /*
175 * Validate input and adjust it to reasonable values.
176 */
177 if (!uAlignment || uAlignment < MMHYPER_HEAP_ALIGN_MIN)
178 uAlignment = MMHYPER_HEAP_ALIGN_MIN;
179 uint32_t cbAligned;
180 switch (uAlignment)
181 {
182 case 8:
183 case 16:
184 case 32:
185 cbAligned = RT_ALIGN(cb, MMHYPER_HEAP_ALIGN_MIN);
186 if (!cbAligned || cbAligned < cb)
187 {
188 Log2(("MMHyperAlloc: cb=%#x uAlignment=%#x returns VERR_INVALID_PARAMETER\n", cb, uAlignment));
189 AssertMsgFailed(("Nice try.\n"));
190 return VERR_INVALID_PARAMETER;
191 }
192 break;
193
194 case PAGE_SIZE:
195 AssertMsg(RT_ALIGN(cb, PAGE_SIZE) == cb, ("The size isn't page aligned. (cb=%#x)\n", cb));
196 cbAligned = RT_ALIGN(cb, PAGE_SIZE);
197 if (!cbAligned)
198 {
199 Log2(("MMHyperAlloc: cb=%#x uAlignment=%#x returns VERR_INVALID_PARAMETER\n", cb, uAlignment));
200 AssertMsgFailed(("Nice try.\n"));
201 return VERR_INVALID_PARAMETER;
202 }
203 break;
204
205 default:
206 Log2(("MMHyperAlloc: cb=%#x uAlignment=%#x returns VERR_INVALID_PARAMETER\n", cb, uAlignment));
207 AssertMsgFailed(("Invalid alignment %u\n", uAlignment));
208 return VERR_INVALID_PARAMETER;
209 }
210
211
212 /*
213 * Get heap and statisticsStatistics.
214 */
215 PMMHYPERHEAP pHeap = CTXSUFF(pVM->mm.s.pHyperHeap);
216#ifdef VBOX_WITH_STATISTICS
217 PMMHYPERSTAT pStat = mmHyperStat(pHeap, enmTag);
218 if (!pStat)
219 {
220 Log2(("MMHyperAlloc: cb=%#x uAlignment=%#x returns VERR_MM_HYPER_NO_MEMORY\n", cb, uAlignment));
221 AssertMsgFailed(("Failed to allocate statistics!\n"));
222 return VERR_MM_HYPER_NO_MEMORY;
223 }
224#endif
225 if (uAlignment < PAGE_SIZE)
226 {
227 /*
228 * Allocate a chunk.
229 */
230 PMMHYPERCHUNK pChunk = mmHyperAllocChunk(pHeap, cbAligned, uAlignment);
231 if (pChunk)
232 {
233#ifdef VBOX_WITH_STATISTICS
234 const uint32_t cbChunk = pChunk->offNext
235 ? pChunk->offNext
236 : CTXSUFF(pHeap->pbHeap) + pHeap->offPageAligned - (uint8_t *)pChunk;
237 pStat->cbAllocated += (uint32_t)cbChunk;
238 pStat->cbCurAllocated += (uint32_t)cbChunk;
239 if (pStat->cbCurAllocated > pStat->cbMaxAllocated)
240 pStat->cbMaxAllocated = pStat->cbCurAllocated;
241 pStat->cAllocations++;
242 pChunk->offStat = (uintptr_t)pStat - (uintptr_t)pChunk;
243#else
244 pChunk->offStat = 0;
245#endif
246 void *pv = pChunk + 1;
247 *ppv = pv;
248 ASMMemZero32(pv, cbAligned);
249 Log2(("MMHyperAlloc: cb=%#x uAlignment=%#x returns VINF_SUCCESS and *ppv=%p\n", cb, uAlignment, pv));
250 return VINF_SUCCESS;
251 }
252 }
253 else
254 {
255 /*
256 * Allocate page aligned memory.
257 */
258 void *pv = mmHyperAllocPages(pHeap, cbAligned);
259 if (pv)
260 {
261#ifdef VBOX_WITH_STATISTICS
262 pStat->cbAllocated += cbAligned;
263 pStat->cbCurAllocated += cbAligned;
264 if (pStat->cbCurAllocated > pStat->cbMaxAllocated)
265 pStat->cbMaxAllocated = pStat->cbCurAllocated;
266 pStat->cAllocations++;
267#endif
268 *ppv = pv;
269 /* ASMMemZero32(pv, cbAligned); - not required since memory is alloc-only and SUPPageAlloc zeros it. */
270 Log2(("MMHyperAlloc: cb=%#x uAlignment=%#x returns VINF_SUCCESS and *ppv=%p\n", cb, uAlignment, ppv));
271 return VINF_SUCCESS;
272 }
273 }
274
275#ifdef VBOX_WITH_STATISTICS
276 pStat->cAllocations++;
277 pStat->cFailures++;
278#endif
279 Log2(("MMHyperAlloc: cb=%#x uAlignment=%#x returns VERR_MM_HYPER_NO_MEMORY\n", cb, uAlignment));
280 AssertMsgFailed(("Failed to allocate %d bytes!\n", cb));
281 return VERR_MM_HYPER_NO_MEMORY;
282}
283
284
285
286/**
287 * Allocates a chunk of memory from the specified heap.
288 * The caller validates the parameters of this request.
289 *
290 * @returns Pointer to the allocated chunk.
291 * @returns NULL on failure.
292 * @param pHeap The heap.
293 * @param cb Size of the memory block to allocate.
294 * @param uAlignment The alignment specifications for the allocated block.
295 * @internal
296 */
297static PMMHYPERCHUNK mmHyperAllocChunk(PMMHYPERHEAP pHeap, uint32_t cb, unsigned uAlignment)
298{
299 Log3(("mmHyperAllocChunk: Enter cb=%#x uAlignment=%#x\n", cb, uAlignment));
300#ifdef MMHYPER_HEAP_STRICT
301 mmr3HyperHeapCheck(pHeap);
302#endif
303
304 /*
305 * Check if there are any free chunks. (NIL_OFFSET use/not-use forces this check)
306 */
307 if (pHeap->offFreeHead == NIL_OFFSET)
308 return NULL;
309
310 /*
311 * Small alignments - from the front of the heap.
312 *
313 * Must split off free chunks at the end to prevent messing up the
314 * last free node which we take the page aligned memory from the top of.
315 */
316 PMMHYPERCHUNK pRet = NULL;
317 PMMHYPERCHUNKFREE pFree = (PMMHYPERCHUNKFREE)((char *)CTXSUFF(pHeap->pbHeap) + pHeap->offFreeHead);
318 while (pFree)
319 {
320 ASSERT_CHUNK_FREE(pHeap, pFree);
321 if (pFree->cb >= cb)
322 {
323 unsigned offAlign = (uintptr_t)(&pFree->core + 1) & (uAlignment - 1);
324 if (offAlign)
325 offAlign = uAlignment - offAlign;
326 if (!offAlign || pFree->cb - offAlign >= cb)
327 {
328 Log3(("mmHyperAllocChunk: Using pFree=%p pFree->cb=%d offAlign=%d\n", pFree, pFree->cb, offAlign));
329
330 /*
331 * Adjust the node in front.
332 * Because of multiple alignments we need to special case allocation of the first block.
333 */
334 if (offAlign)
335 {
336 MMHYPERCHUNKFREE Free = *pFree;
337 if (MMHYPERCHUNK_GET_OFFPREV(&pFree->core))
338 {
339 /* just add a bit of memory to it. */
340 PMMHYPERCHUNKFREE pPrev = (PMMHYPERCHUNKFREE)((char *)pFree + MMHYPERCHUNK_GET_OFFPREV(&Free.core));
341 pPrev->core.offNext += offAlign;
342 AssertMsg(!MMHYPERCHUNK_ISFREE(&pPrev->core), ("Impossible!\n"));
343 Log3(("mmHyperAllocChunk: Added %d bytes to %p\n", offAlign, pPrev));
344 }
345 else
346 {
347 /* make new head node, mark it USED for simplisity. */
348 PMMHYPERCHUNK pPrev = (PMMHYPERCHUNK)CTXSUFF(pHeap->pbHeap);
349 Assert(pPrev == &pFree->core);
350 pPrev->offPrev = 0;
351 MMHYPERCHUNK_SET_TYPE(pPrev, MMHYPERCHUNK_FLAGS_USED);
352 pPrev->offNext = offAlign;
353 Log3(("mmHyperAllocChunk: Created new first node of %d bytes\n", offAlign));
354
355 }
356 Log3(("mmHyperAllocChunk: cbFree %d -> %d (%d)\n", pHeap->cbFree, pHeap->cbFree - offAlign, -(int)offAlign));
357 pHeap->cbFree -= offAlign;
358
359 /* Recreate pFree node and adjusting everything... */
360 pFree = (PMMHYPERCHUNKFREE)((char *)pFree + offAlign);
361 *pFree = Free;
362
363 pFree->cb -= offAlign;
364 if (pFree->core.offNext)
365 {
366 pFree->core.offNext -= offAlign;
367 PMMHYPERCHUNK pNext = (PMMHYPERCHUNK)((char *)pFree + pFree->core.offNext);
368 MMHYPERCHUNK_SET_OFFPREV(pNext, -(int32_t)pFree->core.offNext);
369 ASSERT_CHUNK(pHeap, pNext);
370 }
371 if (MMHYPERCHUNK_GET_OFFPREV(&pFree->core))
372 MMHYPERCHUNK_SET_OFFPREV(&pFree->core, MMHYPERCHUNK_GET_OFFPREV(&pFree->core) - offAlign);
373
374 if (pFree->offNext)
375 {
376 pFree->offNext -= offAlign;
377 PMMHYPERCHUNKFREE pNext = (PMMHYPERCHUNKFREE)((char *)pFree + pFree->offNext);
378 pNext->offPrev = -(int32_t)pFree->offNext;
379 ASSERT_CHUNK_FREE(pHeap, pNext);
380 }
381 else
382 pHeap->offFreeTail += offAlign;
383 if (pFree->offPrev)
384 {
385 pFree->offPrev -= offAlign;
386 PMMHYPERCHUNKFREE pPrev = (PMMHYPERCHUNKFREE)((char *)pFree + pFree->offPrev);
387 pPrev->offNext = -pFree->offPrev;
388 ASSERT_CHUNK_FREE(pHeap, pPrev);
389 }
390 else
391 pHeap->offFreeHead += offAlign;
392 pFree->core.offHeap = (uintptr_t)pHeap - (uintptr_t)pFree;
393 pFree->core.offStat = 0;
394 ASSERT_CHUNK_FREE(pHeap, pFree);
395 Log3(("mmHyperAllocChunk: Realigned pFree=%p\n", pFree));
396 }
397
398 /*
399 * Split off a new FREE chunk?
400 */
401 if (pFree->cb >= cb + RT_ALIGN(sizeof(MMHYPERCHUNKFREE), MMHYPER_HEAP_ALIGN_MIN))
402 {
403 /*
404 * Move the FREE chunk up to make room for the new USED chunk.
405 */
406 const int off = cb + sizeof(MMHYPERCHUNK);
407 PMMHYPERCHUNKFREE pNew = (PMMHYPERCHUNKFREE)((char *)&pFree->core + off);
408 *pNew = *pFree;
409 pNew->cb -= off;
410 if (pNew->core.offNext)
411 {
412 pNew->core.offNext -= off;
413 PMMHYPERCHUNK pNext = (PMMHYPERCHUNK)((char *)pNew + pNew->core.offNext);
414 MMHYPERCHUNK_SET_OFFPREV(pNext, -(int32_t)pNew->core.offNext);
415 ASSERT_CHUNK(pHeap, pNext);
416 }
417 pNew->core.offPrev = -off;
418 MMHYPERCHUNK_SET_TYPE(pNew, MMHYPERCHUNK_FLAGS_FREE);
419
420 if (pNew->offNext)
421 {
422 pNew->offNext -= off;
423 PMMHYPERCHUNKFREE pNext = (PMMHYPERCHUNKFREE)((char *)pNew + pNew->offNext);
424 pNext->offPrev = -(int32_t)pNew->offNext;
425 ASSERT_CHUNK_FREE(pHeap, pNext);
426 }
427 else
428 pHeap->offFreeTail += off;
429 if (pNew->offPrev)
430 {
431 pNew->offPrev -= off;
432 PMMHYPERCHUNKFREE pPrev = (PMMHYPERCHUNKFREE)((char *)pNew + pNew->offPrev);
433 pPrev->offNext = -pNew->offPrev;
434 ASSERT_CHUNK_FREE(pHeap, pPrev);
435 }
436 else
437 pHeap->offFreeHead += off;
438 pNew->core.offHeap = (uintptr_t)pHeap - (uintptr_t)pNew;
439 pNew->core.offStat = 0;
440 ASSERT_CHUNK_FREE(pHeap, pNew);
441
442 /*
443 * Update the old FREE node making it a USED node.
444 */
445 pFree->core.offNext = off;
446 MMHYPERCHUNK_SET_TYPE(&pFree->core, MMHYPERCHUNK_FLAGS_USED);
447
448
449 Log3(("mmHyperAllocChunk: cbFree %d -> %d (%d)\n", pHeap->cbFree,
450 pHeap->cbFree - (cb + sizeof(MMHYPERCHUNK)), -(int)(cb + sizeof(MMHYPERCHUNK))));
451 pHeap->cbFree -= (uint32_t)(cb + sizeof(MMHYPERCHUNK));
452 pRet = &pFree->core;
453 ASSERT_CHUNK(pHeap, &pFree->core);
454 Log3(("mmHyperAllocChunk: Created free chunk pNew=%p cb=%d\n", pNew, pNew->cb));
455 }
456 else
457 {
458 /*
459 * Link out of free list.
460 */
461 if (pFree->offNext)
462 {
463 PMMHYPERCHUNKFREE pNext = (PMMHYPERCHUNKFREE)((char *)pFree + pFree->offNext);
464 if (pFree->offPrev)
465 {
466 pNext->offPrev += pFree->offPrev;
467 PMMHYPERCHUNKFREE pPrev = (PMMHYPERCHUNKFREE)((char *)pFree + pFree->offPrev);
468 pPrev->offNext += pFree->offNext;
469 ASSERT_CHUNK_FREE(pHeap, pPrev);
470 }
471 else
472 {
473 pHeap->offFreeHead += pFree->offNext;
474 pNext->offPrev = 0;
475 }
476 ASSERT_CHUNK_FREE(pHeap, pNext);
477 }
478 else
479 {
480 if (pFree->offPrev)
481 {
482 pHeap->offFreeTail += pFree->offPrev;
483 PMMHYPERCHUNKFREE pPrev = (PMMHYPERCHUNKFREE)((char *)pFree + pFree->offPrev);
484 pPrev->offNext = 0;
485 ASSERT_CHUNK_FREE(pHeap, pPrev);
486 }
487 else
488 {
489 pHeap->offFreeHead = NIL_OFFSET;
490 pHeap->offFreeTail = NIL_OFFSET;
491 }
492 }
493
494 Log3(("mmHyperAllocChunk: cbFree %d -> %d (%d)\n", pHeap->cbFree,
495 pHeap->cbFree - pFree->cb, -(int32_t)pFree->cb));
496 pHeap->cbFree -= pFree->cb;
497 MMHYPERCHUNK_SET_TYPE(&pFree->core, MMHYPERCHUNK_FLAGS_USED);
498 pRet = &pFree->core;
499 ASSERT_CHUNK(pHeap, &pFree->core);
500 Log3(("mmHyperAllocChunk: Converted free chunk %p to used chunk.\n", pFree));
501 }
502 Log3(("mmHyperAllocChunk: Returning %p\n", pRet));
503 break;
504 }
505 }
506
507 /* next */
508 pFree = pFree->offNext ? (PMMHYPERCHUNKFREE)((char *)pFree + pFree->offNext) : NULL;
509 }
510
511#ifdef MMHYPER_HEAP_STRICT
512 mmr3HyperHeapCheck(pHeap);
513#endif
514 return pRet;
515}
516
517
518/**
519 * Allocates one or more pages of memory from the specified heap.
520 * The caller validates the parameters of this request.
521 *
522 * @returns Pointer to the allocated chunk.
523 * @returns NULL on failure.
524 * @param pHeap The heap.
525 * @param cb Size of the memory block to allocate.
526 * @internal
527 */
528static void *mmHyperAllocPages(PMMHYPERHEAP pHeap, uint32_t cb)
529{
530 Log3(("mmHyperAllocPages: Enter cb=%#x\n", cb));
531
532#ifdef MMHYPER_HEAP_STRICT
533 mmr3HyperHeapCheck(pHeap);
534#endif
535
536 /*
537 * Check if there are any free chunks. (NIL_OFFSET use/not-use forces this check)
538 */
539 if (pHeap->offFreeHead == NIL_OFFSET)
540 return NULL;
541
542 /*
543 * Page aligned chunks.
544 *
545 * Page aligned chunks can only be allocated from the last FREE chunk.
546 * This is for reasons of simplicity and fragmentation. Page aligned memory
547 * must also be allocated in page aligned sizes. Page aligned memory cannot
548 * be freed either.
549 *
550 * So, for this to work, the last FREE chunk needs to end on a page aligned
551 * boundrary.
552 */
553 PMMHYPERCHUNKFREE pFree = (PMMHYPERCHUNKFREE)((char *)CTXSUFF(pHeap->pbHeap) + pHeap->offFreeTail);
554 ASSERT_CHUNK_FREE(pHeap, pFree);
555 if ( (((uintptr_t)(&pFree->core + 1) + pFree->cb) & PAGE_OFFSET_MASK - 1)
556 || pFree->cb + sizeof(MMHYPERCHUNK) < cb)
557 {
558 Log3(("mmHyperAllocPages: Not enough/no page aligned memory!\n"));
559 return NULL;
560 }
561
562 void *pvRet;
563 if (pFree->cb > cb)
564 {
565 /*
566 * Simple, just cut the top of the free node and return it.
567 */
568 pFree->cb -= cb;
569 pvRet = (char *)(&pFree->core + 1) + pFree->cb;
570 AssertMsg(ALIGNP(pvRet, PAGE_SIZE) == pvRet, ("pvRet=%p cb=%#x pFree=%p pFree->cb=%#x\n", pvRet, cb, pFree, pFree->cb));
571 Log3(("mmHyperAllocPages: cbFree %d -> %d (%d)\n", pHeap->cbFree, pHeap->cbFree - cb, -(int)cb));
572 pHeap->cbFree -= cb;
573 ASSERT_CHUNK_FREE(pHeap, pFree);
574 Log3(("mmHyperAllocPages: Allocated from pFree=%p new pFree->cb=%d\n", pFree, pFree->cb));
575 }
576 else
577 {
578 /*
579 * Unlink the FREE node.
580 */
581 pvRet = (char *)(&pFree->core + 1) + pFree->cb - cb;
582 Log3(("mmHyperAllocPages: cbFree %d -> %d (%d)\n", pHeap->cbFree, pHeap->cbFree - pFree->cb, -(int32_t)pFree->cb));
583 pHeap->cbFree -= pFree->cb;
584
585 /* a scrap of spare memory (unlikely)? add it to the sprevious chunk. */
586 if (pvRet != (void *)pFree)
587 {
588 AssertMsg(MMHYPERCHUNK_GET_OFFPREV(&pFree->core), ("How the *beep* did someone manage to allocated up all the heap with page aligned memory?!?\n"));
589 PMMHYPERCHUNK pPrev = (PMMHYPERCHUNK)((char *)pFree + MMHYPERCHUNK_GET_OFFPREV(&pFree->core));
590 pPrev->offNext += (uintptr_t)pvRet - (uintptr_t)pFree;
591 AssertMsg(!MMHYPERCHUNK_ISFREE(pPrev), ("Free bug?\n"));
592#ifdef VBOX_WITH_STATISTICS
593 PMMHYPERSTAT pStat = (PMMHYPERSTAT)((uintptr_t)pPrev + pPrev->offStat);
594 pStat->cbAllocated += (uintptr_t)pvRet - (uintptr_t)pFree;
595 pStat->cbCurAllocated += (uintptr_t)pvRet - (uintptr_t)pFree;
596#endif
597 Log3(("mmHyperAllocPages: Added %d to %p (page align)\n", (uintptr_t)pvRet - (uintptr_t)pFree, pFree));
598 }
599
600 /* unlink from FREE chain. */
601 if (pFree->offPrev)
602 {
603 pHeap->offFreeTail += pFree->offPrev;
604 ((PMMHYPERCHUNKFREE)((char *)pFree + pFree->offPrev))->offNext = 0;
605 }
606 else
607 {
608 pHeap->offFreeTail = NIL_OFFSET;
609 pHeap->offFreeHead = NIL_OFFSET;
610 }
611 Log3(("mmHyperAllocPages: Unlinked pFree=%d\n", pFree));
612 }
613 pHeap->offPageAligned = (uintptr_t)pvRet - (uintptr_t)CTXSUFF(pHeap->pbHeap);
614 Log3(("mmHyperAllocPages: Returning %p (page aligned)\n", pvRet));
615
616#ifdef MMHYPER_HEAP_STRICT
617 mmr3HyperHeapCheck(pHeap);
618#endif
619 return pvRet;
620}
621
622
623#ifdef VBOX_WITH_STATISTICS
624/**
625 * Get the statistic record for a tag.
626 *
627 * @returns Pointer to a stat record.
628 * @returns NULL on failure.
629 * @param pHeap The heap.
630 * @param enmTag The tag.
631 */
632static PMMHYPERSTAT mmHyperStat(PMMHYPERHEAP pHeap, MMTAG enmTag)
633{
634 /* try look it up first. */
635 PMMHYPERSTAT pStat = (PMMHYPERSTAT)RTAvloGCPhysGet(&pHeap->HyperHeapStatTree, enmTag);
636 if (!pStat)
637 {
638 /* try allocate a new one */
639 PMMHYPERCHUNK pChunk = mmHyperAllocChunk(pHeap, RT_ALIGN(sizeof(*pStat), MMHYPER_HEAP_ALIGN_MIN), MMHYPER_HEAP_ALIGN_MIN);
640 if (!pChunk)
641 return NULL;
642 pStat = (PMMHYPERSTAT)(pChunk + 1);
643 pChunk->offStat = (uintptr_t)pStat - (uintptr_t)pChunk;
644
645 ASMMemZero32(pStat, sizeof(*pStat));
646 pStat->Core.Key = enmTag;
647 RTAvloGCPhysInsert(&pHeap->HyperHeapStatTree, &pStat->Core);
648 }
649 if (!pStat->fRegistered)
650 {
651#ifdef IN_RING3
652 mmR3HyperStatRegisterOne(pHeap->pVMHC, pStat);
653#else
654 /** @todo schedule a HC action. */
655#endif
656 }
657 return pStat;
658}
659
660#ifdef IN_RING3
661/**
662 * Registers statistics with STAM.
663 *
664 */
665static void mmR3HyperStatRegisterOne(PVM pVM, PMMHYPERSTAT pStat)
666{
667 if (pStat->fRegistered)
668 return;
669 const char *pszTag = mmR3GetTagName((MMTAG)pStat->Core.Key);
670
671 char szName[128];
672 RTStrPrintf(szName, sizeof(szName), "/MM/HyperHeap/%s/cAllocations", pszTag);
673 STAMR3Register(pVM, &pStat->cAllocations, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_COUNT, "Number of alloc calls.");
674
675 RTStrPrintf(szName, sizeof(szName), "/MM/HyperHeap/%s/cFrees", pszTag);
676 STAMR3Register(pVM, &pStat->cFrees, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_COUNT, "Number of free calls.");
677
678 RTStrPrintf(szName, sizeof(szName), "/MM/HyperHeap/%s/cFailures", pszTag);
679 STAMR3Register(pVM, &pStat->cFailures, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_COUNT, "Number of failures.");
680
681 RTStrPrintf(szName, sizeof(szName), "/MM/HyperHeap/%s/cbAllocated", pszTag);
682 STAMR3Register(pVM, &pStat->cbAllocated, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_BYTES, "Total number of allocated bytes.");
683
684 RTStrPrintf(szName, sizeof(szName), "/MM/HyperHeap/%s/cbFreed", pszTag);
685 STAMR3Register(pVM, &pStat->cbFreed, STAMTYPE_U64, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_BYTES, "Total number of freed bytes.");
686
687 RTStrPrintf(szName, sizeof(szName), "/MM/HyperHeap/%s/cbCurAllocated", pszTag);
688 STAMR3Register(pVM, &pStat->cbCurAllocated, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_BYTES, "Number of bytes currently allocated.");
689
690 RTStrPrintf(szName, sizeof(szName), "/MM/HyperHeap/%s/cbMaxAllocated", pszTag);
691 STAMR3Register(pVM, &pStat->cbMaxAllocated, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_BYTES, "Max number of bytes allocated at the same time.");
692
693 pStat->fRegistered = true;
694}
695#endif
696
697#endif
698
699
700/**
701 * Free memory allocated using MMHyperAlloc().
702 * The caller validates the parameters of this request.
703 *
704 * @returns VBox status code.
705 * @param pVM The VM to operate on.
706 * @param pv The memory to free.
707 * @remark Try avoid free hyper memory.
708 */
709MMDECL(int) MMHyperFree(PVM pVM, void *pv)
710{
711 Log2(("MMHyperFree: pv=%p\n", pv));
712 if (!pv)
713 return VINF_SUCCESS;
714 AssertMsgReturn(ALIGNP(pv, MMHYPER_HEAP_ALIGN_MIN) == pv,
715 ("Invalid pointer %p!\n", pv),
716 VERR_INVALID_POINTER);
717
718 /*
719 * Get the heap and stats.
720 * Validate the chunk at the same time.
721 */
722 PMMHYPERCHUNK pChunk = (PMMHYPERCHUNK)((PMMHYPERCHUNK)pv - 1);
723
724 AssertMsgReturn( (uintptr_t)pChunk + pChunk->offNext >= (uintptr_t)pChunk
725 || RT_ALIGN_32(pChunk->offNext, MMHYPER_HEAP_ALIGN_MIN) != pChunk->offNext,
726 ("%p: offNext=%#RX32\n", pv, pChunk->offNext),
727 VERR_INVALID_POINTER);
728
729 AssertMsgReturn(MMHYPERCHUNK_ISUSED(pChunk),
730 ("%p: Not used!\n", pv),
731 VERR_INVALID_POINTER);
732
733 int32_t offPrev = MMHYPERCHUNK_GET_OFFPREV(pChunk);
734 AssertMsgReturn( (uintptr_t)pChunk + offPrev <= (uintptr_t)pChunk
735 && !((uint32_t)-offPrev & (MMHYPER_HEAP_ALIGN_MIN - 1)),
736 ("%p: offPrev=%#RX32!\n", pv, offPrev),
737 VERR_INVALID_POINTER);
738
739 /* statistics */
740#ifdef VBOX_WITH_STATISTICS
741 PMMHYPERSTAT pStat = (PMMHYPERSTAT)((uintptr_t)pChunk + pChunk->offStat);
742 AssertMsgReturn( ALIGNP(pStat, MMHYPER_HEAP_ALIGN_MIN) == (void *)pStat
743 && pChunk->offStat,
744 ("%p: offStat=%#RX32!\n", pv, pChunk->offStat),
745 VERR_INVALID_POINTER);
746#else
747 AssertMsgReturn(!pChunk->offStat,
748 ("%p: offStat=%#RX32!\n", pv, pChunk->offStat),
749 VERR_INVALID_POINTER);
750#endif
751
752 /* The heap structure. */
753 PMMHYPERHEAP pHeap = (PMMHYPERHEAP)((uintptr_t)pChunk + pChunk->offHeap);
754 AssertMsgReturn( !((uintptr_t)pHeap & PAGE_OFFSET_MASK)
755 && pChunk->offHeap,
756 ("%p: pHeap=%#x offHeap=%RX32\n", pv, pHeap->u32Magic, pChunk->offHeap),
757 VERR_INVALID_POINTER);
758
759 AssertMsgReturn(pHeap->u32Magic == MMHYPERHEAP_MAGIC,
760 ("%p: u32Magic=%#x\n", pv, pHeap->u32Magic),
761 VERR_INVALID_POINTER);
762Assert(pHeap == CTXSUFF(pVM->mm.s.pHyperHeap));
763
764 /* Some more verifications using additional info from pHeap. */
765 AssertMsgReturn((uintptr_t)pChunk + offPrev >= (uintptr_t)CTXSUFF(pHeap->pbHeap),
766 ("%p: offPrev=%#RX32!\n", pv, offPrev),
767 VERR_INVALID_POINTER);
768
769 AssertMsgReturn(pChunk->offNext < pHeap->cbHeap,
770 ("%p: offNext=%#RX32!\n", pv, pChunk->offNext),
771 VERR_INVALID_POINTER);
772
773 AssertMsgReturn( (uintptr_t)pv - (uintptr_t)CTXSUFF(pHeap->pbHeap) <= pHeap->offPageAligned,
774 ("Invalid pointer %p! (heap: %p-%p)\n", pv, CTXSUFF(pHeap->pbHeap),
775 (char *)CTXSUFF(pHeap->pbHeap) + pHeap->offPageAligned),
776 VERR_INVALID_POINTER);
777
778#if defined(VBOX_WITH_STATISTICS) || defined(MMHYPER_HEAP_FREE_POISON)
779 /* calc block size. */
780 const uint32_t cbChunk = pChunk->offNext
781 ? pChunk->offNext
782 : CTXSUFF(pHeap->pbHeap) + pHeap->offPageAligned - (uint8_t *)pChunk;
783#endif
784#ifdef MMHYPER_HEAP_FREE_POISON
785 /* poison the block */
786 memset(pChunk + 1, MMHYPER_HEAP_FREE_POISON, cbChunk - sizeof(*pChunk));
787#endif
788
789#ifdef MMHYPER_HEAP_FREE_DELAY
790# ifdef MMHYPER_HEAP_FREE_POISON
791 /*
792 * Check poison.
793 */
794 unsigned i = ELEMENTS(pHeap->aDelayedFrees);
795 while (i-- > 0)
796 if (pHeap->aDelayedFrees[i].offChunk)
797 {
798 PMMHYPERCHUNK pCur = (PMMHYPERCHUNK)((uintptr_t)pHeap + pHeap->aDelayedFrees[i].offChunk);
799 const size_t cb = pCur->offNext
800 ? pCur->offNext - sizeof(*pCur)
801 : CTXSUFF(pHeap->pbHeap) + pHeap->offPageAligned - (uint8_t *)pCur - sizeof(*pCur);
802 uint8_t *pab = (uint8_t *)(pCur + 1);
803 for (unsigned off = 0; off < cb; off++)
804 AssertReleaseMsg(pab[off] == 0xCB,
805 ("caller=%RTptr cb=%#zx off=%#x: %.*Rhxs\n",
806 pHeap->aDelayedFrees[i].uCaller, cb, off, RT_MIN(cb - off, 32), &pab[off]));
807 }
808# endif /* MMHYPER_HEAP_FREE_POISON */
809
810 /*
811 * Delayed freeing.
812 */
813 int rc = VINF_SUCCESS;
814 if (pHeap->aDelayedFrees[pHeap->iDelayedFree].offChunk)
815 {
816 PMMHYPERCHUNK pChunkFree = (PMMHYPERCHUNK)((uintptr_t)pHeap + pHeap->aDelayedFrees[pHeap->iDelayedFree].offChunk);
817 rc = mmHyperFree(pHeap, pChunkFree);
818 }
819 pHeap->aDelayedFrees[pHeap->iDelayedFree].offChunk = (uintptr_t)pChunk - (uintptr_t)pHeap;
820 pHeap->aDelayedFrees[pHeap->iDelayedFree].uCaller = (uintptr_t)ASMReturnAddress();
821 pHeap->iDelayedFree = (pHeap->iDelayedFree + 1) % ELEMENTS(pHeap->aDelayedFrees);
822
823#else /* !MMHYPER_HEAP_FREE_POISON */
824 /*
825 * Call the worker.
826 */
827 int rc = mmHyperFree(pHeap, pChunk);
828#endif /* !MMHYPER_HEAP_FREE_POISON */
829
830 /*
831 * Update statistics.
832 */
833#ifdef VBOX_WITH_STATISTICS
834 pStat->cFrees++;
835 if (VBOX_SUCCESS(rc))
836 {
837 pStat->cbFreed += cbChunk;
838 pStat->cbCurAllocated -= cbChunk;
839 }
840 else
841 pStat->cFailures++;
842#endif
843
844 return rc;
845}
846
847
848/**
849 * Free memory a memory chunk.
850 *
851 * @returns VBox status code.
852 * @param pHeap The heap.
853 * @param pChunk The memory chunk to free.
854 */
855static int mmHyperFree(PMMHYPERHEAP pHeap, PMMHYPERCHUNK pChunk)
856{
857 Log3(("mmHyperFree: Enter pHeap=%p pChunk=%p\n", pHeap, pChunk));
858 PMMHYPERCHUNKFREE pFree = (PMMHYPERCHUNKFREE)pChunk;
859
860#ifdef MMHYPER_HEAP_STRICT
861 mmr3HyperHeapCheck(pHeap);
862#endif
863
864 /*
865 * Insert into the free list (which is sorted on address).
866 *
867 * We'll search towards the end of the heap to locate the
868 * closest FREE chunk.
869 */
870 PMMHYPERCHUNKFREE pLeft = NULL;
871 PMMHYPERCHUNKFREE pRight = NULL;
872 if (pHeap->offFreeTail != NIL_OFFSET)
873 {
874 if (pFree->core.offNext)
875 {
876 pRight = (PMMHYPERCHUNKFREE)((char *)pFree + pFree->core.offNext);
877 ASSERT_CHUNK(pHeap, &pRight->core);
878 while (!MMHYPERCHUNK_ISFREE(&pRight->core))
879 {
880 if (!pRight->core.offNext)
881 {
882 pRight = NULL;
883 break;
884 }
885 pRight = (PMMHYPERCHUNKFREE)((char *)pRight + pRight->core.offNext);
886 ASSERT_CHUNK(pHeap, &pRight->core);
887 }
888 }
889 if (!pRight)
890 pRight = (PMMHYPERCHUNKFREE)((char *)CTXSUFF(pHeap->pbHeap) + pHeap->offFreeTail); /** @todo this can't be correct! 'pLeft = .. ; else' I think */
891 if (pRight)
892 {
893 ASSERT_CHUNK_FREE(pHeap, pRight);
894 if (pRight->offPrev)
895 {
896 pLeft = (PMMHYPERCHUNKFREE)((char *)pRight + pRight->offPrev);
897 ASSERT_CHUNK_FREE(pHeap, pLeft);
898 }
899 }
900 }
901 if (pLeft == pFree)
902 {
903 AssertMsgFailed(("Freed twice! pv=%p (pChunk=%p)\n", pChunk + 1, pChunk));
904 return VERR_INVALID_POINTER;
905 }
906 pChunk->offStat = 0;
907
908 /*
909 * Head free chunk list?
910 */
911 if (!pLeft)
912 {
913 MMHYPERCHUNK_SET_TYPE(&pFree->core, MMHYPERCHUNK_FLAGS_FREE);
914 pFree->offPrev = 0;
915 pHeap->offFreeHead = (uintptr_t)pFree - (uintptr_t)CTXSUFF(pHeap->pbHeap);
916 if (pRight)
917 {
918 pFree->offNext = (uintptr_t)pRight - (uintptr_t)pFree;
919 pRight->offPrev = -(int32_t)pFree->offNext;
920 }
921 else
922 {
923 pFree->offNext = 0;
924 pHeap->offFreeTail = pHeap->offFreeHead;
925 }
926 Log3(("mmHyperFree: Inserted %p at head of free chain.\n", pFree));
927 }
928 else
929 {
930 /*
931 * Can we merge with left hand free chunk?
932 */
933 if ((char *)pLeft + pLeft->core.offNext == (char *)pFree)
934 {
935 if (pFree->core.offNext)
936 {
937 pLeft->core.offNext = pLeft->core.offNext + pFree->core.offNext;
938 MMHYPERCHUNK_SET_OFFPREV(((PMMHYPERCHUNK)((char *)pLeft + pLeft->core.offNext)), -(int32_t)pLeft->core.offNext);
939 }
940 else
941 pLeft->core.offNext = 0;
942 pFree = pLeft;
943 Log3(("mmHyperFree: cbFree %d -> %d (%d)\n", pHeap->cbFree, pHeap->cbFree - pLeft->cb, -(int32_t)pLeft->cb));
944 pHeap->cbFree -= pLeft->cb;
945 Log3(("mmHyperFree: Merging %p into %p (cb=%d).\n", pFree, pLeft, pLeft->cb));
946 }
947 /*
948 * No, just link it into the free list then.
949 */
950 else
951 {
952 MMHYPERCHUNK_SET_TYPE(&pFree->core, MMHYPERCHUNK_FLAGS_FREE);
953 pFree->offPrev = (uintptr_t)pLeft - (uintptr_t)pFree;
954 pLeft->offNext = -pFree->offPrev;
955 if (pRight)
956 {
957 pFree->offNext = (uintptr_t)pRight - (uintptr_t)pFree;
958 pRight->offPrev = -(int32_t)pFree->offNext;
959 }
960 else
961 {
962 pFree->offNext = 0;
963 pHeap->offFreeTail = (uintptr_t)pFree - (uintptr_t)CTXSUFF(pHeap->pbHeap);
964 }
965 Log3(("mmHyperFree: Inserted %p after %p in free list.\n", pFree, pLeft));
966 }
967 }
968
969 /*
970 * Can we merge with right hand free chunk?
971 */
972 if (pRight && (char *)pRight == (char *)pFree + pFree->core.offNext)
973 {
974 /* core */
975 if (pRight->core.offNext)
976 {
977 pFree->core.offNext += pRight->core.offNext;
978 PMMHYPERCHUNK pNext = (PMMHYPERCHUNK)((char *)pFree + pFree->core.offNext);
979 MMHYPERCHUNK_SET_OFFPREV(pNext, -(int32_t)pFree->core.offNext);
980 ASSERT_CHUNK(pHeap, pNext);
981 }
982 else
983 pFree->core.offNext = 0;
984
985 /* free */
986 if (pRight->offNext)
987 {
988 pFree->offNext += pRight->offNext;
989 ((PMMHYPERCHUNKFREE)((char *)pFree + pFree->offNext))->offPrev = -(int32_t)pFree->offNext;
990 }
991 else
992 {
993 pFree->offNext = 0;
994 pHeap->offFreeTail = (uintptr_t)pFree - (uintptr_t)CTXSUFF(pHeap->pbHeap);
995 }
996 Log3(("mmHyperFree: cbFree %d -> %d (%d)\n", pHeap->cbFree, pHeap->cbFree - pRight->cb, -(int32_t)pRight->cb));
997 pHeap->cbFree -= pRight->cb;
998 Log3(("mmHyperFree: Merged %p (cb=%d) into %p.\n", pRight, pRight->cb, pFree));
999 }
1000
1001 /* calculate the size. */
1002 if (pFree->core.offNext)
1003 pFree->cb = pFree->core.offNext - sizeof(MMHYPERCHUNK);
1004 else
1005 pFree->cb = pHeap->offPageAligned - ((uintptr_t)pFree - (uintptr_t)CTXSUFF(pHeap->pbHeap)) - sizeof(MMHYPERCHUNK);
1006 Log3(("mmHyperFree: cbFree %d -> %d (%d)\n", pHeap->cbFree, pHeap->cbFree + pFree->cb, pFree->cb));
1007 pHeap->cbFree += pFree->cb;
1008 ASSERT_CHUNK_FREE(pHeap, pFree);
1009
1010#ifdef MMHYPER_HEAP_STRICT
1011 mmr3HyperHeapCheck(pHeap);
1012#endif
1013 return VINF_SUCCESS;
1014}
1015
1016
1017#ifdef MMHYPER_HEAP_STRICT
1018/**
1019 * Internal consitency check.
1020 */
1021static void mmr3HyperHeapCheck(PMMHYPERHEAP pHeap)
1022{
1023 PMMHYPERCHUNKFREE pPrev = NULL;
1024 PMMHYPERCHUNKFREE pCur = (PMMHYPERCHUNKFREE)CTXSUFF(pHeap->pbHeap);
1025 for (;;)
1026 {
1027 if (MMHYPERCHUNK_ISUSED(&pCur->core))
1028 ASSERT_CHUNK_USED(pHeap, &pCur->core);
1029 else
1030 ASSERT_CHUNK_FREE(pHeap, pCur);
1031 if (pPrev)
1032 AssertMsg((int32_t)pPrev->core.offNext == -MMHYPERCHUNK_GET_OFFPREV(&pCur->core),
1033 ("pPrev->core.offNext=%d offPrev=%d\n", pPrev->core.offNext, MMHYPERCHUNK_GET_OFFPREV(&pCur->core)));
1034
1035 /* next */
1036 if (!pCur->core.offNext)
1037 break;
1038 pPrev = pCur;
1039 pCur = (PMMHYPERCHUNKFREE)((char *)pCur + pCur->core.offNext);
1040 }
1041}
1042#endif
1043
1044
1045#ifdef DEBUG
1046/**
1047 * Dumps the hypervisor heap to Log.
1048 * @param pVM VM Handle.
1049 */
1050MMDECL(void) MMHyperHeapDump(PVM pVM)
1051{
1052 Log(("MMHyperHeapDump: *** heap dump - start ***\n"));
1053 PMMHYPERHEAP pHeap = CTXSUFF(pVM->mm.s.pHyperHeap);
1054 PMMHYPERCHUNKFREE pCur = (PMMHYPERCHUNKFREE)CTXSUFF(pHeap->pbHeap);
1055 for (;;)
1056 {
1057 if (MMHYPERCHUNK_ISUSED(&pCur->core))
1058 {
1059 if (pCur->core.offStat)
1060 {
1061 PMMHYPERSTAT pStat = (PMMHYPERSTAT)((uintptr_t)pCur + pCur->core.offStat);
1062 const char *pszSelf = pCur->core.offStat == sizeof(MMHYPERCHUNK) ? " stat record" : "";
1063#ifdef IN_RING3
1064 Log(("%p %06x USED offNext=%06x offPrev=-%06x %s%s\n",
1065 pCur, (uintptr_t)pCur - (uintptr_t)CTXSUFF(pHeap->pbHeap),
1066 pCur->core.offNext, -MMHYPERCHUNK_GET_OFFPREV(&pCur->core),
1067 mmR3GetTagName((MMTAG)pStat->Core.Key), pszSelf));
1068#else
1069 Log(("%p %06x USED offNext=%06x offPrev=-%06x %d%s\n",
1070 pCur, (uintptr_t)pCur - (uintptr_t)CTXSUFF(pHeap->pbHeap),
1071 pCur->core.offNext, -MMHYPERCHUNK_GET_OFFPREV(&pCur->core),
1072 (MMTAG)pStat->Core.Key, pszSelf));
1073#endif
1074 }
1075 else
1076 Log(("%p %06x USED offNext=%06x offPrev=-%06x\n",
1077 pCur, (uintptr_t)pCur - (uintptr_t)CTXSUFF(pHeap->pbHeap),
1078 pCur->core.offNext, -MMHYPERCHUNK_GET_OFFPREV(&pCur->core)));
1079 }
1080 else
1081 Log(("%p %06x FREE offNext=%06x offPrev=-%06x : cb=%06x offNext=%06x offPrev=-%06x\n",
1082 pCur, (uintptr_t)pCur - (uintptr_t)CTXSUFF(pHeap->pbHeap),
1083 pCur->core.offNext, -MMHYPERCHUNK_GET_OFFPREV(&pCur->core), pCur->cb, pCur->offNext, pCur->offPrev));
1084
1085 /* next */
1086 if (!pCur->core.offNext)
1087 break;
1088 pCur = (PMMHYPERCHUNKFREE)((char *)pCur + pCur->core.offNext);
1089 }
1090 Log(("MMHyperHeapDump: *** heap dump - end ***\n"));
1091}
1092#endif
1093
1094
1095/**
1096 * Query the amount of free memory in the hypervisor heap.
1097 *
1098 * @returns Number of free bytes in the hypervisor heap.
1099 */
1100MMDECL(size_t) MMHyperHeapGetFreeSize(PVM pVM)
1101{
1102 return CTXSUFF(pVM->mm.s.pHyperHeap)->cbFree;
1103}
1104
1105/**
1106 * Query the size the hypervisor heap.
1107 *
1108 * @returns The size of the hypervisor heap in bytes.
1109 */
1110MMDECL(size_t) MMHyperHeapGetSize(PVM pVM)
1111{
1112 return CTXSUFF(pVM->mm.s.pHyperHeap)->cbHeap;
1113}
1114
1115
1116/**
1117 * Query the address and size the hypervisor memory area.
1118 *
1119 * @returns Base address of the hypervisor area.
1120 * @param pVM VM Handle.
1121 * @param pcb Where to store the size of the hypervisor area. (out)
1122 */
1123MMDECL(RTGCPTR) MMHyperGetArea(PVM pVM, size_t *pcb)
1124{
1125 if (pcb)
1126 *pcb = pVM->mm.s.cbHyperArea;
1127 return pVM->mm.s.pvHyperAreaGC;
1128}
1129
1130
1131/**
1132 * Checks if an address is within the hypervisor memory area.
1133 *
1134 * @returns true if inside.
1135 * @returns false if outside.
1136 * @param pVM VM handle.
1137 * @param GCPtr The pointer to check.
1138 */
1139MMDECL(bool) MMHyperIsInsideArea(PVM pVM, RTGCPTR GCPtr)
1140{
1141 return (RTGCUINTPTR)GCPtr - (RTGCUINTPTR)pVM->mm.s.pvHyperAreaGC < pVM->mm.s.cbHyperArea;
1142}
1143
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