X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=qemu%2Froms%2Fopenhackware%2Fsrc%2Flibc%2Fsrc%2Fmalloc.c;fp=qemu%2Froms%2Fopenhackware%2Fsrc%2Flibc%2Fsrc%2Fmalloc.c;h=14bd8e557a3b4cb77b2bd10463217a89889df910;hb=e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb;hp=0000000000000000000000000000000000000000;hpb=9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00;p=kvmfornfv.git diff --git a/qemu/roms/openhackware/src/libc/src/malloc.c b/qemu/roms/openhackware/src/libc/src/malloc.c new file mode 100644 index 000000000..14bd8e557 --- /dev/null +++ b/qemu/roms/openhackware/src/libc/src/malloc.c @@ -0,0 +1,730 @@ +/* + * + * + * Open Hack'Ware BIOS: memory management + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* functions prototypes are here */ +/* NULL is declared here */ +#include +/* memcpy is defined here */ +#include +/* set_errno is defined here */ +#include + +//#define DEBUG_MEMOPS +#if defined (DEBUG_MEMOPS) +#define MEMOPS_PRINTF(fmt, args...) do { dprintf(fmt , ##args); } while (0) +#else +#define MEMOPS_PRINTF(fmt, args...) do { } while (0) +#endif + +#define unused __attribute__ (( unused )) + +/* XXX: TODO: put this elsewhere */ +void *page_get (int nb_pages); +void page_put (void *addr, int nb_pages); + +/* XXX: TOTO: put this elsewhere */ +#if defined (__i386__) +#define NATURAL_ALIGN_BITS 2 +#define PAGE_BITS 12 +#define __32_BITS 1 +#elif defined (__powerpc__) || defined (_ARCH_PPC) +#define NATURAL_ALIGN_BITS 2 +#define PAGE_BITS 12 +#define __32_BITS 1 +#elif defined (__x86_64__) +#define NATURAL_ALIGN_BITS 3 +#define PAGE_BITS 12 +#else +#error "Unsupported architecture" +#endif +#define PAGE_SIZE (1 << PAGE_BITS) +#define PAGE(addr) ((void *)((unsigned long)(addr) & ~(PAGE_SIZE - 1))) + + +#define MIN_ELEM_BITS (NATURAL_ALIGN_BITS + 1) +#define MIN_ELEM_SIZE (1 << (MIN_ELEM_BITS)) +#define MAX_ELEM_BITS (PAGE_BITS) +#define MAX_ELEM_SIZE (1 << (MAX_ELEM_BITS - 1)) +#define POOL_MAX ((MAX_ELEM_BITS) - (MIN_ELEM_BITS)) +#define CACHE_MAX 16 + +typedef struct free_slot_t free_slot_t; +struct free_slot_t { + struct free_slot_t *next; +}; + +typedef struct page_descr_t page_descr_t; +struct page_descr_t { + struct page_descr_t *next; + struct page_descr_t *prev; + void *addr; + unsigned long nb; +}; + +/* + * This points to the first descriptor page where stand: + * - 1 descriptor for this page. + * - 1 decriptor pages list head. + * - POOL_MAX pool descriptors list heads. + * - CACHE_MAX page cache entries list heads. + */ +static void *malloc_base; + +/* Shared functions */ +static inline page_descr_t *main_descr_get (void) +{ + return (page_descr_t *)malloc_base + 1; +} + +static inline page_descr_t *pool_head_get (int pool_idx) +{ + return main_descr_get() + 1 + pool_idx; +} + +static inline page_descr_t *cache_head_get (int cache_idx) +{ + return pool_head_get(POOL_MAX) + 1 + cache_idx; +} + +static void free_slots_init (void *page, int incr, int size) +{ + free_slot_t *slot, *next; + + for (slot = page; + slot < (free_slot_t *)((char *)page + size); slot = next) { + next = (void *)((char *)slot + incr); + slot->next = next; + } + slot = (void *)((char *)slot - incr); + slot->next = NULL; +} + +static page_descr_t *descr_find_free (page_descr_t *head_descr, + page_descr_t *skip_descr) +{ + page_descr_t *cur_descr, *best; + unsigned long max_used; + + /* Try to always return the page with the less free slots to reduce + * memory fragmentation. + */ + max_used = 0; + best = NULL; + for (cur_descr = head_descr->next; + cur_descr != head_descr; cur_descr = cur_descr->next) { + if (cur_descr != skip_descr && cur_descr->addr != NULL && + cur_descr->nb >= max_used) { + max_used = cur_descr->nb; + best = cur_descr; + } + } + + return best; +} + +/* Page descriptors management */ +static void page_descr_free (page_descr_t *head_descr) +{ + head_descr->next->prev = head_descr->prev; + head_descr->prev->next = head_descr->next; + page_put(head_descr, 1); +} + +static page_descr_t *page_descr_get (void) +{ + page_descr_t *main_descr, *head_descr, *page_descr; + free_slot_t *first_free; + + main_descr = main_descr_get(); + head_descr = main_descr->addr; + first_free = head_descr->addr; + if (first_free == NULL) { + /* Find a page with free descriptors */ + head_descr = descr_find_free(main_descr, NULL); + if (head_descr != NULL) { + /* Get the first free slot */ + first_free = head_descr->addr; + } else { + /* Allocate a new page */ + head_descr = page_get(1); + if (head_descr == NULL) { + MEMOPS_PRINTF("%s: cannot get new head descriptor\n", + __func__); + return NULL; + } + /* Initialise free slots */ + free_slots_init(head_descr, sizeof(page_descr_t), PAGE_SIZE); + /* Initialise page head descriptor */ + head_descr->addr = head_descr + 1; + head_descr->nb = 0; + head_descr->next = main_descr; + head_descr->prev = main_descr->prev; + /* Update main descriptor */ + main_descr->prev->next = head_descr; + main_descr->prev = head_descr; + main_descr->nb++; + first_free = head_descr->addr; + } + main_descr->addr = head_descr; + } + head_descr->addr = first_free->next; + if (head_descr->nb == 0) + main_descr->nb--; + head_descr->nb++; + page_descr = (page_descr_t *)first_free; + page_descr->prev = NULL; + page_descr->next = NULL; + page_descr->addr = NULL; + page_descr->nb = 0; + + return page_descr; +} + +static void page_descr_put (page_descr_t *page_descr) +{ + page_descr_t *main_descr, *head_descr, *next_descr, *free_descr; + free_slot_t *first_free, *next_free; + + head_descr = PAGE(page_descr); + /* Mark this descriptor as free */ + next_free = head_descr->addr; + first_free = (free_slot_t *)page_descr; + first_free->next = next_free; + /* Update page descriptor */ + head_descr->addr = first_free; + head_descr->nb--; + main_descr = main_descr_get(); + if (head_descr->nb == 0) { + /* Try to free this page */ + if (main_descr->addr == head_descr || + main_descr->addr == NULL || + main_descr->nb > 0) + free_descr = descr_find_free(main_descr, head_descr); + else + free_descr = main_descr->addr; + if (free_descr != NULL) { + /* Update main descriptor */ + page_descr_free(head_descr); + main_descr->addr = free_descr; + } else { + main_descr->addr = head_descr; + main_descr->nb++; + } + } else if (next_free == NULL) { + free_descr = head_descr; + for (head_descr = main_descr->next; + main_descr->nb > 0 && head_descr != main_descr; + head_descr = next_descr) { + next_descr = head_descr->next; + if (head_descr->nb == 0) { + if (main_descr->addr == head_descr) + main_descr->addr = NULL; + page_descr_free(head_descr); + main_descr->nb--; + } + } + if (main_descr->addr == NULL) + main_descr->addr = free_descr; + } +} + +/* Page cache management */ +static inline unsigned long cache_idx (void *addr) +{ + return ((unsigned long)addr >> PAGE_BITS) & (CACHE_MAX - 1); +} + +static inline unsigned long page_cache_pool_idx (page_descr_t *cache_descr) +{ + return (cache_descr->nb & 0xF); +} + +static inline page_descr_t *page_cache_page_descr (page_descr_t *cache_descr) +{ + return (page_descr_t *)(cache_descr->nb & ~0xF); +} + +static int page_cache_add_page (page_descr_t *page_descr, int pool_idx) +{ + page_descr_t *main_descr, *cache_head, *cache_descr; + + main_descr = main_descr_get(); + cache_head = cache_head_get(cache_idx(page_descr->addr)); + cache_descr = page_descr_get(); + if (cache_descr == NULL) { + MEMOPS_PRINTF("%s: cannot get cache page\n", __func__); + return -1; + } + cache_descr->nb = pool_idx | (unsigned long)page_descr; + cache_descr->prev = cache_head; + cache_descr->next = cache_head->next; + cache_descr->addr = page_descr->addr; + cache_head->next->prev = cache_descr; + cache_head->next = cache_descr; + + return 0; +} + +static page_descr_t *page_cache_get_descr (void *addr) +{ + page_descr_t *main_descr, *cache_head, *cache_descr; + + main_descr = main_descr_get(); + cache_head = cache_head_get(cache_idx(addr)); + for (cache_descr = cache_head->next; + cache_descr != cache_head; cache_descr = cache_descr->next) { + if (cache_descr->addr == addr) { + return cache_descr; + } + } + MEMOPS_PRINTF("%s: cannot get cache page descr\n", __func__); + + return NULL; +} + +static void page_cache_remove_descr (page_descr_t *cache_descr) +{ + cache_descr->next->prev = cache_descr->prev; + cache_descr->prev->next = cache_descr->next; + page_descr_put(cache_descr); +} + +/* Allocation by pool (size <= PAGE_SIZE / 2) */ +static void pool_descr_free (page_descr_t *cache_descr, + page_descr_t *pool_descr) +{ + page_put(PAGE(pool_descr->addr), 1); + page_cache_remove_descr(cache_descr); + pool_descr->next->prev = pool_descr->prev; + pool_descr->prev->next = pool_descr->next; + page_descr_put(pool_descr); +} + +static void *pool_malloc (int pool_idx) +{ + page_descr_t *main_descr, *pool_head, *pool_descr; + free_slot_t *first_free, *next_free; + + main_descr = main_descr_get(); + pool_head = pool_head_get(pool_idx); + pool_descr = pool_head->addr; + if (pool_descr == NULL || pool_descr->addr == NULL) { + pool_descr = descr_find_free(pool_head, NULL); + if (pool_descr == NULL) { + pool_descr = page_descr_get(); + if (pool_descr == NULL) { + MEMOPS_PRINTF("%s: cannot get pool descr\n", __func__); + return NULL; + } + pool_descr->addr = page_get(1); + if (pool_descr->addr == NULL) { + MEMOPS_PRINTF("%s: cannot allocate new page\n", __func__); + page_descr_put(pool_descr); + return NULL; + } + if (page_cache_add_page(pool_descr, pool_idx) < 0) { + MEMOPS_PRINTF("%s: cannot add new page to cache\n", __func__); + page_put(pool_descr->addr, 1); + page_descr_put(pool_descr); + return NULL; + } + free_slots_init(pool_descr->addr, + 1 << (MIN_ELEM_BITS + pool_idx), PAGE_SIZE); + pool_descr->nb = 0; + pool_descr->prev = pool_head->prev; + pool_descr->next = pool_head; + pool_head->prev->next = pool_descr; + pool_head->prev = pool_descr; + pool_head->nb++; + } + pool_head->addr = pool_descr; + } + first_free = pool_descr->addr; + next_free = first_free->next; + // memset(first_free, 0, 1 << (MIN_ELEM_BITS + pool_idx)); + pool_descr->addr = next_free; + if (pool_descr->nb == 0) + pool_head->nb--; + pool_descr->nb++; + + return first_free; +} + +unused static void pool_free (page_descr_t *cache_descr, void *area) +{ + page_descr_t *pool_head, *pool_descr, *pool_next, *free_pool; + free_slot_t *first_free, *next_free; + unsigned long size, pool_idx; + + pool_descr = page_cache_page_descr(cache_descr); + first_free = area; + next_free = pool_descr->addr; + pool_idx = page_cache_pool_idx(cache_descr); + size = 1 << (MIN_ELEM_BITS + pool_idx); + first_free->next = next_free; + pool_descr->addr = first_free; + pool_descr->nb--; + pool_head = pool_head_get(pool_idx); + if (pool_descr->nb == 0) { + if (pool_head->addr == pool_descr || + pool_head->addr == NULL || + pool_head->nb > 0) + free_pool = descr_find_free(pool_head, pool_descr); + else + free_pool = pool_head->addr; + if (free_pool != NULL) { + /* Free page & descriptor */ + pool_descr_free(cache_descr, pool_descr); + pool_head->addr = free_pool; + } else { + pool_head->addr = pool_descr; + pool_head->nb++; + } + } else if (next_free == NULL) { + free_pool = pool_descr; + for (pool_descr = pool_head->next; + pool_head->nb > 0 && pool_descr != pool_head; + pool_descr = pool_next) { + pool_next = pool_descr->next; + if (pool_descr->nb == 0) { + if (pool_head->addr == pool_descr) + pool_head->addr = NULL; + cache_descr = page_cache_get_descr(PAGE(pool_descr->addr)); + if (cache_descr != NULL) { + pool_descr_free(cache_descr, pool_descr); + pool_head->nb--; + } else { + /* Incoherency: what to do ? */ + } + } + } + if (pool_head->addr == NULL) + pool_head->addr = free_pool; + } +} + +/* Big area management (size > PAGE_SIZE / 2) */ +static void *big_malloc (int nb_pages) +{ + page_descr_t *main_descr, *pool_head, *pool_descr; + + main_descr = main_descr_get(); + pool_head = pool_head_get(POOL_MAX); + pool_descr = page_descr_get(); + if (pool_descr == NULL) { + MEMOPS_PRINTF("%s: cannot get pool descr\n", __func__); + return NULL; + } + pool_descr->addr = page_get(nb_pages); + if (pool_descr->addr == NULL) { + page_descr_put(pool_descr); + MEMOPS_PRINTF("%s: cannot get page\n", __func__); + return NULL; + } + if (page_cache_add_page(pool_descr, POOL_MAX) < 0) { + page_put(pool_descr->addr, nb_pages); + page_descr_put(pool_descr); + MEMOPS_PRINTF("%s: cannot get add page to cache\n", __func__); + return NULL; + } + pool_descr->prev = pool_head->prev; + pool_descr->next = pool_head; + pool_descr->nb = nb_pages; + pool_head->prev->next = pool_descr; + pool_head->prev = pool_descr; + + return pool_descr->addr; +} + +static void big_free (page_descr_t *cache_descr) +{ + page_descr_t *pool_descr; + + pool_descr = page_cache_page_descr(cache_descr); + if (pool_descr->addr != NULL && pool_descr->nb != 0) { + page_put(pool_descr->addr, pool_descr->nb); + pool_descr->next->prev = pool_descr->prev; + pool_descr->prev->next = pool_descr->next; + page_descr_put(pool_descr); + page_cache_remove_descr(cache_descr); + } else { + MEMOPS_PRINTF("%s: ERROR %p %d\n", + __func__, pool_descr->addr, (int)pool_descr->nb); + } +} + +unused static void *big_realloc (page_descr_t *cache_descr, int new_size) +{ + void *new_area; + page_descr_t *pool_descr; + unsigned long new_nb; + + pool_descr = page_cache_page_descr(cache_descr); + new_nb = (new_size + PAGE_SIZE - 1) / PAGE_SIZE; + if (new_nb == pool_descr->nb) { + new_area = cache_descr->addr; + } else { + new_area = big_malloc(new_size); + memcpy(new_area, cache_descr->addr, pool_descr->nb * PAGE_SIZE); + big_free(cache_descr); + } + + return new_area; +} + +/* Global entry points */ +int page_descrs_init (void) +{ + page_descr_t *main_descr, *page_descr, *pool_head, *cache_head; + int i; + + /* Allocate first descriptor page */ + malloc_base = page_get(1); + if (malloc_base == NULL) { + set_errno(ENOMEM); + MEMOPS_PRINTF("%s: cannot get main descriptor\n", __func__); + return -1; + } + /* Init free slots in this page */ + free_slots_init(malloc_base, sizeof(page_descr_t), PAGE_SIZE); + /* Init main descriptor */ + page_descr = malloc_base; + main_descr = main_descr_get(); + main_descr->addr = page_descr; + main_descr->nb = 0; + main_descr->next = page_descr; + main_descr->prev = page_descr; + page_descr->nb = 1; + page_descr->addr = page_descr + 2; + page_descr->next = main_descr; + page_descr->prev = main_descr; + /* Init pool lists heads */ + for (i = 0; i <= POOL_MAX; i++) { + pool_head = page_descr_get(); + if (pool_head == NULL) { + page_put(malloc_base, 1); + malloc_base = NULL; + MEMOPS_PRINTF("%s: cannot get pool descriptor %d\n", __func__, i); + return -1; + } + pool_head->prev = pool_head; + pool_head->next = pool_head; + pool_head->addr = NULL; + } + /* Init page caches lists heads */ + for (i = 0; i < CACHE_MAX; i++) { + cache_head = page_descr_get(); + if (cache_head == NULL) { + page_put(malloc_base, 1); + malloc_base = NULL; + MEMOPS_PRINTF("%s: cannot get page cache descriptor %d\n", + __func__, i); + return -1; + } + cache_head->prev = cache_head; + cache_head->next = cache_head; + } + + return 0; +} + +static inline int get_pool_idx (size_t size) +{ + int pool_idx; + + pool_idx = 0; + for (size /= MIN_ELEM_SIZE; size != 0; size = size / 2) + pool_idx++; + + return pool_idx; +} + +#if 1 +void *malloc (size_t size) +{ + void *ret; + int pool_idx; + + if (malloc_base == NULL || size == 0) { + ret = NULL; + } else if (size >= MAX_ELEM_SIZE) { + ret = big_malloc((size + PAGE_SIZE - 1) / PAGE_SIZE); + } else { + if (size <= MIN_ELEM_SIZE) + pool_idx = 0; + else { + pool_idx = get_pool_idx(size); + } + ret = pool_malloc(pool_idx); + } + if (ret != NULL) + memset(ret, 0, size); +#if 0 + memory_dump(); + printf("%s(%d) => %p\n", __func__, size, ret); +#endif + + return ret; +} +#endif + +#if 0 +void free (void *area) +{ + page_descr_t *cache_descr; + int pool_idx; + + if (malloc_base == NULL || area == NULL) + return; + cache_descr = page_cache_get_descr(PAGE(area)); + if (cache_descr != NULL) { + pool_idx = page_cache_pool_idx(cache_descr); + if (pool_idx == POOL_MAX) { + big_free(cache_descr); + } else { + pool_free(cache_descr, area); + } + } else { + /* Given area is not valid */ + MEMOPS_PRINTF("ERROR: area to free not found: %p\n", area); + } +} +#endif + +#if 0 +void *realloc (void *area, size_t new_size) +{ + void *new_area; + page_descr_t *pool_descr, *cache_descr; + size_t size; + int pool_idx, new_pool_idx; + + if (malloc_base == NULL || new_size == 0) { + free(area); + return NULL; + } + if (area == NULL) + return malloc(new_size); + cache_descr = page_cache_get_descr(PAGE(area)); + if (cache_descr == NULL) { + /* Given area is not valid */ + return NULL; + } + pool_idx = page_cache_pool_idx(cache_descr); + if (new_size >= MAX_ELEM_SIZE) { + new_pool_idx = POOL_MAX; + if (pool_idx == POOL_MAX) + return big_realloc(cache_descr, new_size); + } else { + if (new_size <= MIN_ELEM_SIZE) + new_pool_idx = 0; + else + new_pool_idx = get_pool_idx(size); + if (pool_idx == new_pool_idx) + return area; + } + /* Common case: alloc, copy & free */ + if (new_pool_idx == POOL_MAX) + new_area = big_malloc((new_size + PAGE_SIZE - 1) / PAGE_SIZE); + else + new_area = pool_malloc(new_pool_idx); + if (new_area == NULL) + return NULL; + if (pool_idx == POOL_MAX) { + pool_descr = page_cache_page_descr(cache_descr); + size = pool_descr->nb * PAGE_SIZE; + } else { + size = MIN_ELEM_SIZE << pool_idx; + } + memcpy(new_area, area, size); + if (pool_idx == POOL_MAX) + big_free(cache_descr); + else + pool_free(cache_descr, area); + + return new_area; +} +#endif + +void memory_dump (void) +{ +#if defined (DEBUG_MEMOPS) + page_descr_t *main_descr, *page_descr; + page_descr_t *pool_head, *pool_descr, *cache_head, *cache_descr; + int i, n; + + main_descr = main_descr_get(); + /* Dump descriptor pages */ + printf("Descriptor pages dump: %p max=%d %d pages with no alloc descrs\n", + main_descr, (int)(PAGE_SIZE / sizeof(page_descr_t)), (int)main_descr->nb); + n = 0; + for (page_descr = main_descr->next; + page_descr != main_descr; page_descr = page_descr->next) { + printf("Descr %d : %p %p used: %d\n", + n, page_descr, page_descr->addr, (int)page_descr->nb); + n++; + } + /* Dump pool areas pages */ + for (i = 0; i < POOL_MAX; i++) { + n = 0; + pool_head = pool_head_get(i); + printf("Pool %d %p\n", i, pool_head); + for (pool_descr = pool_head->next; + pool_descr != pool_head; pool_descr = pool_descr->next) { + printf("Pool %d descr %d : %p %p used: %d size %d free: %p %p\n", + i, n, pool_descr, PAGE(pool_descr->addr), (int)pool_descr->nb, + 1 << (MIN_ELEM_BITS + i), pool_descr->addr, + ((free_slot_t *)pool_descr->addr)->next); + n++; + } + printf(" => %d pages allocated\n", n); + } +#if 0 + /* Dump big area pages */ + printf("Pool %d\n", POOL_MAX); + n = 0; + pool_head = pool_head_get(POOL_MAX); + for (pool_descr = pool_head->next; + pool_descr != pool_head; pool_descr = pool_descr->next) { + printf("Pool %d descr %d : %p nb pages: %d\n", + POOL_MAX, n, pool_descr->addr, (int)pool_descr->nb); + n++; + } + printf(" => %d pages allocated\n", n); + /* Dump page cache */ + for (i = 0; i < CACHE_MAX; i++) { + printf("Page cache 0x____%x___\n", i); + n = 0; + cache_head = cache_head_get(i); + for (cache_descr = cache_head->next; + cache_descr != cache_head; cache_descr = cache_descr->next) { + pool_descr = page_cache_page_descr(cache_descr); + printf("Cache %d descr %d : %p pool: %d descr: %p %p %d\n", + i, n, cache_descr->addr, + (int)page_cache_pool_idx(cache_descr), + pool_descr, pool_descr->addr, (int)pool_descr->nb); + n++; + } + printf(" => %d pages allocated\n", n); + } +#endif +#endif +}