Changes since April

Clean up of build directories.
Simplifications to capability model.
This commit is contained in:
Bahadir Balban
2010-06-01 15:08:13 +03:00
parent aef14b55ec
commit 6fa4884a5a
450 changed files with 10449 additions and 7383 deletions

View File

@@ -0,0 +1,26 @@
# -*- mode: python; coding: utf-8; -*-
# Codezero -- a microkernel for embedded systems.
#
# Copyright © 2009 B Labs Ltd
import os, sys
PROJRELROOT = '../..'
sys.path.append(PROJRELROOT)
from scripts.config.projpaths import *
from scripts.config.config_invoke import *
Import('env')
e = env.Clone()
e.Append(CPPPATH = ['include', '.', LIBL4_INCLUDE])
objmm = e.StaticObject(Glob('mm/*.c'))
objmc = e.StaticObject(Glob('memcache/*.[cS]'))
objmalloc = e.StaticObject(Glob('malloc/*.[cS]'))
libmem = e.StaticLibrary('mem', objmm + objmc + objmalloc)
#libmc = e.StaticLibrary('mc', objmc)
#libmalloc = e.StaticLibrary('malloc', objmalloc)
Return('libmem')

View File

@@ -0,0 +1,68 @@
#
# Copyright (C) 2007 Bahadir Balban
#
import os, glob, sys
from os.path import join
from string import split
PROJRELROOT = '../../..'
sys.path.append(PROJRELROOT)
from scripts.config.config_invoke import *
from scripts.config.projpaths import *
config = configuration_retrieve()
gcc_arch_flag = config.gcc_arch_flag
mm = "mm"
kmalloc = "kmalloc"
memcache = "memcache"
tests = "tests"
mm_dir = mm
kmalloc_dir = kmalloc
memcache_dir = memcache
tests_dir = tests
# This does not work, need to check
test_env = Environment(CC = config.toolchain_userspace + 'gcc',
CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall',
'-nostdinc', '-Werror', '-march=' + gcc_arch_flag],
ENV = {'PATH' : os.environ['PATH']},
LIBS = ['mm', 'km', 'mc'],
LIBPATH = ['#'],
CPPPATH = ['#include', KERNEL_HEADERS, "#", LIBL4_INCLUDE])
env = Environment(CC = config.toolchain_userspace + 'gcc',
CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99',
'-Wall', '-Werror', '-march=' + gcc_arch_flag],
LINKFLAGS = ['-nostdlib'],
ASFLAGS = ['-D__ASSEMBLY__'],
ENV = {'PATH' : os.environ['PATH']},
LIBS = 'gcc',
CPPPATH = ['.', KERNEL_HEADERS, LIBL4_INCLUDE])
if not os.path.exists(CONFIG_H):
print "\nThis build requires a valid kernel configuration header."
print "Please run `scons configure' in the kernel root directory."
print "Choose the `tests' target to build memory allocator tests,"
print "or any other target for real use.\n"
sys.exit()
mm_src = glob.glob("%s/*.c" % mm_dir)
kmalloc_src = glob.glob("%s/*.c" % kmalloc_dir)
memcache_src = glob.glob("%s/*.c" % memcache_dir)
tests_src = glob.glob ("%s/*.c" % tests_dir)
if "tests" in COMMAND_LINE_TARGETS:
print "WARNING!!! Did you configure the kernel with test target first???"
libmem = test_env.StaticLibrary(mm, mm_src + kmalloc_src + memcache_src)
#libkmalloc = test_env.StaticLibrary("km", kmalloc_src)
#libmemcache = test_env.StaticLibrary("mc", memcache_src)
test_prog = test_env.Program("test", tests_src)
env.Alias("tests", test_prog)
else:
libmem = env.StaticLibrary(mm, mm_src + kmalloc_src + memcache_src)
#libkmalloc = env.StaticLibrary("km", kmalloc_src)
#libmemcache = env.StaticLibrary("mc", memcache_src)

View File

@@ -0,0 +1,30 @@
#ifndef __ALLOC_PAGE_H__
#define __ALLOC_PAGE_H__
#include <mem/memcache.h>
/* List member to keep track of free and unused physical pages.
* Has PAGE_SIZE granularity */
struct page_area {
struct link list;
unsigned int used; /* Used or free */
unsigned int pfn; /* Base pfn */
unsigned int numpages; /* Number of pages this region covers */
struct mem_cache *cache;/* The cache used when freeing the page area for
* quickly finding where the area is stored. */
};
struct page_allocator {
struct link page_area_list;
struct link pga_cache_list;
int pga_free;
};
/* Initialises the page allocator */
void init_page_allocator(unsigned long start, unsigned long end);
/* Page allocation functions */
void *alloc_page(int quantity);
int free_page(void *paddr);
#endif /* __ALLOC_PAGE_H__ */

View File

@@ -0,0 +1,7 @@
#ifndef __CLZ_H__
#define __CLZ_H__
unsigned int __clz(unsigned int bitvector);
#endif /* __CLZ_H__ */

View File

@@ -0,0 +1,17 @@
#ifndef __DEBUG_H__
#define __DEBUG_H__
//#include <kmem/kmalloc.h>
#include <mem/alloc_page.h>
#include <l4/lib/list.h>
#if defined(DEBUG)
#define dprintf printf
#else
#define dprintf(...)
#endif
void print_page_area_list(struct page_allocator *p);
void print_km_area_list(struct link *s);
void print_km_area(struct km_area *s);
#endif /* DEBUG_H */

View File

@@ -0,0 +1,17 @@
/*
* Mock-up l4 library definitions for host testing.
*
*/
#ifndef __TESTS_LIBL4_H__
#define __TESTS_LIBL4_H__
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
u32 l4_map(unsigned long phys, unsigned long virt, u32 size, u32 flags, u32 tid);
u32 l4_unmap(unsigned long a, unsigned long b, u32 npages);
u32 l4_getpid(unsigned int *a, unsigned int *b, unsigned int *c);
#endif

View File

@@ -0,0 +1,19 @@
#ifndef __PRIVATE_MALLOC_H__
#define __PRIVATE_MALLOC_H__
#include <stddef.h>
#include <string.h>
void *kmalloc(size_t size);
void kfree(void *blk);
static inline void *kzalloc(size_t size)
{
void *buf = kmalloc(size);
memset(buf, 0, size);
return buf;
}
#endif /*__PRIVATE_MALLOC_H__ */

View File

@@ -0,0 +1,50 @@
/*
* Bitmap-based link-listable fixed-size memory cache.
*
* Copyright (C) 2007 Bahadir Balban
*/
#ifndef __MEMCACHE_H__
#define __MEMCACHE_H__
#include <l4/config.h>
#include <l4/macros.h>
#include <l4/types.h>
#include <l4/lib/list.h>
/* Very basic cache structure. All it does is, keep an internal bitmap of
* items of struct_size. (Note bitmap is fairly efficient and simple for a
* fixed-size memory cache) Keeps track of free/occupied items within its
* start/end boundaries. Does not grow/shrink but you can link-list it. */
struct mem_cache {
struct link list;
int total;
int free;
unsigned int start;
unsigned int end;
unsigned int struct_size;
unsigned int *bitmap;
};
void *mem_cache_zalloc(struct mem_cache *cache);
void *mem_cache_alloc(struct mem_cache *cache);
int mem_cache_free(struct mem_cache *cache, void *addr);
struct mem_cache *mem_cache_init(void *start, int cache_size,
int struct_size, unsigned int alignment);
static inline int mem_cache_is_full(struct mem_cache *cache)
{
return cache->free == 0;
}
static inline int mem_cache_is_empty(struct mem_cache *cache)
{
return cache->free == cache->total;
}
static inline int mem_cache_is_last_free(struct mem_cache *cache)
{
return cache->free == 1;
}
static inline int mem_cache_total_empty(struct mem_cache *cache)
{
return cache->free;
}
#endif /* __MEMCACHE_H__ */

View File

@@ -0,0 +1,29 @@
#ifndef __TEST_ALLOC_GENERIC_H__
#define __TEST_ALLOC_GENERIC_H__
enum test_state_title {
TEST_STATE_BEGIN = 0,
TEST_STATE_MIDDLE,
TEST_STATE_END,
TEST_STATE_ERROR
};
typedef void (*print_alloc_state_t)(void);
typedef void *(*alloc_func_t)(int size);
typedef int (*free_func_t)(void *addr);
enum alloc_action {
FREE = 0,
ALLOCATE = 1,
};
void get_output_filepaths(FILE **out1, FILE **out2,
char *alloc_func_name);
int test_alloc_free_random_order(const int MAX_ALLOCATIONS,
const int ALLOC_SIZE_MAX,
alloc_func_t alloc, free_func_t free,
print_alloc_state_t print_allocator_state,
FILE *init_state, FILE *exit_state);
#endif /* __TEST_ALLOC_GENERIC_H__ */

View File

@@ -0,0 +1,13 @@
#ifndef __TEST_ALLOCPAGE_H__
#define __TEST_ALLOCPAGE_H__
#include <mem/alloc_page.h>
#include "tests.h"
void test_allocpage(int num_allocs, int alloc_max, FILE *init, FILE *exit);
void print_page_area(struct page_area *a, int no);
void print_caches(struct link *cache_head);
void print_cache(struct mem_cache *c, int cacheno);
void print_areas(struct link *area_head);
void print_page_area(struct page_area *ar, int areano);
#endif

View File

@@ -0,0 +1,8 @@
#ifndef __TEST_KMALLOC_H__
#define __TEST_KMALLOC_H__
#include <kmem/kmalloc.h>
void test_kmalloc(int num_allocs, int allocs_max, FILE *initstate, FILE *exitstate);
#endif

View File

@@ -0,0 +1,10 @@
#ifndef __TEST_MEMCACHE_H__
#define __TEST_MEMCACHE_H__
#include <mem/memcache.h>
int test_memcache(int num_alloc, int alloc_size_max, FILE *initstate, FILE *exitstate, int aligned);
#endif /* __TEST_MEMCACHE_H__ */

View File

@@ -0,0 +1,21 @@
#ifndef __TESTS_H__
#define __TESTS_H__
/* Mock-up physical memory */
extern unsigned int TEST_PHYSMEM_TOTAL_PAGES;
extern unsigned int TEST_PHYSMEM_TOTAL_SIZE;
/* Allocator test */
extern unsigned int PAGE_ALLOCATIONS;
extern unsigned int PAGE_ALLOC_SIZE_MAX;
/* Memcache test */
extern unsigned int MEMCACHE_ALLOCS_MAX;
extern unsigned int TEST_CACHE_ITEM_SIZE;
/* Kmalloc */
extern unsigned int KMALLOC_ALLOCATIONS;
extern unsigned int KMALLOC_ALLOC_SIZE_MAX;
#endif /* __TESTS_H__ */

View File

@@ -0,0 +1,413 @@
/*****************************************************************************
Simple malloc
Chris Giese <geezer@execpc.com> http://www.execpc.com/~geezer
Release date: Oct 30, 2002
This code is public domain (no copyright).
You can do whatever you want with it.
Features:
- First-fit
- free() coalesces adjacent free blocks
- Uses variable-sized heap, enlarged with kbrk()/sbrk() function
- Does not use mmap()
- Can be easily modified to use fixed-size heap
- Works with 16- or 32-bit compilers
Build this program with either of the two main() functions, then run it.
Messages that indicate a software error will contain three asterisks (***).
*****************************************************************************/
#include <string.h> /* memcpy(), memset() */
#include <stdio.h> /* printf() */
#include <l4/macros.h>
#define _32BIT 1
/* use small (32K) heap for 16-bit compilers,
large (500K) heap for 32-bit compilers */
#if defined(_32BIT)
#define HEAP_SIZE 500000uL
#else
#define HEAP_SIZE 32768u
#endif
#define MALLOC_MAGIC 0x6D92 /* must be < 0x8000 */
typedef struct _malloc /* Turbo C DJGPP */
{
size_t size; /* 2 bytes 4 bytes */
struct _malloc *next; /* 2 bytes 4 bytes */
unsigned magic : 15; /* 2 bytes total 4 bytes total */
unsigned used : 1;
} malloc_t; /* total 6 bytes 12 bytes */
static char *g_heap_bot, *g_kbrk, *g_heap_top;
/*****************************************************************************
*****************************************************************************/
void dump_heap(void)
{
unsigned blks_used = 0, blks_free = 0;
size_t bytes_used = 0, bytes_free = 0;
malloc_t *m;
int total;
printf("===============================================\n");
for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next)
{
printf("blk %5p: %6u bytes %s\n", m,
m->size, m->used ? "used" : "free");
if(m->used)
{
blks_used++;
bytes_used += m->size;
}
else
{
blks_free++;
bytes_free += m->size;
}
}
printf("blks: %6u used, %6u free, %6u total\n", blks_used,
blks_free, blks_used + blks_free);
printf("bytes: %6u used, %6u free, %6u total\n", bytes_used,
bytes_free, bytes_used + bytes_free);
printf("g_heap_bot=0x%p, g_kbrk=0x%p, g_heap_top=0x%p\n",
g_heap_bot, g_kbrk, g_heap_top);
total = (bytes_used + bytes_free) +
(blks_used + blks_free) * sizeof(malloc_t);
if(total != g_kbrk - g_heap_bot)
printf("*** some heap memory is not accounted for\n");
printf("===============================================\n");
}
/*****************************************************************************
POSIX sbrk() looks like this
void *sbrk(int incr);
Mine is a bit different so I can signal the calling function
if more memory than desired was allocated (e.g. in a system with paging)
If your kbrk()/sbrk() always allocates the amount of memory you ask for,
this code can be easily changed.
int brk( void *sbrk( void *kbrk(
function void *adr); int delta); int *delta);
---------------------- ------------ ------------ -------------
POSIX? yes yes NO
return value if error -1 -1 NULL
get break value . sbrk(0) int x=0; kbrk(&x);
set break value to X brk(X) sbrk(X - sbrk(0)) int x=X, y=0; kbrk(&x) - kbrk(&y);
enlarge heap by N bytes . sbrk(+N) int x=N; kbrk(&x);
shrink heap by N bytes . sbrk(-N) int x=-N; kbrk(&x);
can you tell if you're
given more memory
than you wanted? no no yes
*****************************************************************************/
static void *kbrk(int *delta)
{
static char heap[HEAP_SIZE];
/**/
char *new_brk, *old_brk;
/* heap doesn't exist yet */
if(g_heap_bot == NULL)
{
g_heap_bot = g_kbrk = heap;
g_heap_top = g_heap_bot + HEAP_SIZE;
}
new_brk = g_kbrk + (*delta);
/* too low: return NULL */
if(new_brk < g_heap_bot)
return NULL;
/* too high: return NULL */
if(new_brk >= g_heap_top)
return NULL;
/* success: adjust brk value... */
old_brk = g_kbrk;
g_kbrk = new_brk;
/* ...return actual delta... (for this sbrk(), they are the same)
(*delta) = (*delta); */
/* ...return old brk value */
return old_brk;
}
/*****************************************************************************
kmalloc() and kfree() use g_heap_bot, but not g_kbrk nor g_heap_top
*****************************************************************************/
void *kmalloc(size_t size)
{
unsigned total_size;
malloc_t *m, *n;
int delta;
if(size == 0)
return NULL;
total_size = size + sizeof(malloc_t);
/* search heap for free block (FIRST FIT) */
m = (malloc_t *)g_heap_bot;
/* g_heap_bot == 0 == NULL if heap does not yet exist */
if(m != NULL)
{
if(m->magic != MALLOC_MAGIC)
// panic("kernel heap is corrupt in kmalloc()");
{
printf("*** kernel heap is corrupt in kmalloc()\n");
return NULL;
}
for(; m->next != NULL; m = m->next)
{
if(m->used)
continue;
/* size == m->size is a perfect fit */
if(size == m->size)
m->used = 1;
else
{
/* otherwise, we need an extra sizeof(malloc_t) bytes for the header
of a second, free block */
if(total_size > m->size)
continue;
/* create a new, smaller free block after this one */
n = (malloc_t *)((char *)m + total_size);
n->size = m->size - total_size;
n->next = m->next;
n->magic = MALLOC_MAGIC;
n->used = 0;
/* reduce the size of this block and mark it used */
m->size = size;
m->next = n;
m->used = 1;
}
return (char *)m + sizeof(malloc_t);
}
}
/* use kbrk() to enlarge (or create!) heap */
delta = total_size;
n = kbrk(&delta);
/* uh-oh */
if(n == NULL)
return NULL;
if(m != NULL)
m->next = n;
n->size = size;
n->magic = MALLOC_MAGIC;
n->used = 1;
/* did kbrk() return the exact amount of memory we wanted?
cast to make "gcc -Wall -W ..." shut the hell up */
if((int)total_size == delta)
n->next = NULL;
else
{
/* it returned more than we wanted (it will never return less):
create a new, free block */
m = (malloc_t *)((char *)n + total_size);
m->size = delta - total_size - sizeof(malloc_t);
m->next = NULL;
m->magic = MALLOC_MAGIC;
m->used = 0;
n->next = m;
}
return (char *)n + sizeof(malloc_t);
}
/*****************************************************************************
*****************************************************************************/
void kfree(void *blk)
{
malloc_t *m, *n;
/* get address of header */
m = (malloc_t *)((char *)blk - sizeof(malloc_t));
if(m->magic != MALLOC_MAGIC)
// panic("attempt to kfree() block at 0x%p "
// "with bad magic value", blk);
{
printf("*** attempt to kfree() block at 0x%p "
"with bad magic value\n", blk);
BUG();
return;
}
/* find this block in the heap */
n = (malloc_t *)g_heap_bot;
if(n->magic != MALLOC_MAGIC)
// panic("kernel heap is corrupt in kfree()");
{
printf("*** kernel heap is corrupt in kfree()\n");
return;
}
for(; n != NULL; n = n->next)
{
if(n == m)
break;
}
/* not found? bad pointer or no heap or something else? */
if(n == NULL)
// panic("attempt to kfree() block at 0x%p "
// "that is not in the heap", blk);
{
printf("*** attempt to kfree() block at 0x%p "
"that is not in the heap\n", blk);
return;
}
/* free the block */
m->used = 0;
/* BB: Addition: put 0xFF to block memory so we know if we use freed memory */
memset(blk, 0xFF, m->size);
/* coalesce adjacent free blocks
Hard to spell, hard to do */
for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next)
{
while(!m->used && m->next != NULL && !m->next->used)
{
/* resize this block */
m->size += sizeof(malloc_t) + m->next->size;
/* merge with next block */
m->next = m->next->next;
}
}
}
/*****************************************************************************
*****************************************************************************/
void *krealloc(void *blk, size_t size)
{
void *new_blk;
malloc_t *m;
/* size == 0: free block */
if(size == 0)
{
if(blk != NULL)
kfree(blk);
new_blk = NULL;
}
else
{
/* allocate new block */
new_blk = kmalloc(size);
/* if allocation OK, and if old block exists, copy old block to new */
if(new_blk != NULL && blk != NULL)
{
m = (malloc_t *)((char *)blk - sizeof(malloc_t));
if(m->magic != MALLOC_MAGIC)
// panic("attempt to krealloc() block at "
// "0x%p with bad magic value", blk);
{
printf("*** attempt to krealloc() block at "
"0x%p with bad magic value\n", blk);
return NULL;
}
/* copy minimum of old and new block sizes */
if(size > m->size)
size = m->size;
memcpy(new_blk, blk, size);
/* free the old block */
kfree(blk);
}
}
return new_blk;
}
/*****************************************************************************
*****************************************************************************/
#if 0
#include <stdlib.h> /* rand() */
#define SLOTS 17
int main(void)
{
unsigned lifetime[SLOTS];
void *blk[SLOTS];
int i, j, k;
dump_heap();
memset(lifetime, 0, sizeof(lifetime));
memset(blk, 0, sizeof(blk));
for(i = 0; i < 1000; i++)
{
printf("Pass %6u\n", i);
for(j = 0; j < SLOTS; j++)
{
/* age the block */
if(lifetime[j] != 0)
{
(lifetime[j])--;
continue;
}
/* too old; free it */
if(blk[j] != NULL)
{
kfree(blk[j]);
blk[j] = NULL;
}
/* alloc new block of random size
Note that size_t==unsigned, but kmalloc() uses integer math,
so block size must be positive integer */
#if defined(_32BIT)
k = rand() % 40960 + 1;
#else
k = rand() % 4096 + 1;
#endif
blk[j] = kmalloc(k);
if(blk[j] == NULL)
printf("failed to alloc %u bytes\n", k);
else
/* give it a random lifetime 0-20 */
lifetime[j] = rand() % 21;
}
}
/* let's see what we've wrought */
printf("\n\n");
dump_heap();
/* free everything */
for(j = 0; j < SLOTS; j++)
{
if(blk[j] != NULL)
{
kfree(blk[j]);
blk[j] = NULL;
}
(lifetime[j]) = 0;
}
/* after all that, we should have a single, unused block */
dump_heap();
return 0;
}
/*****************************************************************************
*****************************************************************************/
int main(void)
{
void *b1, *b2, *b3;
dump_heap();
b1 = kmalloc(42);
dump_heap();
b2 = kmalloc(23);
dump_heap();
b3 = kmalloc(7);
dump_heap();
b2 = krealloc(b2, 24);
dump_heap();
kfree(b1);
dump_heap();
b1 = kmalloc(5);
dump_heap();
kfree(b2);
dump_heap();
kfree(b3);
dump_heap();
kfree(b1);
dump_heap();
return 0;
}
#endif

View File

@@ -0,0 +1,206 @@
/*
* Bitmap-based linked-listable fixed-size memory cache.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <mem/memcache.h>
#include <string.h>
#include <stdio.h>
/* Some definitions from glue/memory.h */
#define align_up(addr, size) ((((unsigned long)addr) + (size - 1)) & (~(size - 1)))
#define SZ_WORD sizeof(unsigned long)
#define WORD_BITS 32
#define BITWISE_GETWORD(x) (x >> 5) /* Divide by 32 */
#define BITWISE_GETBIT(x) (1 << (x % WORD_BITS))
static int find_and_set_first_free_bit(u32 *word, unsigned int limit)
{
int success = 0;
int i;
for(i = 0; i < limit; i++) {
/* Find first unset bit */
if (!(word[BITWISE_GETWORD(i)] & BITWISE_GETBIT(i))) {
/* Set it */
word[BITWISE_GETWORD(i)] |= BITWISE_GETBIT(i);
success = 1;
break;
}
}
/* Return bit just set */
if (success)
return i;
else
return -1;
}
static int check_and_clear_bit(u32 *word, int bit)
{
/* Check that bit was set */
if (word[BITWISE_GETWORD(bit)] & BITWISE_GETBIT(bit)) {
word[BITWISE_GETWORD(bit)] &= ~BITWISE_GETBIT(bit);
return 0;
} else {
//printf("Trying to clear already clear bit\n");
return -1;
}
}
/* Allocate, clear and return element */
void *mem_cache_zalloc(struct mem_cache *cache)
{
void *elem = mem_cache_alloc(cache);
memset(elem, 0, cache->struct_size);
return elem;
}
/* Allocate another element from given @cache. Returns 0 when full. */
void *mem_cache_alloc(struct mem_cache *cache)
{
int bit;
if (cache->free > 0) {
/* NOTE: If needed, must lock here */
cache->free--;
if ((bit = find_and_set_first_free_bit(cache->bitmap,
cache->total)) < 0) {
printk("Error: Anomaly in cache occupied state.\n"
"Bitmap full although cache->free > 0\n");
BUG();
}
/* NOTE: If needed, must unlock here */
return (void *)(cache->start + (cache->struct_size * bit));
} else {
/* Cache full */
return 0;
}
}
/* Free element at @addr in @cache. Return negative on error. */
int mem_cache_free(struct mem_cache *cache, void *addr)
{
unsigned int struct_addr = (unsigned int)addr;
unsigned int bit;
int err = 0;
/* Check boundary */
if (struct_addr < cache->start || struct_addr > cache->end) {
printk("Error: This address doesn't belong to this cache.\n");
return -1;
}
bit = ((struct_addr - cache->start) / cache->struct_size);
/* Check alignment:
* Find out if there was a lost remainder in last division.
* There shouldn't have been, because addresses are allocated at
* struct_size offsets from cache->start. */
if (((bit * cache->struct_size) + cache->start) != struct_addr) {
printk("Error: This address is not aligned on a predefined "
"structure address in this cache.\n");
err = -1;
return err;
}
/* NOTE: If needed, must lock here */
/* Check free/occupied state */
if (check_and_clear_bit(cache->bitmap, bit) < 0) {
printk("Error: Anomaly in cache occupied state:\n"
"Trying to free already free structure.\n");
err = -1;
goto out;
}
cache->free++;
if (cache->free > cache->total) {
printk("Error: Anomaly in cache occupied state:\n"
"More free elements than total.\n");
err = -1;
goto out;
}
out:
/* NOTE: If locked, must unlock here */
return err;
}
struct mem_cache *mem_cache_init(void *start,
int cache_size,
int struct_size,
unsigned int aligned)
{
struct mem_cache *cache = start;
unsigned int area_start;
unsigned int *bitmap;
int bwords_in_structs;
int bwords;
int total;
int bsize;
if ((struct_size < 0) || (cache_size < 0) ||
((unsigned long)start == ~(0))) {
printk("Invalid parameters.\n");
return 0;
}
/* The cache definition itself is at the beginning.
* Skipping it to get to start of free memory. i.e. the cache. */
area_start = (unsigned long)start + sizeof(struct mem_cache);
cache_size -= sizeof(struct mem_cache);
if (cache_size < struct_size) {
printk("Cache too small for given struct_size\n");
return 0;
}
/* Get how much bitmap words occupy */
total = cache_size / struct_size;
bwords = total >> 5; /* Divide by 32 */
if (total & 0x1F) { /* Remainder? */
bwords++; /* Add one more word for remainder */
}
bsize = bwords * 4;
/* This many structures will be chucked from cache for bitmap space */
bwords_in_structs = ((bsize) / struct_size) + 1;
/* Total structs left after deducing bitmaps */
total = total - bwords_in_structs;
cache_size -= bsize;
/* This should always catch too small caches */
if (total <= 0) {
printk("Cache too small for given struct_size\n");
return 0;
}
if (cache_size <= 0) {
printk("Cache too small for given struct_size\n");
return 0;
}
bitmap = (unsigned int *)area_start;
area_start = (unsigned int)(bitmap + bwords);
if (aligned) {
unsigned int addr = area_start;
unsigned int addr_aligned = align_up(area_start, struct_size);
unsigned int diff = addr_aligned - addr;
BUG_ON(diff >= struct_size);
cache_size -= diff;
area_start = addr_aligned;
}
/* Now recalculate total over cache bytes left */
total = cache_size / struct_size;
link_init(&cache->list);
cache->start = area_start;
cache->end = area_start + cache_size;
cache->total = total;
cache->free = cache->total;
cache->struct_size = struct_size;
cache->bitmap = bitmap;
/* NOTE: If needed, must initialise lock here */
memset(cache->bitmap, 0, bwords*SZ_WORD);
return cache;
}

View File

@@ -0,0 +1,251 @@
/*
* A proof-of-concept linked-list based page allocator.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <stdio.h>
#include <string.h>
#include <l4/config.h>
#include <l4/macros.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include <mem/alloc_page.h>
#include INC_GLUE(memory.h)
#include INC_SUBARCH(mm.h)
#include INC_GLUE(memlayout.h)
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syscalls.h)
#include L4LIB_INC_ARCH(syslib.h)
struct page_allocator allocator;
/*
* Allocate a new page area from the page area cache
*/
static struct page_area *new_page_area(struct page_allocator *p)
{
struct mem_cache *cache;
struct page_area *new_area;
list_foreach_struct(cache, &p->pga_cache_list, list) {
if ((new_area = mem_cache_alloc(cache)) != 0) {
new_area->cache = cache;
p->pga_free--;
return new_area;
}
}
return 0;
}
/* Given the page @quantity, finds a free region, divides and returns new area. */
static struct page_area *
get_free_page_area(int quantity, struct page_allocator *p)
{
struct page_area *new, *area;
if (quantity <= 0)
return 0;
list_foreach_struct(area, &p->page_area_list, list) {
/* Check for exact size match */
if (area->numpages == quantity && !area->used) {
area->used = 1;
return area;
}
/* Divide a bigger area */
if (area->numpages > quantity && !area->used) {
new = new_page_area(p);
area->numpages -= quantity;
new->pfn = area->pfn + area->numpages;
new->numpages = quantity;
new->used = 1;
link_init(&new->list);
list_insert(&new->list, &area->list);
return new;
}
}
/* No more pages */
return 0;
}
/*
* All physical memory is tracked by a simple linked list implementation. A
* single list contains both used and free page_area descriptors. Each page_area
* describes a continuous region of physical pages, indicating its location by
* it's pfn.
*
* alloc_page() keeps track of all page-granuled memory, except the bits that
* were in use before the allocator initialised. This covers anything that is
* outside the @start @end range. This includes the page tables, first caches
* allocated by this function, compile-time allocated kernel data and text.
* Also other memory regions like IO are not tracked by alloc_page() but by
* other means.
*/
void init_page_allocator(unsigned long start, unsigned long end)
{
/* Initialise a page area cache in the first page */
struct page_area *freemem, *area;
struct mem_cache *cache;
link_init(&allocator.page_area_list);
link_init(&allocator.pga_cache_list);
/* Initialise the first page area cache */
cache = mem_cache_init(phys_to_virt((void *)start), PAGE_SIZE,
sizeof(struct page_area), 0);
list_insert(&cache->list, &allocator.pga_cache_list);
/* Initialise the first area that describes the page just allocated */
area = mem_cache_alloc(cache);
link_init(&area->list);
area->pfn = __pfn(start);
area->used = 1;
area->numpages = 1;
area->cache = cache;
list_insert(&area->list, &allocator.page_area_list);
/* Update freemem start address */
start += PAGE_SIZE;
/* Initialise first area that describes all of free physical memory */
freemem = mem_cache_alloc(cache);
link_init(&freemem->list);
freemem->pfn = __pfn(start);
freemem->numpages = __pfn(end) - freemem->pfn;
freemem->cache = cache;
freemem->used = 0;
/* Add it as the first unused page area */
list_insert(&freemem->list, &allocator.page_area_list);
/* Initialise free page area counter */
allocator.pga_free = mem_cache_total_empty(cache);
}
/*
* Check if we're about to run out of free page area structures.
* If so, allocate a new cache of page areas.
*/
int check_page_areas(struct page_allocator *p)
{
struct page_area *new;
struct mem_cache *newcache;
/* If only one free area left */
if (p->pga_free == 1) {
/* Use that area to allocate a new page */
if (!(new = get_free_page_area(1, p)))
return -1; /* Out of memory */
/* Free page areas must now be reduced to 0 */
BUG_ON(p->pga_free != 0);
/* Initialise it as a new source of page area structures */
newcache = mem_cache_init(phys_to_virt((void *)__pfn_to_addr(new->pfn)),
PAGE_SIZE, sizeof(struct page_area), 0);
/*
* Update the free page area counter
* NOTE: need to lock the allocator here
*/
p->pga_free += mem_cache_total_empty(newcache);
/*
* Add the new cache to available
* list of free page area caches
*/
list_insert(&newcache->list, &p->pga_cache_list);
/* Unlock here */
}
return 0;
}
void *alloc_page(int quantity)
{
struct page_area *new;
/*
* First make sure we have enough page
* area structures in the cache
*/
if (check_page_areas(&allocator) < 0)
return 0; /* Out of memory */
/*
* Now allocate the actual pages, using the available
* page area structures to describe the allocation
*/
new = get_free_page_area(quantity, &allocator);
/* Return physical address */
return (void *)__pfn_to_addr(new->pfn);
}
/* Merges two page areas, frees area cache if empty, returns the merged area. */
struct page_area *merge_free_areas(struct page_area *before,
struct page_area *after)
{
struct mem_cache *c;
BUG_ON(before->pfn + before->numpages != after->pfn);
BUG_ON(before->used || after->used)
BUG_ON(before == after);
before->numpages += after->numpages;
list_remove(&after->list);
c = after->cache;
mem_cache_free(c, after);
/* Recursively free the cache page */
if (mem_cache_is_empty(c)) {
list_remove(&c->list);
if (free_page(virt_to_phys(c)) < 0) {
printf("Page ptr: 0x%lx, virt_to_phys = 0x%lx\n"
"Page not found in cache.\n",
(unsigned long)c, (unsigned long)virt_to_phys(c));
BUG();
}
}
return before;
}
static int find_and_free_page_area(void *addr, struct page_allocator *p)
{
struct page_area *area, *prev, *next;
/* First find the page area to be freed. */
list_foreach_struct(area, &p->page_area_list, list)
if (__pfn_to_addr(area->pfn) == (unsigned long)addr &&
area->used) { /* Found it */
area->used = 0;
goto found;
}
return -1; /* Finished the loop, but area not found. */
found:
/* Now merge with adjacent areas, if possible */
if (area->list.prev != &p->page_area_list) {
prev = link_to_struct(area->list.prev, struct page_area, list);
if (!prev->used)
area = merge_free_areas(prev, area);
}
if (area->list.next != &p->page_area_list) {
next = link_to_struct(area->list.next, struct page_area, list);
if (!next->used)
area = merge_free_areas(area, next);
}
return 0;
}
int free_page(void *paddr)
{
return find_and_free_page_area(paddr, &allocator);
}

View File

@@ -0,0 +1,110 @@
#!/usr/bin/python
import os
import shutil
from os.path import join
import sys
project_root = join(os.getcwd(), "../..")
source_root = os.path.join(project_root, 'src')
headers_root = os.path.join(project_root, 'include')
tests_run_root = os.path.join(os.getcwd(), 'tmp')
tools_root = os.getcwd()
init_state = "page_init.out"
exit_state = "page_exit.out"
SZ_10MB = 1024 * 1024 * 10
def power(x, y):
res = 1
for i in range(y):
res = res * x;
return res
def test_mm():
'''
Tries to set up meaningful input parameters for page_size, maximum allocation
size, total number of allocations, and total pages, and tests the memory allocator
in every one of these combinations. The parameter guessing is not great, but at least
some test cases are reasonable.
'''
page_sizes = [128, 256, 512, 1024, 2048, 4096, 8192]
max_alloc_sizes = [1, 10, 40, 50, 100, 200]
for page_size in page_sizes:
numpages = SZ_10MB / page_size
for i in range(1, 3):
res = numpages / power(10, i) # Divide numpages to 10, 100, 1000
if res > 0:
max_alloc_sizes.append(numpages/10)
max_alloc_sizes.append(numpages/100)
max_alloc_sizes.append(numpages/1000)
for max_alloc_size in max_alloc_sizes:
if max_alloc_size >= numpages: # If a single allocation exceeds total, adjust.
max_alloc_size = numpages / 2
num_allocs = numpages / (max_alloc_size) * 2 * 2 / 3
cmd = "./test -a=p -n=%d -s=%d -fi=%s -fx=%s -ps=%d -pn=%d" % \
(num_allocs, max_alloc_size, join(tests_run_root, init_state),\
join(tests_run_root, exit_state), page_size, numpages)
print "num_allocs = %d, max_alloc_size = %d, page_size = %d, numpages = %d" % \
(num_allocs, max_alloc_size, page_size, numpages)
os.system(cmd)
#os.system("cat %s" % join(tests_run_root, init_state))
diffcmd = "diff " + join(tests_run_root, init_state) + " " + join(tests_run_root, exit_state)
if os.system(diffcmd) != 0:
print "Error: %s has failed.\n" % cmd
sys.exit(1)
def test_km():
'''
Tries to set up meaningful input parameters for payload size, maximum allocation
size, total number of allocations, and total pages, and tests kmalloc
in every one of these combinations. The parameter guessing is not great, but at least
some test cases are reasonable.
'''
page_sizes = [4096, 8192]
max_alloc_sizes = [1, 10, 40, 50, 100, 200, 1024, 2048, 4096, 10000, 50000, 100000]
numpages = 1024
for page_size in page_sizes:
for max_alloc_size in max_alloc_sizes:
num_allocs = (numpages * page_size * 3) / (max_alloc_size * 2)
cmd = "./test -a=k -n=%d -s=%d -fi=%s -fx=%s -ps=%d -pn=%d" % \
(num_allocs, max_alloc_size, join(tests_run_root, init_state),\
join(tests_run_root, exit_state), page_size, numpages)
print "num_allocs = %d, max_alloc_size = %d, page_size = %d, numpages = %d" %\
(num_allocs, max_alloc_size, page_size, numpages)
diffcmd = "diff " + join(tests_run_root, init_state) + " " +\
join(tests_run_root, exit_state)
if os.system(diffcmd) != 0:
print "Error: %s has failed.\n" % cmd
sys.exit(1)
def test_mm_params(num_allocs, max_alloc_size, page_size, numpages, iterations):
for i in range(iterations):
cmd = "./test -a=p -n=%d -s=%d -fi=%s -fx=%s -ps=%d -pn=%d" % \
(num_allocs, max_alloc_size, join(tests_run_root, init_state),\
join(tests_run_root, exit_state), page_size, numpages)
print "num_allocs = %d, max_alloc_size = %d, page_size = %d, numpages = %d" % \
(num_allocs, max_alloc_size, page_size, numpages)
os.system(cmd)
#os.system("cat %s" % join(tests_run_root, init_state))
diffcmd = "diff " + join(tests_run_root, init_state) + " " + join(tests_run_root, exit_state)
if os.system(diffcmd) != 0:
print "Error: %s has failed.\n" % cmd
sys.exit(1)
def run_tests():
if os.path.exists(tests_run_root):
shutil.rmtree(tests_run_root)
os.mkdir(tests_run_root)
# for i in range (100):
#test_km()
test_mm()
#test_mm_params(10922, 10, 128, 81920, 50)
#test_km()
#test_mc()
if __name__ == '__main__':
run_tests()

View File

@@ -0,0 +1,16 @@
#include <l4/macros.h>
#include <l4/types.h>
#include <l4/config.h>
/* Emulation of CLZ (count leading zeroes) instruction */
unsigned int __clz(unsigned int bitvector)
{
unsigned int x = 0;
while((!(bitvector & ((unsigned)1 << 31))) && (x < 32)) {
bitvector <<= 1;
x++;
}
return x;
}

View File

@@ -0,0 +1,33 @@
#include "debug.h"
#include <stdio.h>
void print_page_area_list(struct page_allocator *p)
{
struct page_area *area;
list_foreach_struct (area, &p->page_area_list, list) {
printf("%-20s\n%-20s\n", "Page area:","-------------------------");
printf("%-20s %u\n", "Pfn:", area->pfn);
printf("%-20s %d\n", "Used:", area->used);
printf("%-20s %d\n\n", "Number of pages:", area->numpages);
}
}
void print_km_area(struct km_area *s)
{
printf("%-20s\n%-20s\n", "Subpage area:","-------------------------");
printf("%-20s 0x%lu\n", "Addr:", s->vaddr);
printf("%-20s 0x%lu\n", "Size:", s->size);
printf("%-20s %d\n", "Used:", s->used);
printf("%-20s %d\n\n", "Head_of_pages:", s->pg_alloc_pages);
}
void print_km_area_list(struct link *km_areas)
{
struct km_area *area;
list_foreach_struct (area, km_areas, list)
print_km_area(area);
}

View File

@@ -0,0 +1,28 @@
#include "libl4.h"
unsigned long virt_to_phys(unsigned long addr)
{
return addr;
}
unsigned long phys_to_virt(unsigned long addr)
{
return addr;
}
u32 l4_getpid(unsigned int *a, unsigned int *b, unsigned int *c)
{
return 0;
}
u32 l4_unmap(unsigned long a, unsigned long b, u32 npages)
{
return 0;
}
u32 l4_map(unsigned long a, unsigned long b, u32 size, u32 flags, unsigned int tid)
{
return 0;
}

View File

View File

@@ -0,0 +1,250 @@
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <l4/macros.h>
#include <l4/config.h>
#include <kmem/kmalloc.h>
#include <mem/alloc_page.h>
#include INC_SUBARCH(mm.h)
#include INC_ARCH(linker.h)
#include INC_PLAT(print-early.h)
#include INC_PLAT(offsets.h)
#include INC_GLUE(memlayout.h)
#include "tests.h"
#include "test_kmalloc.h"
#include "test_allocpage.h"
#include "test_memcache.h"
#include "clz.h"
#include "memory.h"
#include "libl4.h"
#include "debug.h"
unsigned int TEST_PHYSMEM_TOTAL_PAGES = 250;
unsigned int TEST_PHYSMEM_TOTAL_SIZE;
unsigned int PHYS_MEM_START;
unsigned int PHYS_MEM_END;
void *malloced_test_memory;
void memory_initialise(void)
{
init_page_allocator(PHYS_MEM_START, PHYS_MEM_END);
kmalloc_init();
}
/* Allocating memory from the host C library, and
* it is used as if it is the physical memory available
* on the system.
*/
void alloc_test_memory()
{
TEST_PHYSMEM_TOTAL_SIZE = (PAGE_SIZE * TEST_PHYSMEM_TOTAL_PAGES);
if (!(malloced_test_memory = malloc(TEST_PHYSMEM_TOTAL_SIZE)))
printf("Host system out of memory.\n");
PHYS_MEM_START = (unsigned int)malloced_test_memory;
PHYS_MEM_END = PHYS_MEM_START + TEST_PHYSMEM_TOTAL_SIZE;
PHYS_MEM_START = page_align_up(PHYS_MEM_START);
PHYS_MEM_END = page_align(PHYS_MEM_END);
/* Normally _end is to know where the loaded kernel image
* ends in physical memory, so the system can start allocating
* physical memory from there. Because in our mock-up there's no
* used space in the malloc()'ed memory, _end is the same as the
* beginning of malloc()'ed memory.
*/
_end = PHYS_MEM_START;
dprintf("Initialising physical memory\n");
dprintf("Initialising allocators:\n");
memory_initialise();
}
struct cmdline_opts {
char run_allocator;
int allocations;
int alloc_size_max;
int physmem_pages;
int page_size;
int no_of_pages;
char *finit_path;
char *fexit_path;
} options;
int check_options_validity(struct cmdline_opts *opts)
{
if (opts->allocations <= 0) {
printf("Invalid number of allocations: %d\n", opts->allocations);
return -1;
}
if (opts->no_of_pages <= 0) {
printf("Invalid number of pages: %d\n", opts->no_of_pages);
return -1;
}
if (opts->alloc_size_max <= 0) {
printf("Invalid alloc_size_max: %d\n", opts->alloc_size_max);
return -1;
}
if (opts->page_size <= 0) {
printf("Invalid page_size: %d\n", opts->page_size);
return -1;
}
return 0;
}
void print_options(struct cmdline_opts *opts)
{
dprintf("Running: %s\n",
((opts->run_allocator == 'p') ? "page allocator" :
((opts->run_allocator == 'k') ? "kmem/kfree" :
"memcache allocator")));
dprintf("Total allocations: %d\n", opts->allocations);
dprintf("Maximum allocation size: %d, 0x%x(hex)\n\n",
opts->alloc_size_max, opts->alloc_size_max);
dprintf("Initial state file: %s\n", opts->finit_path);
dprintf("Exit state file: %s\n", opts->fexit_path);
}
void display_help(void)
{
printf("Main:\n");
printf("\tUsage:\n");
printf("\tmain\t-a=<p>|<k>|<m> [-n=<number of allocations>] [-s=<maximum size for any allocation>]\n"
"\t\t[-fi=<file to dump init state>] [-fx=<file to dump exit state>]\n"
"\t\t[-ps=<page size>] [-pn=<total number of pages>]\n");
printf("\n");
}
int get_cmdline_opts(int argc, char *argv[], struct cmdline_opts *opts)
{
int parsed = 0;
memset(opts, 0, sizeof (struct cmdline_opts));
if (argc <= 1)
return -1;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][2] == '=') {
if (argv[i][1] == 'a') {
if (argv[i][3] == 'k' ||
argv[i][3] == 'm' ||
argv[i][3] == 'p') {
opts->run_allocator = argv[i][3];
parsed = 1;
}
}
if (argv[i][1] == 'n') {
opts->allocations = atoi(&argv[i][3]);
parsed = 1;
}
if (argv[i][1] == 's') {
opts->alloc_size_max = atoi(&argv[i][3]);
parsed = 1;
}
}
if (argv[i][0] == '-' && argv[i][1] == 'f'
&& argv[i][3] == '=') {
if (argv[i][2] == 'i') {
opts->finit_path = &argv[i][4];
parsed = 1;
}
if (argv[i][2] == 'x') {
opts->fexit_path = &argv[i][4];
parsed = 1;
}
}
if (argv[i][0] == '-' && argv[i][1] == 'p'
&& argv[i][3] == '=') {
if (argv[i][2] == 's') {
opts->page_size = atoi(&argv[i][4]);
parsed = 1;
}
if (argv[i][2] == 'n') {
opts->no_of_pages = atoi(&argv[i][4]);
parsed = 1;
}
}
}
if (!parsed)
return -1;
return 0;
}
void get_output_files(FILE **out1, FILE **out2,
char *alloc_func_name, char *rootpath)
{
char pathbuf[150];
char *root = "/tmp/";
char *initstate_prefix = "test_initstate_";
char *endstate_prefix = "test_endstate_";
char *extension = ".out";
if (!rootpath)
rootpath = root;
/* File path manipulations */
sprintf(pathbuf, "%s%s%s%s", rootpath, initstate_prefix, alloc_func_name, extension);
*out1 = fopen(pathbuf,"w+");
sprintf(pathbuf, "%s%s%s%s", rootpath, endstate_prefix, alloc_func_name, extension);
*out2 = fopen(pathbuf, "w+");
return;
}
int main(int argc, char *argv[])
{
FILE *finit, *fexit;
int output_files = 0;
if (get_cmdline_opts(argc, argv, &options) < 0) {
display_help();
return 1;
}
print_options(&options);
if (check_options_validity(&options) < 0)
exit(1);
if (options.finit_path && options.fexit_path) {
finit = fopen(options.finit_path, "w+");
fexit = fopen(options.fexit_path, "w+");
output_files = 1;
}
if (options.page_size) {
PAGE_SIZE = options.page_size;
PAGE_MASK = PAGE_SIZE - 1;
PAGE_BITS = 32 - __clz(PAGE_MASK);
dprintf("Using: Page Size: %d\n", PAGE_SIZE);
dprintf("Using: Page Mask: 0x%x\n", PAGE_MASK);
dprintf("Using: Page Bits: %d\n", PAGE_BITS);
}
if (options.no_of_pages) {
dprintf("Using: Total pages: %d\n", options.no_of_pages);
TEST_PHYSMEM_TOTAL_PAGES = options.no_of_pages;
}
alloc_test_memory();
if (options.run_allocator == 'p') {
if (!output_files)
get_output_files(&finit, &fexit, "alloc_page", 0);
test_allocpage(options.allocations, options.alloc_size_max,
finit, fexit);
} else if (options.run_allocator == 'k') {
if (!output_files)
get_output_files(&finit, &fexit, "kmalloc", 0);
test_kmalloc(options.allocations, options.alloc_size_max,
finit, fexit);
} else if (options.run_allocator == 'm') {
if (!output_files)
get_output_files(&finit, &fexit, "memcache", 0);
test_memcache(options.allocations, options.alloc_size_max,
finit, fexit, 1);
} else {
printf("Invalid allocator option.\n");
}
free((void *)malloced_test_memory);
fclose(finit);
fclose(fexit);
return 0;
}

View File

@@ -0,0 +1,9 @@
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include INC_GLUE(memory.h)
unsigned int PAGE_SIZE = TEST_PAGE_SIZE;
unsigned int PAGE_MASK = TEST_PAGE_MASK;
unsigned int PAGE_BITS = TEST_PAGE_BITS;

View File

@@ -0,0 +1,216 @@
/*
* Generic random allocation/deallocation test
*
* Copyright 2007 (C) Bahadir Balban
*
*/
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include INC_GLUE(memory.h)
#include <l4/lib/list.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "test_alloc_generic.h"
#include "debug.h"
void print_test_state(unsigned int title,
print_alloc_state_t print_allocator_state)
{
switch (title) {
case TEST_STATE_BEGIN:
printf("=================\n"
"===== BEGIN =====\n"
"=================\n\n");
break;
case TEST_STATE_MIDDLE:
printf("==================\n"
"===== MIDDLE =====\n"
"==================\n\n");
break;
case TEST_STATE_END:
printf("===========\n"
"=== END ===\n"
"===========\n\n");
break;
case TEST_STATE_ERROR:
printf("=================\n"
"===== ERROR =====\n"
"=================\n\n");
break;
default:
printf("Title error.\n");
}
print_allocator_state();
}
void get_output_filepaths(FILE **out1, FILE **out2,
char *alloc_func_name)
{
char pathbuf[150];
char *rootpath = "/tmp/";
char *initstate_prefix = "test_initstate_";
char *endstate_prefix = "test_endstate_";
char *extention = ".out";
/* File path manipulations */
sprintf(pathbuf, "%s%s%s%s", rootpath, initstate_prefix, alloc_func_name, extention);
*out1 = fopen(pathbuf,"w+");
sprintf(pathbuf, "%s%s%s%s", rootpath, endstate_prefix, alloc_func_name, extention);
*out2 = fopen(pathbuf, "w+");
return;
}
/* This function is at the heart of generic random allocation testing.
* It is made as simple as possible, and can be used for testing all
* allocators. It randomly allocates/deallocates data and prints out
* the outcome of the action. Here are a few things it does and doesn't
* do:
* - It does not test false input on the allocators, e.g. attempting
* to free an address that hasn't been allocated, or attempting to
* free address 0.
* - It does capture and compare initial and final states of the
* allocators' internal structures after all allocations are freed.
* This is done by comparing two files filled with allocator state
* by functions supplied by the allocators themselves.
* - It expects the allocator NOT to run out of memory.
*/
int
test_alloc_free_random_order(const int MAX_ALLOCATIONS,
const int ALLOC_SIZE_MAX,
alloc_func_t alloc,
free_func_t free,
print_alloc_state_t print_allocator_state,
FILE *state_init_file, FILE *state_end_file)
{
/* The last element in full_state that tells about any full index.
* This is the limit the random deallocation would use to find a full
* index */
int random_size;
int random_action;
int random_index;
int alloc_so_far = 0;
int full_state_last = -1;
int halfway_through = 0;
FILE * const default_stdout = stdout;
/* Memory pointers */
void *mem[MAX_ALLOCATIONS];
/* Each element keeps track of one currently full index number */
int full_state[MAX_ALLOCATIONS];
/* Check arguments first */
if (!MAX_ALLOCATIONS || !ALLOC_SIZE_MAX || !alloc || !free
|| !print_allocator_state || !state_init_file || !state_end_file) {
printf("Invalid arguments to %s()\n", __FUNCTION__);
return 1;
}
memset(mem, 0, MAX_ALLOCATIONS * sizeof(void *));
memset(full_state, 0, MAX_ALLOCATIONS * sizeof(int));
//print_test_state(TEST_STATE_BEGIN, print_allocator_state);
stdout = state_init_file;
print_test_state(TEST_STATE_BEGIN, print_allocator_state);
stdout = default_stdout;
/* Randomly either allocate/deallocate at a random
* index, of random size */
srand(time(0));
while (1) {
if (alloc_so_far < (MAX_ALLOCATIONS / 2)) {
/* Give more chance to allocations at the beginning */
if ((rand() % 4) == 0) /* 1/4 chance */
random_action = FREE;
else /* 3/4 chance */
random_action = ALLOCATE;
} else {
if (!halfway_through) {
#if defined (DEBUG)
print_test_state(TEST_STATE_MIDDLE,
print_allocator_state);
#endif
halfway_through = 1;
}
/* Give more chane to freeing after halfway-through */
if ((rand() % 3) == 0) /* 1/3 chance */
random_action = ALLOCATE;
else /* 2/3 chance */
random_action = FREE;
}
random_size = (rand() % (ALLOC_SIZE_MAX-1)) + 1;
if (random_action == ALLOCATE) {
if (alloc_so_far < MAX_ALLOCATIONS) {
alloc_so_far++;
for (int i = 0; i < MAX_ALLOCATIONS; i++) {
if (mem[i] == 0) { // Find the first empty slot.
int allocation_error =
((mem[i] = alloc(random_size)) <= 0);
dprintf("%-12s%-8s%-12p%-8s%-10d\n",
"alloc:", "addr:", mem[i],
"size:", random_size);
if (allocation_error) {
print_test_state(TEST_STATE_ERROR,
print_allocator_state);
if (mem[i] < 0) {
printf("Error: alloc() returned negative value\n");
BUG();
} else if (mem[i] == 0) {
printf("Error: Allocator is out of memory.\n");
return 1;
}
}
full_state_last++;
full_state[full_state_last] = i;
break;
}
}
} else
random_action = FREE;
}
if (random_action == FREE) {
/* all are free, can't free anymore */
if (full_state_last < 0)
continue;
else if (full_state_last > 0)
random_index = rand() % full_state_last;
else
random_index = 0; /* Last item */
if(mem[full_state[random_index]] == 0)
BUG();
if (free(mem[full_state[random_index]]) < 0)
BUG();
dprintf("%-12s%-8s%-12p\n","free:",
"addr:", mem[full_state[random_index]]);
mem[full_state[random_index]] = 0;
/* Fill in the empty gap with last element */
full_state[random_index] = full_state[full_state_last];
/* Last element now in the gap
* (somewhere inbetween first and last) */
full_state[full_state_last] = 0;
/* One less in the number of full items */
full_state_last--;
}
/* Check that all allocations and deallocations took place */
if (alloc_so_far == MAX_ALLOCATIONS && full_state_last < 0) {
for (int i = 0; i < MAX_ALLOCATIONS; i++)
BUG_ON(full_state[i] != 0); // A final sanity check.
break;
}
}
//print_test_state(TEST_STATE_END, print_allocator_state);
stdout = state_end_file;
print_test_state(TEST_STATE_BEGIN, print_allocator_state);
stdout = default_stdout;
return 0;
}

View File

@@ -0,0 +1,85 @@
/*
* Testing code for the page allocator.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include INC_GLUE(memory.h)
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "test_allocpage.h"
#include "test_alloc_generic.h"
#include "debug.h"
unsigned int PAGE_ALLOCATIONS = 30;
unsigned int PAGE_ALLOC_SIZE_MAX = 8;
extern struct page_allocator allocator;
void print_page_area(struct page_area *a, int areano)
{
printf("Area starts @: 0x%lu, %s, numpages: %d\n",
__pfn_to_addr(a->pfn),
(a->used) ? "used" : "unused", a->numpages);
return;
}
void print_areas(struct link *area_head)
{
struct page_area *cur;
int areano = 1;
printf("Page areas:\n-------------\n");
list_foreach_struct(cur, area_head, list)
print_page_area(cur, areano++);
}
void print_cache(struct mem_cache *c, int cacheno)
{
printf("Cache %d state:\n-------------\n", cacheno);
printf("Total: %d\n", c->total);
printf("Free: %d\n", c->free);
printf("Start: 0x%x\n", c->start);
}
void print_caches(struct link *cache_head)
{
int caches = 1;
struct mem_cache *cur;
list_foreach_struct(cur, cache_head, list)
print_cache(cur, caches++);
}
void print_page_allocator_state(void)
{
print_areas(&allocator.page_area_list);
printf("Data Cache:\n--------\n");
print_caches(&allocator.dcache_list);
printf("Cache Cache:\n----------\n");
print_caches(&allocator.ccache_list);
}
/* FIXME: with current default parameters (allocations = 30, sizemax = 8),
* for some odd reason, we got the bug at line 280 in alloc_page.c.
* Very weird. Find out why.
*/
void test_allocpage(int page_allocations, int page_alloc_size_max,
FILE *init_state, FILE *exit_state)
{
//if (!page_allocations)
// page_allocations = PAGE_ALLOCATIONS;
//if (!page_alloc_size_max)
// page_alloc_size_max = PAGE_ALLOC_SIZE_MAX;
dprintf("\nPAGE ALLOCATOR TEST:====================================\n\n");
test_alloc_free_random_order(page_allocations, page_alloc_size_max,
alloc_page, free_page,
print_page_allocator_state,
init_state, exit_state);
}

View File

@@ -0,0 +1,42 @@
/*
* Testing code for the kmalloc allocator.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include INC_GLUE(memory.h)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "test_alloc_generic.h"
#include "test_allocpage.h"
#include "debug.h"
#include "tests.h"
extern struct link km_area_start;
void print_kmalloc_state(void)
{
print_km_area_list(&km_area_start);
}
void test_kmalloc(int kmalloc_allocations, int kmalloc_alloc_size_max,
FILE *init_state, FILE *exit_state)
{
unsigned int KMALLOC_ALLOCATIONS = 20;
unsigned int KMALLOC_ALLOC_SIZE_MAX = (PAGE_SIZE * 3);
if (!kmalloc_allocations)
kmalloc_allocations = KMALLOC_ALLOCATIONS;
if (!kmalloc_alloc_size_max)
kmalloc_alloc_size_max = KMALLOC_ALLOC_SIZE_MAX;
test_alloc_free_random_order(kmalloc_allocations, kmalloc_alloc_size_max,
kmalloc, kfree, print_kmalloc_state,
init_state, exit_state);
}

View File

@@ -0,0 +1,115 @@
/*
* Testing code for the memcache structure.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mem/memcache.h>
#include "test_memcache.h"
#include "test_alloc_generic.h"
#include "debug.h"
#include "tests.h"
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include INC_GLUE(memory.h)
unsigned int MEM_CACHE_SIZE;
struct mem_cache *this;
void *buffer;
void *mem_cache_alloc_wrapped(int size)
{
return mem_cache_alloc(this);
}
int mem_cache_free_wrapped(void *addr)
{
return mem_cache_free(this, addr);
}
void print_memcache_state(void)
{
printf("%-15s%d\n","Total:", this->total);
printf("%-15s%d\n","Free:", this->free);
printf("Bitmap has %d words:\n", BITWISE_GETWORD(this->total) + 1);
for (int i = 0; i <= BITWISE_GETWORD(this->total); i++)
printf("0x%x\n", this->bitmap[i]);
}
int test_memcache_init_aligned(int *items_max, int item_size)
{
if (item_size * 10 > MEM_CACHE_SIZE)
MEM_CACHE_SIZE = item_size * 10;
if (!(buffer = calloc(1, MEM_CACHE_SIZE))) {
printf("System out of memory.\n");
BUG();
}
if ((this = mem_cache_init(buffer, MEM_CACHE_SIZE,
item_size, 1)) == 0) {
printf("Unable to initialise cache.\n");
return -1;
}
*items_max = mem_cache_total_empty(this);
printf("\nMEMCACHE TEST: ALIGNED ELEMENTS\n==========================\n");
printf("%-30s%d\n", "Item size:", item_size);
printf("%-30s0x%x\n", "Cache occupied space:", MEM_CACHE_SIZE);
printf("%-30s%d\n","Total items in cache:", *items_max);
printf("%-30s0x%x\n","Total items space:", (*items_max * item_size));
return 0;
}
int test_memcache_init(int *items_max, int item_size)
{
if (item_size * 10 > MEM_CACHE_SIZE)
MEM_CACHE_SIZE = item_size * 10;
printf("%s: Allocating cache memory.\n",__FUNCTION__);
if (!(buffer = calloc(1, MEM_CACHE_SIZE))) {
printf("System out of memory.\n");
BUG();
}
if ((this = mem_cache_init(buffer, MEM_CACHE_SIZE,
item_size, 0)) == 0) {
printf("Unable to initialise cache.\n");
return -1;
}
*items_max = mem_cache_total_empty(this);
printf("\nMEMCACHE TEST:\n========================\n");
printf("%-30s%d\n", "Item size:", item_size);
printf("%-30s0x%x\n", "Cache occupied space:", MEM_CACHE_SIZE);
printf("%-30s%d\n","Total items in cache:", *items_max);
printf("%-30s0x%x\n","Total items space:", (*items_max * item_size));
return 0;
}
int test_memcache(int items_max, int item_size, FILE *init_state, FILE *exit_state, int aligned)
{
const unsigned int TEST_CACHE_ITEM_SIZE = 5;
MEM_CACHE_SIZE = PAGE_SIZE * 5;
if (!item_size)
item_size = TEST_CACHE_ITEM_SIZE;
/* items_max value is ignored and overwritten because caches have fixed size. */
test_memcache_init(&items_max, item_size);
test_alloc_free_random_order(items_max, /* unused */ 2, mem_cache_alloc_wrapped,
mem_cache_free_wrapped, print_memcache_state,
init_state, exit_state);
free(buffer);
if (aligned) {
test_memcache_init_aligned(&items_max, item_size);
test_alloc_free_random_order(items_max, /* unused */ 2, mem_cache_alloc_wrapped,
mem_cache_free_wrapped, print_memcache_state,
init_state, exit_state);
}
free(buffer);
return 0;
}