#include "slab.h"
#include "bump.h"
#include "memory.h"
#include "string.h"
#include "jove.h"
#include "print.h"
#include "klib/format.h"
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 slab_t
*s_slab_new(slab_cache_t *cache, slab_t *last)
{
size_t slab_width = (cache->slab_pages << PAGE_SHIFT);
uintptr_t descr_base = (uintptr_t)bump_alloc(slab_width);
slab_t *descr = (slab_t*)descr_base;
size_t free_listc = s_get_free_listw(
slab_width - sizeof(slab_t),
cache->obj_size);
size_t descriptor_width = sizeof(slab_t)
+ (free_listc * sizeof(uintptr_t));
uintptr_t obj_base = descr_base + descriptor_width;
if(free_listc < 8) {
free_listc = ((slab_width - sizeof(slab_t)) / cache->obj_size);
descr = kmalloc(sizeof(slab_t) + (free_listc * sizeof(uintptr_t)));
obj_base = descr_base;
}
cache->obj_capacity += free_listc;
*descr = (slab_t) {
.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
slabcache_new(slab_cache_t *cache, char *name, size_t objsize)
{
if(objsize % 8 > 0) objsize += (8 - (objsize % 8));
size_t pages = objsize > 512 ? (objsize >> 9) : 1;
*cache = (slab_cache_t){
.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*
slab_alloc(slab_cache_t *cache)
{
// Get a free slab
slab_t *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
slab_free(slab_cache_t *cache, void *ptr)
{
uintptr_t addr = (uintptr_t)ptr;
//Look for the pointer in the bounds of every slab
for(slab_t *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 << PAGE_SHIFT);
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(slab_t *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 << PAGE_SHIFT);
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 == ((int)slab->free_count - 1)) {
cache->list_partial = slab->next;
slab->next = cache->list_free;
cache->list_free = slab;
}
return;
}
}