8319969: os::large_page_init() turns off THPs for ZGC

Reviewed-by: stuefe, aboldtch
This commit is contained in:
Stefan Karlsson 2023-12-06 19:07:30 +00:00
parent 3edc24a71d
commit f4822605af
10 changed files with 326 additions and 48 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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");

View File

@ -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

View File

@ -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";
}
}
}

View File

@ -35,6 +35,7 @@ private:
};
static State _state;
static bool _os_enforced_transparent_mode;
static void pd_initialize();

View File

@ -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<StaticHugePageConfig> 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<StaticHugePageConfig> _staticHugePageConfigurations, long _staticDefaultHugePageSize, THPMode _thpMode, long _thpPageSize) {
public HugePageConfiguration(Set<StaticHugePageConfig> _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<StaticHugePageConfig> 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<String> 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);
}
}

View File

@ -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;
}
}