VirtualBox

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

Last change on this file since 9292 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.8 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-2007 Sun Microsystems, Inc.
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 (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
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 RTCCPHYS physAddr;
290 VBGLPHYSHEAPCHUNK *pChunk;
291 VBGLPHYSHEAPBLOCK *pBlock;
292 VBGL_PH_dprintf(("Allocating new chunk of size %d\n", cbSize));
293
294 /* Compute chunk size to allocate */
295 if (cbSize < VBGL_PH_CHUNKSIZE)
296 {
297 /* Includes case of block size 0 during initialization */
298 cbSize = VBGL_PH_CHUNKSIZE;
299 }
300 else
301 {
302 /* Round up to next chunk size, which must be power of 2 */
303 cbSize = (cbSize + (VBGL_PH_CHUNKSIZE - 1)) & ~(VBGL_PH_CHUNKSIZE - 1);
304 }
305
306 physAddr = 0;
307 pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc (&physAddr, cbSize);
308
309 if (!pChunk)
310 {
311 return NULL;
312 }
313
314 pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE;
315 pChunk->cbSize = cbSize;
316 pChunk->physAddr = physAddr;
317 pChunk->cAllocatedBlocks = 0;
318 pChunk->pNext = g_vbgldata.pChunkHead;
319 pChunk->pPrev = NULL;
320
321 /* Initialize the free block, which now occupies entire chunk. */
322 pBlock = (VBGLPHYSHEAPBLOCK *)((char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK));
323
324 vbglPhysHeapInitBlock (pBlock, pChunk, cbSize - sizeof (VBGLPHYSHEAPCHUNK) - sizeof (VBGLPHYSHEAPBLOCK));
325
326 vbglPhysHeapInsertBlock (NULL, pBlock);
327
328 g_vbgldata.pChunkHead = pChunk;
329
330 VBGL_PH_dprintf(("Allocated chunk %p, block = %p size=%x\n", pChunk, pBlock, cbSize));
331
332 return pBlock;
333}
334
335
336void vbglPhysHeapChunkDelete (VBGLPHYSHEAPCHUNK *pChunk)
337{
338 char *p;
339 VBGL_PH_ASSERT(pChunk != NULL);
340 VBGL_PH_ASSERTMsg(pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE,
341 ("pChunk->u32Signature = %08X\n", pChunk->u32Signature));
342
343 VBGL_PH_dprintf(("Deleting chunk %p size %x\n", pChunk, pChunk->cbSize));
344
345 /* first scan the chunk and exclude all blocks from lists */
346
347 p = (char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK);
348
349 while (p < (char *)pChunk + pChunk->cbSize)
350 {
351 VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)p;
352
353 p += pBlock->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK);
354
355 vbglPhysHeapExcludeBlock (pBlock);
356 }
357
358 VBGL_PH_ASSERTMsg(p == (char *)pChunk + pChunk->cbSize,
359 ("p = %p, (char *)pChunk + pChunk->cbSize = %p, pChunk->cbSize = %08X\n",
360 p, (char *)pChunk + pChunk->cbSize, pChunk->cbSize));
361
362 /* Exclude chunk from the chunk list */
363 if (pChunk->pNext)
364 {
365 pChunk->pNext->pPrev = pChunk->pPrev;
366 }
367 else
368 {
369 /* we do not maintain tail */
370 ;
371 }
372
373 if (pChunk->pPrev)
374 {
375 pChunk->pPrev->pNext = pChunk->pNext;
376 }
377 else
378 {
379 /* the chunk was head */
380 g_vbgldata.pChunkHead = pChunk->pNext;
381 }
382
383 RTMemContFree (pChunk, pChunk->cbSize);
384}
385
386
387DECLVBGL(void *) VbglPhysHeapAlloc (uint32_t cbSize)
388{
389 VBGLPHYSHEAPBLOCK *pBlock, *iter;
390 int rc = vbglPhysHeapEnter ();
391
392 if (VBOX_FAILURE(rc))
393 return NULL;
394
395 dumpheap ("pre alloc");
396
397 pBlock = NULL;
398
399 /* If there are free blocks in the heap, look at them. */
400 iter = g_vbgldata.pFreeBlocksHead;
401
402 /* There will be not many blocks in the heap, so
403 * linear search would be fast enough.
404 */
405
406 while (iter)
407 {
408 if (iter->cbDataSize == cbSize)
409 {
410 /* exact match */
411 pBlock = iter;
412 break;
413 }
414
415 /* Looking for a free block with nearest size */
416 if (iter->cbDataSize > cbSize)
417 {
418 if (pBlock)
419 {
420 if (iter->cbDataSize < pBlock->cbDataSize)
421 {
422 pBlock = iter;
423 }
424 }
425 else
426 {
427 pBlock = iter;
428 }
429 }
430
431 iter = iter->pNext;
432 }
433
434 if (!pBlock)
435 {
436 /* No free blocks, allocate a new chunk,
437 * the only free block of the chunk will
438 * be returned.
439 */
440 pBlock = vbglPhysHeapChunkAlloc (cbSize);
441 }
442
443 if (pBlock)
444 {
445 VBGL_PH_ASSERTMsg(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
446 ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->u32Signature));
447 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0,
448 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
449
450 /* We have a free block, either found or allocated. */
451
452 if (pBlock->cbDataSize > 2*(cbSize + sizeof (VBGLPHYSHEAPBLOCK)))
453 {
454 /* Data will occupy less than a half of the block,
455 * the block should be split.
456 */
457 iter = (VBGLPHYSHEAPBLOCK *)((char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK) + cbSize);
458
459 /* Init the new 'iter' block, initialized blocks are always marked as free. */
460 vbglPhysHeapInitBlock (iter, pBlock->pChunk, pBlock->cbDataSize - cbSize - sizeof (VBGLPHYSHEAPBLOCK));
461
462 pBlock->cbDataSize = cbSize;
463
464 /* Insert the new 'iter' block after the 'pBlock' in the free list */
465 vbglPhysHeapInsertBlock (pBlock, iter);
466 }
467
468 /* Exclude pBlock from free list */
469 vbglPhysHeapExcludeBlock (pBlock);
470
471 /* Mark as allocated */
472 pBlock->fu32Flags |= VBGL_PH_BF_ALLOCATED;
473
474 /* Insert to allocated list */
475 vbglPhysHeapInsertBlock (NULL, pBlock);
476
477 /* Adjust the chunk allocated blocks counter */
478 pBlock->pChunk->cAllocatedBlocks++;
479 }
480
481 dumpheap ("post alloc");
482
483 vbglPhysHeapLeave ();
484 VBGL_PH_dprintf(("VbglPhysHeapAlloc %x size %x\n", vbglPhysHeapBlock2Data (pBlock), pBlock->cbDataSize));
485
486 return vbglPhysHeapBlock2Data (pBlock);
487}
488
489DECLVBGL(RTCCPHYS) VbglPhysHeapGetPhysAddr (void *p)
490{
491 RTCCPHYS physAddr = 0;
492 VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapData2Block (p);
493
494 if (pBlock)
495 {
496 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0,
497 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
498
499 if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
500 physAddr = pBlock->pChunk->physAddr + ((char *)p - (char *)pBlock->pChunk);
501 }
502
503 return physAddr;
504}
505
506DECLVBGL(void) VbglPhysHeapFree (void *p)
507{
508 VBGLPHYSHEAPBLOCK *pBlock;
509 VBGLPHYSHEAPBLOCK *pNeighbour;
510
511 int rc = vbglPhysHeapEnter ();
512 if (VBOX_FAILURE(rc))
513 return;
514
515 dumpheap ("pre free");
516
517 pBlock = vbglPhysHeapData2Block (p);
518
519 if (!pBlock)
520 {
521 vbglPhysHeapLeave ();
522 return;
523 }
524
525 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0,
526 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
527
528 /* Exclude from allocated list */
529 vbglPhysHeapExcludeBlock (pBlock);
530
531 dumpheap ("post exclude");
532
533 VBGL_PH_dprintf(("VbglPhysHeapFree %x size %x\n", p, pBlock->cbDataSize));
534
535 /* Mark as free */
536 pBlock->fu32Flags &= ~VBGL_PH_BF_ALLOCATED;
537
538 /* Insert to free list */
539 vbglPhysHeapInsertBlock (NULL, pBlock);
540
541 dumpheap ("post insert");
542
543 /* Adjust the chunk allocated blocks counter */
544 pBlock->pChunk->cAllocatedBlocks--;
545
546 VBGL_PH_ASSERT(pBlock->pChunk->cAllocatedBlocks >= 0);
547
548 /* Check if we can merge 2 free blocks. To simplify heap maintenance,
549 * we will look at block after the just freed one.
550 * This will not prevent us from detecting free memory chunks.
551 * Also in most cases blocks are deallocated in reverse allocation order
552 * and in that case the merging will work.
553 */
554
555 pNeighbour = (VBGLPHYSHEAPBLOCK *)((char *)p + pBlock->cbDataSize);
556
557 if ((char *)pNeighbour < (char *)pBlock->pChunk + pBlock->pChunk->cbSize
558 && (pNeighbour->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0)
559 {
560 /* The next block is free as well. */
561
562 /* Adjust size of current memory block */
563 pBlock->cbDataSize += pNeighbour->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK);
564
565 /* Exclude the next neighbour */
566 vbglPhysHeapExcludeBlock (pNeighbour);
567 }
568
569 dumpheap ("post merge");
570
571 /* now check if there are 2 or more free chunks */
572 if (pBlock->pChunk->cAllocatedBlocks == 0)
573 {
574 VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead;
575
576 uint32_t u32FreeChunks = 0;
577
578 while (pChunk)
579 {
580 if (pChunk->cAllocatedBlocks == 0)
581 {
582 u32FreeChunks++;
583 }
584
585 pChunk = pChunk->pNext;
586 }
587
588 if (u32FreeChunks > 1)
589 {
590 /* Delete current chunk, it will also exclude all free blocks
591 * remaining in the chunk from the free list, so the pBlock
592 * will also be invalid after this.
593 */
594 vbglPhysHeapChunkDelete (pBlock->pChunk);
595 }
596 }
597
598 dumpheap ("post free");
599
600 vbglPhysHeapLeave ();
601}
602
603DECLVBGL(int) VbglPhysHeapInit (void)
604{
605 int rc = VINF_SUCCESS;
606
607 /* Allocate the first chunk of the heap. */
608 VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapChunkAlloc (0);
609
610 if (!pBlock)
611 rc = VERR_NO_MEMORY;
612
613 RTSemFastMutexCreate(&g_vbgldata.mutexHeap);
614
615 return rc;
616}
617
618DECLVBGL(void) VbglPhysHeapTerminate (void)
619{
620 while (g_vbgldata.pChunkHead)
621 {
622 vbglPhysHeapChunkDelete (g_vbgldata.pChunkHead);
623 }
624
625 RTSemFastMutexDestroy(g_vbgldata.mutexHeap);
626}
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