1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <trylock.h>
18 #include <atomic.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <heap.h>
22 #include <seos.h>
23 
24 #define TIDX_HEAP_EXTRA 2 // must be >= 0; best if > 0, don't make it > 7, since it unnecessarily limits max heap size we can manage
25 
26 #define TIDX_HEAP_BITS (TASK_IDX_BITS + TIDX_HEAP_EXTRA)
27 
28 #define TIDX_MASK ((1 << TIDX_HEAP_BITS) - 1)
29 #define MAX_HEAP_ORDER (31 - TIDX_HEAP_BITS)
30 
31 #if MAX_HEAP_ORDER < 16
32 # error Too little HEAP is available
33 #endif
34 
35 struct HeapNode {
36 
37     struct HeapNode* prev;
38     uint32_t size: MAX_HEAP_ORDER;
39     uint32_t used: 1;
40     uint32_t tidx: TIDX_HEAP_BITS; // TASK_IDX_BITS to uniquely identify task; + extra bits of redundant counter add extra protection
41     uint8_t  data[];
42 };
43 
44 #ifdef FORCE_HEAP_IN_DOT_DATA
45 
46     static uint8_t __attribute__ ((aligned (8))) gHeap[HEAP_SIZE];
47 
48     #define REAL_HEAP_SIZE     ((HEAP_SIZE) &~ 7)
49     #define ALIGNED_HEAP_START (&gHeap)
50 
51 #else
52 
53     extern uint8_t __heap_end[], __heap_start[];
54     #define ALIGNED_HEAP_START  (uint8_t*)((((uintptr_t)&__heap_start) + 7) &~ 7)
55     #define ALIGNED_HEAP_END    (uint8_t*)(((uintptr_t)&__heap_end) &~ 7)
56 
57     #define REAL_HEAP_SIZE      (ALIGNED_HEAP_END - ALIGNED_HEAP_START)
58 
59 
60 #endif
61 
62 static struct HeapNode* gHeapHead;
63 static TRYLOCK_DECL_STATIC(gHeapLock) = TRYLOCK_INIT_STATIC();
64 static volatile uint8_t gNeedFreeMerge = false; /* cannot be bool since its size is ill defined */
65 static struct HeapNode *gHeapTail;
66 
heapPrvGetNext(struct HeapNode * node)67 static inline struct HeapNode* heapPrvGetNext(struct HeapNode* node)
68 {
69     return (gHeapTail == node) ? NULL : (struct HeapNode*)(node->data + node->size);
70 }
71 
heapInit(void)72 bool heapInit(void)
73 {
74     uint32_t size = REAL_HEAP_SIZE;
75     struct HeapNode* node;
76 
77     node = gHeapHead = (struct HeapNode*)ALIGNED_HEAP_START;
78 
79     if (size < sizeof(struct HeapNode))
80         return false;
81 
82     gHeapTail = node;
83 
84     node->used = 0;
85     node->prev = NULL;
86     node->size = size - sizeof(struct HeapNode);
87 
88     return true;
89 }
90 
91 //called to merge free chunks in case free() was unable to last time it tried. only call with lock held please
heapMergeFreeChunks(void)92 static void heapMergeFreeChunks(void)
93 {
94     while (atomicXchgByte(&gNeedFreeMerge, false)) {
95         struct HeapNode *node = gHeapHead, *next;
96 
97         while (node) {
98             next = heapPrvGetNext(node);
99 
100             if (!node->used && next && !next->used) { /* merged */
101                 node->size += sizeof(struct HeapNode) + next->size;
102 
103                 next = heapPrvGetNext(node);
104                 if (next)
105                     next->prev = node;
106                 else
107                     gHeapTail = node;
108             }
109             else
110                 node = next;
111         }
112     }
113 }
114 
heapAlloc(uint32_t sz)115 void* heapAlloc(uint32_t sz)
116 {
117     struct HeapNode *node, *best = NULL;
118     void* ret = NULL;
119 
120     if (!trylockTryTake(&gHeapLock))
121         return NULL;
122 
123     /* merge free chunks to help better use space */
124     heapMergeFreeChunks();
125 
126     sz = (sz + 3) &~ 3;
127     node = gHeapHead;
128 
129     while (node) {
130         if (!node->used && node->size >= sz && (!best || best->size > node->size)) {
131             best = node;
132             if (best->size == sz)
133                 break;
134         }
135 
136         node = heapPrvGetNext(node);
137     }
138 
139     if (!best) //alloc failed
140         goto out;
141 
142     if (best->size - sz > sizeof(struct HeapNode)) {        //there is a point to split up the chunk
143 
144         node = (struct HeapNode*)(best->data + sz);
145 
146         node->used = 0;
147         node->tidx = 0;
148         node->size = best->size - sz - sizeof(struct HeapNode);
149         node->prev = best;
150 
151         if (best != gHeapTail)
152             heapPrvGetNext(node)->prev = node;
153         else
154             gHeapTail = node;
155 
156         best->size = sz;
157     }
158 
159     best->used = 1;
160     best->tidx = osGetCurrentTid();
161     ret = best->data;
162 
163 out:
164     trylockRelease(&gHeapLock);
165     return ret;
166 }
167 
heapFree(void * ptr)168 void heapFree(void* ptr)
169 {
170     struct HeapNode *node, *t;
171     bool haveLock;
172 
173     if (ptr == NULL) {
174         // NULL is a valid reply from heapAlloc, and thus it is not an error for
175         // us to receive it here.  We just ignore it.
176         return;
177     }
178 
179     haveLock = trylockTryTake(&gHeapLock);
180 
181     node = ((struct HeapNode*)ptr) - 1;
182     node->used = 0;
183     node->tidx = 0;
184 
185     if (haveLock) {
186 
187         while (node->prev && !node->prev->used)
188             node = node->prev;
189 
190         while ((t = heapPrvGetNext(node)) && !t->used) {
191             node->size += sizeof(struct HeapNode) + t->size;
192             if (gHeapTail == t)
193                 gHeapTail = node;
194         }
195 
196         if ((t = heapPrvGetNext(node)))
197             t->prev = node;
198 
199         trylockRelease(&gHeapLock);
200     }
201     else
202         gNeedFreeMerge = true;
203 }
204 
heapFreeAll(uint32_t tid)205 int heapFreeAll(uint32_t tid)
206 {
207     struct HeapNode *node;
208     bool haveLock;
209     int count = 0;
210 
211     if (!tid)
212         return -1;
213 
214     // this can only fail if called from interrupt
215     haveLock = trylockTryTake(&gHeapLock);
216     if (!haveLock)
217         return -1;
218 
219     tid &= TIDX_MASK;
220     for (node = gHeapHead; node; node = heapPrvGetNext(node)) {
221         if (node->tidx == tid) {
222             node->used = 0;
223             node->tidx = 0;
224             count++;
225         }
226     }
227     gNeedFreeMerge = count > 0;
228     trylockRelease(&gHeapLock);
229 
230     return count;
231 }
232 
heapGetFreeSize(int * numChunks,int * largestChunk)233 int heapGetFreeSize(int *numChunks, int *largestChunk)
234 {
235     struct HeapNode *node;
236     bool haveLock;
237     int bytes = 0;
238     *numChunks = *largestChunk = 0;
239 
240     // this can only fail if called from interrupt
241     haveLock = trylockTryTake(&gHeapLock);
242     if (!haveLock)
243         return -1;
244 
245     for (node = gHeapHead; node; node = heapPrvGetNext(node)) {
246         if (!node->used) {
247             if (node->size > *largestChunk)
248                 *largestChunk = node->size;
249             bytes += node->size + sizeof(struct HeapNode);
250             (*numChunks)++;
251         }
252     }
253     trylockRelease(&gHeapLock);
254 
255     return bytes;
256 }
257 
heapGetTaskSize(uint32_t tid)258 int heapGetTaskSize(uint32_t tid)
259 {
260     struct HeapNode *node;
261     bool haveLock;
262     int bytes = 0;
263 
264     // this can only fail if called from interrupt
265     haveLock = trylockTryTake(&gHeapLock);
266     if (!haveLock)
267         return -1;
268 
269     tid &= TIDX_MASK;
270     for (node = gHeapHead; node; node = heapPrvGetNext(node)) {
271         if (node->used && node->tidx == tid) {
272             bytes += node->size + sizeof(struct HeapNode);
273         }
274     }
275     trylockRelease(&gHeapLock);
276 
277     return bytes;
278 }
279