VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuestLib/PhysHeap.cpp@ 63206

Last change on this file since 63206 was 62521, checked in by vboxsync, 8 years ago

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.3 KB
Line 
1/* $Id: PhysHeap.cpp 62521 2016-07-22 19:16:33Z vboxsync $ */
2/** @file
3 * VBoxGuestLibR0 - Physical memory heap.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27#include "VBGLInternal.h"
28
29#include <iprt/assert.h>
30#include <iprt/semaphore.h>
31#include <iprt/alloc.h>
32
33/* Physical memory heap consists of double linked list
34 * of chunks. Memory blocks are allocated inside these chunks
35 * and are members of Allocated and Free double linked lists.
36 *
37 * When allocating a block, we search in Free linked
38 * list for a suitable free block. If there is no such block,
39 * a new chunk is allocated and the new block is taken from
40 * the new chunk as the only chunk-sized free block.
41 * Allocated block is excluded from the Free list and goes to
42 * Alloc list.
43 *
44 * When freeing block, we check the pointer and then
45 * exclude block from Alloc list and move it to free list.
46 *
47 * For each chunk we maintain the allocated blocks counter.
48 * if 2 (or more) entire chunks are free they are immediately
49 * deallocated, so we always have at most 1 free chunk.
50 *
51 * When freeing blocks, two subsequent free blocks are always
52 * merged together. Current implementation merges blocks only
53 * when there is a block after the just freed one.
54 *
55 */
56
57#define VBGL_PH_ASSERT Assert
58#define VBGL_PH_ASSERTMsg AssertMsg
59
60// #define DUMPHEAP
61
62#ifdef DUMPHEAP
63# define VBGL_PH_dprintf(a) RTAssertMsg2Weak a
64#else
65# define VBGL_PH_dprintf(a)
66#endif
67
68/* Heap block signature */
69#define VBGL_PH_BLOCKSIGNATURE (0xADDBBBBB)
70
71
72/* Heap chunk signature */
73#define VBGL_PH_CHUNKSIGNATURE (0xADDCCCCC)
74/* Heap chunk allocation unit */
75#define VBGL_PH_CHUNKSIZE (0x10000)
76
77/* Heap block bit flags */
78#define VBGL_PH_BF_ALLOCATED (0x1)
79
80struct _VBGLPHYSHEAPBLOCK
81{
82 uint32_t u32Signature;
83
84 /* Size of user data in the block. Does not include the block header. */
85 uint32_t cbDataSize;
86
87 uint32_t fu32Flags;
88
89 struct _VBGLPHYSHEAPBLOCK *pNext;
90 struct _VBGLPHYSHEAPBLOCK *pPrev;
91
92 struct _VBGLPHYSHEAPCHUNK *pChunk;
93};
94
95struct _VBGLPHYSHEAPCHUNK
96{
97 uint32_t u32Signature;
98
99 /* Size of the chunk. Includes the chunk header. */
100 uint32_t cbSize;
101
102 /* Physical address of the chunk */
103 uint32_t physAddr;
104
105 /* Number of allocated blocks in the chunk */
106 int32_t cAllocatedBlocks;
107
108 struct _VBGLPHYSHEAPCHUNK *pNext;
109 struct _VBGLPHYSHEAPCHUNK *pPrev;
110};
111
112
113#ifndef DUMPHEAP
114#define dumpheap(a)
115#else
116void dumpheap (char *point)
117{
118 VBGL_PH_dprintf(("VBGL_PH dump at '%s'\n", point));
119
120 VBGL_PH_dprintf(("Chunks:\n"));
121
122 VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead;
123
124 while (pChunk)
125 {
126 VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, allocated = %8d, phys = %08X\n",
127 pChunk, pChunk->pNext, pChunk->pPrev, pChunk->u32Signature, pChunk->cbSize, pChunk->cAllocatedBlocks, pChunk->physAddr));
128
129 pChunk = pChunk->pNext;
130 }
131
132 VBGL_PH_dprintf(("Allocated blocks:\n"));
133
134 VBGLPHYSHEAPBLOCK *pBlock = g_vbgldata.pAllocBlocksHead;
135
136 while (pBlock)
137 {
138 VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, flags = %08X, pChunk = %p\n",
139 pBlock, pBlock->pNext, pBlock->pPrev, pBlock->u32Signature, pBlock->cbDataSize, pBlock->fu32Flags, pBlock->pChunk));
140
141 pBlock = pBlock->pNext;
142 }
143
144 VBGL_PH_dprintf(("Free blocks:\n"));
145
146 pBlock = g_vbgldata.pFreeBlocksHead;
147
148 while (pBlock)
149 {
150 VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, flags = %08X, pChunk = %p\n",
151 pBlock, pBlock->pNext, pBlock->pPrev, pBlock->u32Signature, pBlock->cbDataSize, pBlock->fu32Flags, pBlock->pChunk));
152
153 pBlock = pBlock->pNext;
154 }
155
156 VBGL_PH_dprintf(("VBGL_PH dump at '%s' done\n", point));
157}
158#endif
159
160
161DECLINLINE(void *) vbglPhysHeapBlock2Data (VBGLPHYSHEAPBLOCK *pBlock)
162{
163 return (void *)(pBlock? (char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK): NULL);
164}
165
166DECLINLINE(VBGLPHYSHEAPBLOCK *) vbglPhysHeapData2Block (void *p)
167{
168 VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)(p? (char *)p - sizeof (VBGLPHYSHEAPBLOCK): NULL);
169
170 VBGL_PH_ASSERTMsg(pBlock == NULL || pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
171 ("pBlock->u32Signature = %08X\n", pBlock->u32Signature));
172
173 return pBlock;
174}
175
176DECLINLINE(int) vbglPhysHeapEnter (void)
177{
178 int rc = RTSemFastMutexRequest(g_vbgldata.mutexHeap);
179
180 VBGL_PH_ASSERTMsg(RT_SUCCESS(rc),
181 ("Failed to request heap mutex, rc = %Rrc\n", rc));
182
183 return rc;
184}
185
186DECLINLINE(void) vbglPhysHeapLeave (void)
187{
188 RTSemFastMutexRelease(g_vbgldata.mutexHeap);
189}
190
191
192static void vbglPhysHeapInitBlock (VBGLPHYSHEAPBLOCK *pBlock, VBGLPHYSHEAPCHUNK *pChunk, uint32_t cbDataSize)
193{
194 VBGL_PH_ASSERT(pBlock != NULL);
195 VBGL_PH_ASSERT(pChunk != NULL);
196
197 pBlock->u32Signature = VBGL_PH_BLOCKSIGNATURE;
198 pBlock->cbDataSize = cbDataSize;
199 pBlock->fu32Flags = 0;
200 pBlock->pNext = NULL;
201 pBlock->pPrev = NULL;
202 pBlock->pChunk = pChunk;
203}
204
205
206static void vbglPhysHeapInsertBlock (VBGLPHYSHEAPBLOCK *pInsertAfter, VBGLPHYSHEAPBLOCK *pBlock)
207{
208 VBGL_PH_ASSERTMsg(pBlock->pNext == NULL,
209 ("pBlock->pNext = %p\n", pBlock->pNext));
210 VBGL_PH_ASSERTMsg(pBlock->pPrev == NULL,
211 ("pBlock->pPrev = %p\n", pBlock->pPrev));
212
213 if (pInsertAfter)
214 {
215 pBlock->pNext = pInsertAfter->pNext;
216 pBlock->pPrev = pInsertAfter;
217
218 if (pInsertAfter->pNext)
219 {
220 pInsertAfter->pNext->pPrev = pBlock;
221 }
222
223 pInsertAfter->pNext = pBlock;
224 }
225 else
226 {
227 /* inserting to head of list */
228 pBlock->pPrev = NULL;
229
230 if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
231 {
232 pBlock->pNext = g_vbgldata.pAllocBlocksHead;
233
234 if (g_vbgldata.pAllocBlocksHead)
235 {
236 g_vbgldata.pAllocBlocksHead->pPrev = pBlock;
237 }
238
239 g_vbgldata.pAllocBlocksHead = pBlock;
240 }
241 else
242 {
243 pBlock->pNext = g_vbgldata.pFreeBlocksHead;
244
245 if (g_vbgldata.pFreeBlocksHead)
246 {
247 g_vbgldata.pFreeBlocksHead->pPrev = pBlock;
248 }
249
250 g_vbgldata.pFreeBlocksHead = pBlock;
251 }
252 }
253}
254
255static void vbglPhysHeapExcludeBlock (VBGLPHYSHEAPBLOCK *pBlock)
256{
257 if (pBlock->pNext)
258 {
259 pBlock->pNext->pPrev = pBlock->pPrev;
260 }
261 else
262 {
263 /* this is tail of list but we do not maintain tails of block lists.
264 * so do nothing.
265 */
266 ;
267 }
268
269 if (pBlock->pPrev)
270 {
271 pBlock->pPrev->pNext = pBlock->pNext;
272 }
273 else
274 {
275 /* this is head of list but we do not maintain tails of block lists. */
276 if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
277 {
278 g_vbgldata.pAllocBlocksHead = pBlock->pNext;
279 }
280 else
281 {
282 g_vbgldata.pFreeBlocksHead = pBlock->pNext;
283 }
284 }
285
286 pBlock->pNext = NULL;
287 pBlock->pPrev = NULL;
288}
289
290static VBGLPHYSHEAPBLOCK *vbglPhysHeapChunkAlloc (uint32_t cbSize)
291{
292 RTCCPHYS physAddr;
293 VBGLPHYSHEAPCHUNK *pChunk;
294 VBGLPHYSHEAPBLOCK *pBlock;
295 VBGL_PH_dprintf(("Allocating new chunk of size %d\n", cbSize));
296
297 /* Compute chunk size to allocate */
298 if (cbSize < VBGL_PH_CHUNKSIZE)
299 {
300 /* Includes case of block size 0 during initialization */
301 cbSize = VBGL_PH_CHUNKSIZE;
302 }
303 else
304 {
305 /* Round up to next chunk size, which must be power of 2 */
306 cbSize = (cbSize + (VBGL_PH_CHUNKSIZE - 1)) & ~(VBGL_PH_CHUNKSIZE - 1);
307 }
308
309 physAddr = 0;
310 /* This function allocates physical contiguous memory (below 4GB) according to the IPRT docs.
311 * Address < 4G is required for the port IO.
312 */
313 pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc (&physAddr, cbSize);
314
315 if (!pChunk)
316 {
317 LogRel(("vbglPhysHeapChunkAlloc: failed to alloc %u contiguous bytes.\n", cbSize));
318 return NULL;
319 }
320
321 AssertRelease(physAddr < _4G && physAddr + cbSize <= _4G);
322
323 pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE;
324 pChunk->cbSize = cbSize;
325 pChunk->physAddr = (uint32_t)physAddr;
326 pChunk->cAllocatedBlocks = 0;
327 pChunk->pNext = g_vbgldata.pChunkHead;
328 pChunk->pPrev = NULL;
329
330 /* Initialize the free block, which now occupies entire chunk. */
331 pBlock = (VBGLPHYSHEAPBLOCK *)((char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK));
332
333 vbglPhysHeapInitBlock (pBlock, pChunk, cbSize - sizeof (VBGLPHYSHEAPCHUNK) - sizeof (VBGLPHYSHEAPBLOCK));
334
335 vbglPhysHeapInsertBlock (NULL, pBlock);
336
337 g_vbgldata.pChunkHead = pChunk;
338
339 VBGL_PH_dprintf(("Allocated chunk %p, block = %p size=%x\n", pChunk, pBlock, cbSize));
340
341 return pBlock;
342}
343
344
345void vbglPhysHeapChunkDelete (VBGLPHYSHEAPCHUNK *pChunk)
346{
347 char *p;
348 VBGL_PH_ASSERT(pChunk != NULL);
349 VBGL_PH_ASSERTMsg(pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE,
350 ("pChunk->u32Signature = %08X\n", pChunk->u32Signature));
351
352 VBGL_PH_dprintf(("Deleting chunk %p size %x\n", pChunk, pChunk->cbSize));
353
354 /* first scan the chunk and exclude all blocks from lists */
355
356 p = (char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK);
357
358 while (p < (char *)pChunk + pChunk->cbSize)
359 {
360 VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)p;
361
362 p += pBlock->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK);
363
364 vbglPhysHeapExcludeBlock (pBlock);
365 }
366
367 VBGL_PH_ASSERTMsg(p == (char *)pChunk + pChunk->cbSize,
368 ("p = %p, (char *)pChunk + pChunk->cbSize = %p, pChunk->cbSize = %08X\n",
369 p, (char *)pChunk + pChunk->cbSize, pChunk->cbSize));
370
371 /* Exclude chunk from the chunk list */
372 if (pChunk->pNext)
373 {
374 pChunk->pNext->pPrev = pChunk->pPrev;
375 }
376 else
377 {
378 /* we do not maintain tail */
379 ;
380 }
381
382 if (pChunk->pPrev)
383 {
384 pChunk->pPrev->pNext = pChunk->pNext;
385 }
386 else
387 {
388 /* the chunk was head */
389 g_vbgldata.pChunkHead = pChunk->pNext;
390 }
391
392 RTMemContFree (pChunk, pChunk->cbSize);
393}
394
395
396DECLVBGL(void *) VbglPhysHeapAlloc (uint32_t cbSize)
397{
398 VBGLPHYSHEAPBLOCK *pBlock, *iter;
399 int rc = vbglPhysHeapEnter ();
400
401 if (RT_FAILURE(rc))
402 return NULL;
403
404 dumpheap ("pre alloc");
405
406 pBlock = NULL;
407
408 /* If there are free blocks in the heap, look at them. */
409 iter = g_vbgldata.pFreeBlocksHead;
410
411 /* There will be not many blocks in the heap, so
412 * linear search would be fast enough.
413 */
414
415 while (iter)
416 {
417 if (iter->cbDataSize == cbSize)
418 {
419 /* exact match */
420 pBlock = iter;
421 break;
422 }
423
424 /* Looking for a free block with nearest size */
425 if (iter->cbDataSize > cbSize)
426 {
427 if (pBlock)
428 {
429 if (iter->cbDataSize < pBlock->cbDataSize)
430 {
431 pBlock = iter;
432 }
433 }
434 else
435 {
436 pBlock = iter;
437 }
438 }
439
440 iter = iter->pNext;
441 }
442
443 if (!pBlock)
444 {
445 /* No free blocks, allocate a new chunk,
446 * the only free block of the chunk will
447 * be returned.
448 */
449 pBlock = vbglPhysHeapChunkAlloc (cbSize);
450 }
451
452 if (pBlock)
453 {
454 VBGL_PH_ASSERTMsg(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
455 ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->u32Signature));
456 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0,
457 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
458
459 /* We have a free block, either found or allocated. */
460
461 if (pBlock->cbDataSize > 2*(cbSize + sizeof (VBGLPHYSHEAPBLOCK)))
462 {
463 /* Data will occupy less than a half of the block,
464 * the block should be split.
465 */
466 iter = (VBGLPHYSHEAPBLOCK *)((char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK) + cbSize);
467
468 /* Init the new 'iter' block, initialized blocks are always marked as free. */
469 vbglPhysHeapInitBlock (iter, pBlock->pChunk, pBlock->cbDataSize - cbSize - sizeof (VBGLPHYSHEAPBLOCK));
470
471 pBlock->cbDataSize = cbSize;
472
473 /* Insert the new 'iter' block after the 'pBlock' in the free list */
474 vbglPhysHeapInsertBlock (pBlock, iter);
475 }
476
477 /* Exclude pBlock from free list */
478 vbglPhysHeapExcludeBlock (pBlock);
479
480 /* Mark as allocated */
481 pBlock->fu32Flags |= VBGL_PH_BF_ALLOCATED;
482
483 /* Insert to allocated list */
484 vbglPhysHeapInsertBlock (NULL, pBlock);
485
486 /* Adjust the chunk allocated blocks counter */
487 pBlock->pChunk->cAllocatedBlocks++;
488 }
489
490 dumpheap ("post alloc");
491
492 vbglPhysHeapLeave ();
493 VBGL_PH_dprintf(("VbglPhysHeapAlloc %x size %x\n", vbglPhysHeapBlock2Data (pBlock), pBlock->cbDataSize));
494
495 return vbglPhysHeapBlock2Data (pBlock);
496}
497
498DECLVBGL(uint32_t) VbglPhysHeapGetPhysAddr (void *p)
499{
500 uint32_t physAddr = 0;
501 VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapData2Block (p);
502
503 if (pBlock)
504 {
505 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0,
506 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
507
508 if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
509 physAddr = pBlock->pChunk->physAddr + (uint32_t)((uintptr_t)p - (uintptr_t)pBlock->pChunk);
510 }
511
512 return physAddr;
513}
514
515DECLVBGL(void) VbglPhysHeapFree(void *p)
516{
517 VBGLPHYSHEAPBLOCK *pBlock;
518 VBGLPHYSHEAPBLOCK *pNeighbour;
519
520 int rc = vbglPhysHeapEnter ();
521 if (RT_FAILURE(rc))
522 return;
523
524 dumpheap ("pre free");
525
526 pBlock = vbglPhysHeapData2Block (p);
527
528 if (!pBlock)
529 {
530 vbglPhysHeapLeave ();
531 return;
532 }
533
534 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0,
535 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
536
537 /* Exclude from allocated list */
538 vbglPhysHeapExcludeBlock (pBlock);
539
540 dumpheap ("post exclude");
541
542 VBGL_PH_dprintf(("VbglPhysHeapFree %x size %x\n", p, pBlock->cbDataSize));
543
544 /* Mark as free */
545 pBlock->fu32Flags &= ~VBGL_PH_BF_ALLOCATED;
546
547 /* Insert to free list */
548 vbglPhysHeapInsertBlock (NULL, pBlock);
549
550 dumpheap ("post insert");
551
552 /* Adjust the chunk allocated blocks counter */
553 pBlock->pChunk->cAllocatedBlocks--;
554
555 VBGL_PH_ASSERT(pBlock->pChunk->cAllocatedBlocks >= 0);
556
557 /* Check if we can merge 2 free blocks. To simplify heap maintenance,
558 * we will look at block after the just freed one.
559 * This will not prevent us from detecting free memory chunks.
560 * Also in most cases blocks are deallocated in reverse allocation order
561 * and in that case the merging will work.
562 */
563
564 pNeighbour = (VBGLPHYSHEAPBLOCK *)((char *)p + pBlock->cbDataSize);
565
566 if ((char *)pNeighbour < (char *)pBlock->pChunk + pBlock->pChunk->cbSize
567 && (pNeighbour->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0)
568 {
569 /* The next block is free as well. */
570
571 /* Adjust size of current memory block */
572 pBlock->cbDataSize += pNeighbour->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK);
573
574 /* Exclude the next neighbour */
575 vbglPhysHeapExcludeBlock (pNeighbour);
576 }
577
578 dumpheap ("post merge");
579
580 /* now check if there are 2 or more free chunks */
581 if (pBlock->pChunk->cAllocatedBlocks == 0)
582 {
583 VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead;
584
585 uint32_t u32FreeChunks = 0;
586
587 while (pChunk)
588 {
589 if (pChunk->cAllocatedBlocks == 0)
590 {
591 u32FreeChunks++;
592 }
593
594 pChunk = pChunk->pNext;
595 }
596
597 if (u32FreeChunks > 1)
598 {
599 /* Delete current chunk, it will also exclude all free blocks
600 * remaining in the chunk from the free list, so the pBlock
601 * will also be invalid after this.
602 */
603 vbglPhysHeapChunkDelete (pBlock->pChunk);
604 }
605 }
606
607 dumpheap ("post free");
608
609 vbglPhysHeapLeave ();
610}
611
612DECLVBGL(int) VbglPhysHeapInit (void)
613{
614 int rc = VINF_SUCCESS;
615
616 /* Allocate the first chunk of the heap. */
617 VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapChunkAlloc (0);
618
619 if (!pBlock)
620 rc = VERR_NO_MEMORY;
621
622 RTSemFastMutexCreate(&g_vbgldata.mutexHeap);
623
624 return rc;
625}
626
627DECLVBGL(void) VbglPhysHeapTerminate (void)
628{
629 while (g_vbgldata.pChunkHead)
630 {
631 vbglPhysHeapChunkDelete (g_vbgldata.pChunkHead);
632 }
633
634 RTSemFastMutexDestroy(g_vbgldata.mutexHeap);
635}
636
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use