diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index c6a996d69c3..daec53fa132 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -3550,8 +3550,23 @@ bool os::remove_stack_guard_pages(char* addr, size_t size) { // may not start from the requested address. Unlike Linux mmap(), this // function returns null to indicate failure. static char* anon_mmap(char* requested_addr, size_t bytes) { - // MAP_FIXED is intentionally left out, to leave existing mappings intact. - const int flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS; + // If a requested address was given: + // + // The POSIX-conforming way is to *omit* MAP_FIXED. This will leave existing mappings intact. + // If the requested mapping area is blocked by a pre-existing mapping, the kernel will map + // somewhere else. On Linux, that alternative address appears to have no relation to the + // requested address. + // Unfortunately, this is not what we need - if we requested a specific address, we'd want + // to map there and nowhere else. Therefore we will unmap the block again, which means we + // just executed a needless mmap->munmap cycle. + // Since Linux 4.17, the kernel offers MAP_FIXED_NOREPLACE. With this flag, if a pre- + // existing mapping exists, the kernel will not map at an alternative point but instead + // return an error. We can therefore save that unnecessary mmap-munmap cycle. + // + // Backward compatibility: Older kernels will ignore the unknown flag; so mmap will behave + // as in mode (a). + const int flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS | + ((requested_addr != nullptr) ? MAP_FIXED_NOREPLACE : 0); // Map reserved/uncommitted pages PROT_NONE so we fail early if we // touch an uncommitted page. Otherwise, the read/write might @@ -4320,6 +4335,7 @@ char* os::pd_attempt_reserve_memory_at(char* requested_addr, size_t bytes, bool if (addr != nullptr) { // mmap() is successful but it fails to reserve at the requested address + log_trace(os, map)("Kernel rejected " PTR_FORMAT ", offered " PTR_FORMAT ".", p2i(requested_addr), p2i(addr)); anon_munmap(addr, bytes); } diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index fb33cf9c4f9..42b9eaf8450 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -931,3 +931,24 @@ TEST_VM(os, open_O_CLOEXEC) { ::close(fd); #endif } + +TEST_VM(os, reserve_at_wish_address_shall_not_replace_mappings_smallpages) { + char* p1 = os::reserve_memory(M, false, mtTest); + ASSERT_NE(p1, nullptr); + char* p2 = os::attempt_reserve_memory_at(p1, M); + ASSERT_EQ(p2, nullptr); // should have failed + os::release_memory(p1, M); +} + +TEST_VM(os, reserve_at_wish_address_shall_not_replace_mappings_largepages) { + if (UseLargePages && !os::can_commit_large_page_memory()) { // aka special + const size_t lpsz = os::large_page_size(); + char* p1 = os::reserve_memory_aligned(lpsz, lpsz, false); + ASSERT_NE(p1, nullptr); + char* p2 = os::reserve_memory_special(lpsz, lpsz, lpsz, p1, false); + ASSERT_EQ(p2, nullptr); // should have failed + os::release_memory(p1, M); + } else { + tty->print_cr("Skipped."); + } +}