diff options
author | Tom <tomut@yahoo.com> | 2020-08-24 21:32:55 -0600 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-08-25 09:48:48 +0200 |
commit | c2b9f8857c7b9189dab5eca8be06ecc8811968eb (patch) | |
tree | 0135b0d1974622a314e74095557a05719dfb2d4b /Kernel | |
parent | 81780e607da83c60e060288ccdbfb939540d84d3 (diff) | |
download | serenity-c2b9f8857c7b9189dab5eca8be06ecc8811968eb.zip |
Kernel: Optimize SlabAllocator to be lock-free
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/Heap/SlabAllocator.cpp | 69 |
1 files changed, 43 insertions, 26 deletions
diff --git a/Kernel/Heap/SlabAllocator.cpp b/Kernel/Heap/SlabAllocator.cpp index 76eafa432a..b14785a45a 100644 --- a/Kernel/Heap/SlabAllocator.cpp +++ b/Kernel/Heap/SlabAllocator.cpp @@ -45,67 +45,82 @@ public: m_base = kmalloc_eternal(size); m_end = (u8*)m_base + size; FreeSlab* slabs = (FreeSlab*)m_base; - size_t slab_count = size / templated_slab_size; - for (size_t i = 1; i < slab_count; ++i) { + m_slab_count = size / templated_slab_size; + for (size_t i = 1; i < m_slab_count; ++i) { slabs[i].next = &slabs[i - 1]; } slabs[0].next = nullptr; - m_freelist = &slabs[slab_count - 1]; + m_freelist = &slabs[m_slab_count - 1]; m_num_allocated.store(0, AK::MemoryOrder::memory_order_release); - m_num_free.store(slab_count, AK::MemoryOrder::memory_order_release); } constexpr size_t slab_size() const { return templated_slab_size; } + size_t slab_count() const { return m_slab_count; } void* alloc() { - ScopedSpinLock lock(m_lock); - if (!m_freelist) - return kmalloc(slab_size()); - ASSERT(m_freelist); - void* ptr = m_freelist; - m_freelist = m_freelist->next; - m_num_allocated.fetch_add(1, AK::MemoryOrder::memory_order_acq_rel); - m_num_free.fetch_sub(1, AK::MemoryOrder::memory_order_acq_rel); + FreeSlab* free_slab; + { + // We want to avoid being swapped out in the middle of this + ScopedCritical critical; + FreeSlab* next_free; + free_slab = m_freelist.load(AK::memory_order_consume); + do { + if (!free_slab) + return kmalloc(slab_size()); + // It's possible another processor is doing the same thing at + // the same time, so next_free *can* be a bogus pointer. However, + // in that case compare_exchange_strong would fail and we would + // try again. + next_free = free_slab->next; + } while (!m_freelist.compare_exchange_strong(free_slab, next_free, AK::memory_order_acq_rel)); + + m_num_allocated.fetch_add(1, AK::MemoryOrder::memory_order_acq_rel); + } + #ifdef SANITIZE_SLABS - memset(ptr, SLAB_ALLOC_SCRUB_BYTE, slab_size()); + memset(free_slab, SLAB_ALLOC_SCRUB_BYTE, slab_size()); #endif - return ptr; + return free_slab; } void dealloc(void* ptr) { - ScopedSpinLock lock(m_lock); ASSERT(ptr); if (ptr < m_base || ptr >= m_end) { kfree(ptr); return; } - ((FreeSlab*)ptr)->next = m_freelist; + FreeSlab* free_slab = (FreeSlab*)ptr; #ifdef SANITIZE_SLABS if (slab_size() > sizeof(FreeSlab*)) - memset(((FreeSlab*)ptr)->padding, SLAB_DEALLOC_SCRUB_BYTE, sizeof(FreeSlab::padding)); + memset(free_slab->padding, SLAB_DEALLOC_SCRUB_BYTE, sizeof(FreeSlab::padding)); #endif - m_freelist = (FreeSlab*)ptr; + + // We want to avoid being swapped out in the middle of this + ScopedCritical critical; + FreeSlab* next_free = m_freelist.load(AK::memory_order_consume); + do { + free_slab->next = next_free; + } while (!m_freelist.compare_exchange_strong(next_free, free_slab, AK::memory_order_acq_rel)); + m_num_allocated.fetch_sub(1, AK::MemoryOrder::memory_order_acq_rel); - m_num_free.fetch_add(1, AK::MemoryOrder::memory_order_acq_rel); } size_t num_allocated() const { return m_num_allocated.load(AK::MemoryOrder::memory_order_consume); } - size_t num_free() const { return m_num_free.load(AK::MemoryOrder::memory_order_consume); } + size_t num_free() const { return m_slab_count - m_num_allocated.load(AK::MemoryOrder::memory_order_consume); } private: struct FreeSlab { - FreeSlab* next { nullptr }; + FreeSlab* next; char padding[templated_slab_size - sizeof(FreeSlab*)]; }; - FreeSlab* m_freelist { nullptr }; - Atomic<size_t> m_num_allocated; - Atomic<size_t> m_num_free; + Atomic<FreeSlab*> m_freelist { nullptr }; + Atomic<ssize_t> m_num_allocated; + size_t m_slab_count; void* m_base { nullptr }; void* m_end { nullptr }; - SpinLock<u32> m_lock; static_assert(sizeof(FreeSlab) == templated_slab_size); }; @@ -163,7 +178,9 @@ void slab_dealloc(void* ptr, size_t slab_size) void slab_alloc_stats(Function<void(size_t slab_size, size_t allocated, size_t free)> callback) { for_each_allocator([&](auto& allocator) { - callback(allocator.slab_size(), allocator.num_allocated(), allocator.num_free()); + auto num_allocated = allocator.num_allocated(); + auto num_free = allocator.slab_count() - num_allocated; + callback(allocator.slab_size(), num_allocated, num_free); }); } |