[util] Abstract out radix_tree class from page_tree
Generalized the radix tree code from page_tree as util::radix_tree so that it can be used elsewhere.
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
#include "frame_allocator.h"
|
||||
#include "memory.h"
|
||||
#include "objects/vm_area.h"
|
||||
#include "page_tree.h"
|
||||
#include "vm_space.h"
|
||||
|
||||
namespace obj {
|
||||
@@ -146,7 +145,7 @@ vm_area_open::get_page(uintptr_t offset, uintptr_t &phys, bool alloc)
|
||||
if (alloc)
|
||||
return page_tree::find_or_add(m_mapped, offset, phys);
|
||||
else
|
||||
return page_tree::find(m_mapped, offset, phys);
|
||||
return page_tree::find(m_mapped, offset, &phys);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -11,11 +11,10 @@
|
||||
|
||||
#include "block_allocator.h"
|
||||
#include "objects/kobject.h"
|
||||
#include "page_tree.h"
|
||||
|
||||
class page_tree;
|
||||
class vm_space;
|
||||
|
||||
|
||||
namespace obj {
|
||||
|
||||
enum class vm_flags : uint32_t
|
||||
|
||||
@@ -22,129 +22,14 @@
|
||||
// Level 6: 003f 0000 0000 0xxx 4T pages / 16 PiB -- 54-bit addressing
|
||||
// Level 7: 0fc0 0000 0000 0xxx 256T pages / 1 EiB -- 60-bit addressing
|
||||
|
||||
static_assert(sizeof(page_tree) == 66 * sizeof(uintptr_t));
|
||||
|
||||
static constexpr unsigned max_level = 5;
|
||||
static constexpr unsigned bits_per_level = 6;
|
||||
|
||||
inline int level_shift(uint8_t level) { return level * bits_per_level + arch::frame_bits; }
|
||||
inline uint64_t level_mask(uint8_t level) { return ~0x3full << level_shift(level); }
|
||||
inline int index_for(uint64_t off, uint8_t level) { return (off >> level_shift(level)) & 0x3full; }
|
||||
|
||||
page_tree::page_tree(uint64_t base, uint8_t level) :
|
||||
m_base {base & level_mask(level)},
|
||||
m_level {level}
|
||||
{
|
||||
memset(m_entries, 0, sizeof(m_entries));
|
||||
}
|
||||
|
||||
page_tree::~page_tree()
|
||||
{
|
||||
if (m_level) {
|
||||
for (auto &e : m_entries)
|
||||
delete e.child;
|
||||
} else {
|
||||
auto &fa = frame_allocator::get();
|
||||
for (auto &e : m_entries) {
|
||||
if (e.entry & 1)
|
||||
fa.free(e.entry & ~0xfffull, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
page_tree::contains(uint64_t offset, uint8_t &index) const
|
||||
{
|
||||
return (offset & level_mask(m_level)) == m_base;
|
||||
}
|
||||
|
||||
uintptr_t &
|
||||
page_tree::get_entry(page_tree * &root, uint64_t offset)
|
||||
{
|
||||
page_tree *level0 = nullptr;
|
||||
|
||||
if (!root) {
|
||||
// There's no root yet, just make a level0 and make it
|
||||
// the root.
|
||||
level0 = new page_tree(offset, 0);
|
||||
root = level0;
|
||||
} else {
|
||||
// Find or insert an existing level0
|
||||
page_tree **parent = &root;
|
||||
page_tree *node = root;
|
||||
uint8_t parent_level = max_level + 1;
|
||||
|
||||
while (node) {
|
||||
uint8_t level = node->m_level;
|
||||
uint8_t index = 0;
|
||||
if (!node->contains(offset, index)) {
|
||||
// We found a valid parent but the slot where this node should
|
||||
// go contains another node. Insert an intermediate parent of
|
||||
// this node and a new level0 into the parent.
|
||||
uint64_t other = node->m_base;
|
||||
uint8_t lcl = parent_level;
|
||||
while (index_for(offset, lcl) == index_for(other, lcl)) --lcl;
|
||||
|
||||
page_tree *inter = new page_tree(offset, lcl);
|
||||
inter->m_entries[index_for(other, lcl)].child = node;
|
||||
*parent = inter;
|
||||
|
||||
level0 = new page_tree(offset, 0);
|
||||
inter->m_entries[index_for(offset, lcl)].child = level0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!level) {
|
||||
level0 = node;
|
||||
break;
|
||||
}
|
||||
|
||||
parent = &node->m_entries[index].child;
|
||||
node = *parent;
|
||||
}
|
||||
|
||||
kassert( node || parent, "Both node and parent were null in page_tree::get_entry");
|
||||
|
||||
if (!node) {
|
||||
// We found a parent with an empty spot where this node should
|
||||
// be. Insert a new level0 there.
|
||||
level0 = new page_tree(offset, 0);
|
||||
*parent = level0;
|
||||
}
|
||||
}
|
||||
|
||||
kassert(level0, "Got through page_tree::get_entry without a level0");
|
||||
|
||||
uint8_t index = index_for(offset, 0);
|
||||
return level0->m_entries[index].entry;
|
||||
}
|
||||
|
||||
bool
|
||||
page_tree::find(const page_tree *root, uint64_t offset, uintptr_t &page)
|
||||
{
|
||||
page_tree const *node = root;
|
||||
while (node) {
|
||||
uint8_t level = node->m_level;
|
||||
uint8_t index = 0;
|
||||
if (!node->contains(offset, index))
|
||||
return false;
|
||||
|
||||
if (!level) {
|
||||
uintptr_t entry = node->m_entries[index].entry;
|
||||
page = entry & ~0xfffull;
|
||||
return (entry & 1); // bit 0 marks 'present'
|
||||
}
|
||||
|
||||
node = node->m_entries[index].child;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
static_assert(sizeof(page_tree) == 67 * sizeof(uintptr_t));
|
||||
|
||||
bool
|
||||
page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page)
|
||||
{
|
||||
uint64_t &ent = get_entry(root, offset);
|
||||
node_type * radix_root = root;
|
||||
uint64_t &ent = radix_tree::find_or_add(radix_root, offset);
|
||||
root = static_cast<page_tree*>(radix_root);
|
||||
|
||||
if (!(ent & 1)) {
|
||||
// No entry for this page exists, so make one
|
||||
@@ -160,8 +45,10 @@ page_tree::find_or_add(page_tree * &root, uint64_t offset, uintptr_t &page)
|
||||
void
|
||||
page_tree::add_existing(page_tree * &root, uint64_t offset, uintptr_t page)
|
||||
{
|
||||
uint64_t &ent = get_entry(root, offset);
|
||||
kassert(!(ent & 1), "Replacing existing mapping in page_tree::add_existing");
|
||||
node_type * radix_root = root;
|
||||
uint64_t &ent = radix_tree::find_or_add(radix_root, offset);
|
||||
root = static_cast<page_tree*>(radix_root);
|
||||
|
||||
kassert(!(ent & 1), "Replacing existing mapping in page_tree::add_existing");
|
||||
ent = page | 1;
|
||||
}
|
||||
|
||||
@@ -4,19 +4,17 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <arch/memory.h>
|
||||
#include <util/radix_tree.h>
|
||||
|
||||
/// A radix tree node that tracks mapped pages
|
||||
class page_tree
|
||||
class page_tree :
|
||||
public util::radix_tree<uintptr_t, 64, 5, arch::frame_bits>
|
||||
{
|
||||
public:
|
||||
/// Get the physical address of the page at the given offset.
|
||||
/// \arg root The root node of the tree
|
||||
/// \arg offset Offset into the VMA, in bytes
|
||||
/// \arg page [out] Receives the page physical address, if found
|
||||
/// \returns True if a page was found
|
||||
static bool find(const page_tree *root, uint64_t offset, uintptr_t &page);
|
||||
|
||||
/// Get the physical address of the page at the given offset. If one does
|
||||
/// not exist yet, allocate a page, insert it, and return that.
|
||||
/// not exist yet, allocate a page, insert it, and return that. Overrides
|
||||
/// `util::radix_tree::find_or_add`.
|
||||
/// \arg root [inout] The root node of the tree. This pointer may be updated.
|
||||
/// \arg offset Offset into the VMA, in bytes
|
||||
/// \arg page [out] Receives the page physical address, if found
|
||||
@@ -28,32 +26,4 @@ public:
|
||||
/// \arg offset Offset into the VMA, in bytes
|
||||
/// \arg page The mapped page physical address
|
||||
static void add_existing(page_tree * &root, uint64_t offset, uintptr_t page);
|
||||
|
||||
~page_tree();
|
||||
|
||||
private:
|
||||
page_tree(uint64_t base, uint8_t level);
|
||||
|
||||
/// Check if this node should contain the given virtual address
|
||||
/// \arg offset The offset into the VMA, in bytes
|
||||
/// \arg index [out] If found, what entry index should contain addr
|
||||
/// \returns True if the address is contained
|
||||
bool contains(uintptr_t offset, uint8_t &index) const;
|
||||
|
||||
/// Get a (writable) reference to a page in the tree
|
||||
static uintptr_t & get_entry(page_tree * &root, uint64_t offset);
|
||||
|
||||
/// Stores the page offset of the start of this node's pages virtual addresses
|
||||
uint64_t m_base;
|
||||
|
||||
/// Level of this node: 0 maps actual physical pages. Other levels N point to
|
||||
/// nodes of level N-1.
|
||||
uint8_t m_level;
|
||||
|
||||
/// For a level 0 node, the entries area all physical page addresses.
|
||||
/// Other nodes contain pointers to child tree nodes.
|
||||
union {
|
||||
uintptr_t entry;
|
||||
page_tree *child;
|
||||
} m_entries[64];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user