< prev index next >

src/os/linux/vm/os_linux.cpp

Print this page
rev 12363 : 8169373: Work around linux NPTL stack guard error.
Summary: Also skip libc guard page for compiler thread, merge similar code on linux platforms, and streamline libc guard page handling on linuxs390, linuxppc, aixppc.
Reviewed-by: dholmes, dcubed, kvn

@@ -721,15 +721,22 @@
   // init thread attributes
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
-  // calculate stack size if it's not specified by caller
+  // Calculate stack size if it's not specified by caller.
   size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size);
+  // In the Linux NPTL pthread implementation the guard size mechanism
+  // is not implemented properly. The posix standard requires adding
+  // the size of the guard pages to the stack size, instead Linux
+  // takes the space out of 'stacksize'. Thus we adapt the requested
+  // stack_size by the size of the guard pages to mimick proper
+  // behaviour.
+  stack_size = align_size_up(stack_size + os::Linux::default_guard_size(thr_type), vm_page_size());
   pthread_attr_setstacksize(&attr, stack_size);
 
-  // glibc guard page
+  // Configure glibc guard page.
   pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));
 
   ThreadState state;
 
   {

@@ -2838,10 +2845,17 @@
     }
   }
   return false;
 }
 
+size_t os::Linux::default_guard_size(os::ThreadType thr_type) {
+  // Creating guard page is very expensive. Java thread has HotSpot
+  // guard pages, only enable glibc guard page for non-Java threads.
+  // (Remember: compiler thread is a Java thread, too!)
+  return ((thr_type == java_thread || thr_type == compiler_thread) ? 0 : page_size());
+}
+
 // rebuild_cpu_to_node_map() constructs a table mapping cpud id to node id.
 // The table is later used in get_node_by_cpu().
 void os::Linux::rebuild_cpu_to_node_map() {
   const size_t NCPUS = 32768; // Since the buffer size computation is very obscure
                               // in libnuma (possible values are starting from 16,

@@ -6070,10 +6084,105 @@
     yes = false;
   }
   return yes;
 }
 
+
+// Java/Compiler thread:
+//
+//   Low memory addresses
+// P0 +------------------------+
+//    |                        |\  Java thread created by VM does not have glibc
+//    |    glibc guard page    | - guard page, attached Java thread usually has
+//    |                        |/  1 glibc guard page.
+// P1 +------------------------+ Thread::stack_base() - Thread::stack_size()
+//    |                        |\
+//    |  HotSpot Guard Pages   | - red, yellow and reserved pages
+//    |                        |/
+//    +------------------------+ JavaThread::stack_reserved_zone_base()
+//    |                        |\
+//    |      Normal Stack      | -
+//    |                        |/
+// P2 +------------------------+ Thread::stack_base()
+//
+// Non-Java thread:
+//
+//   Low memory addresses
+// P0 +------------------------+
+//    |                        |\
+//    |  glibc guard page      | - usually 1 page
+//    |                        |/
+// P1 +------------------------+ Thread::stack_base() - Thread::stack_size()
+//    |                        |\
+//    |      Normal Stack      | -
+//    |                        |/
+// P2 +------------------------+ Thread::stack_base()
+//
+// ** P1 (aka bottom) and size (P2 = P1 - size) are the address and stack size
+//    returned from pthread_attr_getstack().
+// ** Due to NPTL implementation error, linux takes the glibc guard page out
+//    of the stack size given in pthread_attr. We work around this for
+//    threads created by the VM. (We adapt bottom to be P1 and size accordingly.)
+//
+#ifndef ZERO
+static void current_stack_region(address * bottom, size_t * size) {
+  if (os::Linux::is_initial_thread()) {
+    // initial thread needs special handling because pthread_getattr_np()
+    // may return bogus value.
+    *bottom = os::Linux::initial_thread_stack_bottom();
+    *size   = os::Linux::initial_thread_stack_size();
+  } else {
+    pthread_attr_t attr;
+
+    int rslt = pthread_getattr_np(pthread_self(), &attr);
+
+    // JVM needs to know exact stack location, abort if it fails
+    if (rslt != 0) {
+      if (rslt == ENOMEM) {
+        vm_exit_out_of_memory(0, OOM_MMAP_ERROR, "pthread_getattr_np");
+      } else {
+        fatal("pthread_getattr_np failed with error = %d", rslt);
+      }
+    }
+
+    if (pthread_attr_getstack(&attr, (void **)bottom, size) != 0) {
+      fatal("Cannot locate current stack attributes!");
+    }
+
+    // Work around NPTL stack guard error.
+    size_t guard_size = 0;
+    rslt = pthread_attr_getguardsize(&attr, &guard_size);
+    if (rslt != 0) {
+      fatal("pthread_attr_getguardsize failed with error = %d", rslt);
+    }
+    *bottom += guard_size;
+    *size   -= guard_size;
+
+    pthread_attr_destroy(&attr);
+
+  }
+  assert(os::current_stack_pointer() >= *bottom &&
+         os::current_stack_pointer() < *bottom + *size, "just checking");
+}
+
+address os::current_stack_base() {
+  address bottom;
+  size_t size;
+  current_stack_region(&bottom, &size);
+  return (bottom + size);
+}
+
+size_t os::current_stack_size() {
+  // This stack size includes the usable stack and HotSpot guard pages
+  // (for the threads that have Hotspot guard pages).
+  address bottom;
+  size_t size;
+  current_stack_region(&bottom, &size);
+  return size;
+}
+#endif
+
 static inline struct timespec get_mtime(const char* filename) {
   struct stat st;
   int ret = os::stat(filename, &st);
   assert(ret == 0, "failed to stat() file '%s': %s", filename, strerror(errno));
   return st.st_mtim;
< prev index next >