diff --git a/modules.yaml b/modules.yaml index 5e0593a..a985f3c 100644 --- a/modules.yaml +++ b/modules.yaml @@ -87,7 +87,6 @@ kutil: includes: - src/libraries/kutil/include source: - - src/libraries/kutil/address_manager.cpp - src/libraries/kutil/assert.cpp - src/libraries/kutil/heap_manager.cpp - src/libraries/kutil/memory.cpp diff --git a/src/libraries/kutil/address_manager.cpp b/src/libraries/kutil/address_manager.cpp deleted file mode 100644 index 6dff0fe..0000000 --- a/src/libraries/kutil/address_manager.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#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 diff --git a/src/libraries/kutil/include/kutil/address_manager.h b/src/libraries/kutil/include/kutil/address_manager.h index d5fa8a6..0ef7336 100644 --- a/src/libraries/kutil/include/kutil/address_manager.h +++ b/src/libraries/kutil/include/kutil/address_manager.h @@ -2,77 +2,14 @@ /// \file address_manager.h /// The virtual memory address space manager -#include -#include "kutil/linked_list.h" -#include "kutil/slab_allocator.h" +#include "kutil/buddy_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; - using region_list = linked_list; - - /// 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 & used_bucket(unsigned size) { return m_used[size - size_min]; } - linked_list & 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 m_alloc; -}; +using address_manager = + buddy_allocator< + 16, // Min allocation: 64KiB + 36 // Max allocation: 64GiB + >; } //namespace kutil diff --git a/src/libraries/kutil/include/kutil/buddy_allocator.h b/src/libraries/kutil/include/kutil/buddy_allocator.h new file mode 100644 index 0000000..83f3f69 --- /dev/null +++ b/src/libraries/kutil/include/kutil/buddy_allocator.h @@ -0,0 +1,219 @@ +#pragma once +/// \file buddy_allocator.h +/// Helper base class for buddy allocators with external node storage. + +#include +#include "kutil/assert.h" +#include "kutil/linked_list.h" +#include "kutil/slab_allocator.h" + +namespace kutil { + +struct buddy_region; + + +template< + unsigned size_min, + unsigned size_max, + typename region_type = buddy_region> +class buddy_allocator +{ +public: + /// Constructor. + /// \arg start Initial address in the managed range + /// \arg length Size of the managed range, in bytes + buddy_allocator(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--; + } + } + + /// 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) + { + 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; + } + + /// 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) + { + 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, "buddy_allocator::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; + } + + /// Free a previous allocation. + /// \arg p An address previously retuned by allocate() + void free(uintptr_t p) + { + region_node *found = find(p, true); + + kassert(found, "buddy_allocator::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; + } + } + +protected: + + using region_node = list_node; + using region_list = linked_list; + + /// Split a region of the given size into two smaller regions, returning + /// the new latter half + region_node * split(region_node *reg) + { + region_node *other = m_alloc.pop(); + + other->size = --reg->size; + other->address = reg->address + (1ull << reg->size); + return other; + } + + /// Find a node with the given address + region_node * find(uintptr_t p, bool used = true) + { + 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; + } + + /// Helper to get the buddy for a node, if it's adjacent + region_node * get_buddy(region_node *r) + { + region_node *b = r->elder() ? r->next() : r->prev(); + if (b && b->address == r->buddy()) + return b; + return nullptr; + } + + region_list & used_bucket(unsigned size) { return m_used[size - size_min]; } + region_list & free_bucket(unsigned size) { return m_free[size - size_min]; } + + static const unsigned buckets = (size_max - size_min); + + region_list m_free[buckets]; + region_list m_used[buckets]; + slab_allocator m_alloc; +}; + + +struct buddy_region +{ + inline int compare(const buddy_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; +}; + +} // namespace kutil