From 626eec4a3197cc35981cd647ff037a7fd67a5a70 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Mon, 25 Feb 2019 21:19:43 -0800 Subject: [PATCH] Frame allocator class added --- modules.yaml | 2 + src/kernel/page_manager.h | 3 - src/libraries/kutil/frame_allocator.cpp | 156 ++++++++++++++++++ .../kutil/include/kutil/frame_allocator.h | 109 ++++++++++++ src/tests/frame_allocator.cpp | 53 ++++++ 5 files changed, 320 insertions(+), 3 deletions(-) create mode 100644 src/libraries/kutil/frame_allocator.cpp create mode 100644 src/libraries/kutil/include/kutil/frame_allocator.h create mode 100644 src/tests/frame_allocator.cpp diff --git a/modules.yaml b/modules.yaml index a985f3c..e322286 100644 --- a/modules.yaml +++ b/modules.yaml @@ -88,6 +88,7 @@ kutil: - src/libraries/kutil/include source: - src/libraries/kutil/assert.cpp + - src/libraries/kutil/frame_allocator.cpp - src/libraries/kutil/heap_manager.cpp - src/libraries/kutil/memory.cpp @@ -109,6 +110,7 @@ tests: - kutil source: - src/tests/address_manager.cpp + - src/tests/frame_allocator.cpp - src/tests/linked_list.cpp - src/tests/heap_manager.cpp - src/tests/main.cpp diff --git a/src/kernel/page_manager.h b/src/kernel/page_manager.h index c11ed55..d57174e 100644 --- a/src/kernel/page_manager.h +++ b/src/kernel/page_manager.h @@ -131,9 +131,6 @@ private: page_block_list used, page_block_list cache); - /// Initialize the virtual memory manager based on this object's state - void init_memory_manager(); - /// Create a `page_block` struct or pull one from the cache. /// \returns An empty `page_block` struct page_block * get_block(); diff --git a/src/libraries/kutil/frame_allocator.cpp b/src/libraries/kutil/frame_allocator.cpp new file mode 100644 index 0000000..9809394 --- /dev/null +++ b/src/libraries/kutil/frame_allocator.cpp @@ -0,0 +1,156 @@ +#include + +#include "kutil/frame_allocator.h" + +namespace kutil { + +int +frame_block::compare(const frame_block *rhs) const +{ + if (address < rhs->address) + return -1; + else if (address > rhs->address) + return 1; + + return 0; +} + +frame_block_list +frame_block::consolidate(frame_block_list &list) +{ + frame_block_list freed; + + for (auto *cur : list) { + auto *next = cur->next(); + + while ( next && + cur->flags == next->flags && + cur->end() == next->address) { + cur->count += next->count; + list.remove(next); + freed.push_back(next); + } + } + + return freed; +} + +void +frame_block::zero() +{ + address = 0; + count = 0; + flags = frame_block_flags::none; +} + +void +frame_block::copy(frame_block *other) +{ + address = other->address; + count = other->count; + flags = other->flags; +} + + +frame_allocator::frame_allocator( + frame_block_list free, + frame_block_list used, + frame_block_list cache) +{ + m_free.append(free); + m_used.append(used); + m_block_slab.append(cache); + + consolidate_blocks(); +} + +void +frame_allocator::consolidate_blocks() +{ + m_block_slab.append(frame_block::consolidate(m_free)); + m_block_slab.append(frame_block::consolidate(m_used)); +} + +size_t +frame_allocator::allocate(size_t count, uintptr_t *address) +{ + kassert(!m_free.empty(), "frame_allocator::pop_frames ran out of free frames!"); + + auto *first = m_free.front(); + + unsigned n = std::min(count, static_cast(first->count)); + *address = first->address; + + if (count >= first->count) { + m_free.remove(first); + m_used.sorted_insert(first); + } else { + auto *used = m_block_slab.pop(); + used->copy(first); + used->count = n; + m_used.sorted_insert(used); + + first->address += n * frame_size; + first->count -= n; + } + + m_block_slab.append(frame_block::consolidate(m_used)); + return n; +} + +void +frame_allocator::free(uintptr_t address, size_t count) +{ + size_t block_count = 0; + + for (auto *block : m_used) { + if (!block->contains(address)) continue; + + size_t size = frame_size * count; + uintptr_t end = address + size; + + size_t leading = address - block->address; + size_t trailing = + end > block->end() ? + 0 : (block->end() - end); + + if (leading) { + size_t frames = leading / frame_size; + + auto *lead_block = m_block_slab.pop(); + + lead_block->copy(block); + lead_block->count = frames; + + block->count -= frames; + block->address += leading; + + m_used.insert_before(block, lead_block); + } + + if (trailing) { + size_t frames = trailing / frame_size; + + auto *trail_block = m_block_slab.pop(); + + trail_block->copy(block); + trail_block->count = frames; + trail_block->address += size; + trail_block->address += size; + + block->count -= frames; + + m_used.insert_after(block, trail_block); + } + + address += block->count * frame_size; + + m_used.remove(block); + m_free.sorted_insert(block); + ++block_count; + } + + kassert(block_count, "Couldn't find existing allocated frames to free"); +} + +} // namespace kutil diff --git a/src/libraries/kutil/include/kutil/frame_allocator.h b/src/libraries/kutil/include/kutil/frame_allocator.h new file mode 100644 index 0000000..2c23895 --- /dev/null +++ b/src/libraries/kutil/include/kutil/frame_allocator.h @@ -0,0 +1,109 @@ +#pragma once +/// \file frame_allocator.h +/// Allocator for physical memory frames + +#include + +#include "kutil/enum_bitfields.h" +#include "kutil/linked_list.h" +#include "kutil/slab_allocator.h" + +namespace kutil { + +struct frame_block; +using frame_block_list = kutil::linked_list; +using frame_block_slab = kutil::slab_allocator; + +/// Allocator for physical memory frames +class frame_allocator +{ +public: + /// Size of a single page frame. + static const size_t frame_size = 0x1000; + + /// Constructor. Sets up the frame allocator from bootstraped memory. + /// \arg free List of free blocks + /// \arg used List of currently used blocks + /// \arg cache List of pre-allocated but unused frame_block structures + frame_allocator( + frame_block_list free, + frame_block_list used, + frame_block_list cache); + + /// Get free frames from the free list. Only frames from the first free block + /// are returned, so the number may be less than requested, but they will + /// be contiguous. + /// \arg count The maximum number of frames to get + /// \arg address [out] The physical address of the first frame + /// \returns The number of frames retrieved + size_t allocate(size_t count, uintptr_t *address); + + /// Free previously allocated frames. + /// \arg address The physical address of the first frame to free + /// \arg count The number of frames to be freed + void free(uintptr_t address, size_t count); + +private: + /// Consolidate the free and used block lists. Return freed blocks + /// to the cache. + void consolidate_blocks(); + + frame_block_list m_free; ///< Free frames list + frame_block_list m_used; ///< In-use frames list + frame_block_slab m_block_slab; ///< frame_block slab allocator + + frame_allocator(const frame_allocator &) = delete; +}; + + +/// Flags used by `frame_block`. +enum class frame_block_flags : uint32_t +{ + none = 0x00, + + mmio = 0x01, ///< Memory is a MMIO region + nonvolatile = 0x02, ///< Memory is non-volatile storage + + pending_free = 0x10, ///< Memory should be freed + acpi_wait = 0x40, ///< Memory should be freed after ACPI init + permanent = 0x80 ///< Memory is permanently unusable +}; + +} // namespace kutil + +IS_BITFIELD(kutil::frame_block_flags); + +namespace kutil { + +/// A block of contiguous frames. Each `frame_block` represents contiguous +/// physical frames with the same attributes. +struct frame_block +{ + uintptr_t address; + uint32_t count; + frame_block_flags flags; + + inline bool has_flag(frame_block_flags f) const { return bitfield_has(flags, f); } + inline uintptr_t end() const { return address + (count * frame_allocator::frame_size); } + inline bool contains(uintptr_t addr) const { return addr >= address && addr < end(); } + + /// Helper to zero out a block and optionally set the next pointer. + void zero(); + + /// Helper to copy a bock from another block + /// \arg other The block to copy from + void copy(frame_block *other); + + /// Compare two blocks by address. + /// \arg rhs The right-hand comparator + /// \returns <0 if this is sorts earlier, >0 if this sorts later, 0 for equal + int compare(const frame_block *rhs) const; + + /// Traverse the list, joining adjacent blocks where possible. + /// \arg list The list to consolidate + /// \returns A linked list of freed frame_block structures. + static frame_block_list consolidate(frame_block_list &list); +}; + +} // namespace kutil + diff --git a/src/tests/frame_allocator.cpp b/src/tests/frame_allocator.cpp new file mode 100644 index 0000000..5a56fdc --- /dev/null +++ b/src/tests/frame_allocator.cpp @@ -0,0 +1,53 @@ +#include "kutil/frame_allocator.h" +#include "kutil/heap_manager.h" +#include "kutil/memory.h" +#include "catch.hpp" + +using namespace kutil; + +extern void * grow_callback(void*, size_t); +extern void free_memory(); + +const size_t max_block = 1ull << 36; +const size_t start = max_block; +const size_t GB = 1ull << 30; + +TEST_CASE( "Frame allocator tests", "[memory frame]" ) +{ + heap_manager mm(nullptr, grow_callback); + kutil::setup::set_heap(&mm); + + frame_block_list free; + frame_block_list used; + frame_block_list cache; + + auto *f = new frame_block_list::item_type; + f->address = 0x1000; + f->count = 1; + free.sorted_insert(f); + + f = new frame_block_list::item_type; + f->address = 0x2000; + f->count = 1; + free.sorted_insert(f); + + frame_allocator fa( + std::move(free), + std::move(used), + std::move(cache)); + + uintptr_t a = 0; + size_t c = fa.allocate(2, &a); + CHECK( a == 0x1000 ); + CHECK( c == 2 ); + + fa.free(a, 2); + a = 0; + + c = fa.allocate(2, &a); + CHECK( a == 0x1000 ); + CHECK( c == 2 ); + + kutil::setup::set_heap(nullptr); + free_memory(); +}