Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heap Improvements #364

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions include/sbi/sbi_heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,59 @@

#include <sbi/sbi_types.h>

/* Opaque declaration of heap control struct */
struct heap_control;

/* Alignment of heap base address and size */
#define HEAP_BASE_ALIGN 1024

struct sbi_scratch;

/** Allocate from heap area */
void *sbi_malloc(size_t size);
void *sbi_malloc_from(struct heap_control *hpctrl, size_t size);

/** Allocate aligned from heap area */
void *sbi_memalign(size_t alignment, size_t size);
void *sbi_memalign_from(struct heap_control *hpctrl, size_t alignment,
size_t size);

/** Zero allocate from heap area */
void *sbi_zalloc(size_t size);
void *sbi_zalloc_from(struct heap_control *hpctrl, size_t size);

/** Allocate array from heap area */
static inline void *sbi_calloc(size_t nitems, size_t size)
{
return sbi_zalloc(nitems * size);
}

static inline void *sbi_calloc_from(struct heap_control *hpctrl,
size_t nitems, size_t size)
{
return sbi_zalloc_from(hpctrl, nitems * size);
}

/** Free-up to heap area */
void sbi_free(void *ptr);
void sbi_free_from(struct heap_control *hpctrl, void *ptr);

/** Amount (in bytes) of free space in the heap area */
unsigned long sbi_heap_free_space(void);
unsigned long sbi_heap_free_space_from(struct heap_control *hpctrl);

/** Amount (in bytes) of used space in the heap area */
unsigned long sbi_heap_used_space(void);
unsigned long sbi_heap_used_space_from(struct heap_control *hpctrl);

/** Amount (in bytes) of reserved space in the heap area */
unsigned long sbi_heap_reserved_space(void);
unsigned long sbi_heap_reserved_space_from(struct heap_control *hpctrl);

/** Initialize heap area */
int sbi_heap_init(struct sbi_scratch *scratch);
int sbi_heap_init_new(struct heap_control *hpctrl, unsigned long base,
unsigned long size);
int sbi_heap_alloc_new(struct heap_control **hpctrl);

#endif
181 changes: 128 additions & 53 deletions lib/sbi/sbi_heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,94 +35,135 @@ struct heap_control {
struct sbi_dlist used_space_list;
};

static struct heap_control hpctrl;
static struct heap_control global_hpctrl;

void *sbi_malloc(size_t size)
static void *alloc_with_align(struct heap_control *hpctrl,
size_t align, size_t size)
{
void *ret = NULL;
struct heap_node *n, *np;

if (!size)
return NULL;

size += HEAP_ALLOC_ALIGN - 1;
size &= ~((unsigned long)HEAP_ALLOC_ALIGN - 1);
size += align - 1;
size &= ~((unsigned long)align - 1);

spin_lock(&hpctrl.lock);
spin_lock(&hpctrl->lock);

np = NULL;
sbi_list_for_each_entry(n, &hpctrl.free_space_list, head) {
sbi_list_for_each_entry(n, &hpctrl->free_space_list, head) {
if (size <= n->size) {
np = n;
break;
}
}
if (np) {
if ((size < np->size) &&
!sbi_list_empty(&hpctrl.free_node_list)) {
n = sbi_list_first_entry(&hpctrl.free_node_list,
!sbi_list_empty(&hpctrl->free_node_list)) {
n = sbi_list_first_entry(&hpctrl->free_node_list,
struct heap_node, head);
sbi_list_del(&n->head);
n->addr = np->addr + np->size - size;
n->size = size;
np->size -= size;
sbi_list_add_tail(&n->head, &hpctrl.used_space_list);
sbi_list_add_tail(&n->head, &hpctrl->used_space_list);
ret = (void *)n->addr;
} else if (size == np->size) {
sbi_list_del(&np->head);
sbi_list_add_tail(&np->head, &hpctrl.used_space_list);
sbi_list_add_tail(&np->head, &hpctrl->used_space_list);
ret = (void *)np->addr;
}
}

spin_unlock(&hpctrl.lock);
spin_unlock(&hpctrl->lock);

return ret;
}

void *sbi_zalloc(size_t size)
void *sbi_malloc_from(struct heap_control *hpctrl, size_t size)
{
return alloc_with_align(hpctrl, HEAP_ALLOC_ALIGN, size);
}

void *sbi_malloc(size_t size)
{
return sbi_malloc_from(&global_hpctrl, size);
}

void *sbi_memalign_from(struct heap_control *hpctrl, size_t alignment,
size_t size)
{
if(alignment < HEAP_ALLOC_ALIGN) {
alignment = HEAP_ALLOC_ALIGN;
}

// Make sure alignment is power of two
if((alignment & (alignment - 1)) != 0) {
return NULL;
}

// Make sure size is multiple of alignment
if(size % alignment != 0) {
return NULL;
}

return alloc_with_align(hpctrl, alignment, size);
}

void *sbi_memalign(size_t alignment, size_t size)
{
void *ret = sbi_malloc(size);
return sbi_memalign_from(&global_hpctrl, alignment, size);
}

void *sbi_zalloc_from(struct heap_control *hpctrl, size_t size)
{
void *ret = sbi_malloc_from(hpctrl, size);

if (ret)
sbi_memset(ret, 0, size);
return ret;
}

void sbi_free(void *ptr)
void *sbi_zalloc(size_t size)
{
return sbi_malloc_from(&global_hpctrl, size);
}

void sbi_free_from(struct heap_control *hpctrl, void *ptr)
{
struct heap_node *n, *np;

if (!ptr)
return;

spin_lock(&hpctrl.lock);
spin_lock(&hpctrl->lock);

np = NULL;
sbi_list_for_each_entry(n, &hpctrl.used_space_list, head) {
sbi_list_for_each_entry(n, &hpctrl->used_space_list, head) {
if ((n->addr <= (unsigned long)ptr) &&
((unsigned long)ptr < (n->addr + n->size))) {
np = n;
break;
}
}
if (!np) {
spin_unlock(&hpctrl.lock);
spin_unlock(&hpctrl->lock);
return;
}

sbi_list_del(&np->head);

sbi_list_for_each_entry(n, &hpctrl.free_space_list, head) {
sbi_list_for_each_entry(n, &hpctrl->free_space_list, head) {
if ((np->addr + np->size) == n->addr) {
n->addr = np->addr;
n->size += np->size;
sbi_list_add_tail(&np->head, &hpctrl.free_node_list);
sbi_list_add_tail(&np->head, &hpctrl->free_node_list);
np = NULL;
break;
} else if (np->addr == (n->addr + n->size)) {
n->size += np->size;
sbi_list_add_tail(&np->head, &hpctrl.free_node_list);
sbi_list_add_tail(&np->head, &hpctrl->free_node_list);
np = NULL;
break;
} else if ((n->addr + n->size) < np->addr) {
Expand All @@ -132,73 +173,107 @@ void sbi_free(void *ptr)
}
}
if (np)
sbi_list_add_tail(&np->head, &hpctrl.free_space_list);
sbi_list_add_tail(&np->head, &hpctrl->free_space_list);

spin_unlock(&hpctrl.lock);
spin_unlock(&hpctrl->lock);
}

unsigned long sbi_heap_free_space(void)
void sbi_free(void *ptr)
{
return sbi_free_from(&global_hpctrl, ptr);
}

unsigned long sbi_heap_free_space_from(struct heap_control *hpctrl)
{
struct heap_node *n;
unsigned long ret = 0;

spin_lock(&hpctrl.lock);
sbi_list_for_each_entry(n, &hpctrl.free_space_list, head)
spin_lock(&hpctrl->lock);
sbi_list_for_each_entry(n, &hpctrl->free_space_list, head)
ret += n->size;
spin_unlock(&hpctrl.lock);
spin_unlock(&hpctrl->lock);

return ret;
}

unsigned long sbi_heap_free_space(void)
{
return sbi_heap_free_space_from(&global_hpctrl);
}

unsigned long sbi_heap_used_space_from(struct heap_control *hpctrl)
{
return hpctrl->size - hpctrl->hksize - sbi_heap_free_space();
}

unsigned long sbi_heap_used_space(void)
{
return hpctrl.size - hpctrl.hksize - sbi_heap_free_space();
return sbi_heap_free_space_from(&global_hpctrl);
}

unsigned long sbi_heap_reserved_space_from(struct heap_control *hpctrl)
{
return hpctrl->hksize;
}

unsigned long sbi_heap_reserved_space(void)
{
return hpctrl.hksize;
return sbi_heap_free_space_from(&global_hpctrl);
}

int sbi_heap_init(struct sbi_scratch *scratch)
int sbi_heap_init_new(struct heap_control *hpctrl, unsigned long base,
unsigned long size)
{
unsigned long i;
struct heap_node *n;

/* Sanity checks on heap offset and size */
if (!scratch->fw_heap_size ||
(scratch->fw_heap_size & (HEAP_BASE_ALIGN - 1)) ||
(scratch->fw_heap_offset < scratch->fw_rw_offset) ||
(scratch->fw_size < (scratch->fw_heap_offset + scratch->fw_heap_size)) ||
(scratch->fw_heap_offset & (HEAP_BASE_ALIGN - 1)))
return SBI_EINVAL;

/* Initialize heap control */
SPIN_LOCK_INIT(hpctrl.lock);
hpctrl.base = scratch->fw_start + scratch->fw_heap_offset;
hpctrl.size = scratch->fw_heap_size;
hpctrl.hkbase = hpctrl.base;
hpctrl.hksize = hpctrl.size / HEAP_HOUSEKEEPING_FACTOR;
hpctrl.hksize &= ~((unsigned long)HEAP_BASE_ALIGN - 1);
SBI_INIT_LIST_HEAD(&hpctrl.free_node_list);
SBI_INIT_LIST_HEAD(&hpctrl.free_space_list);
SBI_INIT_LIST_HEAD(&hpctrl.used_space_list);
SPIN_LOCK_INIT(hpctrl->lock);
hpctrl->base = base;
hpctrl->size = size;
hpctrl->hkbase = hpctrl->base;
hpctrl->hksize = hpctrl->size / HEAP_HOUSEKEEPING_FACTOR;
hpctrl->hksize &= ~((unsigned long)HEAP_BASE_ALIGN - 1);
SBI_INIT_LIST_HEAD(&hpctrl->free_node_list);
SBI_INIT_LIST_HEAD(&hpctrl->free_space_list);
SBI_INIT_LIST_HEAD(&hpctrl->used_space_list);

/* Prepare free node list */
for (i = 0; i < (hpctrl.hksize / sizeof(*n)); i++) {
n = (struct heap_node *)(hpctrl.hkbase + (sizeof(*n) * i));
for (i = 0; i < (hpctrl->hksize / sizeof(*n)); i++) {
n = (struct heap_node *)(hpctrl->hkbase + (sizeof(*n) * i));
SBI_INIT_LIST_HEAD(&n->head);
n->addr = n->size = 0;
sbi_list_add_tail(&n->head, &hpctrl.free_node_list);
sbi_list_add_tail(&n->head, &hpctrl->free_node_list);
}

/* Prepare free space list */
n = sbi_list_first_entry(&hpctrl.free_node_list,
n = sbi_list_first_entry(&hpctrl->free_node_list,
struct heap_node, head);
sbi_list_del(&n->head);
n->addr = hpctrl.hkbase + hpctrl.hksize;
n->size = hpctrl.size - hpctrl.hksize;
sbi_list_add_tail(&n->head, &hpctrl.free_space_list);
n->addr = hpctrl->hkbase + hpctrl->hksize;
n->size = hpctrl->size - hpctrl->hksize;
sbi_list_add_tail(&n->head, &hpctrl->free_space_list);

return 0;
}

int sbi_heap_init(struct sbi_scratch *scratch)
{
/* Sanity checks on heap offset and size */
if (!scratch->fw_heap_size ||
(scratch->fw_heap_size & (HEAP_BASE_ALIGN - 1)) ||
(scratch->fw_heap_offset < scratch->fw_rw_offset) ||
(scratch->fw_size < (scratch->fw_heap_offset + scratch->fw_heap_size)) ||
(scratch->fw_heap_offset & (HEAP_BASE_ALIGN - 1)))
return SBI_EINVAL;

return sbi_heap_init_from(&global_hpctrl,
scratch->fw_start + scratch->fw_heap_offset,
scratch->fw_heap_size);
}

int sbi_heap_alloc_new(struct heap_control **hpctrl)
{
*hpctrl = sbi_calloc(1, sizeof(struct heap_control));
return 0;
}