summaryrefslogblamecommitdiffstats
path: root/mem/slab.c
blob: 75b83027540755e76ae84a12c7b68da942b30d04 (plain) (tree)








































































































































































































                                                                                            
#include "slab.h"
#include "memory.h"
#include "lib/format.h"
#include "lib/string.h"
#include "lib/jove.h"
#include "io/log.h"

extern void *_kernel_end;

static uintptr_t s_addr_next_free;

#define GENERIC_CACHEC 8
static struct SlabCache s_generic_caches[GENERIC_CACHEC];

static uintptr_t
s_next_free(size_t width)
{
    uintptr_t ret = s_addr_next_free;
    s_addr_next_free += width;
    mem_ensure_range(ret, s_addr_next_free, true, false);
    return ret;
}

static int
s_get_free_listw(size_t slabw, size_t objw)
{
    int freelistc = 1;
    while(freelistc < 256) {
        int maxobjc = (slabw - (freelistc * sizeof(uintptr_t))) / objw;
        if(maxobjc <= freelistc) return maxobjc;
        freelistc++;
    }
    return freelistc;
}

static struct SlabDescriptor
*s_slab_new(struct SlabCache *cache, struct SlabDescriptor *last)
{
    size_t slab_width = (cache->slab_pages * PAGESIZE);
    uintptr_t descr_base = s_next_free(slab_width);
    struct SlabDescriptor *descr = (struct SlabDescriptor*)descr_base;

    size_t free_listc = s_get_free_listw(
            slab_width - sizeof(struct SlabDescriptor),
            cache->obj_size);
    size_t descriptor_width = sizeof(struct SlabDescriptor)
        + (free_listc * sizeof(uintptr_t));
    uintptr_t obj_base = descr_base + descriptor_width;

    if(free_listc < 8) {
        free_listc = ((slab_width - sizeof(struct SlabDescriptor)) / cache->obj_size);
        descr = mem_alloc(sizeof(struct SlabDescriptor) + (free_listc * sizeof(uintptr_t)));
        obj_base = descr_base;
    }

    *descr = (struct SlabDescriptor) {
        .prev = last,
        .next = (last == NULL ? NULL : last->next),
        .slab_base = (void*)descr_base,
        .obj_base = (void*)obj_base,
        .free_count = free_listc,
        .free_index = free_listc - 1
    };
    for(size_t i = 0; i < free_listc; i++) {
        descr->free[i] = obj_base + (i * cache->obj_size);
    }

    return descr;
}

void
mem_slabcache_new(struct SlabCache *cache, char *name, size_t objsize)
{
    if(objsize % 8 > 0) objsize += (8 - (objsize % 8));
    size_t pages = objsize > 512 ? (objsize >> 9) : 1;
    *cache = (struct SlabCache){
        .obj_size = objsize,
        .slab_pages = pages,
        .list_free = NULL,
        .list_partial = NULL,
        .list_full = NULL
    };
    size_t namelen = strlen(name);
    namelen = namelen > 32 ? 32 : namelen;
    memcpy(cache->name, name, namelen);

    //Allocate the first slab
    cache->list_free = s_slab_new(cache, NULL);
}

void*
mem_slab_alloc(struct SlabCache *cache)
{
    // Get a free slab
    struct SlabDescriptor *slab = NULL;
    if(cache->list_partial != NULL) slab = cache->list_partial;
    if(slab == NULL && cache->list_free != NULL) {
        slab = cache->list_free;
        cache->list_free = slab->next;
    }
    if(slab == NULL) slab = s_slab_new(cache, cache->list_free);
    cache->list_partial = slab;

    // Take an object from the slab.
    uintptr_t objaddr = slab->free[slab->free_index];
    slab->free_index -= 1;

    if(slab->free_index < 0) {
        slab->next = cache->list_full;
        cache->list_full = slab;
    }
    return (void*)objaddr;
}

void
mem_slab_free(struct SlabCache *cache, void *ptr)
{
    uintptr_t addr = (uintptr_t)ptr;
    //Look for the pointer in the bounds of every slab
    for(struct SlabDescriptor *slab = cache->list_full; 
            slab != NULL; slab = slab->next)
    {
        uintptr_t base = (uintptr_t)slab->obj_base;
        uintptr_t limit = ((uintptr_t)slab->slab_base) 
            + (cache->slab_pages * PAGESIZE);
        if(addr > limit || addr < base) continue;
        if((addr - base) % cache->obj_size != 0) {
            klogf("Tried to free offset pointer %#016X in slab %s\n",
                    addr, cache->name);
            return;
        }
        slab->free_index++;
        slab->free[slab->free_index] = addr;

        cache->list_full = slab->next;
        slab->next = cache->list_partial;
        cache->list_partial = slab;
        return;
    }
    for(struct SlabDescriptor *slab = cache->list_partial; 
            slab != NULL; slab = slab->next)
    {
        uintptr_t base = (uintptr_t)slab->obj_base;
        uintptr_t limit = ((uintptr_t)slab->slab_base) 
            + (cache->slab_pages * PAGESIZE);
        if(addr > limit || addr < base) continue;
        if((addr - base) % cache->obj_size != 0) {
            klogf("Tried to free offset pointer %#016X in slab %s\n",
                    addr, cache->name);
            return;
        }
        slab->free_index++;
        slab->free[slab->free_index] = addr;

        if(slab->free_index == (slab->free_count - 1)) {
            cache->list_partial = slab->next;
            slab->next = cache->list_free;
            cache->list_free = slab;
        }
        return;
    }
}

void*
mem_alloc(size_t width)
{
    size_t width_log2 = (__builtin_clz(width) ^ 31) + 1;
    if(width_log2 < 6) width_log2 = 6;
    width_log2 -= 6;
    if(width_log2 >= GENERIC_CACHEC) {
        klogf("Allocation size %i too big for generic caches!\n", width);
        return NULL;
    }

    struct SlabCache *generic_cache = &s_generic_caches[width_log2];
    return mem_slab_alloc(generic_cache);
}

void
mem_free(void *ptr)
{
    for(int i = 0; i < GENERIC_CACHEC; i++) {
        mem_slab_free(&s_generic_caches[i], ptr);
    }
}

void
mem_slab_setup(void)
{
    s_addr_next_free = (uintptr_t)&_kernel_end;
    s_addr_next_free = ((s_addr_next_free >> 12) + 1) << 12;
    s_get_free_listw(PAGESIZE - sizeof(struct SlabDescriptor), 32);

    for(int i = 0; i < GENERIC_CACHEC; i++)
    {
        size_t objsize = 1 << (i + 6);
        char slab_name[SLABCACHE_NAME_LIMIT];
        sfmt(slab_name, SLABCACHE_NAME_LIMIT, "generic_%i", 1 << (i + 6));
        mem_slabcache_new(&s_generic_caches[i], slab_name, objsize);
    }
}