diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 6871f6bd6a816..15c7160fdcdcd 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -2441,7 +2441,7 @@ void os::init(void) { // For now UseLargePages is just ignored. FLAG_SET_ERGO(UseLargePages, false); - _page_sizes[0] = 0; + _page_sizes.add(Aix::_page_size); // debug trace trcVerbose("os::vm_page_size %s", describe_pagesize(os::vm_page_size())); diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 733e2b7e0e72d..c9aec705b9ffa 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -2093,7 +2093,7 @@ void os::init(void) { if (Bsd::page_size() == -1) { fatal("os_bsd.cpp: os::init: sysconf failed (%s)", os::strerror(errno)); } - init_page_sizes((size_t) Bsd::page_size()); + _page_sizes.add(Bsd::page_size()); Bsd::initialize_system_info(); diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 15d1dff4d84c8..78ad6492c0594 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -3769,9 +3769,7 @@ size_t os::Linux::setup_large_page_size() { const size_t default_page_size = (size_t)Linux::page_size(); if (_large_page_size > default_page_size) { - _page_sizes[0] = _large_page_size; - _page_sizes[1] = default_page_size; - _page_sizes[2] = 0; + _page_sizes.add(_large_page_size); } return _large_page_size; @@ -4399,7 +4397,7 @@ void os::init(void) { fatal("os_linux.cpp: os::init: sysconf failed (%s)", os::strerror(errno)); } - init_page_sizes((size_t) Linux::page_size()); + _page_sizes.add(Linux::page_size()); Linux::initialize_system_info(); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 3342ecf781756..0c67dda4281f5 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -3118,12 +3118,9 @@ void os::large_page_init() { } _large_page_size = large_page_init_decide_size(); - const size_t default_page_size = (size_t) vm_page_size(); if (_large_page_size > default_page_size) { - _page_sizes[0] = _large_page_size; - _page_sizes[1] = default_page_size; - _page_sizes[2] = 0; + _page_sizes.add(_large_page_size); } UseLargePages = _large_page_size != 0; @@ -4166,7 +4163,7 @@ void os::init(void) { win32::initialize_system_info(); win32::setmode_streams(); - init_page_sizes((size_t) win32::vm_page_size()); + _page_sizes.add(win32::vm_page_size()); // This may be overridden later when argument processing is done. FLAG_SET_ERGO(UseLargePagesIndividualAllocation, false); diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 4d702eb94699c..3e312ecb082a0 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -64,8 +64,10 @@ #include "services/nmtCommon.hpp" #include "services/threadService.hpp" #include "utilities/align.hpp" +#include "utilities/count_trailing_zeros.hpp" #include "utilities/defaultStream.hpp" #include "utilities/events.hpp" +#include "utilities/powerOfTwo.hpp" # include <signal.h> # include <errno.h> @@ -75,7 +77,7 @@ address os::_polling_page = NULL; volatile unsigned int os::_rand_seed = 1234567; int os::_processor_count = 0; int os::_initial_active_processor_count = 0; -size_t os::_page_sizes[os::page_sizes_max]; +os::PageSizes os::_page_sizes; #ifndef PRODUCT julong os::num_mallocs = 0; // # of calls to malloc/realloc @@ -1390,8 +1392,8 @@ size_t os::page_size_for_region(size_t region_size, size_t min_pages, bool must_ if (UseLargePages) { const size_t max_page_size = region_size / min_pages; - for (size_t i = 0; _page_sizes[i] != 0; ++i) { - const size_t page_size = _page_sizes[i]; + for (size_t page_size = page_sizes().largest(); page_size != 0; + page_size = page_sizes().next_smaller(page_size)) { if (page_size <= max_page_size) { if (!must_be_aligned || is_aligned(region_size, page_size)) { return page_size; @@ -1536,19 +1538,6 @@ const char* os::errno_name(int e) { return errno_to_string(e, true); } -void os::trace_page_sizes(const char* str, const size_t* page_sizes, int count) { - LogTarget(Info, pagesize) log; - if (log.is_enabled()) { - LogStream out(log); - - out.print("%s: ", str); - for (int i = 0; i < count; ++i) { - out.print(" " SIZE_FORMAT, page_sizes[i]); - } - out.cr(); - } -} - #define trace_page_size_params(size) byte_size_in_exact_unit(size), exact_unit_for_byte_size(size) void os::trace_page_sizes(const char* str, @@ -1857,3 +1846,73 @@ void os::naked_sleep(jlong millis) { } naked_short_sleep(millis); } + + +////// Implementation of PageSizes + +void os::PageSizes::add(size_t page_size) { + assert(is_power_of_2(page_size), "page_size must be a power of 2: " SIZE_FORMAT_HEX, page_size); + _v |= page_size; +} + +bool os::PageSizes::contains(size_t page_size) const { + assert(is_power_of_2(page_size), "page_size must be a power of 2: " SIZE_FORMAT_HEX, page_size); + return (_v & page_size) != 0; +} + +size_t os::PageSizes::next_smaller(size_t page_size) const { + assert(is_power_of_2(page_size), "page_size must be a power of 2: " SIZE_FORMAT_HEX, page_size); + size_t v2 = _v & (page_size - 1); + if (v2 == 0) { + return 0; + } + return round_down_power_of_2(v2); +} + +size_t os::PageSizes::next_larger(size_t page_size) const { + assert(is_power_of_2(page_size), "page_size must be a power of 2: " SIZE_FORMAT_HEX, page_size); + if (page_size == max_power_of_2<size_t>()) { // Shift by 32/64 would be UB + return 0; + } + // Remove current and smaller page sizes + size_t v2 = _v & ~(page_size + (page_size - 1)); + if (v2 == 0) { + return 0; + } + return (size_t)1 << count_trailing_zeros(v2); +} + +size_t os::PageSizes::largest() const { + const size_t max = max_power_of_2<size_t>(); + if (contains(max)) { + return max; + } + return next_smaller(max); +} + +size_t os::PageSizes::smallest() const { + // Strictly speaking the set should not contain sizes < os::vm_page_size(). + // But this is not enforced. + return next_larger(1); +} + +void os::PageSizes::print_on(outputStream* st) const { + bool first = true; + for (size_t sz = smallest(); sz != 0; sz = next_larger(sz)) { + if (first) { + first = false; + } else { + st->print_raw(", "); + } + if (sz < M) { + st->print(SIZE_FORMAT "k", sz / K); + } else if (sz < G) { + st->print(SIZE_FORMAT "M", sz / M); + } else { + st->print(SIZE_FORMAT "G", sz / G); + } + } + if (first) { + st->print("empty"); + } +} diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index e38e1a068b43c..3d6b5405c7b3c 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -100,19 +100,30 @@ class os: AllStatic { #endif public: - enum { page_sizes_max = 9 }; // Size of _page_sizes array (8 plus a sentinel) + + // A simple value class holding a set of page sizes (similar to sigset_t) + class PageSizes { + size_t _v; // actually a bitmap. + public: + PageSizes() : _v(0) {} + void add(size_t pagesize); + bool contains(size_t pagesize) const; + // Given a page size, return the next smaller page size in this set, or 0. + size_t next_smaller(size_t pagesize) const; + // Given a page size, return the next larger page size in this set, or 0. + size_t next_larger(size_t pagesize) const; + // Returns the largest page size in this set, or 0 if set is empty. + size_t largest() const; + // Returns the smallest page size in this set, or 0 if set is empty. + size_t smallest() const; + // Prints one line of comma separated, human readable page sizes, "empty" if empty. + void print_on(outputStream* st) const; + }; private: static OSThread* _starting_thread; static address _polling_page; - public: - static size_t _page_sizes[page_sizes_max]; - - private: - static void init_page_sizes(size_t default_page_size) { - _page_sizes[0] = default_page_size; - _page_sizes[1] = 0; // sentinel - } + static PageSizes _page_sizes; static char* pd_reserve_memory(size_t bytes); @@ -274,6 +285,10 @@ class os: AllStatic { // Return the default page size. static int vm_page_size(); + // The set of page sizes which the VM is allowed to use (may be a subset of + // the page sizes actually available on the platform). + static const PageSizes& page_sizes() { return _page_sizes; } + // Returns the page size to use for a region of memory. // region_size / min_pages will always be greater than or equal to the // returned value. The returned value will divide region_size. @@ -285,10 +300,7 @@ class os: AllStatic { static size_t page_size_for_region_unaligned(size_t region_size, size_t min_pages); // Return the largest page size that can be used - static size_t max_page_size() { - // The _page_sizes array is sorted in descending order. - return _page_sizes[0]; - } + static size_t max_page_size() { return page_sizes().largest(); } // Return a lower bound for page sizes. Also works before os::init completed. static size_t min_page_size() { return 4 * K; } diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index 4795a78407ec0..8319a07ff0cc5 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -85,29 +85,29 @@ TEST_VM(os, page_size_for_region_alignment) { TEST_VM(os, page_size_for_region_unaligned) { if (UseLargePages) { // Given exact page size, should return that page size. - for (size_t i = 0; os::_page_sizes[i] != 0; i++) { - size_t expected = os::_page_sizes[i]; - size_t actual = os::page_size_for_region_unaligned(expected, 1); - ASSERT_EQ(expected, actual); + for (size_t s = os::page_sizes().largest(); s != 0; s = os::page_sizes().next_smaller(s)) { + size_t actual = os::page_size_for_region_unaligned(s, 1); + ASSERT_EQ(s, actual); } // Given slightly larger size than a page size, return the page size. - for (size_t i = 0; os::_page_sizes[i] != 0; i++) { - size_t expected = os::_page_sizes[i]; - size_t actual = os::page_size_for_region_unaligned(expected + 17, 1); - ASSERT_EQ(expected, actual); + for (size_t s = os::page_sizes().largest(); s != 0; s = os::page_sizes().next_smaller(s)) { + size_t actual = os::page_size_for_region_unaligned(s + 17, 1); + ASSERT_EQ(s, actual); } // Given a slightly smaller size than a page size, // return the next smaller page size. - if (os::_page_sizes[1] > os::_page_sizes[0]) { - size_t expected = os::_page_sizes[0]; - size_t actual = os::page_size_for_region_unaligned(os::_page_sizes[1] - 17, 1); - ASSERT_EQ(actual, expected); + for (size_t s = os::page_sizes().largest(); s != 0; s = os::page_sizes().next_smaller(s)) { + const size_t expected = os::page_sizes().next_smaller(s); + if (expected != 0) { + size_t actual = os::page_size_for_region_unaligned(expected - 17, 1); + ASSERT_EQ(actual, expected); + } } // Return small page size for values less than a small page. - size_t small_page = small_page_size(); + size_t small_page = os::page_sizes().smallest(); size_t actual = os::page_size_for_region_unaligned(small_page - 17, 1); ASSERT_EQ(small_page, actual); } @@ -363,7 +363,7 @@ static address reserve_multiple(int num_stripes, size_t stripe_len) { // .. release it... EXPECT_TRUE(os::release_memory((char*)p, total_range_len)); // ... re-reserve in the same spot multiple areas... - for (int stripe = 0; stripe < num_stripes; stripe ++) { + for (int stripe = 0; stripe < num_stripes; stripe++) { address q = p + (stripe * stripe_len); q = (address)os::attempt_reserve_memory_at((char*)q, stripe_len); EXPECT_NE(q, (address)NULL); @@ -383,7 +383,7 @@ static address reserve_one_commit_multiple(int num_stripes, size_t stripe_len) { size_t total_range_len = num_stripes * stripe_len; address p = (address)os::reserve_memory(total_range_len); EXPECT_NE(p, (address)NULL); - for (int stripe = 0; stripe < num_stripes; stripe ++) { + for (int stripe = 0; stripe < num_stripes; stripe++) { address q = p + (stripe * stripe_len); if (stripe % 2 == 0) { EXPECT_TRUE(os::commit_memory((char*)q, stripe_len, false)); @@ -396,7 +396,7 @@ static address reserve_one_commit_multiple(int num_stripes, size_t stripe_len) { // Release a range allocated with reserve_multiple carefully, to not trip mapping // asserts on Windows in os::release_memory() static void carefully_release_multiple(address start, int num_stripes, size_t stripe_len) { - for (int stripe = 0; stripe < num_stripes; stripe ++) { + for (int stripe = 0; stripe < num_stripes; stripe++) { address q = start + (stripe * stripe_len); EXPECT_TRUE(os::release_memory((char*)q, stripe_len)); } @@ -574,7 +574,7 @@ TEST_VM(os, find_mapping_3) { address p = reserve_multiple(4, stripe_len); ASSERT_NE(p, (address)NULL); PRINT_MAPPINGS("E"); - for (int stripe = 0; stripe < 4; stripe ++) { + for (int stripe = 0; stripe < 4; stripe++) { ASSERT_TRUE(os::win32::find_mapping(p + (stripe * stripe_len), &mapping_info)); ASSERT_EQ(mapping_info.base, p + (stripe * stripe_len)); ASSERT_EQ(mapping_info.regions, 1); @@ -587,3 +587,78 @@ TEST_VM(os, find_mapping_3) { } } #endif // _WIN32 + +TEST_VM(os, os_pagesizes) { + ASSERT_EQ(os::min_page_size(), 4 * K); + ASSERT_LE(os::min_page_size(), (size_t)os::vm_page_size()); + // The vm_page_size should be the smallest in the set of allowed page sizes + // (contract says "default" page size but a lot of code actually assumes + // this to be the smallest page size; notable, deliberate exception is + // AIX which can have smaller page sizes but those are not part of the + // page_sizes() set). + ASSERT_EQ(os::page_sizes().smallest(), (size_t)os::vm_page_size()); + // The large page size, if it exists, shall be part of the set + if (UseLargePages) { + ASSERT_GT(os::large_page_size(), (size_t)os::vm_page_size()); + ASSERT_TRUE(os::page_sizes().contains(os::large_page_size())); + } + os::page_sizes().print_on(tty); + tty->cr(); +} + +static const int min_page_size_log2 = exact_log2(os::min_page_size()); +static const int max_page_size_log2 = (int)BitsPerWord; + +TEST_VM(os, pagesizes_test_range) { + for (int bit = min_page_size_log2; bit < max_page_size_log2; bit++) { + for (int bit2 = min_page_size_log2; bit2 < max_page_size_log2; bit2++) { + const size_t s = (size_t)1 << bit; + const size_t s2 = (size_t)1 << bit2; + os::PageSizes pss; + ASSERT_EQ((size_t)0, pss.smallest()); + ASSERT_EQ((size_t)0, pss.largest()); + // one size set + pss.add(s); + ASSERT_TRUE(pss.contains(s)); + ASSERT_EQ(s, pss.smallest()); + ASSERT_EQ(s, pss.largest()); + ASSERT_EQ(pss.next_larger(s), (size_t)0); + ASSERT_EQ(pss.next_smaller(s), (size_t)0); + // two set + pss.add(s2); + ASSERT_TRUE(pss.contains(s2)); + if (s2 < s) { + ASSERT_EQ(s2, pss.smallest()); + ASSERT_EQ(s, pss.largest()); + ASSERT_EQ(pss.next_larger(s2), (size_t)s); + ASSERT_EQ(pss.next_smaller(s2), (size_t)0); + ASSERT_EQ(pss.next_larger(s), (size_t)0); + ASSERT_EQ(pss.next_smaller(s), (size_t)s2); + } else if (s2 > s) { + ASSERT_EQ(s, pss.smallest()); + ASSERT_EQ(s2, pss.largest()); + ASSERT_EQ(pss.next_larger(s), (size_t)s2); + ASSERT_EQ(pss.next_smaller(s), (size_t)0); + ASSERT_EQ(pss.next_larger(s2), (size_t)0); + ASSERT_EQ(pss.next_smaller(s2), (size_t)s); + } + for (int bit3 = min_page_size_log2; bit3 < max_page_size_log2; bit3++) { + const size_t s3 = (size_t)1 << bit3; + ASSERT_EQ(s3 == s || s3 == s2, pss.contains(s3)); + } + } + } +} + +TEST_VM(os, pagesizes_test_print) { + os::PageSizes pss; + const size_t sizes[] = { 16 * K, 64 * K, 128 * K, 1 * M, 4 * M, 1 * G, 2 * G, 0 }; + static const char* const expected = "16k, 64k, 128k, 1M, 4M, 1G, 2G"; + for (int i = 0; sizes[i] != 0; i++) { + pss.add(sizes[i]); + } + char buffer[256]; + stringStream ss(buffer, sizeof(buffer)); + pss.print_on(&ss); + ASSERT_EQ(strcmp(expected, buffer), 0); +}