Commit c9f42c0a authored by Rusty Russell's avatar Rusty Russell

Initial cut at file/mem allocator (pages only)

parent bd9f3925
#include <stdio.h>
#include <string.h>
#include "config.h"
/**
* alloc - memory allocator routines
*
* The alloc module implements a simple allocator which you can use to
* dynamically allocate space within a region of memory. This can be useful
* for suballocations within a given region, or a memory-mapped file.
*
* All metadata is kept within the memory handed to the allocator: you only
* need hand the pointer and the size of the memory to each call.
*
* The region contents is always in offsets, so it can be mapped in different
* places, but is not endian-safe.
*
* Example:
* #include <sys/mman.h>
* #include <unistd.h>
* #include <sys/types.h>
* #include <err.h>
* #include "alloc/alloc.h"
*
* static void usage(const char *name)
* {
* errx(1, "Usage: %s --create <mapfile>\n"
* " %s --check <mapfile>\n"
* " %s --alloc <mapfile>\n"
* " %s --free=<offset> <mapfile>\n", name, name, name);
* }
*
* // Create a memory mapped file, and allocate from within it
* int main(int argc, char *argv[])
* {
* void *a, *p;
* int fd;
* enum { CREATE, CHECK, ALLOC, FREE } cmd;
*
* if (argc != 3)
* usage(argv[0]);
*
* if (strcmp(argv[1], "--create") == 0)
* cmd = CREATE;
* else if (strcmp(argv[1], "--check") == 0)
* cmd = CHECK;
* else if (strcmp(argv[1], "--alloc") == 0)
* cmd = ALLOC;
* else if (strncmp(argv[1], "--free=", strlen("--free=")) == 0)
* cmd = FREE;
* else
* usage(argv[0]);
*
* if (cmd == CREATE) {
* fd = open(argv[2], O_RDWR|O_CREAT|O_EXCL, 0600);
* if (fd < 0)
* err(1, "Could not create %s", argv[2]);
* if (ftruncate(fd, 1048576) != 0)
* err(1, "Could not set length on %s", argv[2]);
* } else {
* fd = open(argv[2], O_RDWR);
* if (fd < 0)
* err(1, "Could not open %s", argv[2]);
* }
*
* a = mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_SHARED, fd,0);
* if (a == MAP_FAILED)
* err(1, "Could not map %s", argv[2]);
*
* switch (cmd) {
* case CREATE:
* alloc_init(a, 1048576);
* break;
* case CHECK:
* if (!alloc_check(a, 1048576))
* err(1, "Region is corrupt");
* break;
* case ALLOC:
* p = alloc_get(a, 1048576, 1024, 16);
* if (!p)
* errx(1, "Could not allocate");
* printf("%zu\n", (char *)p - (char *)a);
* break;
* case FREE:
* p = (char *)a + atol(argv[1] + strlen("--free="));
* alloc_free(a, 1048576, p);
* break;
* }
* return 0;
* }
*/
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
return 0;
}
return 1;
}
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include "alloc.h"
/* FIXME: Doesn't handle non-page-aligned poolsize. */
/* FIXME: Doesn't handle sub-page allocations. */
#define MIN_SIZE (getpagesize() * 2)
enum page_state
{
FREE,
TAKEN,
TAKEN_START,
};
void alloc_init(void *pool, unsigned long poolsize)
{
uint8_t *bits = pool;
unsigned int pages = poolsize / getpagesize();
if (poolsize < MIN_SIZE)
return;
memset(bits, 0, (pages * 2 + CHAR_BIT - 1)/ CHAR_BIT);
}
static enum page_state get_page_state(const uint8_t *bits, unsigned long page)
{
return bits[page * 2 / CHAR_BIT] >> (page * 2 % CHAR_BIT) & 3;
}
static void set_page_state(uint8_t *bits, unsigned long page, enum page_state s)
{
bits[page * 2 / CHAR_BIT] &= ~(3 << (page * 2 % CHAR_BIT));
bits[page * 2 / CHAR_BIT] |= ((uint8_t)s << (page * 2 % CHAR_BIT));
}
static unsigned long metadata_length(void *pool, unsigned long poolsize)
{
return ((poolsize / getpagesize() * 2 / CHAR_BIT) + getpagesize() - 1)
& ~(getpagesize() - 1);
}
void *alloc_get(void *pool, unsigned long poolsize,
unsigned long size, unsigned long align)
{
unsigned long i, free, want, metalen;
if (poolsize < MIN_SIZE)
return NULL;
/* FIXME: Necessary for this. */
if (size == 0)
size = 1;
want = (size + getpagesize() - 1) / getpagesize();
metalen = metadata_length(pool, poolsize);
free = 0;
for (i = 0; i < (poolsize - metalen) / getpagesize(); i++) {
switch (get_page_state(pool, i)) {
case FREE:
if (++free >= want) {
unsigned int j;
char *ret = (char *)pool + metalen
+ (i - want + 1) * getpagesize();
if ((unsigned long)ret % align)
continue;
for (j = i; j > i - want + 1; j--)
set_page_state(pool, j, TAKEN);
set_page_state(pool, i - want + 1, TAKEN_START);
return ret;
}
break;
case TAKEN_START:
case TAKEN:
free = 0;
break;
}
}
return NULL;
}
void alloc_free(void *pool, unsigned long poolsize, void *free)
{
unsigned long pagenum, metalen;
if (poolsize < MIN_SIZE)
return;
if (!free)
return;
metalen = metadata_length(pool, poolsize);
assert(free > pool && (char *)pool + poolsize > (char *)free);
assert((unsigned long)free % getpagesize() == 0);
pagenum = ((char *)free - (char *)pool - metalen) / getpagesize();
assert(get_page_state(pool, pagenum) == TAKEN_START);
set_page_state(pool, pagenum, FREE);
while (get_page_state(pool, ++pagenum) == TAKEN)
set_page_state(pool, pagenum, FREE);
}
bool alloc_check(void *pool, unsigned long poolsize)
{
unsigned int i, metalen;
enum page_state last_state = FREE;
if (poolsize < MIN_SIZE)
return true;
metalen = metadata_length(pool, poolsize);
for (i = 0; i < (poolsize - metalen) / getpagesize(); i++) {
enum page_state state = get_page_state(pool, i);
switch (state) {
case FREE:
case TAKEN_START:
break;
case TAKEN:
if (last_state == FREE)
return false;
break;
default:
return false;
}
last_state = state;
}
return true;
}
#ifndef ALLOC_H
#define ALLOC_H
#include <stdbool.h>
void alloc_init(void *pool, unsigned long poolsize);
void *alloc_get(void *pool, unsigned long poolsize,
unsigned long size, unsigned long align);
void alloc_free(void *pool, unsigned long poolsize, void *free);
bool alloc_check(void *pool, unsigned long poolsize);
#endif /* ALLOC_H */
#include "alloc/alloc.h"
#include "tap.h"
#include "alloc/alloc.c"
#include <stdlib.h>
#define POOL_ORD 16
#define POOL_SIZE (1 << POOL_ORD)
#define sort(p, num, cmp) \
qsort((p), (num), sizeof(*p), (int(*)(const void *, const void *))cmp)
static int addr_cmp(void **a, void **b)
{
return (*a) - (*b);
}
static bool unique(void *p[], unsigned int num)
{
unsigned int i;
for (i = 1; i < num; i++)
if (p[i] == p[i-1])
return false;
return true;
}
int main(int argc, char *argv[])
{
void *mem;
unsigned int i, num, max_size;
void *p[POOL_SIZE];
plan_tests(141);
/* FIXME: Needs to be page aligned for now. */
posix_memalign(&mem, getpagesize(), POOL_SIZE);
/* Small pool, all allocs fail, even 0-length. */
alloc_init(mem, 0);
ok1(alloc_check(mem, 0));
ok1(alloc_get(mem, 0, 1, 1) == NULL);
ok1(alloc_get(mem, 0, 128, 1) == NULL);
ok1(alloc_get(mem, 0, 0, 1) == NULL);
/* Free of NULL should work. */
alloc_free(mem, 0, NULL);
alloc_init(mem, POOL_SIZE);
ok1(alloc_check(mem, POOL_SIZE));
/* Find largest allocation which works. */
for (max_size = POOL_SIZE * 2; max_size; max_size--) {
p[0] = alloc_get(mem, POOL_SIZE, max_size, 1);
if (p[0])
break;
}
ok1(max_size < POOL_SIZE);
ok1(max_size > 0);
ok1(alloc_check(mem, POOL_SIZE));
/* Free it, should be able to reallocate it. */
alloc_free(mem, POOL_SIZE, p[0]);
ok1(alloc_check(mem, POOL_SIZE));
p[0] = alloc_get(mem, POOL_SIZE, max_size, 1);
ok1(p[0]);
ok1(alloc_check(mem, POOL_SIZE));
alloc_free(mem, POOL_SIZE, p[0]);
ok1(alloc_check(mem, POOL_SIZE));
/* Allocate a whole heap. */
for (i = 0; i < POOL_SIZE; i++) {
p[i] = alloc_get(mem, POOL_SIZE, 1, 1);
if (!p[i])
break;
}
num = i;
/* Can't allocate this many. */
ok1(num != POOL_SIZE);
ok1(alloc_check(mem, POOL_SIZE));
/* Sort them. */
sort(p, num, addr_cmp);
/* Uniqueness check */
ok1(unique(p, num));
/* Free every second one. */
for (i = 0; i < num; i += 2) {
alloc_free(mem, POOL_SIZE, p[i]);
ok1(alloc_check(mem, POOL_SIZE));
}
for (i = 1; i < num; i += 2) {
alloc_free(mem, POOL_SIZE, p[i]);
ok1(alloc_check(mem, POOL_SIZE));
}
ok1(alloc_check(mem, POOL_SIZE));
/* Should be able to reallocate max size. */
p[0] = alloc_get(mem, POOL_SIZE, max_size, 1);
ok1(p[0]);
ok1(alloc_check(mem, POOL_SIZE));
/* Re-initializing should be the same as freeing everything */
alloc_init(mem, POOL_SIZE);
ok1(alloc_check(mem, POOL_SIZE));
p[0] = alloc_get(mem, POOL_SIZE, max_size, 1);
ok1(p[0]);
ok1(alloc_check(mem, POOL_SIZE));
alloc_free(mem, POOL_SIZE, p[0]);
ok1(alloc_check(mem, POOL_SIZE));
/* Alignment constraints should be met, as long as powers of two */
for (i = 0; i < POOL_ORD-2 /* FIXME: Should be -1 */; i++) {
p[i] = alloc_get(mem, POOL_SIZE, i, 1 << i);
ok1(p[i]);
ok1(((unsigned long)p[i] % (1 << i)) == 0);
ok1(alloc_check(mem, POOL_SIZE));
}
for (i = 0; i < POOL_ORD-2 /* FIXME: Should be -1 */; i++) {
alloc_free(mem, POOL_SIZE, p[i]);
ok1(alloc_check(mem, POOL_SIZE));
}
/* Alignment constraints for a single-byte allocation. */
for (i = 0; i < POOL_ORD; i++) {
p[0] = alloc_get(mem, POOL_SIZE, 1, 1 << i);
ok1(p[0]);
ok1(alloc_check(mem, POOL_SIZE));
alloc_free(mem, POOL_SIZE, p[0]);
ok1(alloc_check(mem, POOL_SIZE));
}
return exit_status();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment