diff --git a/src/hotspot/os/linux/gc/z/zLargePages_linux.cpp b/src/hotspot/os/linux/gc/z/zLargePages_linux.cpp index caf70224599..a00572f08e7 100644 --- a/src/hotspot/os/linux/gc/z/zLargePages_linux.cpp +++ b/src/hotspot/os/linux/gc/z/zLargePages_linux.cpp @@ -23,16 +23,24 @@ #include "precompiled.hpp" #include "gc/z/zLargePages.hpp" +#include "hugepages.hpp" +#include "os_linux.hpp" #include "runtime/globals.hpp" void ZLargePages::pd_initialize() { - if (UseLargePages) { - if (UseTransparentHugePages) { - _state = Transparent; - } else { - _state = Explicit; - } - } else { - _state = Disabled; + if (os::Linux::thp_requested()) { + // Check if the OS config turned off transparent huge pages for shmem. + _os_enforced_transparent_mode = HugePages::shmem_thp_info().is_disabled(); + _state = _os_enforced_transparent_mode ? Disabled : Transparent; + return; } + + if (UseLargePages) { + _state = Explicit; + return; + } + + // Check if the OS config turned on transparent huge pages for shmem. + _os_enforced_transparent_mode = HugePages::shmem_thp_info().is_forced(); + _state = _os_enforced_transparent_mode ? Transparent : Disabled; } diff --git a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp index fb01e05ff78..ff891509365 100644 --- a/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp +++ b/src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp @@ -32,6 +32,7 @@ #include "gc/z/zNUMA.inline.hpp" #include "gc/z/zPhysicalMemoryBacking_linux.hpp" #include "gc/z/zSyscall_linux.hpp" +#include "hugepages.hpp" #include "logging/log.hpp" #include "os_linux.hpp" #include "runtime/init.hpp" @@ -446,8 +447,10 @@ ZErrno ZPhysicalMemoryBacking::fallocate_compat_mmap_tmpfs(zoffset offset, size_ return errno; } - // Advise mapping to use transparent huge pages - os::realign_memory((char*)addr, length, ZGranuleSize); + // Maybe madvise the mapping to use transparent huge pages + if (os::Linux::should_madvise_shmem_thps()) { + os::Linux::madvise_transparent_huge_pages(addr, length); + } // Touch the mapping (safely) to make sure it's backed by memory const bool backed = safe_touch_mapping(addr, length, _block_size); diff --git a/src/hotspot/os/linux/hugepages.cpp b/src/hotspot/os/linux/hugepages.cpp index f9f9dd497c7..67645b91aa5 100644 --- a/src/hotspot/os/linux/hugepages.cpp +++ b/src/hotspot/os/linux/hugepages.cpp @@ -28,6 +28,7 @@ #include "logging/log.hpp" #include "logging/logStream.hpp" +#include "runtime/globals_extension.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" @@ -227,15 +228,97 @@ void THPSupport::print_on(outputStream* os) { } } +ShmemTHPSupport::ShmemTHPSupport() : + _initialized(false), _mode(ShmemTHPMode::unknown) {} + +ShmemTHPMode ShmemTHPSupport::mode() const { + assert(_initialized, "Not initialized"); + return _mode; +} + +bool ShmemTHPSupport::is_forced() const { + return _mode == ShmemTHPMode::always || _mode == ShmemTHPMode::force || _mode == ShmemTHPMode::within_size; +} + +bool ShmemTHPSupport::is_enabled() const { + return is_forced() || _mode == ShmemTHPMode::advise; +} + +bool ShmemTHPSupport::is_disabled() const { + return _mode == ShmemTHPMode::never || _mode == ShmemTHPMode::deny || _mode == ShmemTHPMode::unknown; +} + +void ShmemTHPSupport::scan_os() { + // Scan /sys/kernel/mm/transparent_hugepage/shmem_enabled + // see mm/huge_memory.c + _mode = ShmemTHPMode::unknown; + const char* filename = "/sys/kernel/mm/transparent_hugepage/shmem_enabled"; + FILE* f = ::fopen(filename, "r"); + if (f != nullptr) { + char buf[64]; + char* s = fgets(buf, sizeof(buf), f); + assert(s == buf, "Should have worked"); + if (::strstr(buf, "[always]") != nullptr) { + _mode = ShmemTHPMode::always; + } else if (::strstr(buf, "[within_size]") != nullptr) { + _mode = ShmemTHPMode::within_size; + } else if (::strstr(buf, "[advise]") != nullptr) { + _mode = ShmemTHPMode::advise; + } else if (::strstr(buf, "[never]") != nullptr) { + _mode = ShmemTHPMode::never; + } else if (::strstr(buf, "[deny]") != nullptr) { + _mode = ShmemTHPMode::deny; + } else if (::strstr(buf, "[force]") != nullptr) { + _mode = ShmemTHPMode::force; + } else { + assert(false, "Weird content of %s: %s", filename, buf); + } + fclose(f); + } + + _initialized = true; + + LogTarget(Info, pagesize) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + print_on(&ls); + } +} + +const char* ShmemTHPSupport::mode_to_string(ShmemTHPMode mode) { + switch (mode) { + case ShmemTHPMode::always: return "always"; + case ShmemTHPMode::advise: return "advise"; + case ShmemTHPMode::within_size: return "within_size"; + case ShmemTHPMode::never: return "never"; + case ShmemTHPMode::deny: return "deny"; + case ShmemTHPMode::force: return "force"; + case ShmemTHPMode::unknown: // Fallthrough + default: return "unknown"; + }; +} + +void ShmemTHPSupport::print_on(outputStream* os) { + if (_initialized) { + os->print_cr("Shared memory transparent hugepage (THP) support:"); + os->print_cr(" Shared memory THP mode: %s", mode_to_string(_mode)); + } else { + os->print_cr(" unknown."); + } +} + StaticHugePageSupport HugePages::_static_hugepage_support; THPSupport HugePages::_thp_support; +ShmemTHPSupport HugePages::_shmem_thp_support; void HugePages::initialize() { _static_hugepage_support.scan_os(); _thp_support.scan_os(); + _shmem_thp_support.scan_os(); } void HugePages::print_on(outputStream* os) { _static_hugepage_support.print_on(os); _thp_support.print_on(os); + _shmem_thp_support.print_on(os); } diff --git a/src/hotspot/os/linux/hugepages.hpp b/src/hotspot/os/linux/hugepages.hpp index cb7c992d789..ce9ab36edcc 100644 --- a/src/hotspot/os/linux/hugepages.hpp +++ b/src/hotspot/os/linux/hugepages.hpp @@ -91,23 +91,58 @@ public: void print_on(outputStream* os); }; +enum class ShmemTHPMode { always, within_size, advise, never, deny, force, unknown }; + +// for transparent shmem hugepages +class ShmemTHPSupport { + bool _initialized; + + // See /sys/kernel/mm/transparent_hugepage/shmem_enabled + ShmemTHPMode _mode; + + static const char* mode_to_string(ShmemTHPMode mode); + +public: + + ShmemTHPSupport(); + + // Queries the OS, fills in object + void scan_os(); + + ShmemTHPMode mode() const; + + bool is_forced() const; + bool is_enabled() const; + bool is_disabled() const; + + // Printing + void print_on(outputStream* os); +}; + // Umbrella static interface class HugePages : public AllStatic { static StaticHugePageSupport _static_hugepage_support; static THPSupport _thp_support; + static ShmemTHPSupport _shmem_thp_support; public: static const StaticHugePageSupport& static_info() { return _static_hugepage_support; } static const THPSupport& thp_info() { return _thp_support; } + static const ShmemTHPSupport& shmem_thp_info() { return _shmem_thp_support; } static size_t default_static_hugepage_size() { return _static_hugepage_support.default_hugepage_size(); } static bool supports_static_hugepages() { return default_static_hugepage_size() > 0 && !_static_hugepage_support.inconsistent(); } - static THPMode thp_mode() { return _thp_support.mode(); } + static bool supports_thp() { return thp_mode() == THPMode::madvise || thp_mode() == THPMode::always; } + static THPMode thp_mode() { return _thp_support.mode(); } static size_t thp_pagesize() { return _thp_support.pagesize(); } + static bool supports_shmem_thp() { return _shmem_thp_support.is_enabled(); } + static ShmemTHPMode shmem_thp_mode() { return _shmem_thp_support.mode(); } + static bool forced_shmem_thp() { return _shmem_thp_support.is_forced(); } + static void initialize(); static void print_on(outputStream* os); }; diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 7bde97de907..3fc025e5100 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -176,6 +176,8 @@ bool os::Linux::_supports_fast_thread_cpu_time = false; const char * os::Linux::_libc_version = nullptr; const char * os::Linux::_libpthread_version = nullptr; +bool os::Linux::_thp_requested{false}; + #ifdef __GLIBC__ // We want to be buildable and runnable on older and newer glibcs, so resolve both // mallinfo and mallinfo2 dynamically. @@ -2213,6 +2215,8 @@ void os::Linux::print_system_memory_info(outputStream* st) { // https://www.kernel.org/doc/Documentation/vm/transhuge.txt _print_ascii_file_h("/sys/kernel/mm/transparent_hugepage/enabled", "/sys/kernel/mm/transparent_hugepage/enabled", st); + _print_ascii_file_h("/sys/kernel/mm/transparent_hugepage/shmem_enabled", + "/sys/kernel/mm/transparent_hugepage/shmem_enabled", st); _print_ascii_file_h("/sys/kernel/mm/transparent_hugepage/defrag (defrag/compaction efforts parameter)", "/sys/kernel/mm/transparent_hugepage/defrag", st); } @@ -2909,11 +2913,15 @@ void os::pd_commit_memory_or_exit(char* addr, size_t size, } } +void os::Linux::madvise_transparent_huge_pages(void* addr, size_t bytes) { + // We don't check the return value: madvise(MADV_HUGEPAGE) may not + // be supported or the memory may already be backed by huge pages. + ::madvise(addr, bytes, MADV_HUGEPAGE); +} + void os::pd_realign_memory(char *addr, size_t bytes, size_t alignment_hint) { - if (UseTransparentHugePages && alignment_hint > vm_page_size()) { - // We don't check the return value: madvise(MADV_HUGEPAGE) may not - // be supported or the memory may already be backed by huge pages. - ::madvise(addr, bytes, MADV_HUGEPAGE); + if (Linux::should_madvise_anonymous_thps() && alignment_hint > vm_page_size()) { + Linux::madvise_transparent_huge_pages(addr, bytes); } } @@ -3730,7 +3738,7 @@ static void set_coredump_filter(CoredumpFilterBit bit) { static size_t _large_page_size = 0; -void warn_no_large_pages_configured() { +static void warn_no_large_pages_configured() { if (!FLAG_IS_DEFAULT(UseLargePages)) { log_warning(pagesize)("UseLargePages disabled, no large pages configured and available on the system."); } @@ -3747,15 +3755,56 @@ struct LargePageInitializationLoggerMark { os::page_sizes().print_on(&ls); ls.print_cr(". Default large page size: " EXACTFMT ".", EXACTFMTARGS(os::large_page_size())); } else { - ls.print("Large page support disabled."); + ls.print("Large page support %sdisabled.", uses_zgc_shmem_thp() ? "partially " : ""); } } } + + static bool uses_zgc_shmem_thp() { + return UseZGC && + // If user requested THP + ((os::Linux::thp_requested() && HugePages::supports_shmem_thp()) || + // If OS forced THP + HugePages::forced_shmem_thp()); + } }; +static bool validate_thps_configured() { + assert(UseTransparentHugePages, "Sanity"); + assert(os::Linux::thp_requested(), "Sanity"); + + if (UseZGC) { + if (!HugePages::supports_shmem_thp()) { + log_warning(pagesize)("Shared memory transparent huge pages are not enabled in the OS. " + "Set /sys/kernel/mm/transparent_hugepage/shmem_enabled to 'advise' to enable them."); + // UseTransparentHugePages has historically been tightly coupled with + // anonymous THPs. Fall through here and let the validity be determined + // by the OS configuration for anonymous THPs. ZGC doesn't use the flag + // but instead checks os::Linux::thp_requested(). + } + } + + if (!HugePages::supports_thp()) { + log_warning(pagesize)("Anonymous transparent huge pages are not enabled in the OS. " + "Set /sys/kernel/mm/transparent_hugepage/enabled to 'madvise' to enable them."); + log_warning(pagesize)("UseTransparentHugePages disabled, transparent huge pages are not supported by the operating system."); + return false; + } + + return true; +} + void os::large_page_init() { + Linux::large_page_init(); +} + +void os::Linux::large_page_init() { LargePageInitializationLoggerMark logger; + // Decide if the user asked for THPs before we update UseTransparentHugePages. + const bool large_pages_turned_off = !FLAG_IS_DEFAULT(UseLargePages) && !UseLargePages; + _thp_requested = UseTransparentHugePages && !large_pages_turned_off; + // Query OS information first. HugePages::initialize(); @@ -3774,7 +3823,7 @@ void os::large_page_init() { FLAG_SET_ERGO(THPStackMitigation, false); // Mitigation not needed } - // 1) Handle the case where we do not want to use huge pages + // Handle the case where we do not want to use huge pages if (!UseLargePages && !UseTransparentHugePages) { // Not using large pages. @@ -3787,17 +3836,16 @@ void os::large_page_init() { return; } - // 2) check if the OS supports THPs resp. static hugepages. - if (UseTransparentHugePages && !HugePages::supports_thp()) { - if (!FLAG_IS_DEFAULT(UseTransparentHugePages)) { - log_warning(pagesize)("UseTransparentHugePages disabled, transparent huge pages are not supported by the operating system."); - } + // Check if the OS supports THPs + if (UseTransparentHugePages && !validate_thps_configured()) { UseLargePages = UseTransparentHugePages = false; return; } + + // Check if the OS supports static hugepages. if (!UseTransparentHugePages && !HugePages::supports_static_hugepages()) { warn_no_large_pages_configured(); - UseLargePages = UseTransparentHugePages = false; + UseLargePages = false; return; } @@ -3805,7 +3853,7 @@ void os::large_page_init() { // In THP mode: // - os::large_page_size() is the *THP page size* // - os::pagesizes() has two members, the THP page size and the system page size - assert(HugePages::supports_thp() && HugePages::thp_pagesize() > 0, "Missing OS info"); + assert(HugePages::thp_pagesize() > 0, "Missing OS info"); _large_page_size = HugePages::thp_pagesize(); _page_sizes.add(_large_page_size); _page_sizes.add(os::vm_page_size()); @@ -3830,12 +3878,12 @@ void os::large_page_init() { // doesn't match an available page size set _large_page_size to default_large_page_size // and use it as the maximum. if (FLAG_IS_DEFAULT(LargePageSizeInBytes) || - LargePageSizeInBytes == 0 || - LargePageSizeInBytes == default_large_page_size) { - large_page_size = default_large_page_size; - log_info(pagesize)("Using the default large page size: " SIZE_FORMAT "%s", - byte_size_in_exact_unit(large_page_size), - exact_unit_for_byte_size(large_page_size)); + LargePageSizeInBytes == 0 || + LargePageSizeInBytes == default_large_page_size) { + large_page_size = default_large_page_size; + log_info(pagesize)("Using the default large page size: " SIZE_FORMAT "%s", + byte_size_in_exact_unit(large_page_size), + exact_unit_for_byte_size(large_page_size)); } else { if (all_large_pages.contains(LargePageSizeInBytes)) { large_page_size = LargePageSizeInBytes; @@ -3860,7 +3908,6 @@ void os::large_page_init() { if (!hugetlbfs_sanity_check(large_page_size)) { warn_no_large_pages_configured(); UseLargePages = false; - UseTransparentHugePages = false; return; } @@ -3877,6 +3924,18 @@ void os::large_page_init() { set_coredump_filter(LARGEPAGES_BIT); } +bool os::Linux::thp_requested() { + return _thp_requested; +} + +bool os::Linux::should_madvise_anonymous_thps() { + return _thp_requested && HugePages::thp_mode() == THPMode::madvise; +} + +bool os::Linux::should_madvise_shmem_thps() { + return _thp_requested && HugePages::shmem_thp_mode() == ShmemTHPMode::advise; +} + static void log_on_commit_special_failure(char* req_addr, size_t bytes, size_t page_size, int error) { assert(error == ENOMEM, "Only expect to fail if no memory is available"); @@ -3891,7 +3950,8 @@ static bool commit_memory_special(size_t bytes, size_t page_size, char* req_addr, bool exec) { - assert(UseLargePages && !UseTransparentHugePages, "Should only get here for static hugepage mode (+UseLargePages)"); + assert(UseLargePages, "Should only get here for huge pages"); + assert(!UseTransparentHugePages, "Should only get here for static hugepage mode"); assert(is_aligned(bytes, page_size), "Unaligned size"); assert(is_aligned(req_addr, page_size), "Unaligned address"); assert(req_addr != nullptr, "Must have a requested address for special mappings"); diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp index e8f6f486489..4b2ccf8e370 100644 --- a/src/hotspot/os/linux/os_linux.hpp +++ b/src/hotspot/os/linux/os_linux.hpp @@ -175,6 +175,17 @@ class os::Linux { // fields will contain -1. static bool query_process_memory_info(meminfo_t* info); + // Tells if the user asked for transparent huge pages. + static bool _thp_requested; + + static void large_page_init(); + + static bool thp_requested(); + static bool should_madvise_anonymous_thps(); + static bool should_madvise_shmem_thps(); + + static void madvise_transparent_huge_pages(void* addr, size_t bytes); + // Stack repair handling // none present diff --git a/src/hotspot/share/gc/z/zLargePages.cpp b/src/hotspot/share/gc/z/zLargePages.cpp index 88656d15381..a65bf3b10eb 100644 --- a/src/hotspot/share/gc/z/zLargePages.cpp +++ b/src/hotspot/share/gc/z/zLargePages.cpp @@ -27,6 +27,7 @@ #include "runtime/os.hpp" ZLargePages::State ZLargePages::_state; +bool ZLargePages::_os_enforced_transparent_mode; void ZLargePages::initialize() { pd_initialize(); @@ -41,9 +42,17 @@ const char* ZLargePages::to_string() { return "Enabled (Explicit)"; case Transparent: - return "Enabled (Transparent)"; + if (_os_enforced_transparent_mode) { + return "Enabled (Transparent, OS enforced)"; + } else { + return "Enabled (Transparent)"; + } default: - return "Disabled"; + if (_os_enforced_transparent_mode) { + return "Disabled (OS enforced)"; + } else { + return "Disabled"; + } } } diff --git a/src/hotspot/share/gc/z/zLargePages.hpp b/src/hotspot/share/gc/z/zLargePages.hpp index 9f7c8310e50..72b9e85095c 100644 --- a/src/hotspot/share/gc/z/zLargePages.hpp +++ b/src/hotspot/share/gc/z/zLargePages.hpp @@ -35,6 +35,7 @@ private: }; static State _state; + static bool _os_enforced_transparent_mode; static void pd_initialize(); diff --git a/test/hotspot/jtreg/runtime/os/HugePageConfiguration.java b/test/hotspot/jtreg/runtime/os/HugePageConfiguration.java index f475af4c2de..4607f1bcf85 100644 --- a/test/hotspot/jtreg/runtime/os/HugePageConfiguration.java +++ b/test/hotspot/jtreg/runtime/os/HugePageConfiguration.java @@ -25,7 +25,6 @@ import jdk.test.lib.process.OutputAnalyzer; import java.io.*; -import java.util.Set; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -68,6 +67,9 @@ class HugePageConfiguration { THPMode _thpMode; long _thpPageSize; + enum ShmemTHPMode {always, within_size, advise, never, deny, force, unknown} + ShmemTHPMode _shmemThpMode; + public Set getStaticHugePageConfigurations() { return _staticHugePageConfigurations; } @@ -89,16 +91,21 @@ class HugePageConfiguration { return _thpMode == THPMode.always || _thpMode == THPMode.madvise; } + public ShmemTHPMode getShmemThpMode() { + return _shmemThpMode; + } + // Returns true if static huge pages are supported (whether or not we have configured the pools) public boolean supportsStaticHugePages() { return _staticDefaultHugePageSize > 0 && _staticHugePageConfigurations.size() > 0; } - public HugePageConfiguration(Set _staticHugePageConfigurations, long _staticDefaultHugePageSize, THPMode _thpMode, long _thpPageSize) { + public HugePageConfiguration(Set _staticHugePageConfigurations, long _staticDefaultHugePageSize, THPMode _thpMode, long _thpPageSize, ShmemTHPMode _shmemThpMode) { this._staticHugePageConfigurations = _staticHugePageConfigurations; this._staticDefaultHugePageSize = _staticDefaultHugePageSize; this._thpMode = _thpMode; this._thpPageSize = _thpPageSize; + this._shmemThpMode = _shmemThpMode; } @Override @@ -108,6 +115,7 @@ class HugePageConfiguration { ", _staticDefaultHugePageSize=" + _staticDefaultHugePageSize + ", _thpMode=" + _thpMode + ", _thpPageSize=" + _thpPageSize + + ", _shmemThpMode=" + _shmemThpMode + '}'; } @@ -117,12 +125,12 @@ class HugePageConfiguration { if (o == null || getClass() != o.getClass()) return false; HugePageConfiguration that = (HugePageConfiguration) o; return _staticDefaultHugePageSize == that._staticDefaultHugePageSize && _thpPageSize == that._thpPageSize && - Objects.equals(_staticHugePageConfigurations, that._staticHugePageConfigurations) && _thpMode == that._thpMode; + Objects.equals(_staticHugePageConfigurations, that._staticHugePageConfigurations) && _thpMode == that._thpMode && + _shmemThpMode == that._shmemThpMode; } private static long readDefaultHugePageSizeFromOS() { Pattern pat = Pattern.compile("Hugepagesize: *(\\d+) +kB"); - long result = 0; try (Scanner scanner = new Scanner(new File("/proc/meminfo"))) { while (scanner.hasNextLine()) { Matcher mat = pat.matcher(scanner.nextLine()); @@ -199,12 +207,41 @@ class HugePageConfiguration { return pagesize; } + private static ShmemTHPMode readShmemTHPModeFromOS() { + ShmemTHPMode mode = ShmemTHPMode.unknown; + String file = "/sys/kernel/mm/transparent_hugepage/shmem_enabled"; + try (FileReader fr = new FileReader(file); + BufferedReader reader = new BufferedReader(fr)) { + String s = reader.readLine(); + if (s.contains("[always]")) { + mode = ShmemTHPMode.always; + } else if (s.contains("[within_size]")) { + mode = ShmemTHPMode.within_size; + } else if (s.contains("[advise]")) { + mode = ShmemTHPMode.advise; + } else if (s.contains("[never]")) { + mode = ShmemTHPMode.never; + } else if (s.contains("[deny]")) { + mode = ShmemTHPMode.deny; + } else if (s.contains("[force]")) { + mode = ShmemTHPMode.force; + } else { + throw new RuntimeException("Unexpected content of " + file + ": " + s); + } + } catch (IOException e) { + System.out.println("Failed to read " + file); + // Happens when the kernel is not built to support THPs. + } + return mode; + } + // Fill object with info read from proc file system public static HugePageConfiguration readFromOS() throws IOException { return new HugePageConfiguration(readSupportedHugePagesFromOS(), readDefaultHugePageSizeFromOS(), readTHPModeFromOS(), - readTHPPageSizeFromOS()); + readTHPPageSizeFromOS(), + readShmemTHPModeFromOS()); } public static long parseSIUnit(String num, String unit) { @@ -227,14 +264,18 @@ class HugePageConfiguration { // [0.001s][info][pagesize] Transparent hugepage (THP) support: // [0.001s][info][pagesize] THP mode: madvise // [0.001s][info][pagesize] THP pagesize: 2M + // [0.001s][info][pagesize] Shared memory transparent hugepage (THP) support: + // [0.001s][info][pagesize] Shared memory THP mode: always TreeSet staticHugePageConfigs = new TreeSet<>(); long defaultHugepageSize = 0; THPMode thpMode = THPMode.never; + ShmemTHPMode shmemThpMode = ShmemTHPMode.unknown; long thpPageSize = 0; Pattern patternHugepageSize = Pattern.compile(".*\\[pagesize] *hugepage size: (\\d+)([KMG])"); Pattern patternDefaultHugepageSize = Pattern.compile(".*\\[pagesize] *default hugepage size: (\\d+)([KMG]) *"); - Pattern patternTHPPageSize = Pattern.compile(".*\\[pagesize] *THP pagesize: (\\d+)([KMG])"); - Pattern patternTHPMode = Pattern.compile(".*\\[pagesize] *THP mode: (\\S+)"); + Pattern patternTHPPageSize = Pattern.compile(".*\\[pagesize] * THP pagesize: (\\d+)([KMG])"); + Pattern patternTHPMode = Pattern.compile(".*\\[pagesize] * THP mode: (\\S+)"); + Pattern patternShmemTHPMode = Pattern.compile(".*\\[pagesize] *Shared memory THP mode: (\\S+)"); List lines = output.asLines(); for (String s : lines) { Matcher mat = patternHugepageSize.matcher(s); @@ -262,9 +303,13 @@ class HugePageConfiguration { if (mat.matches()) { thpMode = THPMode.valueOf(mat.group(1)); } + mat = patternShmemTHPMode.matcher(s); + if (mat.matches()) { + shmemThpMode = ShmemTHPMode.valueOf(mat.group(1)); + } } - return new HugePageConfiguration(staticHugePageConfigs, defaultHugepageSize, thpMode, thpPageSize); + return new HugePageConfiguration(staticHugePageConfigs, defaultHugepageSize, thpMode, thpPageSize, shmemThpMode); } } diff --git a/test/hotspot/jtreg/runtime/os/TestTracePageSizes.java b/test/hotspot/jtreg/runtime/os/TestTracePageSizes.java index 805b9732afb..e4ad27b46e5 100644 --- a/test/hotspot/jtreg/runtime/os/TestTracePageSizes.java +++ b/test/hotspot/jtreg/runtime/os/TestTracePageSizes.java @@ -143,10 +143,12 @@ public class TestTracePageSizes { // or the end of file is encountered. static final Pattern SECTION_START_PATT = Pattern.compile("^([a-f0-9]+)-([a-f0-9]+) [\\-rwpsx]{4}.*"); static final Pattern KERNEL_PAGESIZE_PATT = Pattern.compile("^KernelPageSize:\\s*(\\d*) kB"); + static final Pattern THP_ELIGIBLE_PATT = Pattern.compile("^THPeligible:\\s+(\\d*)"); static final Pattern VMFLAGS_PATT = Pattern.compile("^VmFlags: ([\\w\\? ]*)"); String start; String end; String ps; + String thpEligible; String vmFlags; int lineno; @@ -154,12 +156,13 @@ public class TestTracePageSizes { start = null; end = null; ps = null; + thpEligible = null; vmFlags = null; } public void finish() { if (start != null) { - RangeWithPageSize range = new RangeWithPageSize(start, end, ps, vmFlags); + RangeWithPageSize range = new RangeWithPageSize(start, end, ps, thpEligible, vmFlags); ranges.add(range); debug("Added range: " + range); reset(); @@ -167,10 +170,15 @@ public class TestTracePageSizes { } void eatNext(String line) { - debug("" + (lineno++) + " " + line); + // For better debugging experience call finish here before the debug() call. Matcher matSectionStart = SECTION_START_PATT.matcher(line); if (matSectionStart.matches()) { finish(); + } + + debug("" + (lineno++) + " " + line); + + if (matSectionStart.matches()) { start = matSectionStart.group(1); end = matSectionStart.group(2); ps = null; @@ -182,6 +190,11 @@ public class TestTracePageSizes { ps = matKernelPageSize.group(1); return; } + Matcher matTHPEligible = THP_ELIGIBLE_PATT.matcher(line); + if (matTHPEligible.matches()) { + thpEligible = matTHPEligible.group(1); + return; + } Matcher matVmFlags = VMFLAGS_PATT.matcher(line); if (matVmFlags.matches()) { vmFlags = matVmFlags.group(1); @@ -326,15 +339,18 @@ class RangeWithPageSize { private long start; private long end; private long pageSize; + private boolean thpEligible; private boolean vmFlagHG; private boolean vmFlagHT; + private boolean isTHP; - public RangeWithPageSize(String start, String end, String pageSize, String vmFlags) { + public RangeWithPageSize(String start, String end, String pageSize, String thpEligible, String vmFlags) { // Note: since we insist on kernels >= 3.8, all the following information should be present // (none of the input strings be null). this.start = Long.parseUnsignedLong(start, 16); this.end = Long.parseUnsignedLong(end, 16); this.pageSize = Long.parseLong(pageSize); + this.thpEligible = Integer.parseInt(thpEligible) == 1; vmFlagHG = false; vmFlagHT = false; @@ -348,6 +364,13 @@ class RangeWithPageSize { vmFlagHG = true; } } + + // When the THP policy is 'always' instead of 'madvise, the vmFlagHG property is false. + // Check the THPeligible property instead. + isTHP = !vmFlagHT && this.thpEligible; + + // vmFlagHG should imply isTHP + assert !vmFlagHG || isTHP; } public long getPageSize() { @@ -355,7 +378,7 @@ class RangeWithPageSize { } public boolean isTransparentHuge() { - return vmFlagHG; + return isTHP; } public boolean isExplicitHuge() { @@ -368,6 +391,6 @@ class RangeWithPageSize { public String toString() { return "[" + Long.toHexString(start) + ", " + Long.toHexString(end) + ") " + - "pageSize=" + pageSize + "KB isTHP=" + vmFlagHG + " isHUGETLB=" + vmFlagHT; + "pageSize=" + pageSize + "KB isTHP=" + isTHP + " isHUGETLB=" + vmFlagHT; } }