VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibPhysHeap.cpp@ 75724

Last change on this file since 75724 was 72627, checked in by vboxsync, 6 years ago

Additions: relicence components needed for Linux shared folders to MIT.
bugref:9109: Shared folders: update to match in-kernel code more closely
This change makes the code on which the Linux kernel shared folder patch is
based MIT-licenced, so that the version in the Linux kernel can be too. This
would make it easier to move code back and forth.

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