Adding address manager

This commit is contained in:
Justin C. Miller
2019-02-22 19:15:55 -08:00
parent a9ac30b991
commit f9d964cccb
7 changed files with 337 additions and 19 deletions

View File

@@ -0,0 +1,159 @@
#include "kutil/address_manager.h"
#include "kutil/assert.h"
namespace kutil {
address_manager::address_manager(uintptr_t start, size_t length)
{
uintptr_t p = start;
unsigned size = size_max;
while (size >= size_min) {
size_t chunk_size = 1ull << size;
while (p + chunk_size <= start + length) {
region_node *r = m_alloc.pop();
r->size = size_max;
r->address = p;
free_bucket(size).sorted_insert(r);
p += chunk_size;
}
size--;
}
}
address_manager::region_node *
address_manager::split(region_node *reg)
{
region_node *other = m_alloc.pop();
other->size = --reg->size;
other->address = reg->address + (1ull << reg->size);
return other;
}
address_manager::region_node *
address_manager::find(uintptr_t p, bool used)
{
for (unsigned size = size_max; size >= size_min; --size) {
auto &list = used ? used_bucket(size) : free_bucket(size);
for (auto *f : list) {
if (f->address < p) continue;
if (f->address == p) return f;
break;
}
}
return nullptr;
}
uintptr_t
address_manager::allocate(size_t length)
{
unsigned size = size_min;
while ((1ull << size) < length)
if (size++ == size_max)
return 0;
unsigned request = size;
while (free_bucket(request).empty())
if (request++ == size_max)
return 0;
region_node *r = nullptr;
while (request > size) {
r = free_bucket(request).pop_front();
region_node *n = split(r);
request--;
free_bucket(request).sorted_insert(n);
if (request != size)
free_bucket(request).sorted_insert(r);
}
if (r == nullptr) r = free_bucket(size).pop_front();
used_bucket(size).sorted_insert(r);
return r->address;
}
uintptr_t
address_manager::mark(uintptr_t start, size_t length)
{
uintptr_t end = start + length;
region_node *found = nullptr;
for (unsigned i = size_max; i >= size_min && !found; --i) {
for (auto *r : free_bucket(i)) {
if (start >= r->address && end <= r->end()) {
found = r;
break;
}
}
}
kassert(found, "address_manager::mark called for unknown region");
if (!found)
return 0;
while (found->size > size_min) {
// Split if the request fits in the second half
if (start >= found->half()) {
region_node *other = split(found);
free_bucket(found->size).sorted_insert(found);
found = other;
}
// Split if the request fits in the first half
else if (start + length < found->half()) {
region_node *other = split(found);
free_bucket(other->size).sorted_insert(other);
}
// If neither, we've split as much as possible
else
break;
}
used_bucket(found->size).sorted_insert(found);
return found->address;
}
address_manager::region_node *
address_manager::get_buddy(region_node *r)
{
region_node *b = r->elder() ? r->next() : r->prev();
if (b && b->address == r->buddy())
return b;
return nullptr;
}
void
address_manager::free(uintptr_t p)
{
region_node *found = find(p, true);
kassert(found, "address_manager::free called for unknown region");
if (!found)
return;
used_bucket(found->size).remove(found);
free_bucket(found->size).sorted_insert(found);
while (auto *bud = get_buddy(found)) {
region_node *eld = found->elder() ? found : bud;
region_node *other = found->elder() ? bud : found;
free_bucket(other->size).remove(other);
m_alloc.push(other);
free_bucket(eld->size).remove(eld);
eld->size++;
free_bucket(eld->size).sorted_insert(eld);
found = eld;
}
}
} // namespace kutil

View File

@@ -0,0 +1,78 @@
#pragma once
/// \file address_manager.h
/// The virtual memory address space manager
#include <stdint.h>
#include "kutil/linked_list.h"
#include "kutil/slab_allocator.h"
namespace kutil {
class address_manager
{
public:
/// Constructor.
/// \arg start Initial address in the managed range
/// \arg length Size of the managed range, in bytes
address_manager(uintptr_t start, size_t length);
/// Allocate address space from the managed area.
/// \arg length The amount of memory to allocate, in bytes
/// \returns The address of the start of the allocated area, or 0 on
/// failure
uintptr_t allocate(size_t length);
/// Mark a region as allocated.
/// \arg start The start of the region
/// \arg length The size of the region, in bytes
/// \returns The address of the start of the allocated area, or 0 on
/// failure. This may be less than `start`.
uintptr_t mark(uintptr_t start, size_t length);
/// Free a previous allocation.
/// \arg p An address previously retuned by allocate()
void free(uintptr_t p);
private:
struct region
{
inline int compare(const region *o) const {
return address > o->address ? 1 : \
address < o->address ? -1 : 0;
}
inline uintptr_t end() const { return address + (1ull << size); }
inline uintptr_t half() const { return address + (1ull << (size - 1)); }
inline uintptr_t buddy() const { return address ^ (1ull << size); }
inline bool elder() const { return address < buddy(); }
uint16_t size;
uintptr_t address;
};
using region_node = list_node<region>;
using region_list = linked_list<region>;
/// Split a region of the given size into two smaller regions, returning
/// the new latter half
region_node * split(region_node *reg);
/// Find a node with the given address
region_node * find(uintptr_t p, bool used = true);
/// Helper to get the buddy for a node, if it's adjacent
region_node * get_buddy(region_node *r);
linked_list<region> & used_bucket(unsigned size) { return m_used[size - size_min]; }
linked_list<region> & free_bucket(unsigned size) { return m_free[size - size_min]; }
static const unsigned size_min = 16; // Min allocation: 64KiB
static const unsigned size_max = 36; // Max allocation: 64GiB
static const unsigned buckets = (size_max - size_min);
region_list m_free[buckets];
region_list m_used[buckets];
slab_allocator<region> m_alloc;
};
} //namespace kutil

View File

@@ -189,10 +189,7 @@ public:
item_type * pop_front()
{
item_type *item = m_head;
if (m_head) {
m_head = item->m_next;
item->m_next = nullptr;
}
remove(item);
return item;
}
@@ -201,10 +198,7 @@ public:
item_type * pop_back()
{
item_type *item = m_tail;
if (m_tail) {
m_tail = item->m_prev;
item->m_prev = nullptr;
}
remove(item);
return item;
}

View File

@@ -1,6 +1,7 @@
#pragma once
/// \file slab_allocator.h
/// A slab allocator and related definitions
#include "kutil/assert.h"
#include "kutil/linked_list.h"
#include "kutil/memory.h"
@@ -29,6 +30,7 @@ public:
inline item_type * pop()
{
if (this->empty()) allocate();
kassert(!this->empty(), "Slab allocator is empty after allocate()");
item_type *item = this->pop_front();
kutil::memset(item, 0, sizeof(item_type));
return item;