This patch partially addresses the issue #854. In essence it changes malloc_large()
to use mmu::map_anon() if requested memory is >= 2MB and it does not have be
contiguous in physical memory. 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 straight contiguous
page range.
Please not this patch does NOT address scenario where allocation
requests > 4K and < 2MB cannot be satisfied because mamory is fragmented
at that level.
Signed-off-by: Waldemar Kozaczuk <
jwkoz...@gmail.com>
---
core/mempool.cc | 41 ++++++++++++++++++++++++++++++++++++-----
core/mmu.cc | 9 +++++----
2 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/core/mempool.cc b/core/mempool.cc
index d902eea8..0d82f145 100644
--- a/core/mempool.cc
+++ b/core/mempool.cc
@@ -702,10 +702,13 @@ page_range* page_range_allocator::alloc(size_t size)
for (auto&& pr : _free[exact_order - 1]) {
if (pr.size >= size) {
range = ≺
+ remove_list(exact_order - 1, *range);
break;
}
}
- return nullptr;
+ if (!range) {
+ return nullptr;
+ }
} else if (order == max_order) {
range = &*_free_huge.rbegin();
if (range->size < size) {
@@ -820,7 +823,9 @@ 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 size_t huge_page_size = 0x200000;
+
+static void* malloc_large(size_t size, size_t alignment, bool block = true, bool contiguous = true)
{
auto requested_size = size;
size_t offset;
@@ -832,6 +837,16 @@ static void* malloc_large(size_t size, size_t alignment, bool block = true)
size += offset;
size = align_up(size, page_size);
+ if (size >= huge_page_size && !contiguous) {
+ // Map memory if requested memory greater than 2MB and does not need to be contiguous
+ //TODO: For now pre-populate the memory, in future consider turning it off
+ void* obj = mmu::map_anon(nullptr, size, mmu::mmap_populate, mmu::perm_read | mmu::perm_write);
+ page_range* ret_header = reinterpret_cast<page_range*>(obj);
+ ret_header->size = size;
+ trace_memory_malloc_large(obj + offset, requested_size, size, alignment);
+ return obj + offset;
+ }
+
while (true) {
WITH_LOCK(free_page_ranges_lock) {
reclaimer_thread.wait_for_minimum_memory();
@@ -1061,10 +1076,21 @@ static void free_page_range(void *addr, size_t size)
free_page_range(static_cast<page_range*>(addr));
}
+static inline bool is_addr_memory_mmapped(void* addr)
+{
+ return (ulong)addr >= 0x200000000000 && (ulong)addr < 0x800000000000;
+}
+
static void free_large(void* obj)
{
obj = align_down(obj - 1, page_size);
- free_page_range(static_cast<page_range*>(obj));
+ auto range = static_cast<page_range *>(obj);
+ if (is_addr_memory_mmapped(obj)) {
+ mmu::munmap(obj, range->size);
+ }
+ else {
+ free_page_range(range);
+ }
}
static unsigned large_object_size(void *obj)
@@ -1689,7 +1715,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;
@@ -1760,6 +1786,11 @@ void free(void* object)
return;
}
memory::tracker_forget(object);
+ if (memory::is_addr_memory_mmapped(object)) {
+ memory::free_large(object);
+ return;
+ }
+
switch (mmu::get_mem_area(object)) {
case mmu::mem_area::page:
object = mmu::translate_mem_area(mmu::mem_area::page,
@@ -1969,7 +2000,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;
}
diff --git a/core/mmu.cc b/core/mmu.cc
index ff3fab47..d184541c 100644
--- a/core/mmu.cc
+++ b/core/mmu.cc
@@ -115,10 +115,11 @@ phys virt_to_phys(void *virt)
}
#endif
- // For now, only allow non-mmaped areas. Later, we can either
- // bounce such addresses, or lock them in memory and translate
- assert(virt >= phys_mem);
- return reinterpret_cast<uintptr_t>(virt) & (mem_area_size - 1);
+ if (virt >= phys_mem) {
+ return reinterpret_cast<uintptr_t>(virt) & (mem_area_size - 1);
+ } else {
+ return virt_to_phys_pt(virt);
+ }
}
template <int N, typename MakePTE>
--
2.20.1