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

Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>
---
 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 = &pr;
@@ -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

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/20200324045850.21120-2-jwkozaczuk%40gmail.com.

Reply via email to