VirtualBox

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

Last change on this file since 1939 was 769, checked in by vboxsync, 18 years ago

Prevent some warnings as these files are compiled as .c files on Linux now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.7 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 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 {
394 return NULL;
395 }
396
397 dumpheap ("pre alloc");
398
399 pBlock = NULL;
400
401 /* If there are free blocks in the heap, look at them. */
402 iter = g_vbgldata.pFreeBlocksHead;
403
404 /* There will be not many blocks in the heap, so
405 * linear search would be fast enough.
406 */
407
408 while (iter)
409 {
410 if (iter->cbDataSize == cbSize)
411 {
412 /* exact match */
413 pBlock = iter;
414 break;
415 }
416
417 /* Looking for a free block with nearest size */
418 if (iter->cbDataSize > cbSize)
419 {
420 if (pBlock)
421 {
422 if (iter->cbDataSize < pBlock->cbDataSize)
423 {
424 pBlock = iter;
425 }
426 }
427 else
428 {
429 pBlock = iter;
430 }
431 }
432
433 iter = iter->pNext;
434 }
435
436 if (!pBlock)
437 {
438 /* No free blocks, allocate a new chunk,
439 * the only free block of the chunk will
440 * be returned.
441 */
442 pBlock = vbglPhysHeapChunkAlloc (cbSize);
443 }
444
445 if (pBlock)
446 {
447 VBGL_PH_ASSERTMsg(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
448 ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->u32Signature));
449 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0,
450 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
451
452 /* We have a free block, either found or allocated. */
453
454 if (pBlock->cbDataSize > 2*(cbSize + sizeof (VBGLPHYSHEAPBLOCK)))
455 {
456 /* Data will occupy less than a half of the block,
457 * the block should be split.
458 */
459 iter = (VBGLPHYSHEAPBLOCK *)((char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK) + cbSize);
460
461 /* Init the new 'iter' block, initialized blocks are always marked as free. */
462 vbglPhysHeapInitBlock (iter, pBlock->pChunk, pBlock->cbDataSize - cbSize - sizeof (VBGLPHYSHEAPBLOCK));
463
464 pBlock->cbDataSize = cbSize;
465
466 /* Insert the new 'iter' block after the 'pBlock' in the free list */
467 vbglPhysHeapInsertBlock (pBlock, iter);
468 }
469
470 /* Exclude pBlock from free list */
471 vbglPhysHeapExcludeBlock (pBlock);
472
473 /* Mark as allocated */
474 pBlock->fu32Flags |= VBGL_PH_BF_ALLOCATED;
475
476 /* Insert to allocated list */
477 vbglPhysHeapInsertBlock (NULL, pBlock);
478
479 /* Adjust the chunk allocated blocks counter */
480 pBlock->pChunk->cAllocatedBlocks++;
481 }
482
483 dumpheap ("post alloc");
484
485 vbglPhysHeapLeave ();
486 VBGL_PH_dprintf(("VbglPhysHeapAlloc %x size %x\n", vbglPhysHeapBlock2Data (pBlock), pBlock->cbDataSize));
487
488 return vbglPhysHeapBlock2Data (pBlock);
489}
490
491DECLVBGL(RTCCPHYS) VbglPhysHeapGetPhysAddr (void *p)
492{
493 RTCCPHYS physAddr = 0;
494
495 VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapData2Block (p);
496
497 if (pBlock)
498 {
499 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0,
500 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
501
502 physAddr = pBlock->pChunk->physAddr + ((char *)p - (char *)pBlock->pChunk);
503 }
504
505 return physAddr;
506}
507
508DECLVBGL(void) VbglPhysHeapFree (void *p)
509{
510 VBGLPHYSHEAPBLOCK *pBlock;
511 VBGLPHYSHEAPBLOCK *pNeighbour;
512 int rc = vbglPhysHeapEnter ();
513
514 if (VBOX_FAILURE(rc))
515 {
516 return;
517 }
518
519 dumpheap ("pre free");
520
521 pBlock = vbglPhysHeapData2Block (p);
522
523 if (!pBlock)
524 {
525 return;
526 }
527
528 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0,
529 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
530
531 /* Exclude from allocated list */
532 vbglPhysHeapExcludeBlock (pBlock);
533
534 dumpheap ("post exclude");
535
536 VBGL_PH_dprintf(("VbglPhysHeapFree %x size %x\n", p, pBlock->cbDataSize));
537
538 /* Mark as free */
539 pBlock->fu32Flags &= ~VBGL_PH_BF_ALLOCATED;
540
541 /* Insert to free list */
542 vbglPhysHeapInsertBlock (NULL, pBlock);
543
544 dumpheap ("post insert");
545
546 /* Adjust the chunk allocated blocks counter */
547 pBlock->pChunk->cAllocatedBlocks--;
548
549 VBGL_PH_ASSERT(pBlock->pChunk->cAllocatedBlocks >= 0);
550
551 /* Check if we can merge 2 free blocks. To simplify heap maintenance,
552 * we will look at block after the just freed one.
553 * This will not prevent us from detecting free memory chunks.
554 * Also in most cases blocks are deallocated in reverse allocation order
555 * and in that case the merging will work.
556 */
557
558 pNeighbour = (VBGLPHYSHEAPBLOCK *)((char *)p + pBlock->cbDataSize);
559
560 if ((char *)pNeighbour < (char *)pBlock->pChunk + pBlock->pChunk->cbSize
561 && (pNeighbour->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0)
562 {
563 /* The next block is free as well. */
564
565 /* Adjust size of current memory block */
566 pBlock->cbDataSize += pNeighbour->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK);
567
568 /* Exclude the next neighbour */
569 vbglPhysHeapExcludeBlock (pNeighbour);
570 }
571
572 dumpheap ("post merge");
573
574 /* now check if there are 2 or more free chunks */
575 if (pBlock->pChunk->cAllocatedBlocks == 0)
576 {
577 VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead;
578
579 uint32_t u32FreeChunks = 0;
580
581 while (pChunk)
582 {
583 if (pChunk->cAllocatedBlocks == 0)
584 {
585 u32FreeChunks++;
586 }
587
588 pChunk = pChunk->pNext;
589 }
590
591 if (u32FreeChunks > 1)
592 {
593 /* Delete current chunk, it will also exclude all free blocks
594 * remaining in the chunk from the free list, so the pBlock
595 * will also be invalid after this.
596 */
597 vbglPhysHeapChunkDelete (pBlock->pChunk);
598 }
599 }
600
601 dumpheap ("post free");
602
603 vbglPhysHeapLeave ();
604}
605
606DECLVBGL(int) VbglPhysHeapInit (void)
607{
608 int rc = VINF_SUCCESS;
609
610 /* Allocate the first chunk of the heap. */
611 VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapChunkAlloc (0);
612
613 if (!pBlock)
614 {
615 rc = VERR_NO_MEMORY;
616 }
617
618 RTSemFastMutexCreate(&g_vbgldata.mutexHeap);
619
620 return rc;
621}
622
623DECLVBGL(void) VbglPhysHeapTerminate (void)
624{
625 while (g_vbgldata.pChunkHead)
626 {
627 vbglPhysHeapChunkDelete (g_vbgldata.pChunkHead);
628 }
629
630 RTSemFastMutexDestroy(g_vbgldata.mutexHeap);
631}
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