#include #include #include #include #include #include using namespace kernel::mem; using namespace types::list; constexpr std::size_t SLAB_PAGE_SIZE = 0x1000; // 4K kernel::async::mutex slab_lock; std::ptrdiff_t _slab_data_start_offset(std::size_t size) { return (sizeof(slab_head) + size - 1) & ~(size - 1); } std::size_t _slab_max_count(std::size_t size) { return (SLAB_PAGE_SIZE - _slab_data_start_offset(size)) / size; } void* _slab_head_alloc(slab_head* slab) { if (slab->free_count == 0) return nullptr; void* ptr = slab->free; slab->free = *(void**)ptr; slab->free_count--; return ptr; } slab_head* _make_slab(uintptr_t start, std::size_t size) { slab_head* slab = physaddr{start}; slab->obj_size = size; slab->free_count = _slab_max_count(size); slab->next = nullptr; slab->prev = nullptr; slab->free = physaddr{start + _slab_data_start_offset(size)}; std::byte* ptr = (std::byte*)slab->free; for (unsigned i = 0; i < slab->free_count; ++i) { void* nextptr = ptr + size; if (i == slab->free_count - 1) *(void**)ptr = nullptr; else *(void**)ptr = nextptr; ptr = (std::byte*)nextptr; } return slab; } void _slab_add_page(slab_cache* cache) { auto* new_page = paging::alloc_page(); auto new_page_pfn = paging::page_to_pfn(new_page); new_page->flags |= paging::PAGE_SLAB; auto* slab = _make_slab(new_page_pfn, cache->obj_size); slab->cache = cache; list_insert(&cache->slabs_empty, slab); } void* kernel::mem::slab_alloc(slab_cache* cache) { async::lock_guard_irq lock(slab_lock); slab_head* slab = cache->slabs_partial; if (!slab) { // no partial slabs, try to get an empty slab if (!cache->slabs_empty) // no empty slabs, create a new one _slab_add_page(cache); slab = list_get(&cache->slabs_empty); list_insert(&cache->slabs_partial, slab); } void* ptr = _slab_head_alloc(slab); if (slab->free_count == 0) { // slab is full list_remove(&cache->slabs_partial, slab); list_insert(&cache->slabs_full, slab); } return ptr; } void kernel::mem::slab_free(void* ptr) { async::lock_guard_irq lock(slab_lock); slab_head* slab = (slab_head*)((uintptr_t)ptr & ~(SLAB_PAGE_SIZE - 1)); *(void**)ptr = slab->free; slab->free = ptr; slab->free_count++; auto max_count = _slab_max_count(slab->obj_size); if (max_count == 1) { list_remove(&slab->cache->slabs_full, slab); list_insert(&slab->cache->slabs_empty, slab); } if (slab->free_count == 1) { list_remove(&slab->cache->slabs_full, slab); list_insert(&slab->cache->slabs_partial, slab); } if (slab->free_count == max_count) { list_remove(&slab->cache->slabs_partial, slab); list_insert(&slab->cache->slabs_empty, slab); } } void kernel::mem::init_slab_cache(slab_cache* cache, std::size_t obj_size) { cache->obj_size = obj_size; cache->slabs_empty = nullptr; cache->slabs_partial = nullptr; cache->slabs_full = nullptr; _slab_add_page(cache); }