144 lines
3.6 KiB
C
144 lines
3.6 KiB
C
#include <zinnia/alloc.h>
|
|
#include <zinnia/compiler.h>
|
|
#include <zinnia/errno.h>
|
|
#include <zinnia/spin.h>
|
|
#include <string.h>
|
|
|
|
typedef struct {
|
|
size_t num_pages; // Amount of pages connected to this slab.
|
|
size_t size; // Size of this slab.
|
|
} slab_info;
|
|
|
|
struct slab {
|
|
spinlock_t lock; // Access lock.
|
|
size_t ent_size; // Size of one entry.
|
|
void** head;
|
|
};
|
|
|
|
typedef struct {
|
|
struct slab* slab;
|
|
} slab_header;
|
|
|
|
// Initializes the SLAB structures.
|
|
|
|
static struct slab slabs[8] = {0};
|
|
|
|
static errno_t slab_new(struct slab* slab, size_t size) {
|
|
slab->lock = (spinlock_t){0};
|
|
|
|
// Allocate a new page for the head.
|
|
phys_t addr;
|
|
errno_t status = mem_phys_alloc(1, 0, &addr);
|
|
if (status)
|
|
return status;
|
|
|
|
slab->head = (void**)(addr + arch_mem_hhdm_addr());
|
|
slab->ent_size = size;
|
|
|
|
const size_t offset = ALIGN_UP(sizeof(slab_header), size);
|
|
const size_t available_size = arch_mem_page_size() - offset;
|
|
|
|
slab_header* ptr = (slab_header*)slab->head;
|
|
ptr->slab = slab;
|
|
slab->head = (void**)((void*)slab->head + offset);
|
|
|
|
void** arr = slab->head;
|
|
const size_t max = available_size / size - 1;
|
|
const size_t fact = size / sizeof(void*);
|
|
|
|
for (size_t i = 0; i < max; i++) {
|
|
arr[i * fact] = &arr[(i + 1) * fact];
|
|
}
|
|
arr[max * fact] = nullptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void slab_init(void) {
|
|
slab_new(&slabs[0], 16);
|
|
slab_new(&slabs[1], 32);
|
|
slab_new(&slabs[2], 64);
|
|
slab_new(&slabs[3], 128);
|
|
slab_new(&slabs[4], 256);
|
|
slab_new(&slabs[5], 512);
|
|
slab_new(&slabs[6], 1024);
|
|
slab_new(&slabs[7], 2048);
|
|
}
|
|
|
|
static void* slab_do_alloc(struct slab* slab) {
|
|
spin_lock(&slab->lock);
|
|
|
|
if (__unlikely(slab->head == nullptr))
|
|
slab_new(slab, slab->ent_size);
|
|
|
|
void** old_free = slab->head;
|
|
slab->head = *old_free;
|
|
memset(old_free, 0, slab->ent_size);
|
|
|
|
spin_unlock(&slab->lock);
|
|
return old_free;
|
|
}
|
|
|
|
static void slab_do_free(struct slab* slab, void* addr) {
|
|
spin_lock(&slab->lock);
|
|
|
|
if (__unlikely(addr == nullptr))
|
|
goto cleanup;
|
|
|
|
void** new_head = addr;
|
|
*new_head = slab->head;
|
|
slab->head = new_head;
|
|
|
|
cleanup:
|
|
spin_unlock(&slab->lock);
|
|
}
|
|
|
|
static inline struct slab* slab_find_size(size_t size) {
|
|
for (size_t i = 0; i < ARRAY_SIZE(slabs); i++) {
|
|
if (slabs[i].ent_size >= size)
|
|
return &slabs[i];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void* mem_alloc(size_t size, enum alloc_flags flags) {
|
|
if (__unlikely(size == 0))
|
|
return nullptr;
|
|
|
|
struct slab* slab = slab_find_size(size);
|
|
if (slab != nullptr) {
|
|
return slab_do_alloc(slab);
|
|
}
|
|
|
|
size_t num_pages = ROUND_UP(size, arch_mem_page_size());
|
|
|
|
// Allocate the pages plus an additional page for metadata.
|
|
phys_t ret;
|
|
errno_t status = mem_phys_alloc(num_pages + 1, 0, &ret);
|
|
if (__unlikely(status != 0))
|
|
return nullptr;
|
|
|
|
ret = ret + (phys_t)arch_mem_hhdm_addr();
|
|
// Write metadata into the first page.
|
|
slab_info* info = (slab_info*)ret;
|
|
info->num_pages = num_pages;
|
|
info->size = size;
|
|
// Skip the first page and return the next one.
|
|
return (void*)(ret + arch_mem_page_size());
|
|
}
|
|
|
|
void mem_free(void* addr) {
|
|
if (__unlikely(addr == nullptr))
|
|
return;
|
|
|
|
// If the address is page aligned.
|
|
if ((size_t)addr == ALIGN_DOWN((size_t)addr, arch_mem_page_size())) {
|
|
slab_info* info = (slab_info*)(addr - arch_mem_page_size());
|
|
mem_phys_free(((phys_t)info - (phys_t)arch_mem_hhdm_addr()), info->num_pages + 1);
|
|
return;
|
|
}
|
|
|
|
slab_header* header = (slab_header*)(ALIGN_DOWN((size_t)addr, arch_mem_page_size()));
|
|
slab_do_free(header->slab, addr);
|
|
}
|