First pass at vm_space (address_manager replacement)
This commit is contained in:
172
src/libraries/kutil/include/kutil/avl_tree.h
Normal file
172
src/libraries/kutil/include/kutil/avl_tree.h
Normal file
@@ -0,0 +1,172 @@
|
||||
#pragma once
|
||||
/// \file avl_tree.h
|
||||
/// Templated container class for an AVL tree
|
||||
|
||||
#include <stdint.h>
|
||||
#include "kutil/assert.h"
|
||||
|
||||
namespace kutil {
|
||||
|
||||
template <typename T> class avl_tree;
|
||||
|
||||
|
||||
/// A node in a `avl_tree<T>`
|
||||
template <typename T>
|
||||
class avl_node :
|
||||
public T
|
||||
{
|
||||
public:
|
||||
using item_type = T;
|
||||
using node_type = avl_node<T>;
|
||||
|
||||
/// Dereference operator. Helper to cast this node to the contained type.
|
||||
/// \returns A pointer to the node, cast to T*.
|
||||
inline item_type & operator*() { return *this; }
|
||||
|
||||
/// Dereference operator. Helper to cast this node to the contained type.
|
||||
/// \returns A pointer to the node, cast to T*.
|
||||
inline const item_type & operator*() const { return *this; }
|
||||
|
||||
/// Cast operator. Helper to cast this node to the contained type.
|
||||
/// \returns A reference to the node, cast to T&.
|
||||
inline operator item_type& () { return *this; }
|
||||
|
||||
/// Cast operator. Helper to cast this node to the contained type.
|
||||
/// \returns A reference to the node, cast to const T&.
|
||||
inline operator const item_type& () { return *this; }
|
||||
|
||||
/// Helper to cast this node to the contained type.
|
||||
/// \returns A reference to the node, cast to const T&.
|
||||
inline const item_type& item() const { return *this; }
|
||||
|
||||
/// Helper to cast this node to the contained type.
|
||||
/// \returns A reference to the node, cast to T&.
|
||||
inline item_type& item() { return *this; }
|
||||
|
||||
/// Accessor for the left child.
|
||||
/// \returns A pointer to the left child, or nullptr.
|
||||
inline node_type * left() { return m_left; }
|
||||
|
||||
/// Accessor for the left child.
|
||||
/// \returns A pointer to the left child, or nullptr.
|
||||
inline const node_type * left() const { return m_left; }
|
||||
|
||||
/// Accessor for the right child.
|
||||
/// \returns A pointer to the right child, or nullptr.
|
||||
inline node_type * right() { return m_right; }
|
||||
|
||||
/// Accessor for the right child.
|
||||
/// \returns A pointer to the right child, or nullptr.
|
||||
inline const node_type * right() const { return m_right; }
|
||||
|
||||
private:
|
||||
friend class avl_tree<T>;
|
||||
|
||||
inline static int height(node_type *l) { return (l ? l->m_height : 0); }
|
||||
|
||||
// Update this node's height and return its new balance factor
|
||||
inline int update_height()
|
||||
{
|
||||
int left = height(m_left);
|
||||
int right = height(m_right);
|
||||
m_height = std::max(left, right) + 1;
|
||||
return left - right;
|
||||
}
|
||||
|
||||
int bias(node_type *addend)
|
||||
{
|
||||
const item_type &this_item = *this;
|
||||
const item_type &that_item = *addend;
|
||||
if (that_item < this_item)
|
||||
return -1;
|
||||
else if (that_item > this_item)
|
||||
return 1;
|
||||
|
||||
kassert(false, "Equal items not allowed in AVL tree");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static node_type * rotate_right(node_type *existing)
|
||||
{
|
||||
node_type *root = existing->m_left;
|
||||
node_type *left = root->m_right;
|
||||
|
||||
root->m_right = existing;
|
||||
existing->m_left = left;
|
||||
|
||||
existing->update_height();
|
||||
root->update_height();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static node_type * rotate_left(node_type *existing)
|
||||
{
|
||||
node_type *root = existing->m_right;
|
||||
node_type *right = root->m_left;
|
||||
|
||||
root->m_left = existing;
|
||||
existing->m_right = right;
|
||||
|
||||
existing->update_height();
|
||||
root->update_height();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static node_type * insert(node_type *existing, node_type *addend)
|
||||
{
|
||||
if (existing == nullptr)
|
||||
return addend;
|
||||
|
||||
if (existing->compare(addend) < 0)
|
||||
existing->m_left = insert(existing->m_left, addend);
|
||||
else
|
||||
existing->m_right = insert(existing->m_right, addend);
|
||||
|
||||
int balance = existing->update_height();
|
||||
if (balance > 1) {
|
||||
// Left-heavy
|
||||
if (existing->m_left->compare(addend) < 0) {
|
||||
// Left Left
|
||||
return rotate_right(existing);
|
||||
} else {
|
||||
// Left Right
|
||||
existing->m_left = rotate_left(existing->m_left);
|
||||
return rotate_right(existing);
|
||||
}
|
||||
} else if (balance < -1) {
|
||||
// Right-heavy
|
||||
if (existing->m_right->compare(addend) > 0) {
|
||||
// Right Right
|
||||
return rotate_left(existing);
|
||||
} else {
|
||||
// Right Left
|
||||
existing->m_right = rotate_right(existing->m_right);
|
||||
return rotate_left(existing);
|
||||
}
|
||||
}
|
||||
|
||||
return existing;
|
||||
}
|
||||
|
||||
int m_height;
|
||||
node_type *m_left;
|
||||
node_type *m_right;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class avl_tree
|
||||
{
|
||||
public:
|
||||
using item_type = T;
|
||||
using node_type = avl_node<T>;
|
||||
|
||||
inline void insert(node_type *addend) { m_root = node_type::insert(m_root, addend); }
|
||||
inline node_type * root() { return m_root; }
|
||||
|
||||
private:
|
||||
node_type *m_root;
|
||||
};
|
||||
|
||||
} // namespace kutil
|
||||
74
src/libraries/kutil/include/kutil/vm_space.h
Normal file
74
src/libraries/kutil/include/kutil/vm_space.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
/// \file vm_range.h
|
||||
/// Structure for tracking a range of virtual memory addresses
|
||||
|
||||
#include <stdint.h>
|
||||
#include "kutil/allocator.h"
|
||||
#include "kutil/avl_tree.h"
|
||||
#include "kutil/slab_allocator.h"
|
||||
|
||||
namespace kutil {
|
||||
|
||||
enum class vm_state : uint8_t {
|
||||
unknown,
|
||||
none,
|
||||
reserved,
|
||||
committed,
|
||||
mapped
|
||||
};
|
||||
|
||||
struct vm_range
|
||||
{
|
||||
uintptr_t address;
|
||||
size_t size;
|
||||
vm_state state;
|
||||
|
||||
inline uintptr_t end() const { return address + size; }
|
||||
inline int compare(const vm_range *other) const {
|
||||
return other->address - address;
|
||||
}
|
||||
};
|
||||
|
||||
class vm_space
|
||||
{
|
||||
public:
|
||||
vm_space(uintptr_t start, size_t size, kutil::allocator &alloc);
|
||||
|
||||
/// Reserve a section of address space.
|
||||
/// \arg start Starting address of reservaion, or 0 for any address
|
||||
/// \arg size Size of reservation in bytes
|
||||
/// \returns The address of the reservation, or 0 on failure
|
||||
uintptr_t reserve(uintptr_t start, size_t size);
|
||||
|
||||
/// Unreserve (and uncommit, if committed) a section of address space.
|
||||
/// \arg start Starting address of reservaion
|
||||
/// \arg size Size of reservation in bytes
|
||||
void unreserve(uintptr_t start, size_t size);
|
||||
|
||||
/// Mark a section of address space as committed.
|
||||
/// \arg start Starting address of reservaion
|
||||
/// \arg size Size of reservation in bytes
|
||||
/// \returns The address of the reservation, or 0 on failure
|
||||
uintptr_t commit(uintptr_t start, size_t size);
|
||||
|
||||
/// Mark a section of address space as uncommitted, but still reserved.
|
||||
/// \arg start Starting address of reservaion
|
||||
/// \arg size Size of reservation in bytes
|
||||
void uncommit(uintptr_t start, size_t size);
|
||||
|
||||
/// Check the state of the given address.
|
||||
/// \arg addr The address to check
|
||||
/// \returns The state of the memory if known, or 'unknown'
|
||||
vm_state get(uintptr_t addr);
|
||||
|
||||
private:
|
||||
using node_type = kutil::avl_node<vm_range>;
|
||||
using tree_type = kutil::avl_tree<vm_range>;
|
||||
|
||||
node_type * split_out(node_type* node, uintptr_t start, size_t size, vm_state state);
|
||||
|
||||
slab_allocator<node_type> m_alloc;
|
||||
tree_type m_ranges;
|
||||
};
|
||||
|
||||
} // namespace kutil
|
||||
136
src/libraries/kutil/vm_space.cpp
Normal file
136
src/libraries/kutil/vm_space.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <algorithm>
|
||||
#include "kutil/logger.h"
|
||||
#include "kutil/vm_space.h"
|
||||
|
||||
namespace kutil {
|
||||
|
||||
using node_type = kutil::avl_node<vm_range>;
|
||||
|
||||
|
||||
vm_space::vm_space(uintptr_t start, size_t size, allocator &alloc) :
|
||||
m_alloc(alloc)
|
||||
{
|
||||
node_type *node = m_alloc.pop();
|
||||
node->address = start;
|
||||
node->size = size;
|
||||
node->state = vm_state::none;
|
||||
m_ranges.insert(node);
|
||||
|
||||
log::info(logs::vmem, "Creating address space from %016llx-%016llx",
|
||||
start, start+size);
|
||||
}
|
||||
|
||||
inline static bool
|
||||
overlaps(node_type *node, uintptr_t start, size_t size)
|
||||
{
|
||||
return start <= node->end() &&
|
||||
(start + size) >= node->address;
|
||||
}
|
||||
|
||||
static node_type *
|
||||
find_overlapping(node_type *from, uintptr_t start, size_t size)
|
||||
{
|
||||
while (from) {
|
||||
if (overlaps(from, start, size))
|
||||
return from;
|
||||
|
||||
from = start < from->address ?
|
||||
from->left() :
|
||||
from->right();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node_type *
|
||||
vm_space::split_out(node_type *node, uintptr_t start, size_t size, vm_state state)
|
||||
{
|
||||
// No cross-boundary splits allowed for now
|
||||
bool contained =
|
||||
start >= node->address &&
|
||||
start+size <= node->end();
|
||||
|
||||
kassert(contained,
|
||||
"Tried to split an address range across existing boundaries");
|
||||
if (!contained)
|
||||
return nullptr;
|
||||
|
||||
vm_state old_state = node->state;
|
||||
if (state == old_state)
|
||||
return node;
|
||||
|
||||
log::debug(logs::vmem, "Splitting out region %016llx-%016llx[%d] from %016llx-%016llx[%d]",
|
||||
start, start+size, state, node->address, node->end(), old_state);
|
||||
|
||||
if (node->address < start) {
|
||||
// Split off rest into new node
|
||||
size_t leading = start - node->address;
|
||||
|
||||
node_type *next = m_alloc.pop();
|
||||
next->state = state;
|
||||
next->address = start;
|
||||
next->size = node->size - leading;
|
||||
node->size = leading;
|
||||
|
||||
m_ranges.insert(next);
|
||||
node = next;
|
||||
} else {
|
||||
// TODO: check if this can merge with prev node
|
||||
}
|
||||
|
||||
if (node->end() > start + size) {
|
||||
// Split off remaining into new node
|
||||
size_t trailing = node->size - size;
|
||||
node->size -= trailing;
|
||||
|
||||
node_type *next = m_alloc.pop();
|
||||
next->state = old_state;
|
||||
next->address = node->end();
|
||||
next->size = trailing;
|
||||
|
||||
m_ranges.insert(next);
|
||||
} else {
|
||||
// TODO: check if this can merge with next node
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
vm_space::reserve(uintptr_t start, size_t size)
|
||||
{
|
||||
log::debug(logs::vmem, "Reserving region %016llx-%016llx", start, start+size);
|
||||
|
||||
node_type *node = find_overlapping(m_ranges.root(), start, size);
|
||||
if (!node || node->state != vm_state::none)
|
||||
return 0;
|
||||
|
||||
node = split_out(node, start, size, vm_state::reserved);
|
||||
return node ? start : 0;
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::unreserve(uintptr_t start, size_t size)
|
||||
{
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
vm_space::commit(uintptr_t start, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::uncommit(uintptr_t start, size_t size)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
vm_state
|
||||
vm_space::get(uintptr_t addr)
|
||||
{
|
||||
node_type *node = find_overlapping(m_ranges.root(), addr, 0);
|
||||
return node ? node->state : vm_state::unknown;
|
||||
}
|
||||
|
||||
} // namespace kutil
|
||||
Reference in New Issue
Block a user