< prev index next >

src/hotspot/os/linux/os_linux.cpp

Print this page

        

@@ -2946,10 +2946,25 @@
 // Define MAP_HUGETLB here so we can build HotSpot on old systems.
 #ifndef MAP_HUGETLB
   #define MAP_HUGETLB 0x40000
 #endif
 
+// mmap: If MAP_HUGETLB is set, and the system supports multiple huge page sizes,
+// flag bits [26:31] can be used to encode the log2 of the desired huge page size.
+// Otherwise the system's default huge page size will be used.
+#ifndef MAP_HUGE_SHIFT
+  #define MAP_HUGE_SHIFT 26
+#endif
+
+#ifndef MAP_HUGE_2MB
+  #define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT)
+#endif
+
+#ifndef MAP_HUGE_1GB
+  #define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT)
+#endif
+
 // Define MADV_HUGEPAGE here so we can build HotSpot on old systems.
 #ifndef MADV_HUGEPAGE
   #define MADV_HUGEPAGE 14
 #endif
 

@@ -3728,11 +3743,11 @@
 
 // Large page support
 
 static size_t _large_page_size = 0;
 
-size_t os::Linux::find_large_page_size() {
+size_t os::Linux::find_default_large_page_size() {
   size_t large_page_size = 0;
 
   // large_page_size on Linux is used to round up heap size. x86 uses either
   // 2M or 4M page, depending on whether PAE (Physical Address Extensions)
   // mode is enabled. AMD64/EM64T uses 2M page in 64bit mode. IA64 can use

@@ -3776,22 +3791,70 @@
         }
       }
     }
     fclose(fp);
   }
+  return large_page_size;
+}
 
-  if (!FLAG_IS_DEFAULT(LargePageSizeInBytes) && LargePageSizeInBytes != large_page_size) {
-    warning("Setting LargePageSizeInBytes has no effect on this OS. Large page size is "
-            SIZE_FORMAT "%s.", byte_size_in_proper_unit(large_page_size),
-            proper_unit_for_byte_size(large_page_size));
+void os::Linux::find_large_page_sizes() {
+  _page_sizes[0] = 0;
+  // We need to scan /sys/kernel/mm/hugepages
+  // to discover the available page sizes
+  const char* sys_hugepages = "/sys/kernel/mm/hugepages";
+  if (dir_is_empty(sys_hugepages)) {
+    return;
   }
 
-  return large_page_size;
+  DIR *dir = opendir(sys_hugepages);
+  if (dir == NULL) {
+    return;
+  }
+
+  struct dirent *entry;
+  size_t page_size;
+  int count = 0;
+
+  while (count < page_sizes_max - 1 && (entry = readdir(dir)) != NULL) {
+    if(entry->d_type == DT_DIR &&
+        sscanf(entry->d_name, "hugepages-%zukB", &page_size) ) {
+
+      // The kernel is using kB, hotspot uses bytes
+      _page_sizes[count] = page_size * K;
+      count++;
+    }
+  }
+  _page_sizes[count] = 0;
+  closedir(dir);
+  return;
+}
+
+bool os::Linux::is_valid_large_page_size(size_t page_size) {
+  find_large_page_sizes();
+  int count = 0;
+  while (_page_sizes[count] != 0) {
+    if (_page_sizes[count++] == page_size) {
+      return true;
+    }
+  }
+  return false;
 }
 
 size_t os::Linux::setup_large_page_size() {
-  _large_page_size = Linux::find_large_page_size();
+  _large_page_size = Linux::find_default_large_page_size();
+
+  if (!FLAG_IS_DEFAULT(LargePageSizeInBytes) && LargePageSizeInBytes != _large_page_size ) {
+     if (is_valid_large_page_size(LargePageSizeInBytes)) {
+       _large_page_size =  LargePageSizeInBytes;
+     } else {
+       warning("Setting LargePageSizeInBytes=" SIZE_FORMAT " has no effect on this OS. Default large page size is "
+               SIZE_FORMAT "%s.",
+               LargePageSizeInBytes,
+               byte_size_in_proper_unit(_large_page_size), proper_unit_for_byte_size(_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;

@@ -4026,13 +4089,18 @@
   assert(UseLargePages && UseHugeTLBFS, "only for Huge TLBFS large pages");
   assert(is_aligned(bytes, os::large_page_size()), "Unaligned size");
   assert(is_aligned(req_addr, os::large_page_size()), "Unaligned address");
 
   int prot = exec ? PROT_READ|PROT_WRITE|PROT_EXEC : PROT_READ|PROT_WRITE;
-  char* addr = (char*)::mmap(req_addr, bytes, prot,
-                             MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB,
-                             -1, 0);
+  int flags = MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB;
+
+  if (!FLAG_IS_DEFAULT(LargePageSizeInBytes)) {
+     flags |= (os::large_page_size() > (1 << (MAP_HUGE_2MB >> MAP_HUGE_SHIFT)))
+                ? MAP_HUGE_1GB
+                : MAP_HUGE_2MB;
+  }
+  char* addr = (char*)::mmap(req_addr, bytes, prot, flags, -1, 0);
 
   if (addr == MAP_FAILED) {
     warn_on_large_pages_failure(req_addr, bytes, errno);
     return NULL;
   }

@@ -4084,28 +4152,32 @@
     ::munmap(start, end - start);
     return NULL;
   }
 
   int prot = exec ? PROT_READ|PROT_WRITE|PROT_EXEC : PROT_READ|PROT_WRITE;
-
+  int flags = MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED;
   void* result;
 
   // Commit small-paged leading area.
   if (start != lp_start) {
-    result = ::mmap(start, lp_start - start, prot,
-                    MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,
-                    -1, 0);
+    result = ::mmap(start, lp_start - start, prot, flags, -1, 0);
     if (result == MAP_FAILED) {
       ::munmap(lp_start, end - lp_start);
       return NULL;
     }
   }
 
   // Commit large-paged area.
-  result = ::mmap(lp_start, lp_bytes, prot,
-                  MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED|MAP_HUGETLB,
-                  -1, 0);
+  flags |= MAP_HUGETLB;
+
+  if (!FLAG_IS_DEFAULT(LargePageSizeInBytes)) {
+     flags |= (os::large_page_size() > (1 << (MAP_HUGE_2MB >> MAP_HUGE_SHIFT)))
+                ? MAP_HUGE_1GB
+                : MAP_HUGE_2MB;
+  }
+
+  result = ::mmap(lp_start, lp_bytes, prot, flags, -1, 0);
   if (result == MAP_FAILED) {
     warn_on_large_pages_failure(lp_start, lp_bytes, errno);
     // If the mmap above fails, the large pages region will be unmapped and we
     // have regions before and after with small pages. Release these regions.
     //
< prev index next >