mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
Frame allocator class added
This commit is contained in:
@@ -88,6 +88,7 @@ kutil:
|
|||||||
- src/libraries/kutil/include
|
- src/libraries/kutil/include
|
||||||
source:
|
source:
|
||||||
- src/libraries/kutil/assert.cpp
|
- src/libraries/kutil/assert.cpp
|
||||||
|
- src/libraries/kutil/frame_allocator.cpp
|
||||||
- src/libraries/kutil/heap_manager.cpp
|
- src/libraries/kutil/heap_manager.cpp
|
||||||
- src/libraries/kutil/memory.cpp
|
- src/libraries/kutil/memory.cpp
|
||||||
|
|
||||||
@@ -109,6 +110,7 @@ tests:
|
|||||||
- kutil
|
- kutil
|
||||||
source:
|
source:
|
||||||
- src/tests/address_manager.cpp
|
- src/tests/address_manager.cpp
|
||||||
|
- src/tests/frame_allocator.cpp
|
||||||
- src/tests/linked_list.cpp
|
- src/tests/linked_list.cpp
|
||||||
- src/tests/heap_manager.cpp
|
- src/tests/heap_manager.cpp
|
||||||
- src/tests/main.cpp
|
- src/tests/main.cpp
|
||||||
|
|||||||
@@ -131,9 +131,6 @@ private:
|
|||||||
page_block_list used,
|
page_block_list used,
|
||||||
page_block_list cache);
|
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.
|
/// Create a `page_block` struct or pull one from the cache.
|
||||||
/// \returns An empty `page_block` struct
|
/// \returns An empty `page_block` struct
|
||||||
page_block * get_block();
|
page_block * get_block();
|
||||||
|
|||||||
156
src/libraries/kutil/frame_allocator.cpp
Normal file
156
src/libraries/kutil/frame_allocator.cpp
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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<size_t>(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
|
||||||
109
src/libraries/kutil/include/kutil/frame_allocator.h
Normal file
109
src/libraries/kutil/include/kutil/frame_allocator.h
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file frame_allocator.h
|
||||||
|
/// Allocator for physical memory frames
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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<frame_block>;
|
||||||
|
using frame_block_slab = kutil::slab_allocator<frame_block>;
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
|
||||||
53
src/tests/frame_allocator.cpp
Normal file
53
src/tests/frame_allocator.cpp
Normal file
@@ -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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user