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.
