This simple tests creates an anonymous mmap, uses it, and returns it.

However, under certain conditions this could trigger a bug that hangs
the system.  This test attempts to verify the fix commited at
7bf2f652833c3ce784321eb5d2e3133650d47ea4.

Signed-off-by: Timmons C. Player <[email protected]>
---
 core/mempool.cc                   |  8 +++++
 include/osv/mempool.hh            |  1 +
 modules/tests/Makefile            |  6 ++--
 tests/tst-page-range-allocator.cc | 64 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 76 insertions(+), 3 deletions(-)
 create mode 100644 tests/tst-page-range-allocator.cc

diff --git a/core/mempool.cc b/core/mempool.cc
index 1ac3f67..fd60221 100644
--- a/core/mempool.cc
+++ b/core/mempool.cc
@@ -570,6 +570,10 @@ public:
         return size;
     }
 
+    size_t page_count() const {
+        return _bitmap.size();
+    }
+
 private:
     template<bool UseBitmap = true>
     void insert(page_range& pr) {
@@ -819,6 +823,10 @@ void page_range_allocator::for_each(unsigned min_order, 
Func f)
     }
 }
 
+namespace stats {
+    size_t pages() { return free_page_ranges.page_count(); }
+}
+
 static void* malloc_large(size_t size, size_t alignment, bool block = true)
 {
     auto requested_size = size;
diff --git a/include/osv/mempool.hh b/include/osv/mempool.hh
index 5dae764..d3e4c8a 100644
--- a/include/osv/mempool.hh
+++ b/include/osv/mempool.hh
@@ -181,6 +181,7 @@ private:
 namespace stats {
     size_t free();
     size_t total();
+    size_t pages();
     size_t max_no_reclaim();
     size_t jvm_heap();
     void on_jvm_heap_alloc(size_t mem);
diff --git a/modules/tests/Makefile b/modules/tests/Makefile
index b8348dd..715a7c1 100644
--- a/modules/tests/Makefile
+++ b/modules/tests/Makefile
@@ -87,7 +87,7 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so 
tst-bsd-evh.so \
        tst-ifaddrs.so tst-pthread-affinity-inherit.so tst-sem-timed-wait.so \
        tst-ttyname.so tst-pthread-barrier.so tst-feexcept.so tst-math.so \
        tst-sigaltstack.so tst-fread.so tst-tcp-cork.so tst-tcp-v6.so \
-       tst-calloc.so
+       tst-calloc.so tst-page-range-allocator.so
 
 #      libstatic-thread-variable.so tst-static-thread-variable.so \
 
@@ -134,7 +134,7 @@ solaris-tests := tst-solaris-taskq.so
 # FIXME: two of the test below can't compile now because of include path
 # (BSD and OSv header files get mixed up, etc.).
 #zfs-tests := misc-zfs-disk.so misc-zfs-io.so misc-zfs-arc.so
-zfs-tests := misc-zfs-io.so 
+zfs-tests := misc-zfs-io.so
 solaris-tests += $(zfs-tests)
 
 tests += $(solaris-tests)
@@ -142,7 +142,7 @@ tests += $(solaris-tests)
 $(zfs-tests:%=$(out)/tests/%): COMMON+= \
         -DBUILDING_ZFS \
         -I$(src)/bsd/sys/cddl/contrib/opensolaris/uts/common/fs/zfs \
-        -I$(src)/bsd/sys/cddl/contrib/opensolaris/common/zfs 
+        -I$(src)/bsd/sys/cddl/contrib/opensolaris/common/zfs
 
 $(solaris-tests:%=$(out)/tests/%): COMMON+= \
         -Wno-strict-aliasing \
diff --git a/tests/tst-page-range-allocator.cc 
b/tests/tst-page-range-allocator.cc
new file mode 100644
index 0000000..c2849b8
--- /dev/null
+++ b/tests/tst-page-range-allocator.cc
@@ -0,0 +1,64 @@
+#ifndef __OSV__
+#error "This test only runs under OSv"
+#endif
+
+#include <osv/mempool.hh>
+#include <osv/mmu.hh>
+#include <sys/mman.h>
+#include <string.h>
+
+int tests = 0, fails = 0;
+
+constexpr int bufsize = mmu::huge_page_size;
+
+static void report(bool ok, const char *msg)
+{
+    ++tests;
+    fails += !ok;
+    debug("%s: %s\n", (ok ? "PASS" : "FAIL"), msg);
+}
+
+// This test requires some explanation.
+// The page range allocator maintains a bitset indicating which pages are
+// allocated and which are not.  When pages are returned to the allocator,
+// it attempts to merge neighboring page ranges to prevent fragmentation.
+// The original implementation simply calculated the neighboring page range
+// indexes and checked their status.  Of course, if the first or last
+// page range were allocated, returning those pages would cause the allocator
+// to index past the bitset array bounds and cause a hang.
+// This test attempts to allocate the last page range in the system.  However,
+// that can only be done if the amount of memory available to the page
+// range allocator is an integral multiple of the mmu::huge_page_size.
+// In that case, the first mmaped memory that is greater than or equal
+// to the mmu::huge_page_size will be allocated from the top most (last)
+// page range.
+//
+// If the hosting VM lacks the proper amount of memory, the test just
+// logs a message explaining how much more memory is needed.
+int main()
+{
+    // Check the number of pages available
+    auto page_count = memory::stats::pages();
+    constexpr int pages_per_huge_pages = mmu::huge_page_size / mmu::page_size;
+    auto remainder = page_count % pages_per_huge_pages;
+    if (remainder != 0) {
+        // Incorrect number of pages.  Provide user with a helpful error.
+        printf("Please increase the amount of VM memory by %.02f MB "
+               "in order to run this test.\n",
+               4096.0 / 1048576 * (pages_per_huge_pages - remainder));
+        return 0;
+    }
+
+    // XXX: No way to really check if this is the top most page.
+    void *buffer = mmap(NULL, bufsize, PROT_READ|PROT_WRITE, 
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
+    report(buffer != nullptr, "mmap");
+
+    // Force the buffer to get mapped.
+    memset(buffer, 0, bufsize);
+
+    // Test will hang here with unfixed page_range_allocator::free method.
+    int error = munmap(buffer, bufsize);
+    report(error == 0, "mumap");
+
+    printf("SUMMARY: %d tests, %d failures\n", tests, fails);
+}
-- 
2.7.4

-- 
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 [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to