From 3d659eff6e39875dcdf671be616167b7226ed57c Mon Sep 17 00:00:00 2001
From: Stefan Karlsson <stefank@openjdk.org>
Date: Wed, 20 May 2020 08:56:40 +0200
Subject: [PATCH] 8245000: Windows GDI functions don't support large pages

Reviewed-by: kbarrett, sjohanss
---
 src/hotspot/os/windows/os_windows.cpp | 146 ++++++++++++++++----------
 1 file changed, 89 insertions(+), 57 deletions(-)

diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index 983145d035b..6a31ada9c76 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -2716,9 +2716,6 @@ int os::vm_allocation_granularity() {
   #define MEM_LARGE_PAGES 0x20000000
 #endif
 
-static HANDLE    _hProcess;
-static HANDLE    _hToken;
-
 #define VirtualFreeChecked(mem, size, type)                       \
   do {                                                            \
     bool ret = VirtualFree(mem, size, type);                      \
@@ -2784,12 +2781,12 @@ static bool gdi_can_use_memory(void* mem) {
 
 // Test if GDI functions work when memory spans
 // two adjacent memory reservations.
-static bool gdi_can_use_split_reservation_memory() {
-  size_t granule = os::vm_allocation_granularity();
+static bool gdi_can_use_split_reservation_memory(bool use_large_pages, size_t granule) {
+  DWORD mem_large_pages = use_large_pages ? MEM_LARGE_PAGES : 0;
 
-  // Find virtual memory range
+  // Find virtual memory range. Two granules for regions and one for alignment.
   void* reserved = VirtualAlloc(NULL,
-                                granule * 2,
+                                granule * 3,
                                 MEM_RESERVE,
                                 PAGE_NOACCESS);
   if (reserved == NULL) {
@@ -2798,13 +2795,14 @@ static bool gdi_can_use_split_reservation_memory() {
   }
   VirtualFreeChecked(reserved, 0, MEM_RELEASE);
 
-  void* res0 = reserved;
-  void* res1 = (char*)reserved + granule;
+  // Ensure proper alignment
+  void* res0 = align_up(reserved, granule);
+  void* res1 = (char*)res0 + granule;
 
   // Reserve and commit the first part
   void* mem0 = VirtualAlloc(res0,
                             granule,
-                            MEM_RESERVE|MEM_COMMIT,
+                            MEM_RESERVE|MEM_COMMIT|mem_large_pages,
                             PAGE_READWRITE);
   if (mem0 != res0) {
     // Can't proceed with test - pessimistically report false
@@ -2814,7 +2812,7 @@ static bool gdi_can_use_split_reservation_memory() {
   // Reserve and commit the second part
   void* mem1 = VirtualAlloc(res1,
                             granule,
-                            MEM_RESERVE|MEM_COMMIT,
+                            MEM_RESERVE|MEM_COMMIT|mem_large_pages,
                             PAGE_READWRITE);
   if (mem1 != res1) {
     VirtualFreeChecked(mem0, 0, MEM_RELEASE);
@@ -2881,17 +2879,17 @@ class NUMANodeListHolder {
 
 } numa_node_list_holder;
 
-
-
 static size_t _large_page_size = 0;
 
 static bool request_lock_memory_privilege() {
-  _hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
-                          os::current_process_id());
+  HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
+                                os::current_process_id());
 
+  bool success = false;
+  HANDLE hToken = NULL;
   LUID luid;
-  if (_hProcess != NULL &&
-      OpenProcessToken(_hProcess, TOKEN_ADJUST_PRIVILEGES, &_hToken) &&
+  if (hProcess != NULL &&
+      OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken) &&
       LookupPrivilegeValue(NULL, "SeLockMemoryPrivilege", &luid)) {
 
     TOKEN_PRIVILEGES tp;
@@ -2901,20 +2899,21 @@ static bool request_lock_memory_privilege() {
 
     // AdjustTokenPrivileges() may return TRUE even when it couldn't change the
     // privilege. Check GetLastError() too. See MSDN document.
-    if (AdjustTokenPrivileges(_hToken, false, &tp, sizeof(tp), NULL, NULL) &&
+    if (AdjustTokenPrivileges(hToken, false, &tp, sizeof(tp), NULL, NULL) &&
         (GetLastError() == ERROR_SUCCESS)) {
-      return true;
+      success = true;
     }
   }
 
-  return false;
-}
+  // Cleanup
+  if (hProcess != NULL) {
+    CloseHandle(hProcess);
+  }
+  if (hToken != NULL) {
+    CloseHandle(hToken);
+  }
 
-static void cleanup_after_large_page_init() {
-  if (_hProcess) CloseHandle(_hProcess);
-  _hProcess = NULL;
-  if (_hToken) CloseHandle(_hToken);
-  _hToken = NULL;
+  return success;
 }
 
 static bool numa_interleaving_init() {
@@ -2935,7 +2934,7 @@ static bool numa_interleaving_init() {
     return false;
   }
 
-  if (!gdi_can_use_split_reservation_memory()) {
+  if (!gdi_can_use_split_reservation_memory(UseLargePages, min_interleave_granularity)) {
     WARN("Windows GDI cannot handle split reservations.");
     WARN("...Ignoring UseNUMAInterleaving flag.");
     return false;
@@ -3072,51 +3071,84 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags,
   return p_buf;
 }
 
-
-
-void os::large_page_init() {
-  if (!UseLargePages) return;
-
+static size_t 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);
-  bool success = false;
 
 #define WARN(msg) if (warn_on_failure) { warning(msg); }
-  if (request_lock_memory_privilege()) {
-    size_t s = GetLargePageMinimum();
-    if (s) {
-#if defined(IA32) || defined(AMD64)
-      if (s > 4*M || LargePageSizeInBytes > 4*M) {
-        WARN("JVM cannot use large pages bigger than 4mb.");
-      } else {
-#endif
-        if (LargePageSizeInBytes && LargePageSizeInBytes % s == 0) {
-          _large_page_size = LargePageSizeInBytes;
-        } else {
-          _large_page_size = s;
-        }
-        success = true;
-#if defined(IA32) || defined(AMD64)
-      }
-#endif
-    } else {
-      WARN("Large page is not supported by the processor.");
-    }
-  } else {
+
+  if (!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;
   }
+
+  size_t size = GetLargePageMinimum();
+  if (size == 0) {
+    WARN("Large page is not supported by the processor.");
+    return 0;
+  }
+
+#if defined(IA32) || defined(AMD64)
+  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;
+  }
+
+  // Now test allocating a page
+  void* large_page = VirtualAlloc(NULL,
+                                  size,
+                                  MEM_RESERVE|MEM_COMMIT|MEM_LARGE_PAGES,
+                                  PAGE_READWRITE);
+  if (large_page == NULL) {
+    WARN("JVM cannot allocate one single large page.");
+    return 0;
+  }
+
+  // Detect if GDI can use memory backed by large pages
+  if (!gdi_can_use_memory(large_page)) {
+    WARN("JVM cannot use large pages because of bug in Windows GDI.");
+    return 0;
+  }
+
+  // Release test page
+  VirtualFreeChecked(large_page, 0, MEM_RELEASE);
+
 #undef WARN
 
+  return size;
+}
+
+void os::large_page_init() {
+  if (!UseLargePages) {
+    return;
+  }
+
+  _large_page_size = large_page_init_decide_size();
+
   const size_t default_page_size = (size_t) vm_page_size();
-  if (success && _large_page_size > default_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;
   }
 
-  cleanup_after_large_page_init();
-  UseLargePages = success;
+  UseLargePages = _large_page_size != 0;
+
+  if (UseLargePages && UseLargePagesIndividualAllocation) {
+    if (!gdi_can_use_split_reservation_memory(true /* use_large_pages */, _large_page_size)) {
+      if (FLAG_IS_CMDLINE(UseLargePagesIndividualAllocation)) {
+        warning("Windows GDI cannot handle split reservations.");
+        warning("...Ignoring UseLargePagesIndividualAllocation flag.");
+      }
+      UseLargePagesIndividualAllocation = false;
+    }
+  }
 }
 
 int os::create_file_for_heap(const char* dir) {