From c70a5e41478aacff8bb4f0df03473754518d6d1c Mon Sep 17 00:00:00 2001 From: duke Date: Fri, 8 Nov 2024 01:12:53 +0000 Subject: [PATCH] Backport 4ded28380b6756e0679d80706f76bd6e78c370b9 --- src/hotspot/os/windows/globals_windows.hpp | 4 + src/hotspot/os/windows/os_windows.cpp | 51 +++++++-- src/hotspot/os/windows/os_windows.hpp | 2 + .../hotspot/gtest/runtime/test_os_windows.cpp | 108 ++++++++++++++++++ 4 files changed, 153 insertions(+), 12 deletions(-) diff --git a/src/hotspot/os/windows/globals_windows.hpp b/src/hotspot/os/windows/globals_windows.hpp index 78cbac6e9cc..8f0a6261cc0 100644 --- a/src/hotspot/os/windows/globals_windows.hpp +++ b/src/hotspot/os/windows/globals_windows.hpp @@ -38,6 +38,10 @@ product(bool, UseAllWindowsProcessorGroups, false, \ "Use all processor groups on supported Windows versions") \ \ +product(bool, EnableAllLargePageSizesForWindows, false, \ + "Enable support for multiple large page sizes on " \ + "Windows Server") \ + \ product(bool, UseOSErrorReporting, false, \ "Let VM fatal error propagate to the OS (ie. WER on Windows)") diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 252284d8c1c..7dcdaa82452 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -3104,7 +3104,7 @@ class NUMANodeListHolder { static size_t _large_page_size = 0; -static bool request_lock_memory_privilege() { +bool os::win32::request_lock_memory_privilege() { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, os::current_process_id()); @@ -3288,14 +3288,14 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, return p_buf; } -static size_t large_page_init_decide_size() { +size_t os::win32::large_page_init_decide_size() { // print a warning if any large page related flag is specified on command line bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) || !FLAG_IS_DEFAULT(LargePageSizeInBytes); -#define WARN(msg) if (warn_on_failure) { warning(msg); } +#define WARN(...) if (warn_on_failure) { warning(__VA_ARGS__); } - if (!request_lock_memory_privilege()) { + if (!os::win32::request_lock_memory_privilege()) { WARN("JVM cannot use large page memory because it does not have enough privilege to lock pages in memory."); return 0; } @@ -3306,15 +3306,26 @@ static size_t large_page_init_decide_size() { return 0; } -#if defined(IA32) || defined(AMD64) - if (size > 4*M || LargePageSizeInBytes > 4*M) { +#if defined(IA32) + if (size > 4 * M || LargePageSizeInBytes > 4 * M) { WARN("JVM cannot use large pages bigger than 4mb."); return 0; } +#elif defined(AMD64) + if (!EnableAllLargePageSizesForWindows) { + if (size > 4 * M || LargePageSizeInBytes > 4 * M) { + WARN("JVM cannot use large pages bigger than 4mb."); + return 0; + } + } #endif - if (LargePageSizeInBytes > 0 && LargePageSizeInBytes % size == 0) { - size = LargePageSizeInBytes; + if (LargePageSizeInBytes > 0) { + if (LargePageSizeInBytes % size == 0) { + size = LargePageSizeInBytes; + } else { + WARN("The specified large page size (%d) is not a multiple of the minimum large page size (%d), defaulting to minimum page size.", LargePageSizeInBytes, size); + } } #undef WARN @@ -3327,12 +3338,23 @@ void os::large_page_init() { return; } - _large_page_size = large_page_init_decide_size(); + _large_page_size = os::win32::large_page_init_decide_size(); const size_t default_page_size = os::vm_page_size(); if (_large_page_size > default_page_size) { +#if !defined(IA32) + if (EnableAllLargePageSizesForWindows) { + size_t min_size = GetLargePageMinimum(); + + // Populate _page_sizes with large page sizes less than or equal to _large_page_size, ensuring each page size is double the size of the previous one. + for (size_t page_size = min_size; page_size < _large_page_size; page_size *= 2) { + _page_sizes.add(page_size); + } + } +#endif + _page_sizes.add(_large_page_size); } - + // Set UseLargePages based on whether a large page size was successfully determined UseLargePages = _large_page_size != 0; } @@ -3596,7 +3618,6 @@ static char* reserve_large_pages_aligned(size_t size, size_t alignment, bool exe char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_size, char* addr, bool exec) { assert(UseLargePages, "only for large pages"); - assert(page_size == os::large_page_size(), "Currently only support one large page size on Windows"); assert(is_aligned(addr, alignment), "Must be"); assert(is_aligned(addr, page_size), "Must be"); @@ -3605,11 +3626,17 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_ return nullptr; } + // Ensure GetLargePageMinimum() returns a valid positive value + size_t large_page_min = GetLargePageMinimum(); + if (large_page_min <= 0) { + return nullptr; + } + // The requested alignment can be larger than the page size, for example with G1 // the alignment is bound to the heap region size. So this reservation needs to // ensure that the requested alignment is met. When there is a requested address // this solves it self, since it must be properly aligned already. - if (addr == nullptr && alignment > page_size) { + if (addr == nullptr && alignment > large_page_min) { return reserve_large_pages_aligned(bytes, alignment, exec); } diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp index 3bc5ab9eef1..1d523724300 100644 --- a/src/hotspot/os/windows/os_windows.hpp +++ b/src/hotspot/os/windows/os_windows.hpp @@ -65,6 +65,8 @@ class os::win32 { static void setmode_streams(); static bool is_windows_11_or_greater(); static bool is_windows_server_2022_or_greater(); + static bool request_lock_memory_privilege(); + static size_t large_page_init_decide_size(); static int windows_major_version() { assert(_major_version > 0, "windows version not initialized."); return _major_version; diff --git a/test/hotspot/gtest/runtime/test_os_windows.cpp b/test/hotspot/gtest/runtime/test_os_windows.cpp index d611b5287a9..c7e99bcdbc5 100644 --- a/test/hotspot/gtest/runtime/test_os_windows.cpp +++ b/test/hotspot/gtest/runtime/test_os_windows.cpp @@ -722,6 +722,114 @@ TEST_VM(os_windows, processor_count) { } } +TEST_VM(os_windows, large_page_init_multiple_sizes) { + // Call request_lock_memory_privilege() and check the result + if (!os::win32::request_lock_memory_privilege()) { + GTEST_SKIP() << "Skipping test because lock memory privilege is not granted."; + } + // Set globals to make sure we hit the correct code path + AutoSaveRestore guardUseLargePages(UseLargePages); + AutoSaveRestore guardEnableAllLargePageSizesForWindows(EnableAllLargePageSizesForWindows); + AutoSaveRestore guardLargePageSizeInBytes(LargePageSizeInBytes); + FLAG_SET_CMDLINE(UseLargePages, true); + FLAG_SET_CMDLINE(EnableAllLargePageSizesForWindows, true); + + // Determine the minimum page size + const size_t min_size = GetLargePageMinimum(); + + // End the test if GetLargePageMinimum returns 0 + if (min_size == 0) { + GTEST_SKIP() << "Large pages are not supported on this system."; + return; + } + + // Set LargePageSizeInBytes to 4 times the minimum page size + FLAG_SET_CMDLINE(LargePageSizeInBytes, 4 * min_size); // Set a value for multiple page sizes + + // Initialize large page settings + os::large_page_init(); + + // Verify that large pages are enabled + EXPECT_TRUE(UseLargePages) << "UseLargePages should be true after initialization for LargePageSizeInBytes = 4 * min_size"; + + // Verify that decided_large_page_size is greater than the default page size + const size_t default_page_size = os::vm_page_size(); + size_t decided_large_page_size = os::win32::large_page_init_decide_size(); + EXPECT_GT(decided_large_page_size, default_page_size) << "Large page size should be greater than the default page size for LargePageSizeInBytes = 4 * min_size"; + +#if !defined(IA32) + size_t page_size_count = 0; + size_t page_size = os::page_sizes().largest(); + + do { + ++page_size_count; + page_size = os::page_sizes().next_smaller(page_size); + } while (page_size >= os::page_sizes().smallest()); + + EXPECT_GT(page_size_count, 1u) << "There should be multiple large page sizes available."; + + size_t large_page_size = decided_large_page_size; + + for (size_t page_size = os::page_sizes().largest(); page_size >= min_size; page_size = os::page_sizes().next_smaller(page_size)) { + EXPECT_TRUE(page_size % min_size == 0) << "Each page size should be a multiple of the minimum large page size."; + EXPECT_LE(page_size, large_page_size) << "Page size should not exceed the determined large page size."; + } +#endif +} + +TEST_VM(os_windows, large_page_init_decide_size) { + // Initial setup + // Call request_lock_memory_privilege() and check the result + if (!os::win32::request_lock_memory_privilege()) { + GTEST_SKIP() << "Skipping test because lock memory privilege is not granted."; + } + AutoSaveRestore guardUseLargePages(UseLargePages); + AutoSaveRestore guardLargePageSizeInBytes(LargePageSizeInBytes); + FLAG_SET_CMDLINE(UseLargePages, true); + FLAG_SET_CMDLINE(LargePageSizeInBytes, 0); // Reset to default + + // Test for large page support + size_t decided_size = os::win32::large_page_init_decide_size(); + size_t min_size = GetLargePageMinimum(); + if (min_size == 0) { + EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 when large page is not supported by the processor"; + return; + } + + // Scenario 1: Test with 2MB large page size + if (min_size == 2 * M) { + FLAG_SET_CMDLINE(LargePageSizeInBytes, 2 * M); // Set large page size to 2MB + decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size + EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page and OS reported size are both 2M"; + } + + // Scenario 2: Test with 1MB large page size + if (min_size == 2 * M) { + FLAG_SET_CMDLINE(LargePageSizeInBytes, 1 * M); // Set large page size to 1MB + decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size + EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page is 1M and OS reported size is 2M"; + } + +#if defined(IA32) || defined(AMD64) + FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * M); // Set large page size to 5MB + if (!EnableAllLargePageSizesForWindows) { + decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size + EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 for large pages bigger than 4mb on IA32 or AMD64"; + } +#endif + + // Additional check for non-multiple of minimum size + // Set an arbitrary large page size which is not a multiple of min_size + FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * min_size + 1); + + // Recalculate decided size + decided_size = os::win32::large_page_init_decide_size(); + + // Assert that the decided size defaults to minimum page size when LargePageSizeInBytes + // is not a multiple of the minimum size, assuming conditions are always met + EXPECT_EQ(decided_size, 0) << "Expected decided size to default to 0 when LargePageSizeInBytes is not a multiple of minimum size"; +} + class ReserveMemorySpecialRunnable : public TestRunnable { public: void runUnitTest() const {