Skip to content

Commit 37ca902

Browse files
committed
8310233: Fix THP detection on Linux
Reviewed-by: jsjolen, dholmes
1 parent 81c4e8f commit 37ca902

File tree

7 files changed

+716
-131
lines changed

7 files changed

+716
-131
lines changed

src/hotspot/os/linux/hugepages.cpp

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/*
2+
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2011, 2023, Red Hat Inc. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* This code is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* version 2 for more details (a copy is included in the LICENSE file that
14+
* accompanied this code).
15+
*
16+
* You should have received a copy of the GNU General Public License version
17+
* 2 along with this work; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*
20+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21+
* or visit www.oracle.com if you need additional information or have any
22+
* questions.
23+
*
24+
*/
25+
26+
#include "precompiled.hpp"
27+
#include "hugepages.hpp"
28+
29+
#include "logging/log.hpp"
30+
#include "logging/logStream.hpp"
31+
#include "runtime/os.hpp"
32+
#include "utilities/debug.hpp"
33+
#include "utilities/globalDefinitions.hpp"
34+
#include "utilities/ostream.hpp"
35+
36+
#include <dirent.h>
37+
38+
StaticHugePageSupport::StaticHugePageSupport() :
39+
_initialized(false), _pagesizes(), _default_hugepage_size(SIZE_MAX) {}
40+
41+
os::PageSizes StaticHugePageSupport::pagesizes() const {
42+
assert(_initialized, "Not initialized");
43+
return _pagesizes;
44+
}
45+
46+
size_t StaticHugePageSupport::default_hugepage_size() const {
47+
assert(_initialized, "Not initialized");
48+
return _default_hugepage_size;
49+
}
50+
51+
// Scan /proc/meminfo and return value of Hugepagesize
52+
static size_t scan_default_hugepagesize() {
53+
size_t pagesize = 0;
54+
55+
// large_page_size on Linux is used to round up heap size. x86 uses either
56+
// 2M or 4M page, depending on whether PAE (Physical Address Extensions)
57+
// mode is enabled. AMD64/EM64T uses 2M page in 64bit mode. IA64 can use
58+
// page as large as 1G.
59+
//
60+
// Here we try to figure out page size by parsing /proc/meminfo and looking
61+
// for a line with the following format:
62+
// Hugepagesize: 2048 kB
63+
//
64+
// If we can't determine the value (e.g. /proc is not mounted, or the text
65+
// format has been changed), we'll set largest page size to 0
66+
67+
FILE *fp = os::fopen("/proc/meminfo", "r");
68+
if (fp) {
69+
while (!feof(fp)) {
70+
int x = 0;
71+
char buf[16];
72+
if (fscanf(fp, "Hugepagesize: %d", &x) == 1) {
73+
if (x && fgets(buf, sizeof(buf), fp) && strcmp(buf, " kB\n") == 0) {
74+
pagesize = x * K;
75+
break;
76+
}
77+
} else {
78+
// skip to next line
79+
for (;;) {
80+
int ch = fgetc(fp);
81+
if (ch == EOF || ch == (int)'\n') break;
82+
}
83+
}
84+
}
85+
fclose(fp);
86+
}
87+
88+
return pagesize;
89+
}
90+
91+
// Given a file that contains a single (integral) number, return that number in (*out) and true;
92+
// in case of an error, return false.
93+
static bool read_number_file(const char* file, size_t* out) {
94+
FILE* f = ::fopen(file, "r");
95+
bool rc = false;
96+
if (f != nullptr) {
97+
uint64_t i = 0;
98+
if (::fscanf(f, SIZE_FORMAT, out) == 1) {
99+
rc = true;
100+
}
101+
::fclose(f);
102+
}
103+
return rc;
104+
}
105+
106+
static const char* const sys_hugepages = "/sys/kernel/mm/hugepages";
107+
108+
// Scan all directories in /sys/kernel/mm/hugepages/hugepages-xxxx
109+
// to discover the available page sizes
110+
static os::PageSizes scan_hugepages() {
111+
112+
os::PageSizes pagesizes;
113+
114+
DIR *dir = opendir(sys_hugepages);
115+
116+
struct dirent *entry;
117+
size_t pagesize;
118+
while ((entry = readdir(dir)) != nullptr) {
119+
if (entry->d_type == DT_DIR &&
120+
sscanf(entry->d_name, "hugepages-%zukB", &pagesize) == 1) {
121+
// The kernel is using kB, hotspot uses bytes
122+
// Add each found Large Page Size to page_sizes
123+
pagesize *= K;
124+
pagesizes.add(pagesize);
125+
}
126+
}
127+
closedir(dir);
128+
129+
return pagesizes;
130+
}
131+
132+
void StaticHugePageSupport::print_on(outputStream* os) {
133+
if (_initialized) {
134+
os->print_cr("Static hugepage support:");
135+
for (size_t s = _pagesizes.smallest(); s != 0; s = _pagesizes.next_larger(s)) {
136+
os->print_cr(" hugepage size: " EXACTFMT, EXACTFMTARGS(s));
137+
}
138+
os->print_cr(" default hugepage size: " EXACTFMT, EXACTFMTARGS(_default_hugepage_size));
139+
} else {
140+
os->print_cr(" unknown.");
141+
}
142+
}
143+
144+
void StaticHugePageSupport::scan_os() {
145+
_pagesizes = scan_hugepages();
146+
_default_hugepage_size = scan_default_hugepagesize();
147+
assert(_pagesizes.contains(_default_hugepage_size),
148+
"Unexpected configuration: default pagesize (" SIZE_FORMAT ") "
149+
"has no associated directory in /sys/kernel/mm/hugepages..", _default_hugepage_size);
150+
_initialized = true;
151+
LogTarget(Info, pagesize) lt;
152+
if (lt.is_enabled()) {
153+
LogStream ls(lt);
154+
print_on(&ls);
155+
}
156+
}
157+
158+
THPSupport::THPSupport() :
159+
_initialized(false), _mode(THPMode::never), _pagesize(SIZE_MAX) {}
160+
161+
162+
THPMode THPSupport::mode() const {
163+
assert(_initialized, "Not initialized");
164+
return _mode;
165+
}
166+
167+
size_t THPSupport::pagesize() const {
168+
assert(_initialized, "Not initialized");
169+
return _pagesize;
170+
}
171+
172+
void THPSupport::scan_os() {
173+
// Scan /sys/kernel/mm/transparent_hugepage/enabled
174+
// see mm/huge_memory.c
175+
_mode = THPMode::never;
176+
const char* filename = "/sys/kernel/mm/transparent_hugepage/enabled";
177+
FILE* f = ::fopen(filename, "r");
178+
if (f != nullptr) {
179+
char buf[64];
180+
char* s = fgets(buf, sizeof(buf), f);
181+
assert(s == buf, "Should have worked");
182+
if (::strstr(buf, "[madvise]") != nullptr) {
183+
_mode = THPMode::madvise;
184+
} else if (::strstr(buf, "[always]") != nullptr) {
185+
_mode = THPMode::always;
186+
} else {
187+
assert(::strstr(buf, "[never]") != nullptr, "Weird content of %s: %s", filename, buf);
188+
}
189+
fclose(f);
190+
}
191+
192+
// Scan large page size for THP from hpage_pmd_size
193+
_pagesize = 0;
194+
if (read_number_file("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", &_pagesize)) {
195+
assert(_pagesize > 0, "Expected");
196+
}
197+
_initialized = true;
198+
199+
LogTarget(Info, pagesize) lt;
200+
if (lt.is_enabled()) {
201+
LogStream ls(lt);
202+
print_on(&ls);
203+
}
204+
}
205+
206+
void THPSupport::print_on(outputStream* os) {
207+
if (_initialized) {
208+
os->print_cr("Transparent hugepage (THP) support:");
209+
os->print_cr(" THP mode: %s",
210+
(_mode == THPMode::always ? "always" : (_mode == THPMode::never ? "never" : "madvise")));
211+
os->print_cr(" THP pagesize: " EXACTFMT, EXACTFMTARGS(_pagesize));
212+
} else {
213+
os->print_cr(" unknown.");
214+
}
215+
}
216+
217+
StaticHugePageSupport HugePages::_static_hugepage_support;
218+
THPSupport HugePages::_thp_support;
219+
220+
void HugePages::initialize() {
221+
_static_hugepage_support.scan_os();
222+
_thp_support.scan_os();
223+
}
224+
225+
void HugePages::print_on(outputStream* os) {
226+
_static_hugepage_support.print_on(os);
227+
_thp_support.print_on(os);
228+
}

src/hotspot/os/linux/hugepages.hpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2011, 2023, Red Hat Inc. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* This code is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* version 2 for more details (a copy is included in the LICENSE file that
14+
* accompanied this code).
15+
*
16+
* You should have received a copy of the GNU General Public License version
17+
* 2 along with this work; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*
20+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21+
* or visit www.oracle.com if you need additional information or have any
22+
* questions.
23+
*
24+
*/
25+
26+
#ifndef OS_LINUX_HUGEPAGES_HPP
27+
#define OS_LINUX_HUGEPAGES_HPP
28+
29+
#include "memory/allStatic.hpp"
30+
#include "runtime/os.hpp" // for os::PageSizes
31+
#include "utilities/globalDefinitions.hpp"
32+
33+
class outputStream;
34+
35+
// Header contains the interface that reads OS information about
36+
// available hugepage support:
37+
// - class StaticHugePageSupport - about static (non-THP) hugepages
38+
// - class THPSupport - about transparent huge pages
39+
// and:
40+
// - class HugePages - a static umbrella wrapper
41+
42+
// Information about static (non-thp) hugepages
43+
class StaticHugePageSupport {
44+
bool _initialized;
45+
46+
// All supported hugepage sizes (sizes for which entries exist
47+
// in /sys/kernel/mm/hugepages/hugepage-xxx)
48+
os::PageSizes _pagesizes;
49+
50+
// Contains the default hugepage. The "default hugepage size" is the one that
51+
// - is marked in /proc/meminfo as "Hugepagesize"
52+
// - is the size one gets when using mmap(MAP_HUGETLB) when omitting size specifiers like MAP_HUGE_SHIFT)
53+
size_t _default_hugepage_size;
54+
55+
public:
56+
StaticHugePageSupport();
57+
58+
void scan_os();
59+
60+
os::PageSizes pagesizes() const;
61+
size_t default_hugepage_size() const;
62+
void print_on(outputStream* os);
63+
};
64+
65+
enum class THPMode { always, never, madvise };
66+
67+
// 2) for transparent hugepages
68+
class THPSupport {
69+
bool _initialized;
70+
71+
// See /sys/kernel/mm/transparent_hugepages/enabled
72+
THPMode _mode;
73+
74+
// Contains the THP page size
75+
size_t _pagesize;
76+
77+
public:
78+
79+
THPSupport();
80+
81+
// Queries the OS, fills in object
82+
void scan_os();
83+
84+
THPMode mode() const;
85+
size_t pagesize() const;
86+
void print_on(outputStream* os);
87+
};
88+
89+
// Umbrella static interface
90+
class HugePages : public AllStatic {
91+
92+
static StaticHugePageSupport _static_hugepage_support;
93+
static THPSupport _thp_support;
94+
95+
public:
96+
97+
static const StaticHugePageSupport& static_info() { return _static_hugepage_support; }
98+
static const THPSupport& thp_info() { return _thp_support; }
99+
100+
static size_t default_static_hugepage_size() { return _static_hugepage_support.default_hugepage_size(); }
101+
static bool supports_static_hugepages() { return default_static_hugepage_size() > 0; }
102+
static THPMode thp_mode() { return _thp_support.mode(); }
103+
static bool supports_thp() { return thp_mode() == THPMode::madvise || thp_mode() == THPMode::always; }
104+
static size_t thp_pagesize() { return _thp_support.pagesize(); }
105+
106+
static void initialize();
107+
static void print_on(outputStream* os);
108+
};
109+
110+
#endif // OS_LINUX_HUGEPAGES_HPP

0 commit comments

Comments
 (0)