1616 lines
68 KiB
C
1616 lines
68 KiB
C
/*********************************************************************
|
|
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
|
|
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
|
|
|
|
Authors: Gustav Janssens, Jonas Van Nieuwenberg, Sam Van Den Berge
|
|
*********************************************************************/
|
|
|
|
#include "pico_config.h"
|
|
#include "pico_mm.h"
|
|
#include "pico_tree.h"
|
|
#include "pico_config.h"
|
|
#include "pico_protocol.h" /* For pico_err */
|
|
|
|
#ifdef DEBUG_MM
|
|
#define DBG_MM(x, args ...) dbg("[%s:%s:%i] "x" \n",__FILE__,__func__,__LINE__ ,##args )
|
|
#define DBG_MM_RED(x, args ...) dbg("\033[31m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args )
|
|
#define DBG_MM_GREEN(x, args ...) dbg("\033[32m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args )
|
|
#define DBG_MM_YELLOW(x, args ...) dbg("\033[33m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args )
|
|
#define DBG_MM_BLUE(x, args ...) dbg("\033[34m[%s:%s:%i] "x" \033[0m\n",__FILE__,__func__,__LINE__ ,##args )
|
|
#else
|
|
#define DBG_MM(x, args ...) do {} while(0)
|
|
#define DBG_MM_RED(x, args ...) do {} while(0)
|
|
#define DBG_MM_GREEN(x, args ...) do {} while(0)
|
|
#define DBG_MM_YELLOW(x, args ...) do {} while(0)
|
|
#define DBG_MM_BLUE(x, args ...) do {} while(0)
|
|
#endif
|
|
|
|
/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has.
|
|
* These nodes should be placed in the manager page which is in a different memory region then the nodes
|
|
* which are used for the pico stack in general.
|
|
* Therefore the following 2 functions are created so that pico_tree can use them to to put these nodes
|
|
* into the correct memory regions.
|
|
*/
|
|
void*pico_mem_page0_zalloc(size_t len);
|
|
void pico_mem_page0_free(void*ptr);
|
|
|
|
|
|
/* this is a wrapper function for pico_tree_insert. The function pointers that are used by pico_tree
|
|
* to zalloc/free are modified so that pico_tree will insert the node in another memory region
|
|
*/
|
|
static void *manager_tree_insert(struct pico_tree*tree, void *key)
|
|
{
|
|
return (void*) pico_tree_insert_implementation(tree, key, USE_PICO_PAGE0_ZALLOC);
|
|
}
|
|
|
|
/* this is a wrapper function for pico_tree_insert. The function pointers that are used by pico_tree
|
|
* to zalloc/free are modified so that pico_tree will insert the node in another memory region
|
|
*/
|
|
static void *manager_tree_delete(struct pico_tree *tree, void *key)
|
|
{
|
|
return (void *) pico_tree_delete_implementation(tree, key, USE_PICO_PAGE0_ZALLOC);
|
|
}
|
|
|
|
|
|
static const uint32_t slab_sizes[] = {
|
|
1200, 1400, 1600
|
|
}; /* Sizes must be from small to big */
|
|
static uint32_t slab_size_statistics[] = {
|
|
0, 0, 0
|
|
};
|
|
static uint32_t slab_size_global = PICO_MEM_DEFAULT_SLAB_SIZE;
|
|
/*
|
|
typedef struct pico_mem_manager pico_mem_manager;
|
|
typedef struct pico_mem_manager_extra pico_mem_manager_extra;
|
|
typedef struct pico_mem_page pico_mem_page;
|
|
typedef struct pico_mem_heap pico_mem_heap;
|
|
typedef struct pico_mem_slab pico_mem_slab;
|
|
typedef struct pico_mem_heap_block pico_mem_heap_block;
|
|
typedef struct pico_mem_slab_block pico_mem_slab_block;
|
|
typedef struct pico_mem_slab_node pico_mem_slab_node;
|
|
typedef struct pico_mem_block pico_mem_block;
|
|
typedef struct pico_tree pico_tree;
|
|
typedef struct pico_tree_node pico_tree_node;
|
|
typedef union block_internals block_internals;
|
|
*/
|
|
#define HEAP_BLOCK_NOT_FREE 0xCAFED001
|
|
#define HEAP_BLOCK_FREE 0xCAFED00E
|
|
|
|
#define SLAB_BLOCK_TYPE 0
|
|
#define HEAP_BLOCK_TYPE 1
|
|
/*
|
|
* page
|
|
* <---------------------------------------------------------------------->
|
|
*
|
|
*
|
|
* +------------<------------+----------<-----------+
|
|
* | ^ ^
|
|
* v | |
|
|
* +---------+------------+--+----+---------------+-+-----+---------------+
|
|
* | | | | | | |
|
|
* | pico_ | | pico_ | | pico_ | |
|
|
* | mem_ | ...HEAP... | mem_ | slab | mem_ | slab |
|
|
* | page | | block | | block | |
|
|
* | | | | | | |
|
|
* +---------+------------+-------+-----+---------+-------+----------+----+
|
|
* ^ | ^ |
|
|
* +-------+ | | |
|
|
* | | +-+ |
|
|
* +------|-----+ | |
|
|
* | | +-----|--------------------+
|
|
* v | v |
|
|
* +---------+-----+-------+------+-+-----+-----+--+
|
|
* | | | | | | |
|
|
* | pico_ | | pico_ | pico_ | | pico_ |
|
|
* | mem_ | ... | tree_ | mem_ | ... | mem_ |
|
|
* | manager | | node | slab_ | | slab_ |
|
|
* | | | | node | | node |
|
|
* +---------+-----+-+-----+-----+--+-----+-----+--+
|
|
* | ^ | ^ |
|
|
* | | +---->---+ |
|
|
* +-->--+----<-----------<---+
|
|
*
|
|
* <----------------------------------------------->
|
|
* manager page
|
|
*
|
|
*
|
|
* +----------------+
|
|
* | |
|
|
* | pico_tree_node +-------------------------------------+
|
|
* | (size x) | |
|
|
* +--+----------+--+ |
|
|
* | | +---------v----------+
|
|
* | | | |
|
|
* v v +----> pico_mem_slab_node +----+
|
|
* | | | | | |
|
|
* +----<----+ +---->----+ | +--------------------+ |
|
|
* | | | |
|
|
* | | ^ v
|
|
* | | | |
|
|
* | | | +--------------------+ |
|
|
* +------v---------+ +---------v------+ +----+ <----+
|
|
* | | | | | pico_mem_slab_node |
|
|
* | pico_tree_node | | pico_tree_node | +----> +----+
|
|
* | (size x/2) | | (size 2x) | | +--------------------+ |
|
|
* +----------------+ +----------------+ | |
|
|
* | |
|
|
* | |
|
|
* ^ v
|
|
* ... ...
|
|
*
|
|
*/
|
|
|
|
/* Housekeeping memory manager (start of page 0) */
|
|
struct pico_mem_manager
|
|
{
|
|
uint32_t size; /* Maximum size in bytes */
|
|
uint32_t used_size; /* Used size in bytes */
|
|
struct pico_tree tree;
|
|
struct pico_mem_page*first_page;
|
|
struct pico_mem_manager_extra*manager_extra; /* this is a pointer to a page with extra heap space used by the manager */
|
|
};
|
|
/* Housekeeping additionnal memory manager heap pages */
|
|
struct pico_mem_manager_extra
|
|
{
|
|
struct pico_mem_manager_extra*next;
|
|
uint32_t timestamp;
|
|
uint32_t blocks;
|
|
};
|
|
/* Housekeeping of every page (start of all pages except the manager pages) */
|
|
struct pico_mem_page
|
|
{
|
|
uint32_t slab_size;
|
|
uint16_t slabs_max;
|
|
uint16_t slabs_free;
|
|
uint32_t heap_max_size;
|
|
uint32_t heap_max_free_space;
|
|
uint32_t timestamp;
|
|
struct pico_mem_page*next_page;
|
|
};
|
|
/* Housekeeping struct for a heap block (kept per block of memory in heap) */
|
|
struct pico_mem_heap_block
|
|
{
|
|
uint32_t size;
|
|
/* uint8_t free; */
|
|
uint32_t free;
|
|
};
|
|
/* Housekeeping struct for a slab block (kept per block of memory in slabs) */
|
|
struct pico_mem_slab_block
|
|
{
|
|
struct pico_mem_page*page;
|
|
struct pico_mem_slab_node*slab_node;
|
|
};
|
|
union block_internals
|
|
{
|
|
struct pico_mem_heap_block heap_block;
|
|
struct pico_mem_slab_block slab_block;
|
|
};
|
|
struct pico_mem_block
|
|
{
|
|
union block_internals internals; /* Union has to be in first place!!! */
|
|
uint8_t type;
|
|
};
|
|
/* Used to store the slab objects in the RB-tree */
|
|
struct pico_mem_slab_node
|
|
{
|
|
struct pico_mem_block*slab;
|
|
struct pico_mem_slab_node*prev;
|
|
struct pico_mem_slab_node*next;
|
|
};
|
|
|
|
static struct pico_mem_manager*manager = NULL;
|
|
|
|
/*
|
|
* This compare function will be called by pico_tree.c to compare 2 keyValues (type: struct pico_mem_slab_nodes)
|
|
* We want to compare slab_nodes by their size. We also want to be able to directly compare an integer, which explains
|
|
* the casts from void* to uint32_t***
|
|
*/
|
|
static int compare_slab_keys(void*keyA, void*keyB)
|
|
{
|
|
/* keyValues are pico_mem_slab_nodes */
|
|
/* We want to compare the sizes */
|
|
/* first element of pico_mem_slab_node: pico_mem_block* slab_block */
|
|
/* first element of pico_mem_block: (slab_block in union): pico_mem_page* page */
|
|
/* first element of pico_mem_page: uint32_t slab_size */
|
|
uint32_t sizeKeyA = ***(uint32_t***) keyA;
|
|
uint32_t sizeKeyB = ***(uint32_t***) keyB;
|
|
DBG_MM_BLUE("Compare called: sizeA = %i, sizeB = %i", sizeKeyA, sizeKeyB);
|
|
if(sizeKeyA == sizeKeyB)
|
|
{
|
|
return 0;
|
|
}
|
|
else if(sizeKeyA < sizeKeyB)
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Pico_mem_init_page is called to initialize a block of memory pointed to by pico_mem_page* page.
|
|
* Slabs of size slabsize are created, and the page, heap and slab housekeeping is initialized.
|
|
*/
|
|
static void _pico_mem_init_page(struct pico_mem_page*page, size_t slabsize)
|
|
{
|
|
uint8_t*byteptr = (uint8_t*) page;
|
|
struct pico_mem_block*slab_block;
|
|
struct pico_mem_block*heap_block;
|
|
struct pico_tree_node*tree_node;
|
|
struct pico_mem_slab_node*slab_node;
|
|
void*temp;
|
|
uint16_t i;
|
|
|
|
DBG_MM_YELLOW("Initializing page %p with slabsize %u", page, slabsize);
|
|
|
|
page->next_page = manager->first_page;
|
|
manager->first_page = page;
|
|
page->slab_size = (uint32_t)slabsize;
|
|
page->slabs_max = (uint16_t)((PICO_MEM_PAGE_SIZE - sizeof(struct pico_mem_page) - sizeof(struct pico_mem_block)) / (slabsize + sizeof(struct pico_mem_block)));
|
|
page->heap_max_size = (uint32_t)(PICO_MEM_PAGE_SIZE - sizeof(struct pico_mem_page) - sizeof(struct pico_mem_block) - (page->slabs_max * (sizeof(struct pico_mem_block) + slabsize)));
|
|
if(page->heap_max_size < PICO_MIN_HEAP_SIZE)
|
|
{
|
|
DBG_MM_BLUE("Not enough heap size available with slabsize %u, allocating one slab to heap.", slabsize);
|
|
page->slabs_max--;
|
|
/* DBG_MM_BLUE("Heap size %u -> %lu",page->heap_max_size, page->heap_max_size + sizeof(pico_mem_slab_block) + slabsize); */
|
|
DBG_MM_BLUE("Heap size %u -> %lu", page->heap_max_size, page->heap_max_size + sizeof(struct pico_mem_block) + slabsize);
|
|
page->heap_max_size += (uint32_t)(sizeof(struct pico_mem_block) + slabsize);
|
|
}
|
|
|
|
page->slabs_free = page->slabs_max;
|
|
page->heap_max_free_space = page->heap_max_size;
|
|
page->timestamp = 0;
|
|
DBG_MM_BLUE("max slab objects = %i, object_size = %i", page->slabs_max, page->slab_size);
|
|
DBG_MM_BLUE("Heap size: %i", page->heap_max_size);
|
|
byteptr += sizeof(struct pico_mem_page); /* jump over page struct so byteptr points to start of heap */
|
|
|
|
/* Init HEAP at the beginning of the page */
|
|
heap_block = (struct pico_mem_block*) byteptr;
|
|
heap_block->type = HEAP_BLOCK_TYPE;
|
|
heap_block->internals.heap_block.free = HEAP_BLOCK_FREE;
|
|
heap_block->internals.heap_block.size = page->heap_max_free_space;
|
|
|
|
byteptr += sizeof(struct pico_mem_block) + heap_block->internals.heap_block.size;
|
|
for(i = 0; i < page->slabs_max; i++)
|
|
{
|
|
slab_block = (struct pico_mem_block*) byteptr;
|
|
DBG_MM_BLUE("Slab object %i at %p. Start of object data at %p", i, slab_block, (uint8_t*) slab_block + sizeof(struct pico_mem_slab_block));
|
|
slab_block->type = SLAB_BLOCK_TYPE;
|
|
slab_block->internals.slab_block.page = page;
|
|
|
|
DBG_MM_BLUE("Calling find_node with size %u", **((uint32_t**) slab_block));
|
|
tree_node = pico_tree_findNode(&(manager->tree), &slab_block);
|
|
|
|
DBG_MM("Creating slab_node..");
|
|
slab_node = pico_mem_page0_zalloc(sizeof(struct pico_mem_slab_node));
|
|
if(slab_node == NULL)
|
|
{
|
|
DBG_MM_RED("No more space in the manager heap for the housekeeping of slab %i, and no more space for extra manager pages!", i + 1);
|
|
DBG_MM_RED("Debug info:\nUsed size: %u/%u\nmanager_extra = %p", manager->used_size, manager->size, manager->manager_extra);
|
|
DBG_MM_RED("This page will be initialized with %u slabs instead of %u slabs", i, page->slabs_max);
|
|
page->slabs_max = i;
|
|
page->slabs_free = page->slabs_max;
|
|
return;
|
|
/* exit(1); */
|
|
}
|
|
|
|
slab_node->slab = slab_block;
|
|
slab_node->prev = NULL;
|
|
slab_node->next = NULL;
|
|
|
|
slab_block->internals.slab_block.slab_node = slab_node;
|
|
|
|
if(tree_node != NULL)
|
|
{
|
|
struct pico_mem_slab_node*first_node = (struct pico_mem_slab_node*) tree_node->keyValue;
|
|
tree_node->keyValue = slab_node;
|
|
slab_node->next = first_node;
|
|
first_node->prev = slab_node;
|
|
}
|
|
else
|
|
{
|
|
/* Insert new slab_node */
|
|
DBG_MM_BLUE("Inserting new slab node in the tree of size %u", slabsize);
|
|
/* pico_err_t pico_err_backup = pico_err; */
|
|
/* pico_err = 0; */
|
|
|
|
|
|
/* temp = pico_tree_insert(&manager->tree, slab_node); */
|
|
temp = manager_tree_insert(&manager->tree, slab_node);
|
|
|
|
/* IF SLAB_NODE COULDN'T BE INSERTED */
|
|
/* if(pico_err == PICO_ERR_ENOMEM) */
|
|
/* if(temp == &LEAF) */
|
|
if(temp != NULL)
|
|
{
|
|
DBG_MM_RED("No more space in the manager heap for the housekeeping of slab %i, and no more space for extra manager pages!", i + 1);
|
|
DBG_MM_RED("This page will be initialized without slabs.");
|
|
pico_mem_page0_free(slab_node);
|
|
page->slabs_max = (uint16_t) i;
|
|
page->slabs_free = page->slabs_max;
|
|
/* pico_err = pico_err_backup; */
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* byteptr = (uint8_t*) (slab_block+1); */
|
|
byteptr = (uint8_t*) slab_block;
|
|
byteptr += sizeof(struct pico_mem_block);
|
|
byteptr += page->slab_size;
|
|
}
|
|
DBG_MM_GREEN("Initialized page %p with slabsize %u", page, slabsize);
|
|
}
|
|
|
|
/*
|
|
* Initializes the memory by creating a memory manager page and one page with default slab size
|
|
* A maximum space of memsize can be occupied by the memory manager at any time
|
|
*/
|
|
void pico_mem_init(uint32_t memsize)
|
|
{
|
|
struct pico_mem_block*first_block;
|
|
struct pico_mem_page*page;
|
|
uint8_t*startofmanagerheap;
|
|
|
|
DBG_MM_YELLOW("Initializing memory with memsize %u", memsize);
|
|
if(memsize < PICO_MEM_PAGE_SIZE * 2)
|
|
{
|
|
/* Not enough memory was provided to initialize a manager page and a data page, return without initializing memory */
|
|
/* Set pico_err to an appropriate value */
|
|
pico_err = PICO_ERR_ENOMEM;
|
|
DBG_MM_RED("The memsize provided is too small, memory not initialized!");
|
|
|
|
return;
|
|
}
|
|
|
|
/* First pico_mem_page is already included in pico_mem_manager. Others are added. */
|
|
/* manager = pico_azalloc(sizeof(pico_mem_manager) + sizeof(pico_mem_page*)*(pages - 1)); //Points to usermanager if one present */
|
|
manager = pico_zalloc(PICO_MEM_PAGE_SIZE);
|
|
if( NULL != manager )
|
|
{
|
|
manager->size = memsize;
|
|
manager->used_size = PICO_MEM_PAGE_SIZE;
|
|
manager->first_page = NULL;
|
|
manager->manager_extra = NULL;
|
|
|
|
manager->tree.compare = compare_slab_keys;
|
|
manager->tree.root = &LEAF;
|
|
DBG_MM_BLUE("Manager page is at %p", manager);
|
|
|
|
DBG_MM_BLUE("Start of tree: %p, sizeof(pico_tree): %lu", &manager->tree, sizeof(struct pico_tree));
|
|
DBG_MM_BLUE("Root node of tree at %p", manager->tree.root);
|
|
|
|
/* Init manager heap. Used to store the RB-tree nodes which store pointers to free slab objects */
|
|
startofmanagerheap = (uint8_t*) manager + sizeof(struct pico_mem_manager); /* manager heap is after struct pico_mem_manager */
|
|
DBG_MM_BLUE("Start of manager heap = %p", startofmanagerheap);
|
|
first_block = (struct pico_mem_block*) startofmanagerheap;
|
|
first_block->type = HEAP_BLOCK_TYPE;
|
|
first_block->internals.heap_block.free = HEAP_BLOCK_FREE;
|
|
first_block->internals.heap_block.size = PICO_MEM_PAGE_SIZE - sizeof(struct pico_mem_manager) - sizeof(struct pico_mem_block);
|
|
|
|
/* Initialize the first page only! */
|
|
page = pico_zalloc(PICO_MEM_PAGE_SIZE);
|
|
if(page != NULL)
|
|
{
|
|
manager->used_size += PICO_MEM_PAGE_SIZE;
|
|
DBG_MM_BLUE("Page 1 at %p, manager used size = %u", page, manager->used_size);
|
|
_pico_mem_init_page(page, PICO_MEM_DEFAULT_SLAB_SIZE);
|
|
}
|
|
else
|
|
{
|
|
/* Not enough memory was provided to initialize a manager page and a data page, return without initializing memory */
|
|
/* Set pico_err to an appropriate value */
|
|
pico_err = PICO_ERR_ENOMEM;
|
|
/* Free the manager page */
|
|
pico_free(manager);
|
|
manager = NULL;
|
|
DBG_MM_RED("Not enough space to allocate page 1, memory not initialized!");
|
|
return;
|
|
}
|
|
|
|
DBG_MM_GREEN("Memory initialized. Returning from pico_mem_init.");
|
|
}
|
|
else
|
|
{
|
|
/* Not enough memory was provided to initialize a manager page and a data page, return without initializing memory */
|
|
/* Set pico_err to an appropriate value */
|
|
pico_err = PICO_ERR_ENOMEM;
|
|
DBG_MM_RED("Not enough space to allocate manager page, memory not initialized!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Deinitializes the memory manager, returning all its memory to the system's control.
|
|
*/
|
|
void pico_mem_deinit()
|
|
{
|
|
struct pico_mem_page*next_page;
|
|
struct pico_mem_manager_extra*next_manager_page;
|
|
|
|
DBG_MM_YELLOW("Pico_mem_deinit called");
|
|
if(manager == NULL)
|
|
{
|
|
DBG_MM_GREEN("No memory instance initialized, returning");
|
|
}
|
|
else
|
|
{
|
|
while(manager->first_page != NULL)
|
|
{
|
|
next_page = manager->first_page->next_page;
|
|
pico_free(manager->first_page);
|
|
manager->first_page = next_page;
|
|
}
|
|
while(manager->manager_extra != NULL)
|
|
{
|
|
next_manager_page = manager->manager_extra->next;
|
|
pico_free(manager->manager_extra);
|
|
manager->manager_extra = next_manager_page;
|
|
}
|
|
DBG_MM_BLUE("Freeing manager page at %p", manager);
|
|
pico_free(manager);
|
|
manager = NULL;
|
|
slab_size_global = PICO_MEM_DEFAULT_SLAB_SIZE;
|
|
DBG_MM_GREEN("Memory manager reset");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function is called internally by page0_zalloc if there isn't enough space left in the heap of the initial memory page
|
|
* This function allocates heap space in extra manager pages, creating new pages as necessary.
|
|
*/
|
|
static void*_pico_mem_manager_extra_alloc(struct pico_mem_manager_extra*heap_page, size_t len)
|
|
{
|
|
struct pico_mem_manager_extra*extra_heap_page;
|
|
struct pico_mem_block*heap_block;
|
|
struct pico_mem_block*first_block;
|
|
struct pico_mem_block*new_block;
|
|
uint8_t*startOfData;
|
|
uint8_t*byteptr;
|
|
uint32_t sizeleft;
|
|
|
|
DBG_MM_YELLOW("Searching for a block of len %u in extra manager page %p (%u blocks in use)", len, heap_page, heap_page->blocks);
|
|
/* Linearly search for a free heap block */
|
|
|
|
/* heap_block = (pico_mem_block*) (heap_page+1); */
|
|
byteptr = (uint8_t*) heap_page + sizeof(struct pico_mem_manager_extra);
|
|
heap_block = (struct pico_mem_block*) byteptr;
|
|
|
|
sizeleft = PICO_MEM_PAGE_SIZE - sizeof(struct pico_mem_manager_extra);
|
|
|
|
while(heap_block->internals.heap_block.free == HEAP_BLOCK_NOT_FREE || heap_block->internals.heap_block.size < len)
|
|
{
|
|
sizeleft -= (uint32_t)sizeof(struct pico_mem_block);
|
|
sizeleft -= heap_block->internals.heap_block.size;
|
|
/* DBG_MM("Sizeleft=%i", sizeleft); */
|
|
/* byteptr = (uint8_t*) (heap_block+1); */
|
|
byteptr = (uint8_t*) heap_block + sizeof(struct pico_mem_block);
|
|
byteptr += heap_block->internals.heap_block.size;
|
|
heap_block = (struct pico_mem_block*) byteptr;
|
|
if(sizeleft <= sizeof(struct pico_mem_block))
|
|
{
|
|
DBG_MM_RED("No more heap space left in the extra manager heap page!");
|
|
if(heap_page->next == NULL)
|
|
{
|
|
/* TODO: Probably need another function for this */
|
|
DBG_MM_RED("Trying to allocate a new page for extra heap space: space usage %uB/%uB", manager->used_size, manager->size);
|
|
if(manager->used_size + PICO_MEM_PAGE_SIZE > manager->size)
|
|
{
|
|
DBG_MM_RED("No more space left for this page!");
|
|
/* exit(1); */
|
|
return NULL;
|
|
}
|
|
|
|
extra_heap_page = pico_zalloc(PICO_MEM_PAGE_SIZE);
|
|
if(extra_heap_page != NULL)
|
|
{
|
|
extra_heap_page->blocks = 0;
|
|
extra_heap_page->next = NULL;
|
|
extra_heap_page->timestamp = 0;
|
|
byteptr = (uint8_t*) extra_heap_page + sizeof(struct pico_mem_manager_extra);
|
|
first_block = (struct pico_mem_block*) byteptr;
|
|
first_block->type = HEAP_BLOCK_TYPE;
|
|
first_block->internals.heap_block.free = HEAP_BLOCK_FREE;
|
|
first_block->internals.heap_block.size = PICO_MEM_PAGE_SIZE - sizeof(struct pico_mem_manager_extra) - sizeof(struct pico_mem_block);
|
|
extra_heap_page->next = heap_page;
|
|
manager->manager_extra = extra_heap_page;
|
|
manager->used_size += PICO_MEM_PAGE_SIZE;
|
|
DBG_MM_BLUE("Allocated an extra manager heap page at %p, manager space usage: %uB/%uB", extra_heap_page, manager->used_size, manager->size);
|
|
return _pico_mem_manager_extra_alloc(extra_heap_page, len);
|
|
}
|
|
else
|
|
{
|
|
/* This should be a dirty crash */
|
|
DBG_MM_RED("Page not allocated even though the max size for the memory manager hasn't been reached yet!");
|
|
/* exit(1); */
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_RED("This should never happen: debug information:");
|
|
DBG_MM_RED("manager->manager_extra = %p", manager->manager_extra);
|
|
DBG_MM_RED("heap_page = %p", heap_page);
|
|
DBG_MM_RED("heap_page->next = %p", heap_page->next);
|
|
/* exit(1); */
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
heap_page->blocks++;
|
|
heap_page->timestamp = 0;
|
|
DBG_MM_BLUE("Found free heap block in extra manager page %p at: %p (%u blocks in use)", heap_page, heap_block, heap_page->blocks);
|
|
heap_block->internals.heap_block.free = HEAP_BLOCK_NOT_FREE;
|
|
|
|
if(heap_block->internals.heap_block.size == sizeleft - sizeof(struct pico_mem_block))
|
|
{
|
|
DBG_MM_BLUE("End of heap, splitting up into a new block");
|
|
heap_block->internals.heap_block.size = (uint32_t)len;
|
|
sizeleft = (uint32_t)(sizeleft - (uint32_t)sizeof(struct pico_mem_block) - len);
|
|
if(sizeleft > sizeof(struct pico_mem_block))
|
|
{
|
|
sizeleft -= (uint32_t)sizeof(struct pico_mem_block);
|
|
byteptr = (uint8_t*) heap_block + sizeof(struct pico_mem_block);
|
|
byteptr += len;
|
|
new_block = (struct pico_mem_block*) byteptr;
|
|
new_block->type = HEAP_BLOCK_TYPE;
|
|
new_block->internals.heap_block.free = HEAP_BLOCK_FREE;
|
|
new_block->internals.heap_block.size = sizeleft;
|
|
DBG_MM_BLUE("New block: %p, size = %u", new_block, new_block->internals.heap_block.size);
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_RED("No more space in extra manager heap page left to initialize a new heap block!");
|
|
DBG_MM_RED("A new page will be allocated when even more space is needed");
|
|
}
|
|
}
|
|
|
|
startOfData = (uint8_t*) heap_block + sizeof(struct pico_mem_block);
|
|
DBG_MM_GREEN("Start of data = %p", startOfData);
|
|
|
|
return startOfData;
|
|
}
|
|
|
|
/*
|
|
* Page0 zalloc is called by pico_tree.c so that nodes which contain pointers to the free slab objects are put in the
|
|
* manager page. Additional manager pages can be created if necessary.
|
|
*/
|
|
void*pico_mem_page0_zalloc(size_t len)
|
|
{
|
|
struct pico_mem_manager_extra*heap_page;
|
|
struct pico_mem_block*heap_block;
|
|
struct pico_mem_block*first_block;
|
|
struct pico_mem_block*new_block;
|
|
uint8_t*startOfData;
|
|
uint8_t*byteptr;
|
|
uint32_t sizeleft;
|
|
|
|
DBG_MM_YELLOW("pico_mem_page0_zalloc(%u) called", len);
|
|
|
|
byteptr = (uint8_t*) manager + sizeof(struct pico_mem_manager);
|
|
heap_block = (struct pico_mem_block*) byteptr;
|
|
|
|
/* If heap_block == NULL then a free block at the end of the list is found. */
|
|
/* Else, if the block is free and the size > len, an available block is also found. */
|
|
sizeleft = PICO_MEM_PAGE_SIZE - sizeof(struct pico_mem_manager);
|
|
/* this would mean that heap_block is never NULL */
|
|
/* while(heap_block != NULL && ( heap_block->internals.heap_block.free == HEAP_BLOCK_NOT_FREE || heap_block->internals.heap_block.size < len)) */
|
|
while(heap_block->internals.heap_block.free == HEAP_BLOCK_NOT_FREE || heap_block->internals.heap_block.size < len)
|
|
{
|
|
sizeleft -= (uint32_t)sizeof(struct pico_mem_block);
|
|
sizeleft -= heap_block->internals.heap_block.size;
|
|
/* DBG_MM("Sizeleft=%i", sizeleft); */
|
|
byteptr = (uint8_t*) heap_block + sizeof(struct pico_mem_block); /* byteptr points to start of heap block data */
|
|
byteptr += heap_block->internals.heap_block.size; /* jump over that data to start of next heap_block */
|
|
heap_block = (struct pico_mem_block*) byteptr;
|
|
if(sizeleft <= sizeof(struct pico_mem_block))
|
|
{
|
|
DBG_MM_RED("No more heap space left in the manager page!");
|
|
if(manager->manager_extra == NULL)
|
|
{
|
|
DBG_MM_RED("Trying to allocate a new page for extra heap space: space usage: %uB/%uB", manager->used_size, manager->size);
|
|
if(manager->used_size + PICO_MEM_PAGE_SIZE > manager->size)
|
|
{
|
|
DBG_MM_RED("No more space left for this page!");
|
|
/* exit(1); */
|
|
return NULL;
|
|
}
|
|
|
|
heap_page = pico_zalloc(PICO_MEM_PAGE_SIZE);
|
|
if(heap_page != NULL)
|
|
{
|
|
/* Initialize the new heap page */
|
|
heap_page->blocks = 0;
|
|
heap_page->next = NULL;
|
|
heap_page->timestamp = 0;
|
|
byteptr = (uint8_t*) heap_page + sizeof(struct pico_mem_manager_extra);
|
|
first_block = (struct pico_mem_block*) byteptr;
|
|
first_block->type = HEAP_BLOCK_TYPE;
|
|
first_block->internals.heap_block.free = HEAP_BLOCK_FREE;
|
|
first_block->internals.heap_block.size = PICO_MEM_PAGE_SIZE - sizeof(struct pico_mem_manager_extra) - sizeof(struct pico_mem_block);
|
|
manager->manager_extra = heap_page;
|
|
manager->used_size += PICO_MEM_PAGE_SIZE;
|
|
DBG_MM_BLUE("Allocated an extra manager heap page at %p, manager space usage: %uB/%uB", heap_page, manager->used_size, manager->size);
|
|
return _pico_mem_manager_extra_alloc(heap_page, len);
|
|
}
|
|
else
|
|
{
|
|
/* This should be a dirty crash */
|
|
DBG_MM_RED("Page not allocated even though the max size for the memory manager hasn't been reached yet!");
|
|
/* exit(1); */
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return _pico_mem_manager_extra_alloc(manager->manager_extra, len);
|
|
}
|
|
}
|
|
}
|
|
DBG_MM_BLUE("Found free heap block in manager page at : %p", heap_block);
|
|
heap_block->internals.heap_block.free = HEAP_BLOCK_NOT_FREE;
|
|
|
|
if(heap_block->internals.heap_block.size == sizeleft - sizeof(struct pico_mem_block))
|
|
{
|
|
sizeleft = (uint32_t)(sizeleft - (uint32_t)sizeof(struct pico_mem_block) - len);
|
|
if(sizeleft > sizeof(struct pico_mem_block))
|
|
{
|
|
DBG_MM_BLUE("End of heap, splitting up into a new block");
|
|
heap_block->internals.heap_block.size = (uint32_t)len;
|
|
sizeleft -= (uint32_t)sizeof(struct pico_mem_block);
|
|
byteptr = (uint8_t*) heap_block + sizeof(struct pico_mem_block);
|
|
byteptr += len;
|
|
new_block = (struct pico_mem_block*) byteptr;
|
|
new_block->internals.heap_block.free = HEAP_BLOCK_FREE;
|
|
new_block->internals.heap_block.size = sizeleft;
|
|
DBG_MM_BLUE("New block: %p, size = %u", new_block, new_block->internals.heap_block.size);
|
|
}
|
|
else
|
|
{
|
|
/* DBG_MM_RED("ERROR! No more space in manager heap left to initialise a new heap_block!"); */
|
|
/* exit(1); */
|
|
DBG_MM_RED("No more space in manager heap left to initialize a new heap block!");
|
|
DBG_MM_RED("A new page will be allocated when more space is needed");
|
|
}
|
|
}
|
|
|
|
startOfData = (uint8_t*) heap_block + sizeof(struct pico_mem_block);
|
|
DBG_MM_GREEN("Start of data = %p", startOfData);
|
|
|
|
return startOfData;
|
|
}
|
|
|
|
|
|
/*
|
|
* This method will free a given heap block and try to merge it with
|
|
* surrounding blocks if they are free.
|
|
*/
|
|
static void _pico_mem_free_and_merge_heap_block(struct pico_mem_page*page, struct pico_mem_block*mem_block)
|
|
{
|
|
uint8_t*byteptr;
|
|
/* pico_mem_block* prev = NULL; */
|
|
struct pico_mem_block*prev;
|
|
struct pico_mem_block*curr;
|
|
struct pico_mem_block*next;
|
|
|
|
DBG_MM_YELLOW("Freeing heap block %p with size %u in page %p", mem_block, mem_block->internals.heap_block.size, page);
|
|
|
|
mem_block->internals.heap_block.free = HEAP_BLOCK_FREE;
|
|
|
|
byteptr = (uint8_t*) page + sizeof(struct pico_mem_page);
|
|
curr = (struct pico_mem_block*) byteptr;
|
|
byteptr = (uint8_t*) curr + sizeof(struct pico_mem_block);
|
|
byteptr += curr->internals.heap_block.size;
|
|
next = (struct pico_mem_block*) byteptr;
|
|
|
|
while(curr->type == HEAP_BLOCK_TYPE && next->type == HEAP_BLOCK_TYPE)
|
|
{
|
|
DBG_MM("Checking heap block (%s) with size %u at %p", (curr->internals.heap_block.free == HEAP_BLOCK_FREE) ? "free" : "not free", curr->internals.heap_block.size, curr);
|
|
if(curr->internals.heap_block.free == HEAP_BLOCK_FREE && next->internals.heap_block.free == HEAP_BLOCK_FREE)
|
|
{
|
|
DBG_MM_BLUE("Merging blocks with sizes %u and %u", curr->internals.heap_block.size, next->internals.heap_block.size);
|
|
curr->internals.heap_block.size += (uint32_t)sizeof(struct pico_mem_block) + next->internals.heap_block.size;
|
|
}
|
|
|
|
prev = curr;
|
|
byteptr = (uint8_t*) curr + sizeof(struct pico_mem_block);
|
|
byteptr += curr->internals.heap_block.size;
|
|
curr = (struct pico_mem_block*) byteptr;
|
|
byteptr = (uint8_t*) curr + sizeof(struct pico_mem_block);
|
|
byteptr += curr->internals.heap_block.size;
|
|
next = (struct pico_mem_block*) byteptr;
|
|
}
|
|
DBG_MM("Checking heap block (%s) with size %u at %p", (curr->internals.heap_block.free == HEAP_BLOCK_FREE) ? "free" : "not free", curr->internals.heap_block.size, curr);
|
|
if(curr->type == HEAP_BLOCK_TYPE && prev->internals.heap_block.free == HEAP_BLOCK_FREE && curr->internals.heap_block.free == HEAP_BLOCK_FREE)
|
|
{
|
|
DBG_MM_BLUE("Merging blocks with sizes %u and %u", prev->internals.heap_block.size, curr->internals.heap_block.size);
|
|
prev->internals.heap_block.size += (uint32_t)sizeof(struct pico_mem_block) + curr->internals.heap_block.size;
|
|
}
|
|
|
|
DBG_MM_GREEN("Heap block freed and heap space defragmentized");
|
|
}
|
|
|
|
/*
|
|
* This method will return the max. available contiguous free space in the heap
|
|
* from a given page.
|
|
*/
|
|
static uint32_t _pico_mem_determine_max_free_space(struct pico_mem_page*page)
|
|
{
|
|
uint32_t maxfreespace = 0;
|
|
uint8_t*byteptr;
|
|
struct pico_mem_block*mem_block;
|
|
|
|
DBG_MM_YELLOW("Determining new maximum free space in page %p (old free space: %u)", page, page->heap_max_free_space);
|
|
|
|
/* pico_mem_block* mem_block = (pico_mem_block*) (page+1); //reset mem_block to first block in the heap */
|
|
byteptr = (uint8_t*) page + sizeof(struct pico_mem_page);
|
|
mem_block = (struct pico_mem_block*) byteptr; /* reset mem_block to first block in the heap */
|
|
|
|
/* Determine max free space by iterating trough the list */
|
|
/* while(mem_block != NULL && mem_block->type == HEAP_BLOCK_TYPE) */
|
|
while(mem_block->type == HEAP_BLOCK_TYPE)
|
|
{
|
|
/* DBG_MM("Memblock %p of size %i is free %i\n",block, block->size, block->free); */
|
|
DBG_MM("Memblock %s (size %u) at %p", (mem_block->internals.heap_block.free == HEAP_BLOCK_FREE) ? "not in use" : "in use", mem_block->internals.heap_block.size, mem_block);
|
|
if(mem_block->internals.heap_block.free == HEAP_BLOCK_FREE && mem_block->internals.heap_block.size > maxfreespace)
|
|
{
|
|
maxfreespace = mem_block->internals.heap_block.size;
|
|
page->heap_max_free_space = maxfreespace;
|
|
}
|
|
|
|
byteptr = (uint8_t*) mem_block + sizeof(struct pico_mem_block);
|
|
byteptr += mem_block->internals.heap_block.size;
|
|
mem_block = (struct pico_mem_block*) byteptr;
|
|
}
|
|
page->heap_max_free_space = maxfreespace;
|
|
DBG_MM_GREEN("New free space: %u", page->heap_max_free_space);
|
|
return maxfreespace;
|
|
}
|
|
|
|
/*
|
|
* This method will make a slab object available again by putting it in the RB-tree.
|
|
* Slab objects of the same size are stored in a double linked list. One pico_tree_node represents
|
|
* all the slab objects of the same size by making the keyvalue of a pico_tree_node point to
|
|
* the first element of the linked list.
|
|
* An element in this linked list is a struct pico_mem_slab_node. All the elements are also
|
|
* stored in the heap of the manager page (page0), or in the heap of extra manager spaces if there isn't enough space.
|
|
*/
|
|
static void _pico_mem_free_slab_block(struct pico_mem_block*slab_block)
|
|
{
|
|
struct pico_mem_slab_node*slab_node;
|
|
struct pico_mem_slab_node*first_slab_node;
|
|
struct pico_tree_node*tree_node;
|
|
void*temp;
|
|
|
|
DBG_MM_YELLOW("Freeing slab object");
|
|
|
|
slab_node = pico_mem_page0_zalloc(sizeof(struct pico_mem_slab_node));
|
|
|
|
if(slab_node == NULL)
|
|
{
|
|
/* Update the page householding without making the slab available again! */
|
|
DBG_MM_RED("No more space in the manager heap and no more space for extra pages!");
|
|
DBG_MM_RED("This slab will be leaked, but the leak will be plugged at the next cleanup, if and when the page is empty");
|
|
slab_block->internals.slab_block.page->slabs_free++;
|
|
return;
|
|
}
|
|
|
|
slab_node->slab = slab_block;
|
|
slab_block->internals.slab_block.slab_node = slab_node;
|
|
tree_node = pico_tree_findNode(&manager->tree, slab_node);
|
|
if(tree_node != NULL)
|
|
{
|
|
first_slab_node = (struct pico_mem_slab_node*) tree_node->keyValue;
|
|
tree_node->keyValue = slab_node;
|
|
first_slab_node->prev = slab_node;
|
|
slab_node->prev = NULL;
|
|
slab_node->next = first_slab_node;
|
|
}
|
|
else{
|
|
DBG_MM_BLUE("No node found for size %i so calling pico_tree_insert", slab_node->slab->internals.slab_block.page->slab_size);
|
|
slab_node->next = NULL;
|
|
slab_node->prev = NULL;
|
|
/* pico_err_t pico_err_backup = pico_err; */
|
|
/* pico_err = 0; */
|
|
|
|
|
|
/* temp = pico_tree_insert(&manager->tree, slab_node); */
|
|
temp = manager_tree_insert(&manager->tree, slab_node);
|
|
|
|
/* if(pico_err == PICO_ERR_ENOMEM) */
|
|
if(temp == &LEAF)
|
|
{
|
|
DBG_MM_RED("No more space in the manager heap and no more space for extra pages!");
|
|
DBG_MM_RED("This slab will be leaked, but the leak will be plugged at the next cleanup, if and when the page is empty");
|
|
pico_mem_page0_free(slab_node);
|
|
/* pico_err = pico_err_backup; */
|
|
slab_block->internals.slab_block.page->slabs_free++;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Update free slabs in page householding */
|
|
slab_block->internals.slab_block.page->slabs_free++;
|
|
DBG_MM_GREEN("Freed slab object, there are now %i free slab objects in the corresponding page", slab_block->internals.slab_block.page->slabs_free);
|
|
}
|
|
|
|
/*
|
|
* This method zero initializes a block of memory pointed to by startOfData, of size len
|
|
*/
|
|
static void _pico_mem_zero_initialize(void*startOfData, size_t len)
|
|
{
|
|
if(startOfData != NULL)
|
|
{
|
|
DBG_MM_YELLOW("Zero initializing user memory at %p of %u bytes", startOfData, len);
|
|
memset(startOfData, 0, len);
|
|
DBG_MM_GREEN("Zero initialized.");
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_RED("Got a NULL pointer to zero initialize!");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This method will try to find a free heap block of size len in a given page.
|
|
*/
|
|
static void*_pico_mem_find_heap_block(struct pico_mem_page*page, size_t len)
|
|
{
|
|
struct pico_mem_block*mem_block;
|
|
struct pico_mem_block*inserted_block;
|
|
uint8_t*startOfData;
|
|
uint8_t*byteptr;
|
|
|
|
DBG_MM_YELLOW("Searching for a heap block of length %u in page %p (largest free block size = %u)", len, page, page->heap_max_free_space);
|
|
if(page->heap_max_free_space < len )
|
|
{
|
|
DBG_MM_RED("Size %u > max free space %u of the page. This should only happen when this page is newly created, and its heap space is not large enough for the heap length!", len, page->heap_max_free_space);
|
|
return NULL;
|
|
}
|
|
|
|
byteptr = (uint8_t*) page + sizeof(struct pico_mem_page);
|
|
mem_block = (struct pico_mem_block*) byteptr; /* Jump over the page struct to the start of the heap */
|
|
|
|
/* If mem_block == NULL then a free block at the end of the list is found. */
|
|
/* Else, if the block is free and the size > len, an available block is also found. */
|
|
/* while(mem_block != NULL && mem_block->type == HEAP_BLOCK_TYPE && ( mem_block->internals.heap_block.free == HEAP_BLOCK_NOT_FREE || mem_block->internals.heap_block.size < len)) */
|
|
while(mem_block->type == HEAP_BLOCK_TYPE && (mem_block->internals.heap_block.free == HEAP_BLOCK_NOT_FREE || mem_block->internals.heap_block.size < len))
|
|
{
|
|
/* DBG_MM_RED("Skipping heap block in use at %p of size %i", mem_block, mem_block->size); */
|
|
DBG_MM_BLUE("Skipping heap block %s (size %u) at %p", (mem_block->internals.heap_block.free == HEAP_BLOCK_FREE) ? "not in use" : "in use", mem_block->internals.heap_block.size, mem_block);
|
|
byteptr = (uint8_t*) mem_block + sizeof(struct pico_mem_block);
|
|
byteptr += mem_block->internals.heap_block.size;
|
|
mem_block = (struct pico_mem_block*) byteptr;
|
|
}
|
|
if(mem_block->type == SLAB_BLOCK_TYPE)
|
|
{
|
|
DBG_MM_RED("No free heap block of contiguous size %u could be found in page %p", len, page);
|
|
/* exit(1); */
|
|
return NULL;
|
|
}
|
|
|
|
DBG_MM_BLUE("Found free heap block of size %u at %p", mem_block->internals.heap_block.size, mem_block);
|
|
mem_block->internals.heap_block.free = HEAP_BLOCK_NOT_FREE;
|
|
page->timestamp = 0;
|
|
|
|
/* Check to split the block into two smaller blocks */
|
|
if(mem_block->internals.heap_block.size >= (len + sizeof(struct pico_mem_block) + PICO_MEM_MINIMUM_OBJECT_SIZE))
|
|
{
|
|
byteptr = (uint8_t*) mem_block + sizeof(struct pico_mem_block);
|
|
byteptr += len;
|
|
inserted_block = (struct pico_mem_block*) byteptr;
|
|
|
|
/* Update newly inserted block */
|
|
inserted_block->type = HEAP_BLOCK_TYPE;
|
|
inserted_block->internals.heap_block.free = HEAP_BLOCK_FREE;
|
|
inserted_block->internals.heap_block.size = (uint32_t)(mem_block->internals.heap_block.size - (uint32_t)sizeof(struct pico_mem_block) - len);
|
|
/* Update block that was split up */
|
|
mem_block->internals.heap_block.size = (uint32_t)len;
|
|
DBG_MM_BLUE("Splitting up the block, creating a new block of size %u at %p", inserted_block->internals.heap_block.size, inserted_block);
|
|
}
|
|
|
|
startOfData = (uint8_t*) mem_block + sizeof(struct pico_mem_block);
|
|
|
|
page->heap_max_free_space = _pico_mem_determine_max_free_space(page);
|
|
|
|
/* Zero-initialize */
|
|
_pico_mem_zero_initialize(startOfData, len);
|
|
DBG_MM_GREEN("Returning %p", startOfData);
|
|
return startOfData;
|
|
}
|
|
|
|
/*
|
|
* This method will be called from pico_mem_zalloc. If an appropriate slab object is found,
|
|
* it is deleted from the RB tree and a pointer to the start of data in the slab object
|
|
* is returned.
|
|
*/
|
|
static void*_pico_mem_find_slab(size_t len)
|
|
{
|
|
size_t*lenptr = &len;
|
|
size_t**doublelenptr = &lenptr;
|
|
struct pico_tree_node*node;
|
|
uint8_t *returnVal = NULL;
|
|
|
|
DBG_MM_YELLOW("Finding slab with size %u", len);
|
|
/* The compare function takes an int*** length */
|
|
node = pico_tree_findNode(&manager->tree, &doublelenptr);
|
|
|
|
if(node != NULL) {
|
|
/* DBG_MM_BLUE("Found node, size = %d ", ((pico_mem_slab_node*) node->keyValue)->slab->size); */
|
|
struct pico_mem_slab_node*slab_node = node->keyValue;
|
|
slab_node->slab->internals.slab_block.page->slabs_free--;
|
|
slab_node->slab->internals.slab_block.page->timestamp = 0;
|
|
DBG_MM_BLUE("Found node, size = %u at page %p, %u free slabs left in page", slab_node->slab->internals.slab_block.page->slab_size, slab_node->slab->internals.slab_block.page, slab_node->slab->internals.slab_block.page->slabs_free);
|
|
if(slab_node->next == NULL)
|
|
{
|
|
DBG_MM_BLUE("This was the last available slab object. Deleting the tree node now.");
|
|
/* if this is the last slab object of this size in the tree, then also delete the tree_node! */
|
|
|
|
|
|
/* pico_tree_delete(&manager->tree, &doublelenptr); */
|
|
manager_tree_delete(&manager->tree, &doublelenptr);
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Remove the pico_mem_slab_node by making the keyvalue of the pico_tree_node point to the next element. */
|
|
slab_node->next->prev = NULL;
|
|
node->keyValue = slab_node->next;
|
|
}
|
|
|
|
returnVal = ((uint8_t*) (slab_node->slab)) + sizeof(struct pico_mem_block);
|
|
DBG_MM_BLUE("Start of slab: %p -> start of data : %p", slab_node->slab, returnVal);
|
|
/* Update the slab block housekeeping */
|
|
slab_node->slab->internals.slab_block.slab_node = NULL;
|
|
/* Zero-initialize */
|
|
_pico_mem_zero_initialize(returnVal, len);
|
|
/* Free the struct that was used by the linked list in the RB-tree */
|
|
pico_mem_page0_free(slab_node);
|
|
}
|
|
|
|
DBG_MM_GREEN("Returning %p", returnVal);
|
|
return returnVal;
|
|
}
|
|
|
|
/*
|
|
* This method is called by the picotcp stack to free memory.
|
|
*/
|
|
void pico_mem_free(void*ptr)
|
|
{
|
|
struct pico_mem_block*generic_block;
|
|
struct pico_mem_page*page;
|
|
#ifdef DEBUG_MM
|
|
uint16_t i = 0;
|
|
#endif
|
|
|
|
DBG_MM_YELLOW("Free called on %p", ptr);
|
|
|
|
if(ptr == NULL) return;
|
|
|
|
generic_block = (struct pico_mem_block*) ptr;
|
|
generic_block--;
|
|
|
|
if(generic_block->type == SLAB_BLOCK_TYPE)
|
|
{
|
|
if(generic_block->internals.slab_block.slab_node)
|
|
{
|
|
DBG_MM_RED("ERROR: Double free on a slab block (recovered)!");
|
|
return;
|
|
}
|
|
|
|
DBG_MM_BLUE("Request to free a slab block");
|
|
_pico_mem_free_slab_block(generic_block);
|
|
}
|
|
else if(generic_block->type == HEAP_BLOCK_TYPE)
|
|
{
|
|
if(generic_block->internals.heap_block.free == HEAP_BLOCK_FREE)
|
|
{
|
|
DBG_MM_RED("ERROR: Double free on a heap block (recovered)!");
|
|
return;
|
|
}
|
|
|
|
DBG_MM_BLUE("Request to free a heap block");
|
|
|
|
/* Update the page housekeeping */
|
|
/* Update the housekeeping of the extra manager pages */
|
|
page = manager->first_page;
|
|
while(page != NULL)
|
|
{
|
|
DBG_MM_BLUE("Checking page %i at %p", i++, page);
|
|
if(((uint8_t*) page < (uint8_t*) ptr) && ((uint8_t*) ptr < (uint8_t*) page + PICO_MEM_PAGE_SIZE))
|
|
{
|
|
/* DBG_MM_RED("page < ptr < page + PICO_MEM_PAGE_SIZE"); */
|
|
/* DBG_MM_RED("%p < %p < %p", (uint8_t*) page, (uint8_t*) ptr, (uint8_t*) page + PICO_MEM_PAGE_SIZE); */
|
|
_pico_mem_free_and_merge_heap_block(page, generic_block);
|
|
_pico_mem_determine_max_free_space(page);
|
|
break;
|
|
}
|
|
|
|
page = page->next_page;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_RED("ERROR: You tried to free a pointer from which the type ( heap block or slab object ) could not be determined!!");
|
|
}
|
|
}
|
|
|
|
/************************NEW***************************/
|
|
static void _pico_mem_reset_slab_statistics(void)
|
|
{
|
|
slab_size_statistics[0] = 0;
|
|
slab_size_statistics[1] = 0;
|
|
slab_size_statistics[2] = 0;
|
|
}
|
|
|
|
static size_t _pico_mem_determine_slab_size(size_t len)
|
|
{
|
|
DBG_MM_YELLOW("Determining slab size to use, request for %u bytes", len);
|
|
if (len > slab_sizes[1])
|
|
{
|
|
slab_size_statistics[2]++;
|
|
if(slab_size_statistics[2] > 3)
|
|
{
|
|
_pico_mem_reset_slab_statistics();
|
|
if(slab_size_global != slab_sizes[2])
|
|
{
|
|
slab_size_global = slab_sizes[2];
|
|
}
|
|
}
|
|
|
|
if(slab_size_global != slab_sizes[2])
|
|
{
|
|
DBG_MM_RED("Using slab size %u, but we have to use a slab size of %u for the request of %u bytes", slab_size_global, slab_sizes[2], len);
|
|
return slab_sizes[2];
|
|
}
|
|
}
|
|
else if(len > slab_sizes[0])
|
|
{
|
|
slab_size_statistics[1]++;
|
|
if (slab_size_statistics[1] > 3)
|
|
{
|
|
_pico_mem_reset_slab_statistics();
|
|
if(slab_size_global != slab_sizes[1])
|
|
{
|
|
slab_size_global = slab_sizes[1];
|
|
}
|
|
}
|
|
|
|
if(len > slab_size_global)
|
|
{
|
|
DBG_MM_RED("Using slab size %u, but we have to use a slab size of %u for the request of %u bytes", slab_size_global, slab_sizes[1], len);
|
|
return slab_sizes[1];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
slab_size_statistics[0]++;
|
|
if (slab_size_statistics[0] > 3)
|
|
{
|
|
_pico_mem_reset_slab_statistics();
|
|
if(slab_size_global != slab_sizes[0])
|
|
{
|
|
slab_size_global = slab_sizes[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
DBG_MM_GREEN("Using slab size %u", slab_size_global);
|
|
return slab_size_global;
|
|
}
|
|
/************************NEW***************************/
|
|
|
|
/*
|
|
* This method will be called by the picotcp stack to allocate new memory.
|
|
* If the requested size is bigger than the threshold of a slab object,
|
|
* then the manager will try to find an appropriate slab object and return a pointer
|
|
* to the beginning of the data in that slab object.
|
|
*
|
|
* If no slab objects could be found, or the requested size is less then the threshold
|
|
* of a slab object, the manager will try to allocate a heap block and return a pointer
|
|
* to the beginning of the data of that heap block.
|
|
*
|
|
* If still no memory could be found, then the manager will check again if the
|
|
* requested size is smaller than the threshold of a slab object.
|
|
* If so, the manager will try to find a slab object again but now ignoring the threshold.
|
|
* By doing so, there will be a large amount of internal fragmentation, but at least the
|
|
* memory request could be fulfilled.
|
|
*
|
|
* In any other case, the manager will return NULL.
|
|
*/
|
|
void*pico_mem_zalloc(size_t len)
|
|
{
|
|
struct pico_mem_page*page;
|
|
void*returnCandidate;
|
|
uint32_t pagenr;
|
|
void *ret;
|
|
|
|
DBG_MM_YELLOW("===> pico_mem_zalloc(%i) called", len);
|
|
len += (len % 4 == 0) ? 0 : 4 - len % 4;
|
|
DBG_MM_YELLOW("Aligned size: %i", len);
|
|
|
|
if(manager == NULL)
|
|
{
|
|
DBG_MM_RED("Invalid alloc, a memory manager hasn't been instantiated yet!");
|
|
return NULL;
|
|
}
|
|
|
|
if(len > PICO_MAX_SLAB_SIZE)
|
|
{
|
|
DBG_MM_RED("Invalid alloc, the size you requested is larger than the maximum slab size! (%uB>%uB)", len, PICO_MAX_SLAB_SIZE);
|
|
return NULL;
|
|
}
|
|
|
|
/* /////// FIND SLAB OBJECTS ///////// */
|
|
if(len >= PICO_MIN_SLAB_SIZE)
|
|
{
|
|
/* feed the size into a statistic engine that determines the slabsize to use */
|
|
/* DBG_MM_RED("Placeholder: determine correct slab size to use!"); */
|
|
len = _pico_mem_determine_slab_size(len);
|
|
ret = _pico_mem_find_slab(len);
|
|
if(ret != NULL) return ret;
|
|
|
|
/* No slab object could be found. => Init new page? */
|
|
|
|
DBG_MM_BLUE("No free slab found, trying to create a new page (Used size = %u, max size = %u)", manager->used_size, manager->size);
|
|
if(manager->used_size + PICO_MEM_PAGE_SIZE <= manager->size)
|
|
{
|
|
struct pico_mem_page*newpage = pico_zalloc(PICO_MEM_PAGE_SIZE);
|
|
if(newpage != NULL)
|
|
{
|
|
manager->used_size += PICO_MEM_PAGE_SIZE;
|
|
DBG_MM_BLUE("Created new page at %p -> used size = %u", newpage, manager->used_size);
|
|
_pico_mem_init_page(newpage, len);
|
|
/* Return pointer to first slab in that page */
|
|
return _pico_mem_find_slab(len); /* Find the new slab object! */
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_RED("Not enough space to allocate a new page, even though the max size hasn't been reached yet!");
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_RED("Not enough space to allocate a new page!");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* /////// FIND HEAP BLOCKS ///////// */
|
|
if(len < PICO_MEM_MINIMUM_OBJECT_SIZE)
|
|
len = PICO_MEM_MINIMUM_OBJECT_SIZE;
|
|
|
|
DBG_MM_BLUE("Searching for heap space of length %u now.", len);
|
|
|
|
pagenr = 1;
|
|
page = manager->first_page;
|
|
|
|
/* The algorithm to find a heap block is based on first fit. */
|
|
/* But when the internal fragmentation is too big, the block is split. */
|
|
while(page != NULL)
|
|
{
|
|
/* DBG_MM_RED("Max free space in page %i = %i bytes", pagecounter+1, page->heap.max_free_space); */
|
|
DBG_MM_BLUE("Max free space in page %u = %uB (page=%p)", pagenr, page->heap_max_free_space, page);
|
|
if(len <= page->heap_max_free_space)
|
|
{
|
|
return _pico_mem_find_heap_block(page, len);
|
|
}
|
|
|
|
pagenr++;
|
|
page = page->next_page;
|
|
}
|
|
/* No free heap block could be found, try to alloc a new page */
|
|
DBG_MM_BLUE("No free heap block found, trying to create a new page (Used size = %u, max size = %u)", manager->used_size, manager->size);
|
|
if(manager->used_size + PICO_MEM_PAGE_SIZE <= manager->size)
|
|
{
|
|
struct pico_mem_page*newpage = pico_zalloc(PICO_MEM_PAGE_SIZE);
|
|
if(newpage != NULL)
|
|
{
|
|
manager->used_size += PICO_MEM_PAGE_SIZE;
|
|
DBG_MM_BLUE("Created new page at %p -> used size = %u", newpage, manager->used_size);
|
|
/* TODO: Careful, if the current slabsize is determined in another way, this needs to change too */
|
|
_pico_mem_init_page(newpage, slab_size_global);
|
|
returnCandidate = _pico_mem_find_heap_block(newpage, len);
|
|
if(returnCandidate != NULL)
|
|
return returnCandidate;
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_RED("Not enough space to allocate a new page, even though the max size hasn't been reached yet!");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* DBG_MM_RED("NO HEAP BLOCK FOUND!"); */
|
|
|
|
/* /////// TRY TO FIND NEW SLAB OBJECT, BUT INCREASE SIZE ///////// */
|
|
DBG_MM_RED("TRYING TO FIND FREE SLAB OBJECT WITH DANGER OF LARGE INTERNAL FRAGMENTATION");
|
|
/* TODO: Careful, if the current slabsize is determined in another way, this needs to change too */
|
|
return _pico_mem_find_slab(slab_size_global);
|
|
}
|
|
/*
|
|
* This method frees heap space used in the manager page, or in one of the extra manager pages
|
|
*/
|
|
void pico_mem_page0_free(void*ptr)
|
|
{
|
|
struct pico_mem_block*node = ptr;
|
|
struct pico_mem_manager_extra*heap_page;
|
|
#ifdef DEBUG_MM
|
|
uint16_t i = 0;
|
|
#endif
|
|
|
|
/* TODO: should be able to merge free neighbouring blocks (??) */
|
|
DBG_MM_YELLOW("page0_free called");
|
|
|
|
node--;
|
|
node->internals.heap_block.free = HEAP_BLOCK_FREE;
|
|
/* Update the housekeeping of the extra manager pages */
|
|
heap_page = manager->manager_extra;
|
|
while(heap_page != NULL)
|
|
{
|
|
DBG_MM_BLUE("Checking extra heap page %i at %p", i++, heap_page);
|
|
if(((uint8_t*) heap_page < (uint8_t*) ptr) && ((uint8_t*) ptr < (uint8_t*) heap_page + PICO_MEM_PAGE_SIZE))
|
|
{
|
|
/* DBG_MM_RED("heap_page < ptr < heap_page + PICO_MEM_PAGE_SIZE"); */
|
|
/* DBG_MM_RED("%p < %p < %p", (uint8_t*) heap_page, (uint8_t*) ptr, (uint8_t*) heap_page + PICO_MEM_PAGE_SIZE); */
|
|
heap_page->blocks--;
|
|
DBG_MM_BLUE("Updating heap page housekeeping: %u->%u used blocks", heap_page->blocks + 1, heap_page->blocks);
|
|
break;
|
|
}
|
|
|
|
heap_page = heap_page->next;
|
|
}
|
|
DBG_MM_GREEN("Heap block (located in %s) succesfully freed", (i != -1) ? "main manager page" : "extra manager page");
|
|
}
|
|
|
|
/*
|
|
* This cleanup function must be called externally at downtime moments. A system timestamp must be passed to the function.
|
|
* All pages and extra manager pages will be checked. If they are empty, the timestamp of the page will be updated. If the
|
|
* page has been empty for a time longer than PICO_MEM_PAGE_LIFETIME, the page is returned to the system's control, and all
|
|
* the housekeeping is updated.
|
|
*/
|
|
void pico_mem_cleanup(uint32_t timestamp)
|
|
{
|
|
struct pico_mem_slab_node*slab_node;
|
|
struct pico_tree_node*tree_node;
|
|
struct pico_mem_block*slab_block;
|
|
struct pico_mem_page*next_page;
|
|
struct pico_mem_page*prev_page;
|
|
struct pico_mem_page*page;
|
|
struct pico_mem_manager_extra*heap_page;
|
|
struct pico_mem_manager_extra*next;
|
|
struct pico_mem_manager_extra*prev_heap_page;
|
|
uint8_t*byteptr;
|
|
int pagenr = 1;
|
|
int i;
|
|
|
|
DBG_MM_YELLOW("Starting cleanup with timestamp %u", timestamp);
|
|
/* Iterate over all pages */
|
|
page = manager->first_page;
|
|
prev_page = NULL;
|
|
while(page != NULL)
|
|
{
|
|
DBG_MM_BLUE("Checking page %i at %p", pagenr, page);
|
|
/* Check the timestamp of the page. If it doesn't have one (0), update it with the new timestamp if the page is completely empty. */
|
|
if(page->timestamp == 0)
|
|
{
|
|
if((page->heap_max_size == page->heap_max_free_space) && (page->slabs_free == page->slabs_max))
|
|
{
|
|
DBG_MM_BLUE("Page %i empty, updating timestamp", pagenr);
|
|
page->timestamp = timestamp;
|
|
}
|
|
}
|
|
/* If the timestamp is old enough, remove the page and all its slabs. This means we have to: */
|
|
/* > Remove all slabs out of the RB tree */
|
|
/* > Update the page list */
|
|
/* > Return the page to the system's control */
|
|
/* > Update manager housekeeping */
|
|
else if(timestamp > page->timestamp)
|
|
{
|
|
if(timestamp - page->timestamp > PICO_MEM_PAGE_LIFETIME)
|
|
{
|
|
DBG_MM_BLUE("Page %i is empty and has exceeded the lifetime (%u > lifetime=%u)", pagenr, timestamp - page->timestamp, PICO_MEM_PAGE_LIFETIME);
|
|
/* Remove all the slabs out of the RB tree */
|
|
byteptr = (uint8_t*) page + sizeof(struct pico_mem_page); /* byteptr points to the start of the heap (a pico_mem_block), after page housekeeping */
|
|
byteptr += sizeof(struct pico_mem_block); /* jump over pico_mem_block, containing the housekeeping for the heap space */
|
|
byteptr += page->heap_max_size; /* jump over heap space, byteptr now points to the start of the slabs */
|
|
slab_block = (struct pico_mem_block*) byteptr;
|
|
slab_node = slab_block->internals.slab_block.slab_node;
|
|
/* The corresponding tree_node */
|
|
tree_node = pico_tree_findNode(&manager->tree, slab_node);
|
|
for(i = 0; i < page->slabs_max; i++)
|
|
{
|
|
DBG_MM("Removing slab %i at %p", i, slab_block);
|
|
if(slab_node->prev == NULL && slab_node->next == NULL)
|
|
{
|
|
DBG_MM("This node is the last node in the tree_node, removing tree_node");
|
|
/* slab_node is the last node in the tree leaf, delete it */
|
|
|
|
|
|
/* pico_tree_delete(&manager->tree, slab_node); */
|
|
manager_tree_delete(&manager->tree, slab_node);
|
|
|
|
|
|
}
|
|
else if(slab_node->prev == NULL)
|
|
{
|
|
DBG_MM("This node is the first node in the linked list, adjusting tree_node");
|
|
tree_node->keyValue = slab_node->next;
|
|
slab_node->next->prev = NULL;
|
|
}
|
|
else if(slab_node->next == NULL)
|
|
{
|
|
DBG_MM("This node is the last node in the linked list");
|
|
slab_node->prev->next = NULL;
|
|
}
|
|
else
|
|
{
|
|
DBG_MM("This node is neither the first, nor the last node in the list");
|
|
slab_node->prev->next = slab_node->next;
|
|
slab_node->next->prev = slab_node->prev;
|
|
}
|
|
|
|
pico_mem_page0_free(slab_node);
|
|
byteptr = (uint8_t*) slab_block + sizeof(struct pico_mem_block); /* byteptr points to the start of the slab data, after the housekeeping */
|
|
byteptr += page->slab_size; /* jump over the slab data, byteptr now points to the start of the next slab block */
|
|
slab_block = (struct pico_mem_block*) byteptr;
|
|
slab_node = slab_block->internals.slab_block.slab_node;
|
|
}
|
|
/* Update the page list */
|
|
if(prev_page == NULL) /* prev_page == NULL when pagenr=1, or when previous pages were deleted */
|
|
{
|
|
DBG_MM("Updating page list, manager->first_page = page->next_page");
|
|
manager->first_page = page->next_page;
|
|
}
|
|
else
|
|
{
|
|
DBG_MM("Updating page list, prev_page->next_page = page->next_page");
|
|
prev_page->next_page = page->next_page;
|
|
}
|
|
|
|
/* Return the page to the system's control */
|
|
next_page = page->next_page;
|
|
DBG_MM("Freeing page, manager used size = %u", manager->used_size);
|
|
pico_free(page);
|
|
/* Update the manager housekeeping */
|
|
manager->used_size -= PICO_MEM_PAGE_SIZE;
|
|
DBG_MM("Freed page, manager used size = %u, down from %u", manager->used_size, manager->used_size + PICO_MEM_PAGE_SIZE);
|
|
/* ITERATION */
|
|
page = next_page;
|
|
pagenr++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_BLUE("Page %i is empty, but has not exceeded the lifetime (%u < lifetime=%u)", pagenr, timestamp - page->timestamp, PICO_MEM_PAGE_LIFETIME);
|
|
}
|
|
}
|
|
else /* timestamp < page->timestamp */
|
|
{
|
|
DBG_MM_RED("Page %i is empty, but the system timestamp < page timestamp! (%u<%u)", pagenr, timestamp, page->timestamp);
|
|
DBG_MM_RED("Updating page %i timestamp!", pagenr);
|
|
page->timestamp = timestamp;
|
|
}
|
|
|
|
pagenr++;
|
|
prev_page = page;
|
|
page = page->next_page;
|
|
}
|
|
/* Check all extra manager pages if they are empty */
|
|
heap_page = manager->manager_extra;
|
|
prev_heap_page = NULL;
|
|
pagenr = 1;
|
|
while(heap_page != NULL)
|
|
{
|
|
DBG_MM_BLUE("Checking extra manager page %i at %p", pagenr, heap_page);
|
|
if(heap_page->timestamp == 0)
|
|
{
|
|
if( heap_page->blocks == 0 )
|
|
{
|
|
DBG_MM_BLUE("Extra manager page %i empty, updating timestamp", pagenr);
|
|
heap_page->timestamp = timestamp;
|
|
}
|
|
}
|
|
else if(timestamp > heap_page->timestamp)
|
|
{
|
|
if(timestamp - heap_page->timestamp > PICO_MEM_PAGE_LIFETIME)
|
|
{
|
|
DBG_MM_BLUE("Extra manager page %i empty and has exceeded the lifetime (%u > lifetime=%u)", pagenr, timestamp - heap_page->timestamp, PICO_MEM_PAGE_LIFETIME);
|
|
/* Update the page list */
|
|
if(prev_heap_page == NULL)
|
|
{
|
|
DBG_MM("Updating page list, manager->manager_extra = heap_page->next");
|
|
manager->manager_extra = heap_page->next;
|
|
}
|
|
else
|
|
{
|
|
DBG_MM("Updating page list, prev_heap_page->next = heap_page->next");
|
|
prev_heap_page->next = heap_page->next;
|
|
}
|
|
|
|
/* Return the page to the system's control */
|
|
next = heap_page->next;
|
|
DBG_MM("Freeing page, manager used size = %u", manager->used_size);
|
|
pico_free(heap_page);
|
|
/* Update the manager housekeeping */
|
|
manager->used_size -= PICO_MEM_PAGE_SIZE;
|
|
DBG_MM("Freed page, manager used size = %u, down from %u", manager->used_size, manager->used_size + PICO_MEM_PAGE_SIZE);
|
|
/* ITERATION */
|
|
heap_page = next;
|
|
pagenr++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_BLUE("Page %i is empty, but has not exceeded the lifetime (%u < lifetime=%u)", pagenr, timestamp - heap_page->timestamp, PICO_MEM_PAGE_LIFETIME);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_MM_RED("Page %i is empty, but the system timestamp < page timestamp! (%u<%u)", pagenr, timestamp, heap_page->timestamp);
|
|
DBG_MM_RED("Updating page %i timestamp!", pagenr);
|
|
heap_page->timestamp = timestamp;
|
|
}
|
|
|
|
/* ITERATION */
|
|
pagenr++;
|
|
prev_heap_page = heap_page;
|
|
heap_page = heap_page->next;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef PICO_SUPPORT_MM_PROFILING
|
|
/***********************************************************************************************************************
|
|
***********************************************************************************************************************
|
|
MEMORY PROFILING FUNCTIONS
|
|
***********************************************************************************************************************
|
|
***********************************************************************************************************************/
|
|
|
|
static struct pico_mem_manager*manager_profile;
|
|
|
|
static void _pico_mem_print_tree(struct pico_tree_node*root)
|
|
{
|
|
struct pico_mem_slab_node*iterator;
|
|
int j;
|
|
|
|
if (root == &LEAF || root == NULL)
|
|
{
|
|
DBG_MM("No tree nodes at this time.\n");
|
|
return;
|
|
}
|
|
|
|
iterator = (struct pico_mem_slab_node*) root->keyValue;
|
|
DBG_MM("Tree node for size %u:\n", iterator->slab->internals.slab_block.page->slab_size);
|
|
j = 0;
|
|
while(iterator != NULL)
|
|
{
|
|
DBG_MM("\tSlab_node %i at %p:\n", j, iterator);
|
|
DBG_MM("\t\tPrev:%p\n", iterator->prev);
|
|
DBG_MM("\t\tNext:%p\n", iterator->next);
|
|
DBG_MM("\t\tSlab:%p\n", iterator->slab);
|
|
j++;
|
|
iterator = iterator->next;
|
|
}
|
|
if(root->leftChild != &LEAF && root->leftChild != NULL)
|
|
_pico_mem_print_tree(root->leftChild);
|
|
|
|
if(root->rightChild != &LEAF && root->rightChild != NULL)
|
|
_pico_mem_print_tree(root->rightChild);
|
|
}
|
|
|
|
void pico_mem_profile_scan_data()
|
|
{
|
|
if(manager == NULL)
|
|
{
|
|
DBG_MM("No memory manager instantiated!\n");
|
|
}
|
|
else
|
|
{
|
|
int manager_pages = 0;
|
|
int pages = 0;
|
|
int counter = 0;
|
|
struct pico_mem_manager_extra*heap_page;
|
|
struct pico_mem_page*page;
|
|
uint8_t*byteptr;
|
|
struct pico_mem_block*mem_block;
|
|
|
|
DBG_MM("Memory manager: %uB/%uB in use\n", manager->used_size, manager->size);
|
|
_pico_mem_print_tree(manager->tree.root);
|
|
|
|
/* Iterate over every extra manager page: */
|
|
heap_page = manager->manager_extra;
|
|
while(heap_page != NULL)
|
|
{
|
|
manager_pages++;
|
|
DBG_MM("Extra manager page %i:\n\tBlocks in use: %u\n\tTimestamp: %u\n", manager_pages, heap_page->blocks, heap_page->timestamp);
|
|
heap_page = heap_page->next;
|
|
}
|
|
/* Iterate over every page: */
|
|
pages = (manager->used_size / PICO_MEM_PAGE_SIZE) - manager_pages - 1;
|
|
page = manager->first_page;
|
|
while(page != NULL)
|
|
{
|
|
counter++;
|
|
DBG_MM("Page %i/%i:\n\tSlabsize: %u\n\tSlabs free: %u/%u\n\tTimestamp: %u\n", counter, pages, page->slab_size, page->slabs_free, page->slabs_max, page->timestamp);
|
|
byteptr = (uint8_t*) page + sizeof(struct pico_mem_page);
|
|
mem_block = (struct pico_mem_block*) byteptr;
|
|
DBG_MM("\tHeap:\n");
|
|
while(mem_block->type == HEAP_BLOCK_TYPE)
|
|
{
|
|
DBG_MM("\t\tBlock: size %u, %s\n", mem_block->internals.heap_block.size, (mem_block->internals.heap_block.free == HEAP_BLOCK_FREE) ? "free" : "not free");
|
|
byteptr = (uint8_t*) mem_block + sizeof(struct pico_mem_block);
|
|
byteptr += mem_block->internals.heap_block.size;
|
|
mem_block = (struct pico_mem_block*) byteptr;
|
|
}
|
|
page = page->next_page;
|
|
}
|
|
}
|
|
}
|
|
|
|
void pico_mem_profile_collect_data(struct profiling_data*profiling_struct)
|
|
{
|
|
struct pico_mem_block*mem_block;
|
|
uint8_t*byteptr;
|
|
|
|
profiling_struct->free_heap_space = 0;
|
|
profiling_struct->free_slab_space = 0;
|
|
profiling_struct->used_heap_space = 0;
|
|
profiling_struct->used_slab_space = 0;
|
|
if(manager != NULL)
|
|
{
|
|
struct pico_mem_page*page = manager->first_page;
|
|
while(page != NULL)
|
|
{
|
|
profiling_struct->free_slab_space += page->slab_size * page->slabs_free;
|
|
profiling_struct->used_slab_space += page->slab_size * page->slabs_max;
|
|
|
|
byteptr = (uint8_t*) page + sizeof(struct pico_mem_page);
|
|
mem_block = (struct pico_mem_block*) byteptr;
|
|
|
|
while(mem_block->type == HEAP_BLOCK_TYPE)
|
|
{
|
|
if(mem_block->internals.heap_block.free == HEAP_BLOCK_FREE)
|
|
{
|
|
profiling_struct->free_heap_space += mem_block->internals.heap_block.size;
|
|
}
|
|
else
|
|
{
|
|
/* dbg("Block: size=%u\n", mem_block->internals.heap_block.size); */
|
|
profiling_struct->used_heap_space += mem_block->internals.heap_block.size;
|
|
}
|
|
|
|
byteptr += sizeof(struct pico_mem_block) + mem_block->internals.heap_block.size;
|
|
mem_block = (struct pico_mem_block*) byteptr;
|
|
}
|
|
page = page->next_page;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t pico_mem_profile_used_size()
|
|
{
|
|
if(manager != NULL)
|
|
{
|
|
return manager->used_size;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
struct pico_mem_manager*pico_mem_profile_manager()
|
|
{
|
|
return manager;
|
|
}
|
|
#endif /* PICO_SUPPORT_MM_PROFILING */
|
|
|