diff --git a/kernel-src/arch/x86-64/main.c b/kernel-src/arch/x86-64/main.c index eb497257..41150e44 100644 --- a/kernel-src/arch/x86-64/main.c +++ b/kernel-src/arch/x86-64/main.c @@ -7,6 +7,7 @@ #include #include #include +#include static cpu_t bsp_cpu; @@ -21,5 +22,6 @@ void kernel_entry() { logging_sethook(term_putchar); arch_mmu_init(); vmm_init(); + slab_init(); __assert(!"kernel entry end"); } diff --git a/kernel-src/include/kernel/slab.h b/kernel-src/include/kernel/slab.h new file mode 100644 index 00000000..d3534fe4 --- /dev/null +++ b/kernel-src/include/kernel/slab.h @@ -0,0 +1,29 @@ +#ifndef _SLAB_H +#define _SLAB_H + +#include +#include + +typedef struct slab_t { + struct slab_t *next; + struct slab_t *prev; + size_t used; + void **free; +} slab_t; + +typedef struct scache_t { + // TODO lock + void (*ctor)(struct scache_t *cache, void *obj); + void (*dtor)(struct scache_t *cache, void *obj); + slab_t *full; + slab_t *partial; + slab_t *empty; + size_t size; + size_t truesize; + size_t alignment; + size_t slabobjcount; +} scache_t; + +void slab_init(); + +#endif diff --git a/kernel-src/mm/slab.c b/kernel-src/mm/slab.c new file mode 100644 index 00000000..7532f524 --- /dev/null +++ b/kernel-src/mm/slab.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include + +#define SLAB_INDIRECT_CUTOFF 512 +#define SLAB_INDIRECT_COUNT 16 + +#define SLAB_PAGE_OFFSET (PAGE_SIZE - sizeof(slab_t)) +#define SLAB_DATA_SIZE SLAB_PAGE_OFFSET + +#define GET_SLAB(x) (slab_t *)(ROUND_DOWN((uintptr_t)x, PAGE_SIZE) + SLAB_PAGE_OFFSET) + +// the cache responsible for allocating all others +static scache_t selfcache = { + .size = sizeof(scache_t), + .alignment = 8, + .truesize = ROUND_UP(sizeof(scache_t) + sizeof(void **), 8), + .slabobjcount = SLAB_DATA_SIZE / ROUND_UP(sizeof(scache_t) + sizeof(void **), 8) +}; + +static scache_t *slabcache; + +static void initsmall(scache_t *cache, slab_t *slab, void *base) { + slab->free = NULL; + slab->used = 0; + + for (uintmax_t offset = 0; offset < cache->truesize * cache->slabobjcount; offset += cache->truesize) { + if (cache->ctor) + cache->ctor(cache, (void *)((uintptr_t)base + offset)); + + void **freenext = (void **)((uintptr_t)base + offset + cache->size); + *freenext = slab->free; + slab->free = freenext; + } +} + +static bool growcache(scache_t *cache) { + __assert(cache->size < SLAB_INDIRECT_CUTOFF); // TODO bigger than this allocation and support + void *_slab = vmm_map(NULL, PAGE_SIZE, VMM_FLAGS_ALLOCATE, ARCH_MMU_FLAG_READ | ARCH_MMU_FLAG_WRITE | ARCH_MMU_FLAG_NOEXEC, NULL); + if (_slab == NULL) + return false; + slab_t *slab = GET_SLAB(_slab); + + initsmall(cache, slab, _slab); + + slab->next = cache->empty; + cache->empty = slab; + return true; +} + +static void *takeobject(scache_t *cache, slab_t *slab) { + if (slab->free == NULL) + return NULL; + + void *objend = slab->free; + slab->free = *slab->free; + slab->used += 1; + return (void *)((uintptr_t)objend - cache->size); +} + +void *slab_allocate(scache_t *cache) { + // TODO lock + slab_t *slab = NULL; + if (cache->partial != NULL) + slab = cache->partial; + else if (cache->empty != NULL) + slab = cache->empty; + + void *ret = NULL; + + if (slab == NULL) { + if (growcache(cache)) + slab = cache->empty; + else + goto cleanup; + } + + ret = takeobject(cache, slab); + + if (slab == cache->empty) { + cache->empty = slab->next; + slab->next = cache->partial; + cache->partial = slab; + } + + if (slab->used == cache->slabobjcount) { + cache->partial = slab->next; + slab->next = cache->full; + cache->full = slab; + } + + cleanup: + // TODO unlock + return ret; +} + +scache_t *slab_newcache(size_t size, size_t alignment, void (*ctor)(scache_t *, void *), void (*dtor)(scache_t *, void *)) { + if (alignment == 0) + alignment = 8; + + scache_t *cache = slab_allocate(&selfcache); + if (cache == NULL) + return NULL; + + cache->size = size; + cache->alignment = alignment; + cache->truesize = ROUND_UP(size + sizeof(void **), alignment); + cache->ctor = ctor; + cache->dtor = dtor; + cache->slabobjcount = size < SLAB_INDIRECT_CUTOFF ? SLAB_DATA_SIZE / cache->truesize : SLAB_INDIRECT_COUNT; + + printf("slab: new cache: size %lu align %lu truesize %lu objcount %lu\n", cache->size, cache->alignment, cache->truesize, cache->slabobjcount); + + return cache; +} + +void slab_init() { + slabcache = slab_newcache(sizeof(slab_t), 0, NULL, NULL); + __assert(slabcache); +}