This patch addresses the issue #854. In essence it changes malloc_large()
to use mmu::map_anon() if requested memory is greater than size of "huge page"
and it does not have be contiguous in physical memory. Also it makes it fallback
to map_anon() based allocation for requests > 4K and < 2MB that cannot be satisfied
when mamory is fragmented.
It is supposed to help OSv memory allocation behave better when memory
in free_page_ranges is heavily fragmented and large allocations (>=2MB)
cannot be satisfied with single contiguous page range.
Fixes #854
core/mempool.cc | 54 +++++++++++++++++++++++++++++++++++++++----------
1 file changed, 43 insertions(+), 11 deletions(-)
diff --git a/core/mempool.cc b/core/mempool.cc
index 11fd1456..f14a6a68 100644
--- a/core/mempool.cc
+++ b/core/mempool.cc
@@ -546,7 +546,7 @@ public:
page_range_allocator() : _deferred_free(nullptr) { }
template<bool UseBitmap = true>
- page_range* alloc(size_t size);
+ page_range* alloc(size_t size, bool contiguous = true);
page_range* alloc_aligned(size_t size, size_t offset, size_t alignment,
bool fill = false);
void free(page_range* pr);
@@ -678,7 +678,7 @@ void page_range_allocator::bitmap_allocator<T>::deallocate(T* p, size_t n)
}
template<bool UseBitmap>
-page_range* page_range_allocator::alloc(size_t size)
+page_range* page_range_allocator::alloc(size_t size, bool contiguous)
{
auto exact_order = ilog2_roundup(size / page_size);
if (exact_order > max_order) {
@@ -692,13 +692,12 @@ page_range* page_range_allocator::alloc(size_t size)
page_range* range = nullptr;
if (!bitset) {
- if (!exact_order || _free[exact_order - 1].empty()) {
+ if (!contiguous || !exact_order || _free[exact_order - 1].empty()) {
return nullptr;
}
- // TODO: This linear search makes worst case complexity of the allocator
- // O(n). It would be better to fall back to non-contiguous allocation
- // and make worst case complexity depend on the size of requested memory
- // block and the logarithm of the number of free huge page ranges.
+ // This linear search makes worst case complexity of the allocator
+ // O(n). Unfortunately we do not have choice for contiguous allocation
+ // so let us hope there is large enough range.
for (auto&& pr : _free[exact_order - 1]) {
if (pr.size >= size) {
range = ≺
@@ -823,7 +822,16 @@ void page_range_allocator::for_each(unsigned min_order, Func f)
}
}
-static void* malloc_large(size_t size, size_t alignment, bool block = true)
+static void* mapped_malloc_large(size_t size, size_t offset)
+{
+ //TODO: For now pre-populate the memory, in future consider doing lazy population
+ void* obj = mmu::map_anon(nullptr, size, mmu::mmap_populate, mmu::perm_read | mmu::perm_write);
+ size_t* ret_header = reinterpret_cast<size_t*>(obj);
+ *ret_header = size;
+ return obj + offset;
+}
+
+static void* malloc_large(size_t size, size_t alignment, bool block = true, bool contiguous = true)
{
auto requested_size = size;
size_t offset;
@@ -835,6 +843,14 @@ static void* malloc_large(size_t size, size_t alignment, bool block = true)
size += offset;
size = align_up(size, page_size);
+ // Use mmap if requested memory greater than "huge page" size
+ // and does not need to be contiguous
+ if (size >= mmu::huge_page_size && !contiguous) {
+ void* obj = mapped_malloc_large(size, offset);
+ trace_memory_malloc_large(obj, requested_size, size, alignment);
+ return obj;
+ }
+
while (true) {
WITH_LOCK(free_page_ranges_lock) {
reclaimer_thread.wait_for_minimum_memory();
@@ -842,7 +858,7 @@ static void* malloc_large(size_t size, size_t alignment, bool block = true)
if (alignment > page_size) {
ret_header = free_page_ranges.alloc_aligned(size, page_size, alignment);
} else {
- ret_header = free_page_ranges.alloc(size);
+ ret_header = free_page_ranges.alloc(size, contiguous);
}
if (ret_header) {
on_alloc(size);
@@ -850,6 +866,10 @@ static void* malloc_large(size_t size, size_t alignment, bool block = true)
obj += offset;
trace_memory_malloc_large(obj, requested_size, size, alignment);
return obj;
+ } else if (!contiguous) {
+ // If we failed to get contiguous memory allocation and
+ // the caller does not require one let us use map-based allocation
+ break;
}
if (block)
reclaimer_thread.wait_for_memory(size);
@@ -857,6 +877,10 @@ static void* malloc_large(size_t size, size_t alignment, bool block = true)
return nullptr;
}
}
+
+ void* obj = mapped_malloc_large(size, offset);
+ trace_memory_malloc_large(obj, requested_size, size, alignment);
+ return obj;
}
void shrinker::deactivate_shrinker()
@@ -1692,7 +1716,7 @@ static inline void* std_malloc(size_t size, size_t alignment)
memory::alloc_page());
trace_memory_malloc_page(ret, size, mmu::page_size, alignment);
} else {
- ret = memory::malloc_large(size, alignment);
+ ret = memory::malloc_large(size, alignment, true, false);
}
memory::tracker_remember(ret, size);
return ret;
@@ -1763,6 +1787,14 @@ void free(void* object)
return;
}
memory::tracker_forget(object);
+
+ if (!mmu::is_linear_mapped(object, 0)) {
+ object = align_down(object - 1, mmu::page_size);
+ size_t* ret_header = reinterpret_cast<size_t*>(object);
+ mmu::munmap(object, *ret_header);
+ return;
+ }
+
switch (mmu::get_mem_area(object)) {
case mmu::mem_area::page:
object = mmu::translate_mem_area(mmu::mem_area::page,
@@ -1972,7 +2004,7 @@ void* alloc_phys_contiguous_aligned(size_t size, size_t align, bool block)
assert(is_power_of_two(align));
// make use of the standard large allocator returning properly aligned
// physically contiguous memory:
- auto ret = malloc_large(size, align, block);
+ auto ret = malloc_large(size, align, block, true);
assert (!(reinterpret_cast<uintptr_t>(ret) & (align - 1)));
return ret;
}
--
2.20.1