VirtualBox

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

Last change on this file since 24686 was 21211, checked in by vboxsync, 15 years ago

VBoxGuest.h,VBoxGuestLib: Moved the VbglR3 API out of VBoxGuest.h and did some cleanup.

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