VirtualBox

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

Last change on this file since 15606 was 14297, checked in by vboxsync, 16 years ago

Corrected a couple of grammos.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 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 Assert
55#define VBGL_PH_ASSERTMsg AssertMsg
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 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(RT_SUCCESS(rc),
178 ("Failed to request heap mutex, rc = %Rrc\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 /* This function allocates physical contiguous memory (below 4GB) according to the IPRT docs.
308 * Address < 4G is required for the port IO.
309 */
310 pChunk = (VBGLPHYSHEAPCHUNK *)RTMemContAlloc (&physAddr, cbSize);
311
312 if (!pChunk)
313 {
314 return NULL;
315 }
316
317 pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE;
318 pChunk->cbSize = cbSize;
319 pChunk->physAddr = physAddr;
320 pChunk->cAllocatedBlocks = 0;
321 pChunk->pNext = g_vbgldata.pChunkHead;
322 pChunk->pPrev = NULL;
323
324 /* Initialize the free block, which now occupies entire chunk. */
325 pBlock = (VBGLPHYSHEAPBLOCK *)((char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK));
326
327 vbglPhysHeapInitBlock (pBlock, pChunk, cbSize - sizeof (VBGLPHYSHEAPCHUNK) - sizeof (VBGLPHYSHEAPBLOCK));
328
329 vbglPhysHeapInsertBlock (NULL, pBlock);
330
331 g_vbgldata.pChunkHead = pChunk;
332
333 VBGL_PH_dprintf(("Allocated chunk %p, block = %p size=%x\n", pChunk, pBlock, cbSize));
334
335 return pBlock;
336}
337
338
339void vbglPhysHeapChunkDelete (VBGLPHYSHEAPCHUNK *pChunk)
340{
341 char *p;
342 VBGL_PH_ASSERT(pChunk != NULL);
343 VBGL_PH_ASSERTMsg(pChunk->u32Signature == VBGL_PH_CHUNKSIGNATURE,
344 ("pChunk->u32Signature = %08X\n", pChunk->u32Signature));
345
346 VBGL_PH_dprintf(("Deleting chunk %p size %x\n", pChunk, pChunk->cbSize));
347
348 /* first scan the chunk and exclude all blocks from lists */
349
350 p = (char *)pChunk + sizeof (VBGLPHYSHEAPCHUNK);
351
352 while (p < (char *)pChunk + pChunk->cbSize)
353 {
354 VBGLPHYSHEAPBLOCK *pBlock = (VBGLPHYSHEAPBLOCK *)p;
355
356 p += pBlock->cbDataSize + sizeof (VBGLPHYSHEAPBLOCK);
357
358 vbglPhysHeapExcludeBlock (pBlock);
359 }
360
361 VBGL_PH_ASSERTMsg(p == (char *)pChunk + pChunk->cbSize,
362 ("p = %p, (char *)pChunk + pChunk->cbSize = %p, pChunk->cbSize = %08X\n",
363 p, (char *)pChunk + pChunk->cbSize, pChunk->cbSize));
364
365 /* Exclude chunk from the chunk list */
366 if (pChunk->pNext)
367 {
368 pChunk->pNext->pPrev = pChunk->pPrev;
369 }
370 else
371 {
372 /* we do not maintain tail */
373 ;
374 }
375
376 if (pChunk->pPrev)
377 {
378 pChunk->pPrev->pNext = pChunk->pNext;
379 }
380 else
381 {
382 /* the chunk was head */
383 g_vbgldata.pChunkHead = pChunk->pNext;
384 }
385
386 RTMemContFree (pChunk, pChunk->cbSize);
387}
388
389
390DECLVBGL(void *) VbglPhysHeapAlloc (uint32_t cbSize)
391{
392 VBGLPHYSHEAPBLOCK *pBlock, *iter;
393 int rc = vbglPhysHeapEnter ();
394
395 if (RT_FAILURE(rc))
396 return NULL;
397
398 dumpheap ("pre alloc");
399
400 pBlock = NULL;
401
402 /* If there are free blocks in the heap, look at them. */
403 iter = g_vbgldata.pFreeBlocksHead;
404
405 /* There will be not many blocks in the heap, so
406 * linear search would be fast enough.
407 */
408
409 while (iter)
410 {
411 if (iter->cbDataSize == cbSize)
412 {
413 /* exact match */
414 pBlock = iter;
415 break;
416 }
417
418 /* Looking for a free block with nearest size */
419 if (iter->cbDataSize > cbSize)
420 {
421 if (pBlock)
422 {
423 if (iter->cbDataSize < pBlock->cbDataSize)
424 {
425 pBlock = iter;
426 }
427 }
428 else
429 {
430 pBlock = iter;
431 }
432 }
433
434 iter = iter->pNext;
435 }
436
437 if (!pBlock)
438 {
439 /* No free blocks, allocate a new chunk,
440 * the only free block of the chunk will
441 * be returned.
442 */
443 pBlock = vbglPhysHeapChunkAlloc (cbSize);
444 }
445
446 if (pBlock)
447 {
448 VBGL_PH_ASSERTMsg(pBlock->u32Signature == VBGL_PH_BLOCKSIGNATURE,
449 ("pBlock = %p, pBlock->u32Signature = %08X\n", pBlock, pBlock->u32Signature));
450 VBGL_PH_ASSERTMsg((pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) == 0,
451 ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags));
452
453 /* We have a free block, either found or allocated. */
454
455 if (pBlock->cbDataSize > 2*(cbSize + sizeof (VBGLPHYSHEAPBLOCK)))
456 {
457 /* Data will occupy less than a half of the block,
458 * the block should be split.
459 */
460 iter = (VBGLPHYSHEAPBLOCK *)((char *)pBlock + sizeof (VBGLPHYSHEAPBLOCK) + cbSize);
461
462 /* Init the new 'iter' block, initialized blocks are always marked as free. */
463 vbglPhysHeapInitBlock (iter, pBlock->pChunk, pBlock->cbDataSize - cbSize - sizeof (VBGLPHYSHEAPBLOCK));
464
465 pBlock->cbDataSize = cbSize;
466
467 /* Insert the new 'iter' block after the 'pBlock' in the free list */
468 vbglPhysHeapInsertBlock (pBlock, iter);
469 }
470
471 /* Exclude pBlock from free list */
472 vbglPhysHeapExcludeBlock (pBlock);
473
474 /* Mark as allocated */
475 pBlock->fu32Flags |= VBGL_PH_BF_ALLOCATED;
476
477 /* Insert to allocated list */
478 vbglPhysHeapInsertBlock (NULL, pBlock);
479
480 /* Adjust the chunk allocated blocks counter */
481 pBlock->pChunk->cAllocatedBlocks++;
482 }
483
484 dumpheap ("post alloc");
485
486 vbglPhysHeapLeave ();
487 VBGL_PH_dprintf(("VbglPhysHeapAlloc %x size %x\n", vbglPhysHeapBlock2Data (pBlock), pBlock->cbDataSize));
488
489 return vbglPhysHeapBlock2Data (pBlock);
490}
491
492DECLVBGL(RTCCPHYS) VbglPhysHeapGetPhysAddr (void *p)
493{
494 RTCCPHYS physAddr = 0;
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 if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED)
503 physAddr = pBlock->pChunk->physAddr + ((char *)p - (char *)pBlock->pChunk);
504 }
505
506 return physAddr;
507}
508
509DECLVBGL(void) VbglPhysHeapFree (void *p)
510{
511 VBGLPHYSHEAPBLOCK *pBlock;
512 VBGLPHYSHEAPBLOCK *pNeighbour;
513
514 int rc = vbglPhysHeapEnter ();
515 if (RT_FAILURE(rc))
516 return;
517
518 dumpheap ("pre free");
519
520 pBlock = vbglPhysHeapData2Block (p);
521
522 if (!pBlock)
523 {
524 vbglPhysHeapLeave ();
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 rc = VERR_NO_MEMORY;
615
616 RTSemFastMutexCreate(&g_vbgldata.mutexHeap);
617
618 return rc;
619}
620
621DECLVBGL(void) VbglPhysHeapTerminate (void)
622{
623 while (g_vbgldata.pChunkHead)
624 {
625 vbglPhysHeapChunkDelete (g_vbgldata.pChunkHead);
626 }
627
628 RTSemFastMutexDestroy(g_vbgldata.mutexHeap);
629}
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