VirtualBox

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

Last change on this file since 4968 was 4113, checked in by vboxsync, 17 years ago

Backed out accidental commit

  • 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-2007 innotek 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
19#include <VBox/VBoxGuestLib.h>
20#include "VBGLInternal.h"
21
22#include <iprt/assert.h>
23#include <iprt/semaphore.h>
24#include <iprt/alloc.h>
25
26/* Physical memory heap consists of double linked list
27 * of chunks. Memory blocks are allocated inside these chunks
28 * and are members of Allocated and Free double linked lists.
29 *
30 * When allocating a block, we search in Free linked
31 * list for a suitable free block. If there is no such block,
32 * a new chunk is allocated and the new block is taken from
33 * the new chunk as the only chunk-sized free block.
34 * Allocated block is excluded from the Free list and goes to
35 * Alloc list.
36 *
37 * When freeing block, we check the pointer and then
38 * exclude block from Alloc list and move it to free list.
39 *
40 * For each chunk we maintain the allocated blocks counter.
41 * if 2 (or more) entire chunks are free they are immediately
42 * deallocated, so we always have at most 1 free chunk.
43 *
44 * When freeing blocks, two subsequent free blocks are always
45 * merged together. Current implementation merges blocks only
46 * when there is a block after the just freed one.
47 *
48 */
49
50#define VBGL_PH_ASSERT AssertRelease
51#define VBGL_PH_ASSERTMsg AssertReleaseMsg
52
53// #define DUMPHEAP
54
55#ifdef DUMPHEAP
56#define VBGL_PH_dprintf(a) AssertMsg2 a
57#else
58#define VBGL_PH_dprintf(a)
59#endif
60
61/* Heap block signature */
62#define VBGL_PH_BLOCKSIGNATURE (0xADDBBBBB)
63
64
65/* Heap chunk signarure */
66#define VBGL_PH_CHUNKSIGNATURE (0xADDCCCCC)
67/* Heap chunk allocation unit */
68#define VBGL_PH_CHUNKSIZE (0x10000)
69
70/* Heap block bit flags */
71#define VBGL_PH_BF_ALLOCATED (0x1)
72
73struct _VBGLPHYSHEAPBLOCK
74{
75 uint32_t u32Signature;
76
77 /* Size of user data in the block. Does not include the block header. */
78 uint32_t cbDataSize;
79
80 uint32_t fu32Flags;
81
82 struct _VBGLPHYSHEAPBLOCK *pNext;
83 struct _VBGLPHYSHEAPBLOCK *pPrev;
84
85 struct _VBGLPHYSHEAPCHUNK *pChunk;
86};
87
88struct _VBGLPHYSHEAPCHUNK
89{
90 uint32_t u32Signature;
91
92 /* Size of the the chunk. Includes the chunk header. */
93 uint32_t cbSize;
94
95 /* Physical address of the chunk */
96 RTCCPHYS physAddr;
97
98 /* Number of allocated blocks in the chunk */
99 int32_t cAllocatedBlocks;
100
101 struct _VBGLPHYSHEAPCHUNK *pNext;
102 struct _VBGLPHYSHEAPCHUNK *pPrev;
103};
104
105
106#ifndef DUMPHEAP
107#define dumpheap(a)
108#else
109void dumpheap (char *point)
110{
111 VBGL_PH_dprintf(("VBGL_PH dump at '%s'\n", point));
112
113 VBGL_PH_dprintf(("Chunks:\n"));
114
115 VBGLPHYSHEAPCHUNK *pChunk = g_vbgldata.pChunkHead;
116
117 while (pChunk)
118 {
119 VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, allocated = %8d, phys = %08X\n",
120 pChunk, pChunk->pNext, pChunk->pPrev, pChunk->u32Signature, pChunk->cbSize, pChunk->cAllocatedBlocks, pChunk->physAddr));
121
122 pChunk = pChunk->pNext;
123 }
124
125 VBGL_PH_dprintf(("Allocated blocks:\n"));
126
127 VBGLPHYSHEAPBLOCK *pBlock = g_vbgldata.pAllocBlocksHead;
128
129 while (pBlock)
130 {
131 VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, flags = %08X, pChunk = %p\n",
132 pBlock, pBlock->pNext, pBlock->pPrev, pBlock->u32Signature, pBlock->cbDataSize, pBlock->fu32Flags, pBlock->pChunk));
133
134 pBlock = pBlock->pNext;
135 }
136
137 VBGL_PH_dprintf(("Free blocks:\n"));
138
139 pBlock = g_vbgldata.pFreeBlocksHead;
140
141 while (pBlock)
142 {
143 VBGL_PH_dprintf(("%p: pNext = %p, pPrev = %p, sign = %08X, size = %8d, flags = %08X, pChunk = %p\n",
144 pBlock, pBlock->pNext, pBlock->pPrev, pBlock->u32Signature, pBlock->cbDataSize, pBlock->fu32Flags, pBlock->pChunk));
145
146 pBlock = pBlock->pNext;
147 }
148
149 VBGL_PH_dprintf(("VBGL_PH dump at '%s' done\n", point));
150}
151#endif
152
153
154DECLINLINE(void *) vbglPhysHeapBlock2Data (VBGLPHYSHEAPBLOCK *pBlock)
155{
156 return (void *)(pBlock? (char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK): NULL);
157}
158
159DECLINLINE(VBGLPHYSHEAPBLOCK *) vbglPhysHeapData2Block (void *p)
160{
161 VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)(p? (char *)p - sizeof (VBGLPHYSHEAPBLOCK): NULL);
162
163 VBGL_PH_ASSERTMsg(pBlock == NULL || pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
164 ("pBlock->u32Signature = %08X\n", pBlock->u32Signature));
165
166 return pBlock;
167}
168
169DECLINLINE(int) vbglPhysHeapEnter (void)
170{
171 int rc = RTSemFastMutexRequest(g_vbgldata.mutexHeap);
172
173 VBGL_PH_ASSERTMsg(VBOX_SUCCESS(rc),
174 ("Failed to request heap mutex, rc = %Vrc\n", rc));
175
176 return rc;
177}
178
179DECLINLINE(void) vbglPhysHeapLeave (void)
180{
181 RTSemFastMutexRelease(g_vbgldata.mutexHeap);
182}
183
184
185static void vbglPhysHeapInitBlock (VBGLPHYSHEAPBLOCK *pBlock, VBGLPHYSHEAPCHUNK *pChunk, uint32_t cbDataSize)
186{
187 VBGL_PH_ASSERT(pBlock != NULL);
188 VBGL_PH_ASSERT(pChunk != NULL);
189
190 pBlock->u32Signature = VBGL_PH_BLOCKSIGNATURE;
191 pBlock->cbDataSize = cbDataSize;
192 pBlock->fu32Flags = 0;
193 pBlock->pNext = NULL;
194 pBlock->pPrev = NULL;
195 pBlock->pChunk = pChunk;
196}
197
198
199static void vbglPhysHeapInsertBlock (VBGLPHYSHEAPBLOCK *pInsertAfter, VBGLPHYSHEAPBLOCK *pBlock)
200{
201 VBGL_PH_ASSERTMsg(pBlock->pNext == NULL,
202 ("pBlock->pNext = %p\n", pBlock->pNext));
203 VBGL_PH_ASSERTMsg(pBlock->pPrev == NULL,
204 ("pBlock->pPrev = %p\n", pBlock->pPrev));
205
206 if (pInsertAfter)
207 {
208 pBlock->pNext = pInsertAfter->pNext;
209 pBlock->pPrev = pInsertAfter;
210
211 if (pInsertAfter->pNext)
212 {
213 pInsertAfter->pNext->pPrev = pBlock;
214 }
215
216 pInsertAfter->pNext = pBlock;
217 }
218 else
219 {
220 /* inserting to head of list */
221 pBlock->pPrev = NULL;
222
223 if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
224 {
225 pBlock->pNext = g_vbgldata.pAllocBlocksHead;
226
227 if (g_vbgldata.pAllocBlocksHead)
228 {
229 g_vbgldata.pAllocBlocksHead->pPrev = pBlock;
230 }
231
232 g_vbgldata.pAllocBlocksHead = pBlock;
233 }
234 else
235 {
236 pBlock->pNext = g_vbgldata.pFreeBlocksHead;
237
238 if (g_vbgldata.pFreeBlocksHead)
239 {
240 g_vbgldata.pFreeBlocksHead->pPrev = pBlock;
241 }
242
243 g_vbgldata.pFreeBlocksHead = pBlock;
244 }
245 }
246}
247
248static void vbglPhysHeapExcludeBlock (VBGLPHYSHEAPBLOCK *pBlock)
249{
250 if (pBlock->pNext)
251 {
252 pBlock->pNext->pPrev = pBlock->pPrev;
253 }
254 else
255 {
256 /* this is tail of list but we do not maintain tails of block lists.
257 * so do nothing.
258 */
259 ;
260 }
261
262 if (pBlock->pPrev)
263 {
264 pBlock->pPrev->pNext = pBlock->pNext;
265 }
266 else
267 {
268 /* this is head of list but we do not maintain tails of block lists. */
269 if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
270 {
271 g_vbgldata.pAllocBlocksHead = pBlock->pNext;
272 }
273 else
274 {
275 g_vbgldata.pFreeBlocksHead = pBlock->pNext;
276 }
277 }
278
279 pBlock->pNext = NULL;
280 pBlock->pPrev = NULL;
281}
282
283static VBGLPHYSHEAPBLOCK *vbglPhysHeapChunkAlloc (uint32_t cbSize)
284{
285 RTCCPHYS physAddr;
286 VBGLPHYSHEAPCHUNK *pChunk;
287 VBGLPHYSHEAPBLOCK *pBlock;
288 VBGL_PH_dprintf(("Allocating new chunk of size %d\n", cbSize));
289
290 /* Compute chunk size to allocate */
291 if (cbSize < VBGL_PH_CHUNKSIZE)
292 {
293 /* Includes case of block size 0 during initialization */
294 cbSize = VBGL_PH_CHUNKSIZE;
295 }
296 else
297 {
298 /* Round up to next chunk size, which must be power of 2 */
299 cbSize = (cbSize + (VBGL_PH_CHUNKSIZE - 1)) & ~(VBGL_PH_CHUNKSIZE - 1);
300 }
301
302 physAddr = 0;
303 pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc (&physAddr, cbSize);
304
305 if (!pChunk)
306 {
307 return NULL;
308 }
309
310 pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE;
311 pChunk->cbSize = cbSize;
312 pChunk->physAddr = physAddr;
313 pChunk->cAllocatedBlocks = 0;
314 pChunk->pNext = g_vbgldata.pChunkHead;
315 pChunk->pPrev = NULL;
316
317 /* Initialize the free block, which now occupies entire chunk. */
318 pBlock = (VBGLPHYSHEAPBLOCK *)((char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK));
319
320 vbglPhysHeapInitBlock (pBlock, pChunk, cbSize - sizeof (VBGLPHYSHEAPCHUNK) - sizeof (VBGLPHYSHEAPBLOCK));
321
322 vbglPhysHeapInsertBlock (NULL, pBlock);
323
324 g_vbgldata.pChunkHead = pChunk;
325
326 VBGL_PH_dprintf(("Allocated chunk %p, block = %p size=%x\n", pChunk, pBlock, cbSize));
327
328 return pBlock;
329}
330
331
332void vbglPhysHeapChunkDelete (VBGLPHYSHEAPCHUNK *pChunk)
333{
334 char *p;
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 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 VBGLPHYSHEAPBLOCK *pBlock, *iter;
386 int rc = vbglPhysHeapEnter ();
387
388 if (VBOX_FAILURE(rc))
389 return NULL;
390
391 dumpheap ("pre alloc");
392
393 pBlock = NULL;
394
395 /* If there are free blocks in the heap, look at them. */
396 iter = g_vbgldata.pFreeBlocksHead;
397
398 /* There will be not many blocks in the heap, so
399 * linear search would be fast enough.
400 */
401
402 while (iter)
403 {
404 if (iter->cbDataSize == cbSize)
405 {
406 /* exact match */
407 pBlock = iter;
408 break;
409 }
410
411 /* Looking for a free block with nearest size */
412 if (iter->cbDataSize > cbSize)
413 {
414 if (pBlock)
415 {
416 if (iter->cbDataSize < pBlock->cbDataSize)
417 {
418 pBlock = iter;
419 }
420 }
421 else
422 {
423 pBlock = iter;
424 }
425 }
426
427 iter = iter->pNext;
428 }
429
430 if (!pBlock)
431 {
432 /* No free blocks, allocate a new chunk,
433 * the only free block of the chunk will
434 * be returned.
435 */
436 pBlock = vbglPhysHeapChunkAlloc (cbSize);
437 }
438
439 if (pBlock)
440 {
441 VBGL_PH_ASSERTMsg(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
442 ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->u32Signature));
443 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0,
444 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
445
446 /* We have a free block, either found or allocated. */
447
448 if (pBlock->cbDataSize > 2*(cbSize + sizeof (VBGLPHYSHEAPBLOCK)))
449 {
450 /* Data will occupy less than a half of the block,
451 * the block should be split.
452 */
453 iter = (VBGLPHYSHEAPBLOCK *)((char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK) + cbSize);
454
455 /* Init the new 'iter' block, initialized blocks are always marked as free. */
456 vbglPhysHeapInitBlock (iter, pBlock->pChunk, pBlock->cbDataSize - cbSize - sizeof (VBGLPHYSHEAPBLOCK));
457
458 pBlock->cbDataSize = cbSize;
459
460 /* Insert the new 'iter' block after the 'pBlock' in the free list */
461 vbglPhysHeapInsertBlock (pBlock, iter);
462 }
463
464 /* Exclude pBlock from free list */
465 vbglPhysHeapExcludeBlock (pBlock);
466
467 /* Mark as allocated */
468 pBlock->fu32Flags |= VBGL_PH_BF_ALLOCATED;
469
470 /* Insert to allocated list */
471 vbglPhysHeapInsertBlock (NULL, pBlock);
472
473 /* Adjust the chunk allocated blocks counter */
474 pBlock->pChunk->cAllocatedBlocks++;
475 }
476
477 dumpheap ("post alloc");
478
479 vbglPhysHeapLeave ();
480 VBGL_PH_dprintf(("VbglPhysHeapAlloc %x size %x\n", vbglPhysHeapBlock2Data (pBlock), pBlock->cbDataSize));
481
482 return vbglPhysHeapBlock2Data (pBlock);
483}
484
485DECLVBGL(RTCCPHYS) VbglPhysHeapGetPhysAddr (void *p)
486{
487 RTCCPHYS physAddr = 0;
488 VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapData2Block (p);
489
490 if (pBlock)
491 {
492 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) != 0,
493 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
494
495 if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
496 physAddr = pBlock->pChunk->physAddr + ((char *)p - (char *)pBlock->pChunk);
497 }
498
499 return physAddr;
500}
501
502DECLVBGL(void) VbglPhysHeapFree (void *p)
503{
504 VBGLPHYSHEAPBLOCK *pBlock;
505 VBGLPHYSHEAPBLOCK *pNeighbour;
506
507 int rc = vbglPhysHeapEnter ();
508 if (VBOX_FAILURE(rc))
509 return;
510
511 dumpheap ("pre free");
512
513 pBlock = vbglPhysHeapData2Block (p);
514
515 if (!pBlock)
516 {
517 vbglPhysHeapLeave ();
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 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 rc = VERR_NO_MEMORY;
608
609 RTSemFastMutexCreate(&g_vbgldata.mutexHeap);
610
611 return rc;
612}
613
614DECLVBGL(void) VbglPhysHeapTerminate (void)
615{
616 while (g_vbgldata.pChunkHead)
617 {
618 vbglPhysHeapChunkDelete (g_vbgldata.pChunkHead);
619 }
620
621 RTSemFastMutexDestroy(g_vbgldata.mutexHeap);
622}
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