]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_heap.c
a5affb978f4eb7863c2c2662bc212a4f16cb0294
[karo-tx-linux.git] / drivers / mxc / gpu-viv / hal / kernel / gc_hal_kernel_heap.c
1 /****************************************************************************
2 *
3 *    Copyright (C) 2005 - 2013 by Vivante Corp.
4 *
5 *    This program is free software; you can redistribute it and/or modify
6 *    it under the terms of the GNU General Public License as published by
7 *    the Free Software Foundation; either version 2 of the license, or
8 *    (at your option) any later version.
9 *
10 *    This program is distributed in the hope that it will be useful,
11 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 *    GNU General Public License for more details.
14 *
15 *    You should have received a copy of the GNU General Public License
16 *    along with this program; if not write to the Free Software
17 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 *****************************************************************************/
20
21
22 /**
23 **  @file
24 **  gckHEAP object for kernel HAL layer.  The heap implemented here is an arena-
25 **  based memory allocation.  An arena-based memory heap allocates data quickly
26 **  from specified arenas and reduces memory fragmentation.
27 **
28 */
29 #include "gc_hal_kernel_precomp.h"
30
31 #define _GC_OBJ_ZONE            gcvZONE_HEAP
32
33 /*******************************************************************************
34 ***** Structures ***************************************************************
35 *******************************************************************************/
36
37 #define gcdIN_USE               ((gcskNODE_PTR) ~0)
38
39 typedef struct _gcskNODE *      gcskNODE_PTR;
40 typedef struct _gcskNODE
41 {
42     /* Number of byets in node. */
43     gctSIZE_T                   bytes;
44
45     /* Pointer to next free node, or gcvNULL to mark the node as freed, or
46     ** gcdIN_USE to mark the node as used. */
47     gcskNODE_PTR                next;
48
49 #if gcmIS_DEBUG(gcdDEBUG_CODE)
50     /* Time stamp of allocation. */
51     gctUINT64                   timeStamp;
52 #endif
53 }
54 gcskNODE;
55
56 typedef struct _gcskHEAP    *   gcskHEAP_PTR;
57 typedef struct _gcskHEAP
58 {
59     /* Linked list. */
60     gcskHEAP_PTR                next;
61     gcskHEAP_PTR                prev;
62
63     /* Heap size. */
64     gctSIZE_T                   size;
65
66     /* Free list. */
67     gcskNODE_PTR                freeList;
68 }
69 gcskHEAP;
70
71 struct _gckHEAP
72 {
73     /* Object. */
74     gcsOBJECT                   object;
75
76     /* Pointer to a gckOS object. */
77     gckOS                       os;
78
79     /* Locking mutex. */
80     gctPOINTER                  mutex;
81
82     /* Allocation parameters. */
83     gctSIZE_T                   allocationSize;
84
85     /* Heap list. */
86     gcskHEAP_PTR                heap;
87 #if gcmIS_DEBUG(gcdDEBUG_CODE)
88     gctUINT64                   timeStamp;
89 #endif
90
91 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
92     /* Profile information. */
93     gctUINT32                   allocCount;
94     gctUINT64                   allocBytes;
95     gctUINT64                   allocBytesMax;
96     gctUINT64                   allocBytesTotal;
97     gctUINT32                   heapCount;
98     gctUINT32                   heapCountMax;
99     gctUINT64                   heapMemory;
100     gctUINT64                   heapMemoryMax;
101 #endif
102 };
103
104 /*******************************************************************************
105 ***** Static Support Functions *************************************************
106 *******************************************************************************/
107
108 #if gcmIS_DEBUG(gcdDEBUG_CODE)
109 static gctSIZE_T
110 _DumpHeap(
111     IN gcskHEAP_PTR Heap
112     )
113 {
114     gctPOINTER p;
115     gctSIZE_T leaked = 0;
116
117     /* Start at first node. */
118     for (p = Heap + 1;;)
119     {
120         /* Convert the pointer. */
121         gcskNODE_PTR node = (gcskNODE_PTR) p;
122
123         /* Check if this is a used node. */
124         if (node->next == gcdIN_USE)
125         {
126             /* Print the leaking node. */
127             gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_HEAP,
128                            "Detected leaking: node=0x%x bytes=%lu timeStamp=%llu "
129                            "(%08X %c%c%c%c)",
130                            node, node->bytes, node->timeStamp,
131                            ((gctUINT32_PTR) (node + 1))[0],
132                            gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[0]),
133                            gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[1]),
134                            gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[2]),
135                            gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[3]));
136
137             /* Add leaking byte count. */
138             leaked += node->bytes;
139         }
140
141         /* Test for end of heap. */
142         if (node->bytes == 0)
143         {
144             break;
145         }
146
147         else
148         {
149             /* Move to next node. */
150             p = (gctUINT8_PTR) node + node->bytes;
151         }
152     }
153
154     /* Return the number of leaked bytes. */
155     return leaked;
156 }
157 #endif
158
159 static gceSTATUS
160 _CompactKernelHeap(
161     IN gckHEAP Heap
162     )
163 {
164     gcskHEAP_PTR heap, next;
165     gctPOINTER p;
166     gcskHEAP_PTR freeList = gcvNULL;
167
168     gcmkHEADER_ARG("Heap=0x%x", Heap);
169
170     /* Walk all the heaps. */
171     for (heap = Heap->heap; heap != gcvNULL; heap = next)
172     {
173         gcskNODE_PTR lastFree = gcvNULL;
174
175         /* Zero out the free list. */
176         heap->freeList = gcvNULL;
177
178         /* Start at the first node. */
179         for (p = (gctUINT8_PTR) (heap + 1);;)
180         {
181             /* Convert the pointer. */
182             gcskNODE_PTR node = (gcskNODE_PTR) p;
183
184             gcmkASSERT(p <= (gctPOINTER) ((gctUINT8_PTR) (heap + 1) + heap->size));
185
186             /* Test if this node not used. */
187             if (node->next != gcdIN_USE)
188             {
189                 /* Test if this is the end of the heap. */
190                 if (node->bytes == 0)
191                 {
192                     break;
193                 }
194
195                 /* Test of this is the first free node. */
196                 else if (lastFree == gcvNULL)
197                 {
198                     /* Initialzie the free list. */
199                     heap->freeList = node;
200                     lastFree       = node;
201                 }
202
203                 else
204                 {
205                     /* Test if this free node is contiguous with the previous
206                     ** free node. */
207                     if ((gctUINT8_PTR) lastFree + lastFree->bytes == p)
208                     {
209                         /* Just increase the size of the previous free node. */
210                         lastFree->bytes += node->bytes;
211                     }
212                     else
213                     {
214                         /* Add to linked list. */
215                         lastFree->next = node;
216                         lastFree       = node;
217                     }
218                 }
219             }
220
221             /* Move to next node. */
222             p = (gctUINT8_PTR) node + node->bytes;
223         }
224
225         /* Mark the end of the chain. */
226         if (lastFree != gcvNULL)
227         {
228             lastFree->next = gcvNULL;
229         }
230
231         /* Get next heap. */
232         next = heap->next;
233
234         /* Check if the entire heap is free. */
235         if ((heap->freeList != gcvNULL)
236         &&  (heap->freeList->bytes == heap->size - gcmSIZEOF(gcskNODE))
237         )
238         {
239             /* Remove the heap from the linked list. */
240             if (heap->prev == gcvNULL)
241             {
242                 Heap->heap = next;
243             }
244             else
245             {
246                 heap->prev->next = next;
247             }
248
249             if (heap->next != gcvNULL)
250             {
251                 heap->next->prev = heap->prev;
252             }
253
254 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
255             /* Update profiling. */
256             Heap->heapCount  -= 1;
257             Heap->heapMemory -= heap->size + gcmSIZEOF(gcskHEAP);
258 #endif
259
260             /* Add this heap to the list of heaps that need to be freed. */
261             heap->next = freeList;
262             freeList   = heap;
263         }
264     }
265
266     if (freeList != gcvNULL)
267     {
268         /* Release the mutex, remove any chance for a dead lock. */
269         gcmkVERIFY_OK(
270             gckOS_ReleaseMutex(Heap->os, Heap->mutex));
271
272         /* Free all heaps in the free list. */
273         for (heap = freeList; heap != gcvNULL; heap = next)
274         {
275             /* Get pointer to the next heap. */
276             next = heap->next;
277
278             /* Free the heap. */
279             gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HEAP,
280                            "Freeing heap 0x%x (%lu bytes)",
281                            heap, heap->size + gcmSIZEOF(gcskHEAP));
282             gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, heap));
283         }
284
285         /* Acquire the mutex again. */
286         gcmkVERIFY_OK(
287             gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE));
288     }
289
290     /* Success. */
291     gcmkFOOTER_NO();
292     return gcvSTATUS_OK;
293 }
294
295 /*******************************************************************************
296 ***** gckHEAP API Code *********************************************************
297 *******************************************************************************/
298
299 /*******************************************************************************
300 **
301 **  gckHEAP_Construct
302 **
303 **  Construct a new gckHEAP object.
304 **
305 **  INPUT:
306 **
307 **      gckOS Os
308 **          Pointer to a gckOS object.
309 **
310 **      gctSIZE_T AllocationSize
311 **          Minimum size per arena.
312 **
313 **  OUTPUT:
314 **
315 **      gckHEAP * Heap
316 **          Pointer to a variable that will hold the pointer to the gckHEAP
317 **          object.
318 */
319 gceSTATUS
320 gckHEAP_Construct(
321     IN gckOS Os,
322     IN gctSIZE_T AllocationSize,
323     OUT gckHEAP * Heap
324     )
325 {
326     gceSTATUS status;
327     gckHEAP heap = gcvNULL;
328     gctPOINTER pointer = gcvNULL;
329
330     gcmkHEADER_ARG("Os=0x%x AllocationSize=%lu", Os, AllocationSize);
331
332     /* Verify the arguments. */
333     gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
334     gcmkVERIFY_ARGUMENT(Heap != gcvNULL);
335
336     /* Allocate the gckHEAP object. */
337     gcmkONERROR(gckOS_AllocateMemory(Os,
338                                      gcmSIZEOF(struct _gckHEAP),
339                                      &pointer));
340
341     heap = pointer;
342
343     /* Initialize the gckHEAP object. */
344     heap->object.type    = gcvOBJ_HEAP;
345     heap->os             = Os;
346     heap->allocationSize = AllocationSize;
347     heap->heap           = gcvNULL;
348 #if gcmIS_DEBUG(gcdDEBUG_CODE)
349     heap->timeStamp      = 0;
350 #endif
351
352 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
353     /* Zero the counters. */
354     heap->allocCount      = 0;
355     heap->allocBytes      = 0;
356     heap->allocBytesMax   = 0;
357     heap->allocBytesTotal = 0;
358     heap->heapCount       = 0;
359     heap->heapCountMax    = 0;
360     heap->heapMemory      = 0;
361     heap->heapMemoryMax   = 0;
362 #endif
363
364     /* Create the mutex. */
365     gcmkONERROR(gckOS_CreateMutex(Os, &heap->mutex));
366
367     /* Return the pointer to the gckHEAP object. */
368     *Heap = heap;
369
370     /* Success. */
371     gcmkFOOTER_ARG("*Heap=0x%x", *Heap);
372     return gcvSTATUS_OK;
373
374 OnError:
375     /* Roll back. */
376     if (heap != gcvNULL)
377     {
378         /* Free the heap structure. */
379         gcmkVERIFY_OK(gckOS_FreeMemory(Os, heap));
380     }
381
382     /* Return the status. */
383     gcmkFOOTER();
384     return status;
385 }
386
387 /*******************************************************************************
388 **
389 **  gckHEAP_Destroy
390 **
391 **  Destroy a gckHEAP object.
392 **
393 **  INPUT:
394 **
395 **      gckHEAP Heap
396 **          Pointer to a gckHEAP object to destroy.
397 **
398 **  OUTPUT:
399 **
400 **      Nothing.
401 */
402 gceSTATUS
403 gckHEAP_Destroy(
404     IN gckHEAP Heap
405     )
406 {
407     gcskHEAP_PTR heap;
408 #if gcmIS_DEBUG(gcdDEBUG_CODE)
409     gctSIZE_T leaked = 0;
410 #endif
411
412     gcmkHEADER_ARG("Heap=0x%x", Heap);
413
414     for (heap = Heap->heap; heap != gcvNULL; heap = Heap->heap)
415     {
416         /* Unlink heap from linked list. */
417         Heap->heap = heap->next;
418
419 #if gcmIS_DEBUG(gcdDEBUG_CODE)
420         /* Check for leaked memory. */
421         leaked += _DumpHeap(heap);
422 #endif
423
424         /* Free the heap. */
425         gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, heap));
426     }
427
428     /* Free the mutex. */
429     gcmkVERIFY_OK(gckOS_DeleteMutex(Heap->os, Heap->mutex));
430
431     /* Free the heap structure. */
432     gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, Heap));
433
434     /* Success. */
435 #if gcmIS_DEBUG(gcdDEBUG_CODE)
436     gcmkFOOTER_ARG("leaked=%lu", leaked);
437 #else
438     gcmkFOOTER_NO();
439 #endif
440     return gcvSTATUS_OK;
441 }
442
443 /*******************************************************************************
444 **
445 **  gckHEAP_Allocate
446 **
447 **  Allocate data from the heap.
448 **
449 **  INPUT:
450 **
451 **      gckHEAP Heap
452 **          Pointer to a gckHEAP object.
453 **
454 **      IN gctSIZE_T Bytes
455 **          Number of byte to allocate.
456 **
457 **  OUTPUT:
458 **
459 **      gctPOINTER * Memory
460 **          Pointer to a variable that will hold the address of the allocated
461 **          memory.
462 */
463 gceSTATUS
464 gckHEAP_Allocate(
465     IN gckHEAP Heap,
466     IN gctSIZE_T Bytes,
467     OUT gctPOINTER * Memory
468     )
469 {
470     gctBOOL acquired = gcvFALSE;
471     gcskHEAP_PTR heap;
472     gceSTATUS status;
473     gctSIZE_T bytes;
474     gcskNODE_PTR node, used, prevFree = gcvNULL;
475     gctPOINTER memory = gcvNULL;
476
477     gcmkHEADER_ARG("Heap=0x%x Bytes=%lu", Heap, Bytes);
478
479     /* Verify the arguments. */
480     gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP);
481     gcmkVERIFY_ARGUMENT(Bytes > 0);
482     gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
483
484     /* Determine number of bytes required for a node. */
485     bytes = gcmALIGN(Bytes + gcmSIZEOF(gcskNODE), 8);
486
487     /* Acquire the mutex. */
488     gcmkONERROR(
489         gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE));
490
491     acquired = gcvTRUE;
492
493     /* Check if this allocation is bigger than the default allocation size. */
494     if (bytes > Heap->allocationSize - gcmSIZEOF(gcskHEAP) - gcmSIZEOF(gcskNODE))
495     {
496         /* Adjust allocation size. */
497         Heap->allocationSize = bytes * 2;
498     }
499
500     else if (Heap->heap != gcvNULL)
501     {
502         gctINT i;
503
504         /* 2 retries, since we might need to compact. */
505         for (i = 0; i < 2; ++i)
506         {
507             /* Walk all the heaps. */
508             for (heap = Heap->heap; heap != gcvNULL; heap = heap->next)
509             {
510                 /* Check if this heap has enough bytes to hold the request. */
511                 if (bytes <= heap->size - gcmSIZEOF(gcskNODE))
512                 {
513                     prevFree = gcvNULL;
514
515                     /* Walk the chain of free nodes. */
516                     for (node = heap->freeList;
517                          node != gcvNULL;
518                          node = node->next
519                     )
520                     {
521                         gcmkASSERT(node->next != gcdIN_USE);
522
523                         /* Check if this free node has enough bytes. */
524                         if (node->bytes >= bytes)
525                         {
526                             /* Use the node. */
527                             goto UseNode;
528                         }
529
530                         /* Save current free node for linked list management. */
531                         prevFree = node;
532                     }
533                 }
534             }
535
536             if (i == 0)
537             {
538                 /* Compact the heap. */
539                 gcmkVERIFY_OK(_CompactKernelHeap(Heap));
540
541 #if gcmIS_DEBUG(gcdDEBUG_CODE)
542                 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
543                                "===== KERNEL HEAP =====");
544                 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
545                                "Number of allocations           : %12u",
546                                Heap->allocCount);
547                 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
548                                "Number of bytes allocated       : %12llu",
549                                Heap->allocBytes);
550                 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
551                                "Maximum allocation size         : %12llu",
552                                Heap->allocBytesMax);
553                 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
554                                "Total number of bytes allocated : %12llu",
555                                Heap->allocBytesTotal);
556                 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
557                                "Number of heaps                 : %12u",
558                                Heap->heapCount);
559                 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
560                                "Heap memory in bytes            : %12llu",
561                                Heap->heapMemory);
562                 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
563                                "Maximum number of heaps         : %12u",
564                                Heap->heapCountMax);
565                 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
566                                "Maximum heap memory in bytes    : %12llu",
567                                Heap->heapMemoryMax);
568 #endif
569             }
570         }
571     }
572
573     /* Release the mutex. */
574     gcmkONERROR(
575         gckOS_ReleaseMutex(Heap->os, Heap->mutex));
576
577     acquired = gcvFALSE;
578
579     /* Allocate a new heap. */
580     gcmkONERROR(
581         gckOS_AllocateMemory(Heap->os,
582                              Heap->allocationSize,
583                              &memory));
584
585     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HEAP,
586                    "Allocated heap 0x%x (%lu bytes)",
587                    memory, Heap->allocationSize);
588
589     /* Acquire the mutex. */
590     gcmkONERROR(
591         gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE));
592
593     acquired = gcvTRUE;
594
595     /* Use the allocated memory as the heap. */
596     heap = (gcskHEAP_PTR) memory;
597
598     /* Insert this heap to the head of the chain. */
599     heap->next = Heap->heap;
600     heap->prev = gcvNULL;
601     heap->size = Heap->allocationSize - gcmSIZEOF(gcskHEAP);
602
603     if (heap->next != gcvNULL)
604     {
605         heap->next->prev = heap;
606     }
607     Heap->heap = heap;
608
609     /* Mark the end of the heap. */
610     node = (gcskNODE_PTR) ( (gctUINT8_PTR) heap
611                           + Heap->allocationSize
612                           - gcmSIZEOF(gcskNODE)
613                           );
614     node->bytes = 0;
615     node->next  = gcvNULL;
616
617     /* Create a free list. */
618     node           = (gcskNODE_PTR) (heap + 1);
619     heap->freeList = node;
620
621     /* Initialize the free list. */
622     node->bytes = heap->size - gcmSIZEOF(gcskNODE);
623     node->next  = gcvNULL;
624
625     /* No previous free. */
626     prevFree = gcvNULL;
627
628 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
629     /* Update profiling. */
630     Heap->heapCount  += 1;
631     Heap->heapMemory += Heap->allocationSize;
632
633     if (Heap->heapCount > Heap->heapCountMax)
634     {
635         Heap->heapCountMax = Heap->heapCount;
636     }
637     if (Heap->heapMemory > Heap->heapMemoryMax)
638     {
639         Heap->heapMemoryMax = Heap->heapMemory;
640     }
641 #endif
642
643 UseNode:
644     /* Verify some stuff. */
645     gcmkASSERT(heap != gcvNULL);
646     gcmkASSERT(node != gcvNULL);
647     gcmkASSERT(node->bytes >= bytes);
648
649     if (heap->prev != gcvNULL)
650     {
651         /* Unlink the heap from the linked list. */
652         heap->prev->next = heap->next;
653         if (heap->next != gcvNULL)
654         {
655             heap->next->prev = heap->prev;
656         }
657
658         /* Move the heap to the front of the list. */
659         heap->next       = Heap->heap;
660         heap->prev       = gcvNULL;
661         Heap->heap       = heap;
662         heap->next->prev = heap;
663     }
664
665     /* Check if there is enough free space left after usage for another free
666     ** node. */
667     if (node->bytes - bytes >= gcmSIZEOF(gcskNODE))
668     {
669         /* Allocated used space from the back of the free list. */
670         used = (gcskNODE_PTR) ((gctUINT8_PTR) node + node->bytes - bytes);
671
672         /* Adjust the number of free bytes. */
673         node->bytes -= bytes;
674         gcmkASSERT(node->bytes >= gcmSIZEOF(gcskNODE));
675     }
676     else
677     {
678         /* Remove this free list from the chain. */
679         if (prevFree == gcvNULL)
680         {
681             heap->freeList = node->next;
682         }
683         else
684         {
685             prevFree->next = node->next;
686         }
687
688         /* Consume the entire free node. */
689         used  = (gcskNODE_PTR) node;
690         bytes = node->bytes;
691     }
692
693     /* Mark node as used. */
694     used->bytes     = bytes;
695     used->next      = gcdIN_USE;
696 #if gcmIS_DEBUG(gcdDEBUG_CODE)
697     used->timeStamp = ++Heap->timeStamp;
698 #endif
699
700 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
701     /* Update profile counters. */
702     Heap->allocCount      += 1;
703     Heap->allocBytes      += bytes;
704     Heap->allocBytesMax    = gcmMAX(Heap->allocBytes, Heap->allocBytesMax);
705     Heap->allocBytesTotal += bytes;
706 #endif
707
708     /* Release the mutex. */
709     gcmkVERIFY_OK(
710         gckOS_ReleaseMutex(Heap->os, Heap->mutex));
711
712     /* Return pointer to memory. */
713     *Memory = used + 1;
714
715     /* Success. */
716     gcmkFOOTER_ARG("*Memory=0x%x", *Memory);
717     return gcvSTATUS_OK;
718
719 OnError:
720     if (acquired)
721     {
722         /* Release the mutex. */
723         gcmkVERIFY_OK(
724             gckOS_ReleaseMutex(Heap->os, Heap->mutex));
725     }
726
727     if (memory != gcvNULL)
728     {
729         /* Free the heap memory. */
730         gckOS_FreeMemory(Heap->os, memory);
731     }
732
733     /* Return the status. */
734     gcmkFOOTER();
735     return status;
736 }
737
738 /*******************************************************************************
739 **
740 **  gckHEAP_Free
741 **
742 **  Free allocated memory from the heap.
743 **
744 **  INPUT:
745 **
746 **      gckHEAP Heap
747 **          Pointer to a gckHEAP object.
748 **
749 **      IN gctPOINTER Memory
750 **          Pointer to memory to free.
751 **
752 **  OUTPUT:
753 **
754 **      NOTHING.
755 */
756 gceSTATUS
757 gckHEAP_Free(
758     IN gckHEAP Heap,
759     IN gctPOINTER Memory
760     )
761 {
762     gcskNODE_PTR node;
763     gceSTATUS status;
764
765     gcmkHEADER_ARG("Heap=0x%x Memory=0x%x", Heap, Memory);
766
767     /* Verify the arguments. */
768     gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP);
769     gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
770
771     /* Acquire the mutex. */
772     gcmkONERROR(
773         gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE));
774
775     /* Pointer to structure. */
776     node = (gcskNODE_PTR) Memory - 1;
777
778     /* Mark the node as freed. */
779     node->next = gcvNULL;
780
781 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
782     /* Update profile counters. */
783     Heap->allocBytes -= node->bytes;
784 #endif
785
786     /* Release the mutex. */
787     gcmkVERIFY_OK(
788         gckOS_ReleaseMutex(Heap->os, Heap->mutex));
789
790     /* Success. */
791     gcmkFOOTER_NO();
792     return gcvSTATUS_OK;
793
794 OnError:
795     /* Return the status. */
796     gcmkFOOTER();
797     return status;
798 }
799
800 #if VIVANTE_PROFILER
801 gceSTATUS
802 gckHEAP_ProfileStart(
803     IN gckHEAP Heap
804     )
805 {
806     gcmkHEADER_ARG("Heap=0x%x", Heap);
807
808     /* Verify the arguments. */
809     gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP);
810
811     /* Zero the counters. */
812     Heap->allocCount      = 0;
813     Heap->allocBytes      = 0;
814     Heap->allocBytesMax   = 0;
815     Heap->allocBytesTotal = 0;
816     Heap->heapCount       = 0;
817     Heap->heapCountMax    = 0;
818     Heap->heapMemory      = 0;
819     Heap->heapMemoryMax   = 0;
820
821     /* Success. */
822     gcmkFOOTER_NO();
823     return gcvSTATUS_OK;
824 }
825
826 gceSTATUS
827 gckHEAP_ProfileEnd(
828     IN gckHEAP Heap,
829     IN gctCONST_STRING Title
830     )
831 {
832     gcmkHEADER_ARG("Heap=0x%x Title=0x%x", Heap, Title);
833
834     /* Verify the arguments. */
835     gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP);
836     gcmkVERIFY_ARGUMENT(Title != gcvNULL);
837
838     gcmkPRINT("");
839     gcmkPRINT("=====[ HEAP - %s ]=====", Title);
840     gcmkPRINT("Number of allocations           : %12u",   Heap->allocCount);
841     gcmkPRINT("Number of bytes allocated       : %12llu", Heap->allocBytes);
842     gcmkPRINT("Maximum allocation size         : %12llu", Heap->allocBytesMax);
843     gcmkPRINT("Total number of bytes allocated : %12llu", Heap->allocBytesTotal);
844     gcmkPRINT("Number of heaps                 : %12u",   Heap->heapCount);
845     gcmkPRINT("Heap memory in bytes            : %12llu", Heap->heapMemory);
846     gcmkPRINT("Maximum number of heaps         : %12u",   Heap->heapCountMax);
847     gcmkPRINT("Maximum heap memory in bytes    : %12llu", Heap->heapMemoryMax);
848     gcmkPRINT("==============================================");
849
850     /* Success. */
851     gcmkFOOTER_NO();
852     return gcvSTATUS_OK;
853 }
854 #endif /* VIVANTE_PROFILER */
855
856 /*******************************************************************************
857 ***** Test Code ****************************************************************
858 *******************************************************************************/
859