VirtualBox

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

Last change on this file since 424 was 309, checked in by vboxsync, 18 years ago

DECLINLINE is supposed to be static of course.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette