kernel.resource: TLSF - increase memory limit on 64-bit systems, implement aligned...
[AROS.git] / rom / kernel / tlsf.c
blob5d2644caf0fb322afcbc1bb362db8fb606ecd62a
1 #include <exec/types.h>
2 #include <exec/memory.h>
3 #include <exec/memheaderext.h>
4 #include <proto/exec.h>
5 #include <string.h>
7 #include "tlsf.h"
8 #include "kernel_base.h"
9 #include "kernel_debug.h"
11 #define D(x)
13 #undef USE_MACROS
15 #include <stddef.h>
17 * Minimal alignment as required by AROS. In contrary to the default
18 * TLSF implementation, we do not allow smaller blocks here.
19 * Size needs to be aligned to at least 8, see THIS_FREE_MASK comment.
21 #define SIZE_ALIGN AROS_WORSTALIGN
24 * Settings for TLSF allocator:
25 * MAX_LOG2_SLI - amount of bits used for the second level list
26 * MAX_FLI - maximal allowable allocation size - 2^32 should be enough on 32bit systems
27 * 64bit systems use 128GB limit.
29 #define MAX_LOG2_SLI (5)
30 #define MAX_SLI (1 << MAX_LOG2_SLI)
31 #if __WORDSIZE == 64
32 #define MAX_FLI (32+5)
33 #else
34 #define MAX_FLI (32)
35 #endif
36 #define FLI_OFFSET (6)
37 #define SMALL_BLOCK (2 << FLI_OFFSET)
39 #define REAL_FLI (MAX_FLI - FLI_OFFSET)
41 #define ROUNDUP(x) (((x) + SIZE_ALIGN - 1) & ~(SIZE_ALIGN - 1))
42 #define ROUNDDOWN(x) ((x) & ~(SIZE_ALIGN - 1))
44 /* Fields used in the block header length field to identify busy/free blocks */
45 #define THIS_FREE_MASK (IPTR)1
46 #define THIS_FREE (IPTR)1
47 #define THIS_BUSY (IPTR)0
49 #define PREV_FREE_MASK (IPTR)2
50 #define PREV_FREE (IPTR)2
51 #define PREV_BUSY (IPTR)0
53 #define SIZE_MASK (~(THIS_FREE_MASK | PREV_FREE_MASK))
55 #define likely(x) __builtin_expect(!!(x), 1)
56 #define unlikely(x) __builtin_expect(!!(x), 0)
58 /* Size of additional memory needed to manage new block */
59 #define HEADERS_SIZE (((3 * ROUNDUP(sizeof(hdr_t))) + ROUNDUP(sizeof(tlsf_area_t))))
61 /* free node links together all free blocks if similar size */
62 typedef struct free_node_s {
63 struct bhdr_s * prev;
64 struct bhdr_s * next;
65 } free_node_t;
67 /* block header in front of each block - both free and busy */
68 typedef struct hdr_s {
69 struct bhdr_s * prev;
70 IPTR length;
71 } hdr_t;
74 * Each block is defined by bhdr_t structure. Free blocks contain only
75 * the header which allows us to go through all memory blocks in the system.
76 * The free blocks contain additionally the node which chains them in one
77 * of the free block lists
79 typedef struct bhdr_s {
80 union {
81 hdr_t header;
82 UBYTE __min_align[SIZE_ALIGN];
84 union {
85 UBYTE mem[1];
86 free_node_t free_node;
88 } bhdr_t;
90 /* Memory area within the TLSF pool */
91 typedef struct tlsf_area_s {
92 struct tlsf_area_s * next; // Next memory area
93 bhdr_t * end; // Pointer to "end-of-area" block header
94 LONG autogrown; // Automatically allocated by TLSF pool
95 } tlsf_area_t;
97 typedef struct {
98 tlsf_area_t * memory_area;
100 IPTR total_size;
101 IPTR free_size;
103 ULONG flbitmap;
104 ULONG slbitmap[REAL_FLI];
106 IPTR autogrow_puddle_size;
107 ULONG autogrow_requirements;
108 APTR autogrow_data;
109 autogrow_get autogrow_get_fn;
110 autogrow_release autogrow_release_fn;
112 UBYTE autodestroy_self;
114 bhdr_t * matrix[REAL_FLI][MAX_SLI];
115 } tlsf_t;
117 static inline __attribute__((always_inline)) int LS(IPTR i)
119 if (sizeof(IPTR) == 4)
120 return __builtin_ffs(i) - 1;
121 else
122 return __builtin_ffsl(i) - 1;
125 static inline __attribute__((always_inline)) int MS(IPTR i)
127 if (sizeof(IPTR) == 4)
128 return 31 - __builtin_clz(i);
129 else
130 return 63 - __builtin_clzl(i);
133 static inline __attribute__((always_inline)) void SetBit(int nr, ULONG *ptr)
135 ptr[nr >> 5] |= (1 << (nr & 31));
138 static inline __attribute__((always_inline)) void ClrBit(int nr, ULONG *ptr)
140 ptr[nr >> 5] &= ~(1 << (nr & 31));
143 static inline __attribute__((always_inline)) void MAPPING_INSERT(IPTR r, int *fl, int *sl)
145 if (r < SMALL_BLOCK)
147 *fl = 0;
148 *sl = (int)(r / (SMALL_BLOCK / MAX_SLI));
150 else
152 *fl = MS(r);
153 *sl = (int)(((IPTR)r >> (*fl - MAX_LOG2_SLI)) - MAX_SLI);
154 *fl -= FLI_OFFSET;
158 static inline __attribute__((always_inline)) void MAPPING_SEARCH(IPTR *r, int *fl, int *sl)
160 if (*r < SMALL_BLOCK)
162 *fl = 0;
163 *sl = (int)(*r / (SMALL_BLOCK / MAX_SLI));
165 else
167 IPTR tmp = ((IPTR)1 << (MS(*r) - MAX_LOG2_SLI)) - 1;
168 IPTR tr = *r + tmp;
170 *fl = MS(tr);
171 *sl = (int)(((IPTR)tr >> (*fl - MAX_LOG2_SLI)) - MAX_SLI);
172 *fl -= FLI_OFFSET;
173 *r = tr & ~tmp;
177 static inline __attribute__((always_inline)) bhdr_t * FIND_SUITABLE_BLOCK(tlsf_t *tlsf, int *fl, int *sl)
179 IPTR bitmap_tmp = tlsf->slbitmap[*fl] & (~0 << *sl);
180 bhdr_t *b = NULL;
182 if (bitmap_tmp)
184 *sl = LS(bitmap_tmp);
185 b = tlsf->matrix[*fl][*sl];
187 else
189 bitmap_tmp = tlsf->flbitmap & (~0 << (*fl + 1));
190 if (likely(bitmap_tmp != 0))
192 *fl = LS(bitmap_tmp);
193 *sl = LS(tlsf->slbitmap[*fl]);
194 b = tlsf->matrix[*fl][*sl];
198 return b;
202 #ifdef USE_MACROS
204 #define GET_SIZE(b) ({ IPTR size = b->header.length & SIZE_MASK; size; })
205 #define GET_FLAGS(b) ({ IPTR flags = b->header.length & (THIS_FREE_MASK | PREV_FREE_MASK); flags; })
206 #define SET_SIZE(b, size) do{ b->header.length = GET_FLAGS(b) | (size); }while(0)
207 #define SET_FLAGS(b, flags) do{ b->header.length = GET_SIZE(b) | (flags); }while(0)
208 #define SET_SIZE_AND_FLAGS(b, size, flags) do{b->header.length = (size) | (flags);}while(0)
209 #define FREE_BLOCK(b) ((b->header.length & THIS_FREE_MASK) == THIS_FREE)
210 #define SET_FREE_BLOCK(b) do{b->header.length = (b->header.length & ~THIS_FREE_MASK) | THIS_FREE;}while(0)
211 #define SET_BUSY_BLOCK(b) do{b->header.length = (b->header.length & ~THIS_FREE_MASK) | THIS_BUSY;}while(0)
212 #define SET_FREE_PREV_BLOCK(b) do{b->header.length = (b->header.length & ~PREV_FREE_MASK) | PREV_FREE;}while(0)
213 #define SET_BUSY_PREV_BLOCK(b) do{b->header.length = (b->header.length & ~PREV_FREE_MASK) | PREV_BUSY;}while(0)
214 #define FREE_PREV_BLOCK(b) ((b->header.length & PREV_FREE_MASK) == PREV_FREE)
215 #define GET_NEXT_BHDR(hdr, size) ({ bhdr_t * __b = (bhdr_t *)((UBYTE *)&hdr->mem[0] + (size)); __b; })
216 #define MEM_TO_BHDR(ptr) ({ bhdr_t * b = (bhdr_t*)((void*)(ptr) - offsetof(bhdr_t, mem)); b; })
218 #define REMOVE_HEADER(tlsf, b, fl, sl) do{ \
219 if (b->free_node.next) \
220 b->free_node.next->free_node.prev = b->free_node.prev; \
221 if (b->free_node.prev) \
222 b->free_node.prev->free_node.next = b->free_node.next; \
223 if (tlsf->matrix[fl][sl] == b) { \
224 tlsf->matrix[fl][sl] = b->free_node.next; \
225 if (!tlsf->matrix[fl][sl]) \
226 ClrBit(sl, &tlsf->slbitmap[fl]); \
227 if (!tlsf->slbitmap[fl]) \
228 ClrBit(fl, &tlsf->flbitmap); \
229 } } while(0)
231 #define INSERT_FREE_BLOCK(tlsf, b) do { \
232 int fl, sl; MAPPING_INSERT(GET_SIZE(b), &fl, &sl); \
233 b->free_node.prev = NULL; \
234 b->free_node.next = tlsf->matrix[fl][sl]; \
235 if (tlsf->matrix[fl][sl]) \
236 tlsf->matrix[fl][sl]->free_node.prev = b; \
237 tlsf->matrix[fl][sl] = b; \
238 SetBit(fl, &tlsf->flbitmap); \
239 SetBit(sl, &tlsf->slbitmap[fl]); }while(0)
241 #else
243 static inline __attribute__((always_inline)) IPTR GET_SIZE(bhdr_t *b)
245 return b->header.length & SIZE_MASK;
248 static inline __attribute__((always_inline)) IPTR GET_FLAGS(bhdr_t *b)
250 return b->header.length & (THIS_FREE_MASK | PREV_FREE_MASK);
253 static inline __attribute__((always_inline)) void SET_SIZE(bhdr_t *b, IPTR size)
255 b->header.length = GET_FLAGS(b) | size;
258 static inline __attribute__((always_inline)) void SET_SIZE_AND_FLAGS(bhdr_t *b, IPTR size, IPTR flags)
260 b->header.length = size | flags;
263 static inline __attribute__((always_inline)) int FREE_BLOCK(bhdr_t *b)
265 return ((b->header.length & THIS_FREE_MASK) == THIS_FREE);
268 static inline __attribute__((always_inline)) void SET_FREE_BLOCK(bhdr_t *b)
270 b->header.length = (b->header.length & ~THIS_FREE_MASK) | THIS_FREE;
273 static inline __attribute__((always_inline)) void SET_BUSY_BLOCK(bhdr_t *b)
275 b->header.length = (b->header.length & ~THIS_FREE_MASK) | THIS_BUSY;
278 static inline __attribute__((always_inline)) void SET_FREE_PREV_BLOCK(bhdr_t *b)
280 b->header.length = (b->header.length & ~PREV_FREE_MASK) | PREV_FREE;
283 static inline __attribute__((always_inline)) void SET_BUSY_PREV_BLOCK(bhdr_t *b)
285 b->header.length = (b->header.length & ~PREV_FREE_MASK) | PREV_BUSY;
288 static inline __attribute__((always_inline)) int FREE_PREV_BLOCK(bhdr_t *b)
290 return ((b->header.length & PREV_FREE_MASK) == PREV_FREE);
293 static inline __attribute__((always_inline)) bhdr_t * GET_NEXT_BHDR(bhdr_t *hdr, IPTR size)
295 return (bhdr_t *)((UBYTE *)&hdr->mem[0] + size);
298 static inline __attribute__((always_inline)) bhdr_t * MEM_TO_BHDR(void *ptr)
300 return (bhdr_t *)(ptr - offsetof(bhdr_t, mem));
303 static inline __attribute__((always_inline)) void REMOVE_HEADER(tlsf_t *tlsf, bhdr_t *b, int fl, int sl)
305 if (b->free_node.next)
306 b->free_node.next->free_node.prev = b->free_node.prev;
307 if (b->free_node.prev)
308 b->free_node.prev->free_node.next = b->free_node.next;
310 if (tlsf->matrix[fl][sl] == b)
312 tlsf->matrix[fl][sl] = b->free_node.next;
313 if (!tlsf->matrix[fl][sl])
314 ClrBit(sl, &tlsf->slbitmap[fl]);
315 if (!tlsf->slbitmap[fl])
316 ClrBit(fl, &tlsf->flbitmap);
320 static inline __attribute__((always_inline)) void INSERT_FREE_BLOCK(tlsf_t *tlsf, bhdr_t *b)
322 int fl, sl;
324 MAPPING_INSERT(GET_SIZE(b), &fl, &sl);
326 b->free_node.prev = NULL;
327 b->free_node.next = tlsf->matrix[fl][sl];
329 if (tlsf->matrix[fl][sl])
330 tlsf->matrix[fl][sl]->free_node.prev = b;
332 tlsf->matrix[fl][sl] = b;
334 SetBit(fl, &tlsf->flbitmap);
335 SetBit(sl, &tlsf->slbitmap[fl]);
338 #endif /* USE_MACROS */
340 void * tlsf_malloc(struct MemHeaderExt *mhe, IPTR size, ULONG *flags)
342 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
343 int fl, sl;
344 bhdr_t *b = NULL;
346 size = ROUNDUP(size);
348 if (unlikely(!size)) return NULL;
350 D(nbug("tlsf_malloc(%p, %ld)\n", tlsf, size));
352 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
353 ObtainSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
355 /* Find the indices fl and sl for given size */
356 MAPPING_SEARCH(&size, &fl, &sl);
358 /* Find block of either the right size or larger */
359 b = FIND_SUITABLE_BLOCK(tlsf, &fl, &sl);
361 D(nbug(" tlsf_malloc - adjusted size %ld\n", size));
363 /* No block found? Either failure or tlsf will get more memory. */
364 if (unlikely(!b))
366 D(nbug("tlsf_malloc out of memory\n"));
368 /* Do we have the autogrow feature? */
369 if (tlsf->autogrow_get_fn)
371 /* Increase the size of requested block so that we can fit the headers too */
372 IPTR sz = size + HEADERS_SIZE;
374 /* Requested size less than puddle size? Get puddle size then */
375 if (sz < tlsf->autogrow_puddle_size)
376 sz = tlsf->autogrow_puddle_size;
378 D(nbug("querying for %d bytes\n", sz));
380 /* Try to get some memory */
381 void * ptr = tlsf->autogrow_get_fn(tlsf->autogrow_data, &sz);
383 /* Got it? Add to tlsf then */
384 if (ptr)
386 tlsf_add_memory(mhe, ptr, sz);
388 /* We know the newly added memory is first in the list. Set the autogrown feature there */
389 tlsf->memory_area->autogrown = 1;
391 /* Memory is there. Try to find the block again */
392 MAPPING_SEARCH(&size, &fl, &sl);
393 b = FIND_SUITABLE_BLOCK(tlsf, &fl, &sl);
397 /* No block? FAILURE! */
398 if (!b)
400 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
401 ReleaseSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
403 return NULL;
407 /* Next header */
408 bhdr_t *next = GET_NEXT_BHDR(b, GET_SIZE(b));
410 /* Remove the found block from the free list */
411 REMOVE_HEADER(tlsf, b, fl, sl);
413 /* Is this block larger then requested? Try to split it then */
414 if (likely(GET_SIZE(b) > (size + ROUNDUP(sizeof(hdr_t)))))
416 /* New split block */
417 bhdr_t *sb = GET_NEXT_BHDR(b, size);
418 sb->header.prev = b;
420 /* Set size, this free and previous busy */
421 SET_SIZE_AND_FLAGS(sb, GET_SIZE(b) - size - ROUNDUP(sizeof(hdr_t)), THIS_FREE | PREV_BUSY);
423 /* The next header points to free block now */
424 next->header.prev = sb;
426 /* previous block (sb) is free */
427 SET_FREE_PREV_BLOCK(next);
429 /* Allocated block size truncated */
430 SET_SIZE(b, size);
432 D(nbug(" new splitted block of size %ld\n", GET_SIZE(sb)));
433 /* Free block is inserted to free list */
434 INSERT_FREE_BLOCK(tlsf, sb);
436 else
438 /* The block was of right size. Set it just busy in next pointer */
439 SET_BUSY_PREV_BLOCK(next);
442 /* The allocated block is busy */
443 SET_BUSY_BLOCK(b);
445 /* Clear the pointers just in case */
446 b->free_node.next = NULL;
447 b->free_node.prev = NULL;
449 /* Update counters */
450 tlsf->free_size -= GET_SIZE(b);
451 mhe->mhe_MemHeader.mh_Free = tlsf->free_size;
453 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
454 ReleaseSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
456 if (flags && (*flags & MEMF_CLEAR))
457 bzero(&b->mem[0], size);
459 /* And return memory */
460 return &b->mem[0];
463 static inline __attribute__((always_inline)) void MERGE(bhdr_t *b1, bhdr_t *b2)
465 /* Merging adjusts the size - it's sum of both sizes plus size of block header */
466 SET_SIZE(b1, GET_SIZE(b1) + GET_SIZE(b2) + ROUNDUP(sizeof(hdr_t)));
469 static inline __attribute__((always_inline)) bhdr_t * MERGE_PREV(tlsf_t *tlsf, bhdr_t *block)
471 /* Is previous block free? */
472 if (FREE_PREV_BLOCK(block))
474 int fl, sl;
475 bhdr_t *prev = block->header.prev;
477 /* Calculate index for removal */
478 MAPPING_INSERT(GET_SIZE(prev), &fl, &sl);
480 /* Do remove the header from the list */
481 REMOVE_HEADER(tlsf, prev, fl, sl);
483 /* Merge */
484 MERGE(prev, block);
486 return prev;
488 else
489 return block;
492 static inline __attribute__((always_inline)) bhdr_t * MERGE_NEXT(tlsf_t *tlsf, bhdr_t *block)
494 bhdr_t *next = GET_NEXT_BHDR(block, GET_SIZE(block));
496 /* Is next block free? */
497 if (FREE_BLOCK(next))
499 int fl, sl;
501 /* Calculate index for removal */
502 MAPPING_INSERT(GET_SIZE(next), &fl, &sl);
504 /* Remove the header from the list */
505 REMOVE_HEADER(tlsf, next, fl, sl);
507 /* merge blocks */
508 MERGE(block, next);
511 return block;
514 static void tlsf_release_memory_area(struct MemHeaderExt * mhe, tlsf_area_t * area)
516 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
517 tlsf_area_t *p = (tlsf_area_t *)(&tlsf->memory_area - offsetof(tlsf_area_t, next));
518 bhdr_t *b;
519 void *begin;
520 void *end;
521 IPTR size;
523 /* get the begin of this area */
524 begin = MEM_TO_BHDR(area);
526 /* get sentinel block */
527 b = area->end;
529 /* end of this area is end of sentinel block */
530 end = GET_NEXT_BHDR(b, 0);
532 /* calculate the size of area */
533 size = (IPTR)end - (IPTR)begin;
535 /* update counters */
536 tlsf->total_size -= size;
537 tlsf->free_size -= GET_SIZE(area->end->header.prev);
539 /* remove area from list */
540 for (;p->next != NULL; p = p->next)
541 if (p->next == area)
543 p->next = area->next;
544 break;
547 /* release */
548 if (tlsf->autogrow_release_fn)
549 tlsf->autogrow_release_fn(tlsf->autogrow_data, begin, size);
552 void * tlsf_malloc_aligned(struct MemHeaderExt *mhe, IPTR size, IPTR align, ULONG *flags)
554 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
555 void * ptr;
556 bhdr_t *b;
558 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
559 ObtainSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
561 size = ROUNDUP(size);
563 D(nbug("[TLSF] tlsf_malloc_aligned(%p, %lx, %d)\n", mhe, size, align));
565 /* Adjust align to the top nearest power of two */
566 align = 1 << MS(align);
568 D(nbug("[TLSF] adjusted align = %d\n", align));
570 ptr = tlsf_malloc(mhe, size+align, flags);
571 b = MEM_TO_BHDR(ptr);
573 D(nbug("[TLSF] allocated region @%p\n", ptr));
575 if (align > SIZE_ALIGN)
577 void *aligned_ptr = (void *)(((IPTR)ptr + align - 1) & ~(align - 1));
578 bhdr_t *aligned_bhdr = MEM_TO_BHDR(aligned_ptr);
579 IPTR diff_begin = (IPTR)aligned_bhdr - (IPTR)b;
580 IPTR diff_end = (IPTR)GET_NEXT_BHDR(b, GET_SIZE(b)) - (IPTR)GET_NEXT_BHDR(aligned_bhdr, size);
582 SET_SIZE(aligned_bhdr, size);
584 if (aligned_ptr != ptr)
586 D(nbug("[TLSF] aligned ptr: %p\n", aligned_ptr));
587 D(nbug("[TLSF] difference begin: %d\n", diff_begin));
588 D(nbug("[TLSF] difference end: %d\n", diff_end));
590 if (diff_begin > 0)
592 SET_SIZE(b, diff_begin - ROUNDUP(sizeof(hdr_t)));
594 tlsf->free_size += GET_SIZE(b);
596 aligned_bhdr->header.prev = b;
597 SET_FREE_PREV_BLOCK(aligned_bhdr);
598 SET_FREE_BLOCK(b);
600 b = MERGE_PREV(tlsf, b);
602 D(nbug("[TLSF] block @%p, b->next %p\n", b, GET_NEXT_BHDR(b, GET_SIZE(b))));
604 /* Insert free block into the proper list */
605 INSERT_FREE_BLOCK(tlsf, b);
608 ptr = &aligned_bhdr->mem[0];
611 if (diff_end > 0)
613 bhdr_t *b1 = GET_NEXT_BHDR(aligned_bhdr, GET_SIZE(aligned_bhdr));
614 bhdr_t *next;
616 b1->header.prev = aligned_bhdr;
618 SET_SIZE(b1, diff_end - ROUNDUP(sizeof(hdr_t)));
619 SET_BUSY_PREV_BLOCK(b1);
620 SET_FREE_BLOCK(b1);
622 next = GET_NEXT_BHDR(b1, GET_SIZE(b1));
623 next->header.prev = b1;
624 SET_FREE_PREV_BLOCK(next);
626 b1 = MERGE_NEXT(tlsf, b1);
628 INSERT_FREE_BLOCK(tlsf, b1);
633 bhdr_t *b2 = b;
634 while(b2 && GET_SIZE(b2))
636 nbug("[TLSF] bhdr %p, mem %p, size=%08x, flags=%x, prev=%p\n",
637 b2, &b2->mem[0], GET_SIZE(b2), GET_FLAGS(b2), b2->header.prev);
639 b2 = GET_NEXT_BHDR(b2, GET_SIZE(b2));
643 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
644 ReleaseSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
646 return ptr;
650 void tlsf_freevec(struct MemHeaderExt * mhe, APTR ptr)
652 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
653 bhdr_t *fb;
654 bhdr_t *next;
655 tlsf_area_t * area;
657 if (unlikely(!ptr))
658 return;
660 fb = MEM_TO_BHDR(ptr);
662 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
663 ObtainSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
665 /* Mark block as free */
666 SET_FREE_BLOCK(fb);
668 /* adjust free size field on tlsf */
669 tlsf->free_size += GET_SIZE(fb);
671 /* Try to merge with previous and next blocks (if free) */
672 fb = MERGE_PREV(tlsf, fb);
673 fb = MERGE_NEXT(tlsf, fb);
675 /* Tell next block that previous one is free. Also update the prev link in case it changed */
676 next = GET_NEXT_BHDR(fb, GET_SIZE(fb));
677 SET_FREE_PREV_BLOCK(next);
678 next->header.prev = fb;
680 /* Check if this was the last used block of an autogrown area */
681 area = fb->header.prev->header.prev == NULL ? (tlsf_area_t *)fb->header.prev->mem : NULL;
682 if (area != NULL && area->end == next && area->autogrown == 1)
683 tlsf_release_memory_area(mhe, area);
684 else
685 /* Insert free block into the proper list */
686 INSERT_FREE_BLOCK(tlsf, fb);
688 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
689 ReleaseSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
692 void tlsf_freemem(struct MemHeaderExt * mhe, APTR ptr, IPTR size)
694 (void)size;
695 tlsf_freevec(mhe, ptr);
698 void * tlsf_realloc(struct MemHeaderExt *mhe, APTR ptr, IPTR new_size)
700 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
701 bhdr_t *b;
702 bhdr_t *bnext;
703 int fl;
704 int sl;
706 /* NULL pointer? just allocate the memory */
707 if (unlikely(!ptr))
708 return tlsf_malloc(mhe, new_size, NULL);
710 /* size = 0? free memory */
711 if (unlikely(!new_size))
713 tlsf_freevec(mhe, ptr);
714 return NULL;
717 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
718 ObtainSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
720 new_size = ROUNDUP(new_size);
722 b = MEM_TO_BHDR(ptr);
724 if (unlikely(new_size == GET_SIZE(b)))
725 return ptr;
727 bnext = GET_NEXT_BHDR(b, GET_SIZE(b));
729 /* Is new size smaller than the previous one? Try to split the block if this is the case */
730 if (new_size <= (GET_SIZE(b)))
732 /* New header starts right after the current block b */
733 bhdr_t * b1 = GET_NEXT_BHDR(b, new_size);
735 /* Update pointer and size */
736 b1->header.prev = b;
737 SET_SIZE_AND_FLAGS(b1, GET_SIZE(b) - new_size - ROUNDUP(sizeof(hdr_t)), THIS_FREE | PREV_BUSY);
739 /* Current block gets smaller */
740 SET_SIZE(b, new_size);
742 tlsf->free_size += GET_SIZE(b1);
744 /* Try to merge with next block */
745 b1 = MERGE_NEXT(tlsf, b1);
747 /* Tell next block that previous one is free. Also update the prev link in case it changed */
748 bnext = GET_NEXT_BHDR(b1, GET_SIZE(b1));
749 SET_FREE_PREV_BLOCK(bnext);
750 bnext->header.prev = b1;
752 /* Insert free block into the proper list */
753 INSERT_FREE_BLOCK(tlsf, b1);
755 else
757 /* Is next block free? Is there enough free space? */
758 if (FREE_BLOCK(bnext) && new_size <= GET_SIZE(b) + GET_SIZE(bnext) + ROUNDUP(sizeof(hdr_t)))
760 bhdr_t *b1;
761 IPTR rest_size = ROUNDUP(sizeof(hdr_t)) + GET_SIZE(bnext) + GET_SIZE(b) - new_size;
763 MAPPING_INSERT(GET_SIZE(bnext), &fl, &sl);
765 REMOVE_HEADER(tlsf, bnext, fl, sl);
767 if (rest_size > ROUNDUP(sizeof(hdr_t)))
769 rest_size -= ROUNDUP(sizeof(hdr_t));
771 SET_SIZE(b, new_size);
773 b1 = GET_NEXT_BHDR(b, GET_SIZE(b));
774 b1->header.prev = b;
776 SET_SIZE_AND_FLAGS(b1, rest_size, THIS_FREE | PREV_BUSY);
778 bnext = GET_NEXT_BHDR(b1, GET_SIZE(b1));
779 bnext->header.prev = b1;
780 SET_FREE_PREV_BLOCK(bnext);
782 INSERT_FREE_BLOCK(tlsf, b1);
784 else
786 if (rest_size)
787 SET_SIZE(b, new_size + ROUNDUP(sizeof(hdr_t)));
788 else
789 SET_SIZE(b, new_size);
791 bnext = GET_NEXT_BHDR(b, GET_SIZE(b));
792 bnext->header.prev = b;
793 SET_BUSY_PREV_BLOCK(bnext);
796 else
798 /* Next block was not free. Create new buffer and copy old contents there */
799 void * p = tlsf_malloc(mhe, new_size, NULL);
800 if (p)
802 CopyMemQuick(ptr, p, GET_SIZE(b));
803 tlsf_freevec(mhe, ptr);
804 b = MEM_TO_BHDR(p);
809 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
810 ReleaseSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
812 return b->mem;
816 void * tlsf_allocabs(struct MemHeaderExt * mhe, IPTR size, void * ptr)
818 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
819 UBYTE *region_start;
820 UBYTE *region_end;
822 int fl, sl;
823 IPTR sz = ROUNDUP(size);
825 D(nbug("[TLSF] allocabs(%p, %ld)\n", ptr, size));
827 region_start = ptr;
828 region_end = (UBYTE *)ptr + sz;
830 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
831 ObtainSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
833 /* Start searching here. It doesn't make sense to go through regions which are smaller */
834 MAPPING_SEARCH(&sz, &fl, &sl);
836 /* Start looking now :) */
837 for (; fl < MAX_FLI; fl++)
839 for (; sl < MAX_SLI; sl++)
841 bhdr_t *b0 = tlsf->matrix[fl][sl];
843 /* If block was there, check it */
844 while (b0)
846 bhdr_t *b1 = GET_NEXT_BHDR(b0, GET_SIZE(b0));
848 /* The block has to contain _whole_ requested region, max exceed it in size though */
849 if (b0->mem <= region_start && (UBYTE *)b1 >= region_end)
851 /* block header of requested region */
852 bhdr_t *breg = MEM_TO_BHDR(ptr);
855 This is the block we're looking for. Unchain it from the bidirectional list of
856 free blocks now.
858 Previous entry's next will point to this block's next. If previous is NULL, matrix
859 will be set to block's next
861 if (b0->free_node.prev)
862 b0->free_node.prev->free_node.next = b0->free_node.next;
863 else
864 tlsf->matrix[fl][sl] = b0->free_node.next;
867 Next entry's prev will point to this block's previous.
869 if (b0->free_node.next)
870 b0->free_node.next->free_node.prev = b0->free_node.prev;
872 /* Empty SL matrix for size j? Clear bit */
873 if (!tlsf->matrix[fl][sl])
875 ClrBit(sl, &tlsf->slbitmap[fl]);
877 /* Empty entire SL matrix for given FL index? clear that bit too */
878 if (tlsf->slbitmap[fl])
879 ClrBit(fl, &tlsf->flbitmap);
882 b0->free_node.prev = NULL;
883 b0->free_node.next = NULL;
884 SET_BUSY_BLOCK(b0);
887 At this point the block is removed from free list and marked as used.
888 Now, split it if necessary...
891 /* begin of the block != begin of the block header of requested region? */
892 if (b0 != breg)
895 Adjust region's block header. Mark in size that previous (aka b0) is free.
896 Reduce the size of b0 as well as size of breg too.
898 breg->header.prev = b0;
899 SET_SIZE_AND_FLAGS(breg, GET_SIZE(b0)-((IPTR)breg - (IPTR)b0), PREV_FREE | THIS_BUSY);
901 /* Update the next block. Mark in size that previous (breg) is used */
902 b1->header.prev = breg;
903 SET_BUSY_PREV_BLOCK(b1);
905 /* b0's prev state is keept. b0 itself is marked as free block */
906 SET_FREE_BLOCK(b0);
907 SET_SIZE(b0, (IPTR)breg - (IPTR)b0->mem);
909 /* Insert b0 to free list */
910 MAPPING_INSERT(GET_SIZE(b0), &fl, &sl);
911 INSERT_FREE_BLOCK(tlsf, b0);
914 /* Is it necessary to split the requested region at the end? */
915 if ((SIZE_ALIGN + GET_SIZE(breg)) > size)
917 IPTR tmp_size = GET_SIZE(breg) - size - SIZE_ALIGN;
919 /* New region header directly at end of the requested region */
920 bhdr_t *b2 = GET_NEXT_BHDR(breg, size);
922 /* Adjust fields */
923 b2->header.prev = breg;
924 SET_SIZE_AND_FLAGS(b2, tmp_size, PREV_BUSY | THIS_FREE);
926 /* requested region's size is now smaller */
927 SET_SIZE(breg, size);
929 /* The next block header point to newly created one */
930 b1->header.prev = b2;
931 SET_FREE_PREV_BLOCK(b1);
933 /* Insert newly created block to free list */
934 MAPPING_INSERT(GET_SIZE(b2), &fl, &sl);
935 INSERT_FREE_BLOCK(tlsf, b2);
938 tlsf->free_size -= GET_SIZE(breg);
941 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
942 ReleaseSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
944 return breg->mem;
947 b0 = b0->free_node.next;
952 if (mhe->mhe_MemHeader.mh_Attributes & MEMF_SEM_PROTECTED)
953 ReleaseSemaphore((struct SignalSemaphore *)mhe->mhe_MemHeader.mh_Node.ln_Name);
955 return NULL;
958 /* Allocation of headers in memory:
959 * hdr
960 * header (ROUNDUP(sizeof(hdr_t))
961 * mem (ROUNDUP(sizeof(tlst_area_t))
963 * header (ROUNDUP(sizeof(hdr_t))
964 * free space (size - HEADERS_SIZE)
965 * bend
966 * header (ROUNDUP(sizeof(hdr_t))
968 tlsf_area_t * init_memory_area(void * memory, IPTR size)
970 bhdr_t * hdr = (bhdr_t *)memory;
971 bhdr_t * b;
972 bhdr_t * bend;
974 tlsf_area_t * area;
976 size = ROUNDDOWN(size);
978 /* Prepare first header, which protects the tlst_area_t header */
979 hdr->header.length = ROUNDUP(sizeof(tlsf_area_t)) | THIS_BUSY | PREV_BUSY;
980 hdr->header.prev = NULL;
982 b = GET_NEXT_BHDR(hdr, ROUNDUP(sizeof(tlsf_area_t)));
983 b->header.prev = hdr;
984 b->header.length = (size - HEADERS_SIZE) | PREV_BUSY | THIS_BUSY;
986 bend = GET_NEXT_BHDR(b, GET_SIZE(b));
987 bend->header.length = 0 | THIS_BUSY | PREV_BUSY;
988 bend->header.prev = b;
990 area = (tlsf_area_t *)hdr->mem;
991 area->end = bend;
993 return area;
996 void tlsf_add_memory(struct MemHeaderExt *mhe, void *memory, IPTR size)
998 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
1000 D(nbug("tlsf_add_memory(%p, %p, %d)\n", tlsf, memory, size));
1002 if (memory && size > HEADERS_SIZE)
1004 tlsf_area_t *area = init_memory_area(memory, size);
1005 bhdr_t *b;
1007 D(nbug(" adding memory\n"));
1009 area->next = tlsf->memory_area;
1010 tlsf->memory_area = area;
1012 /* User added memory. Not autogrown */
1013 area->autogrown = 0;
1015 b = MEM_TO_BHDR(area);
1016 b = GET_NEXT_BHDR(b, GET_SIZE(b));
1018 tlsf->total_size += size;
1020 D(nbug(" total_size=%08x\n", tlsf->total_size));
1022 /* Add the initialized memory */
1023 tlsf_freevec(mhe, b->mem);
1027 void tlsf_add_memory_and_merge(struct MemHeaderExt *mhe, void *memory, IPTR size)
1029 tlsf_add_memory(mhe, memory, size);
1030 // TODO: add memory and merge...
1033 #if 0
1034 void bzero(void *ptr, IPTR len)
1036 UBYTE *p = (UBYTE *)ptr;
1038 while(len--)
1039 *p++ = 0;
1041 #endif
1043 void * tlsf_init(struct MemHeaderExt * mhe)
1045 tlsf_t *tlsf = NULL;
1046 void * ptr = mhe->mhe_MemHeader.mh_Lower;
1048 /* if MemHeaderExt starts at the beginning of handled memory, advance the ptr */
1049 if (mhe == ptr)
1050 ptr += ROUNDUP(sizeof(struct MemHeaderExt));
1052 /* Is there enough room for tlsf in the mem header itself? */
1053 if (mhe->mhe_MemHeader.mh_Free >= (ROUNDUP(sizeof(tlsf_t)) + 3 * ROUNDUP(sizeof(bhdr_t))))
1055 /* tlsf will be stored inside handled memory */
1056 tlsf = (tlsf_t *)ptr;
1058 ptr += ROUNDUP(sizeof(tlsf_t));
1060 bzero(tlsf, sizeof(tlsf_t));
1061 tlsf->autodestroy_self = 0;
1063 else
1065 /* No place for tlsf header in MemHeaderExt? Allocate it separately */
1066 tlsf = AllocMem(sizeof(tlsf_t), MEMF_ANY);
1068 if (tlsf)
1070 bzero(tlsf, sizeof(tlsf_t));
1071 tlsf->autodestroy_self = 1;
1075 /* Store the tlsf pointer in UserData field */
1076 mhe->mhe_UserData = tlsf;
1078 if (tlsf && ptr < mhe->mhe_MemHeader.mh_Upper)
1080 tlsf_add_memory(mhe, ptr, (IPTR)mhe->mhe_MemHeader.mh_Upper - (IPTR)ptr);
1083 return tlsf;
1086 static void * tlsf_init_autogrow(struct MemHeaderExt * mhe, IPTR puddle_size, ULONG requirements, autogrow_get grow_function, autogrow_release release_function, APTR autogrow_data)
1088 tlsf_t *tlsf = tlsf_init(mhe);
1090 if (tlsf)
1092 if (puddle_size < 4096)
1093 puddle_size = 4096;
1095 tlsf->autogrow_puddle_size = puddle_size;
1096 tlsf->autogrow_requirements = requirements;
1097 tlsf->autogrow_data = autogrow_data;
1098 tlsf->autogrow_get_fn = grow_function;
1099 tlsf->autogrow_release_fn = release_function;
1102 return tlsf;
1105 void tlsf_destroy(struct MemHeaderExt * mhe)
1107 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
1109 D(nbug("tlsf_destroy(%p)\n", tlsf));
1111 if (tlsf)
1113 tlsf_area_t *area = tlsf->memory_area;
1115 if (tlsf->autogrow_release_fn)
1117 while(area)
1119 tlsf_area_t *next = area->next;
1122 Autogrown area? Release it here.
1123 Otherwise it's the responsibility of add_memory_area caller
1125 if (area->autogrown)
1126 tlsf_release_memory_area(mhe, area);
1128 area = next;
1132 if (tlsf->autodestroy_self)
1133 FreeMem(tlsf, sizeof(tlsf_t));
1137 IPTR tlsf_avail(struct MemHeaderExt * mhe, ULONG requirements)
1139 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
1140 IPTR ret = 0;
1142 if (requirements & MEMF_TOTAL)
1143 ret = tlsf->total_size;
1144 else if (requirements & MEMF_LARGEST)
1146 bhdr_t *b = NULL;
1148 if (tlsf->flbitmap)
1150 int fl = MS(tlsf->flbitmap);
1152 if (tlsf->slbitmap[fl])
1154 int sl = MS(tlsf->slbitmap[fl]);
1156 b = tlsf->matrix[fl][sl];
1160 while (b)
1162 if (GET_SIZE(b) > ret)
1163 ret = GET_SIZE(b);
1165 b = b->free_node.next;
1168 else
1169 ret = tlsf->free_size;
1171 return ret;
1174 BOOL tlsf_in_bounds(struct MemHeaderExt * mhe, void * begin, void * end)
1176 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
1177 tlsf_area_t *area;
1179 area = tlsf->memory_area;
1181 D(nbug("tlsf_in_bounds(%p, %p, %p)\n", tlsf, begin, end));
1183 while (area)
1185 D(nbug(" area %p\n"));
1187 * Do checks only if questioned memory ends before the end (sentinel bhdr)
1188 * of area
1190 if ((IPTR)end <= (IPTR)area->end)
1192 D(nbug(" end <= area->end (%p <= %p)\n", end, area->end));
1194 /* Get the bhdr of this area */
1195 bhdr_t *b = MEM_TO_BHDR(area);
1197 /* Forward to the begin of the memory */
1198 b = GET_NEXT_BHDR(b, GET_SIZE(b));
1200 /* requested memory starts at begin or after begin of the area */
1201 if ((IPTR)begin >= (IPTR)b->mem)
1203 D(nbug(" begin >= b->mem (%p >= %p)\n", begin, b->mem));
1204 return TRUE;
1208 area = area->next;
1211 return FALSE;
1215 static void destroy_Pool(struct MemHeaderExt *mhe)
1217 tlsf_destroy(mhe);
1220 static APTR fetch_more_ram(void * data, IPTR *size)
1222 struct MemHeaderExt *mhe = (struct MemHeaderExt *)data;
1223 tlsf_t *tlsf = (tlsf_t *)mhe->mhe_UserData;
1225 D(nbug("[TLSF] fetch_more_ram(%p, %d)\n", mhe, *size));
1227 APTR ptr = AllocMem(*size, tlsf->autogrow_requirements);
1228 return ptr;
1231 static VOID release_ram(void * data, APTR ptr, IPTR size)
1233 D(nbug("[TLSF] release_ram(%p, %d)\n", ptr, size));
1235 FreeMem(ptr, size);
1238 static void * init_Pool(struct MemHeaderExt *mhe, IPTR puddleSize, IPTR initialSize)
1240 return tlsf_init_autogrow(mhe, puddleSize, (ULONG)mhe->mhe_MemHeader.mh_First, fetch_more_ram, release_ram, mhe);
1243 void krnCreateTLSFMemHeader(CONST_STRPTR name, BYTE pri, APTR start, IPTR size, ULONG flags)
1245 /* If the end is less than (1 << 31), MEMF_31BIT is implied */
1246 if (((IPTR)start+size) < (1UL << 31))
1247 flags |= MEMF_31BIT;
1248 else
1249 flags &= ~MEMF_31BIT;
1251 flags |= MEMF_MANAGED;
1253 struct MemHeaderExt *mhe = start;
1255 mhe->mhe_Magic = MEMHEADER_EXT_MAGIC;
1257 mhe->mhe_DestroyPool = destroy_Pool;
1258 mhe->mhe_InitPool = init_Pool;
1260 mhe->mhe_Alloc = tlsf_malloc;
1261 mhe->mhe_AllocVec = tlsf_malloc;
1262 mhe->mhe_AllocAligned = tlsf_malloc_aligned;
1263 mhe->mhe_AllocVecAligned=tlsf_malloc_aligned;
1264 mhe->mhe_Free = tlsf_freemem;
1265 mhe->mhe_FreeVec = tlsf_freevec;
1266 mhe->mhe_AllocAbs = tlsf_allocabs;
1267 mhe->mhe_ReAlloc = tlsf_realloc;
1268 mhe->mhe_Avail = tlsf_avail;
1269 mhe->mhe_InBounds = tlsf_in_bounds;
1271 mhe->mhe_MemHeader.mh_Node.ln_Succ = NULL;
1272 mhe->mhe_MemHeader.mh_Node.ln_Pred = NULL;
1273 mhe->mhe_MemHeader.mh_Node.ln_Type = NT_MEMORY;
1274 mhe->mhe_MemHeader.mh_Node.ln_Name = (STRPTR)name;
1275 mhe->mhe_MemHeader.mh_Node.ln_Pri = pri;
1276 mhe->mhe_MemHeader.mh_Attributes = flags;
1277 /* The first MemChunk needs to be aligned. We do it by adding MEMHEADER_TOTAL. */
1278 mhe->mhe_MemHeader.mh_First = NULL;
1280 mhe->mhe_UserData = NULL;
1283 * mh_Lower and mh_Upper are informational only. Since our MemHeader resides
1284 * inside the region it describes, the region includes MemHeader.
1286 mhe->mhe_MemHeader.mh_Lower = start;
1287 mhe->mhe_MemHeader.mh_Upper = start + size;
1288 mhe->mhe_MemHeader.mh_Free = size;
1290 tlsf_init(mhe);