ladybird/Kernel/VM/RangeAllocator.cpp
Andreas Kling ac20919b13 Kernel: Make it possible to turn off VM guard pages at compile time
This might be useful for debugging since guard pages introduce a fair
amount of noise in the virtual address space.
2019-09-30 17:22:16 +02:00

158 lines
4.7 KiB
C++

#include <AK/QuickSort.h>
#include <Kernel/VM/RangeAllocator.h>
#include <Kernel/kstdio.h>
//#define VRA_DEBUG
#define VM_GUARD_PAGES
RangeAllocator::RangeAllocator(VirtualAddress base, size_t size)
{
m_available_ranges.append({ base, size });
#ifdef VRA_DEBUG
dump();
#endif
}
RangeAllocator::RangeAllocator(const RangeAllocator& parent_allocator)
: m_available_ranges(parent_allocator.m_available_ranges)
{
}
RangeAllocator::~RangeAllocator()
{
}
void RangeAllocator::dump() const
{
dbgprintf("RangeAllocator{%p}\n", this);
for (auto& range : m_available_ranges) {
dbgprintf(" %x -> %x\n", range.base().get(), range.end().get() - 1);
}
}
Vector<Range, 2> Range::carve(const Range& taken)
{
Vector<Range, 2> parts;
if (taken == *this)
return {};
if (taken.base() > base())
parts.append({ base(), taken.base().get() - base().get() });
if (taken.end() < end())
parts.append({ taken.end(), end().get() - taken.end().get() });
#ifdef VRA_DEBUG
dbgprintf("VRA: carve: take %x-%x from %x-%x\n",
taken.base().get(), taken.end().get() - 1,
base().get(), end().get() - 1);
for (int i = 0; i < parts.size(); ++i)
dbgprintf(" %x-%x\n", parts[i].base().get(), parts[i].end().get() - 1);
#endif
return parts;
}
void RangeAllocator::carve_at_index(int index, const Range& range)
{
auto remaining_parts = m_available_ranges[index].carve(range);
ASSERT(remaining_parts.size() >= 1);
m_available_ranges[index] = remaining_parts[0];
if (remaining_parts.size() == 2)
m_available_ranges.insert(index + 1, move(remaining_parts[1]));
}
Range RangeAllocator::allocate_anywhere(size_t size)
{
#ifdef VM_GUARD_PAGES
// NOTE: We pad VM allocations with a guard page on each side.
size_t effective_size = size + PAGE_SIZE * 2;
size_t offset_from_effective_base = PAGE_SIZE;
#else
size_t effective_size = size;
size_t offset_from_effective_base = 0;
#endif
for (int i = 0; i < m_available_ranges.size(); ++i) {
auto& available_range = m_available_ranges[i];
if (available_range.size() < effective_size)
continue;
Range allocated_range(available_range.base().offset(offset_from_effective_base), size);
if (available_range.size() == effective_size) {
#ifdef VRA_DEBUG
dbgprintf("VRA: Allocated perfect-fit anywhere(%u): %x\n", size, allocated_range.base().get());
#endif
m_available_ranges.remove(i);
return allocated_range;
}
carve_at_index(i, allocated_range);
#ifdef VRA_DEBUG
dbgprintf("VRA: Allocated anywhere(%u): %x\n", size, allocated_range.base().get());
dump();
#endif
return allocated_range;
}
kprintf("VRA: Failed to allocate anywhere: %u\n", size);
return {};
}
Range RangeAllocator::allocate_specific(VirtualAddress base, size_t size)
{
Range allocated_range(base, size);
for (int i = 0; i < m_available_ranges.size(); ++i) {
auto& available_range = m_available_ranges[i];
if (!available_range.contains(base, size))
continue;
if (available_range == allocated_range) {
m_available_ranges.remove(i);
return allocated_range;
}
carve_at_index(i, allocated_range);
#ifdef VRA_DEBUG
dbgprintf("VRA: Allocated specific(%u): %x\n", size, available_range.base().get());
dump();
#endif
return allocated_range;
}
kprintf("VRA: Failed to allocate specific range: %x(%u)\n", base.get(), size);
return {};
}
void RangeAllocator::deallocate(Range range)
{
#ifdef VRA_DEBUG
dbgprintf("VRA: Deallocate: %x(%u)\n", range.base().get(), range.size());
dump();
#endif
for (auto& available_range : m_available_ranges) {
if (available_range.end() == range.base()) {
available_range.m_size += range.size();
goto sort_and_merge;
}
}
m_available_ranges.append(range);
sort_and_merge:
// FIXME: We don't have to sort if we insert at the right position immediately.
quick_sort(m_available_ranges.begin(), m_available_ranges.end(), [](auto& a, auto& b) {
return a.base() < b.base();
});
Vector<Range> merged_ranges;
merged_ranges.ensure_capacity(m_available_ranges.size());
for (auto& range : m_available_ranges) {
if (merged_ranges.is_empty()) {
merged_ranges.append(range);
continue;
}
if (range.base() == merged_ranges.last().end()) {
merged_ranges.last().m_size += range.size();
continue;
}
merged_ranges.append(range);
}
m_available_ranges = move(merged_ranges);
#ifdef VRA_DEBUG
dbgprintf("VRA: After deallocate\n");
dump();
#endif
}