Skip to content

Commit 55a7cf1

Browse files
committedSep 11, 2024
8322420: [Linux] cgroup v2: Limits in parent nested control groups are not detected
Reviewed-by: stuefe, asmehra
1 parent 5977888 commit 55a7cf1

9 files changed

+362
-111
lines changed
 

‎src/hotspot/os/linux/cgroupSubsystem_linux.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -609,7 +609,7 @@ jlong CgroupSubsystem::memory_limit_in_bytes() {
609609
bool CgroupController::read_string(const char* filename, char* buf, size_t buf_size) {
610610
assert(buf != nullptr, "buffer must not be null");
611611
assert(filename != nullptr, "filename must be given");
612-
char* s_path = subsystem_path();
612+
const char* s_path = subsystem_path();
613613
if (s_path == nullptr) {
614614
log_debug(os, container)("read_string: subsystem path is null");
615615
return false;
@@ -679,7 +679,7 @@ bool CgroupController::read_numerical_key_value(const char* filename, const char
679679
assert(key != nullptr, "key must be given");
680680
assert(result != nullptr, "result pointer must not be null");
681681
assert(filename != nullptr, "file to search in must be given");
682-
char* s_path = subsystem_path();
682+
const char* s_path = subsystem_path();
683683
if (s_path == nullptr) {
684684
log_debug(os, container)("read_numerical_key_value: subsystem path is null");
685685
return false;

‎src/hotspot/os/linux/cgroupSubsystem_linux.hpp

+17-1
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,15 @@
103103
}
104104

105105
class CgroupController: public CHeapObj<mtInternal> {
106+
protected:
107+
char* _cgroup_path;
108+
char* _mount_point;
106109
public:
107-
virtual char* subsystem_path() = 0;
110+
virtual const char* subsystem_path() = 0;
108111
virtual bool is_read_only() = 0;
112+
const char* cgroup_path() { return _cgroup_path; }
113+
const char* mount_point() { return _mount_point; }
114+
virtual bool needs_hierarchy_adjustment() { return false; }
109115

110116
/* Read a numerical value as unsigned long
111117
*
@@ -202,7 +208,12 @@ class CgroupCpuController: public CHeapObj<mtInternal> {
202208
virtual int cpu_quota() = 0;
203209
virtual int cpu_period() = 0;
204210
virtual int cpu_shares() = 0;
211+
virtual bool needs_hierarchy_adjustment() = 0;
205212
virtual bool is_read_only() = 0;
213+
virtual const char* subsystem_path() = 0;
214+
virtual void set_subsystem_path(const char* cgroup_path) = 0;
215+
virtual const char* mount_point() = 0;
216+
virtual const char* cgroup_path() = 0;
206217
};
207218

208219
// Pure virtual class representing version agnostic memory controllers
@@ -217,7 +228,12 @@ class CgroupMemoryController: public CHeapObj<mtInternal> {
217228
virtual jlong rss_usage_in_bytes() = 0;
218229
virtual jlong cache_usage_in_bytes() = 0;
219230
virtual void print_version_specific_info(outputStream* st, julong host_mem) = 0;
231+
virtual bool needs_hierarchy_adjustment() = 0;
220232
virtual bool is_read_only() = 0;
233+
virtual const char* subsystem_path() = 0;
234+
virtual void set_subsystem_path(const char* cgroup_path) = 0;
235+
virtual const char* mount_point() = 0;
236+
virtual const char* cgroup_path() = 0;
221237
};
222238

223239
class CgroupSubsystem: public CHeapObj<mtInternal> {

‎src/hotspot/os/linux/cgroupUtil_linux.cpp

+111
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
*
2323
*/
2424

25+
#include "os_linux.hpp"
2526
#include "cgroupUtil_linux.hpp"
2627

2728
int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) {
@@ -46,3 +47,113 @@ int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) {
4647
log_trace(os, container)("OSContainer::active_processor_count: %d", result);
4748
return result;
4849
}
50+
51+
void CgroupUtil::adjust_controller(CgroupMemoryController* mem) {
52+
if (!mem->needs_hierarchy_adjustment()) {
53+
// nothing to do
54+
return;
55+
}
56+
log_trace(os, container)("Adjusting controller path for memory: %s", mem->subsystem_path());
57+
assert(mem->cgroup_path() != nullptr, "invariant");
58+
char* orig = os::strdup(mem->cgroup_path());
59+
char* cg_path = os::strdup(orig);
60+
char* last_slash;
61+
assert(cg_path[0] == '/', "cgroup path must start with '/'");
62+
julong phys_mem = os::Linux::physical_memory();
63+
char* limit_cg_path = nullptr;
64+
jlong limit = mem->read_memory_limit_in_bytes(phys_mem);
65+
jlong lowest_limit = phys_mem;
66+
while ((last_slash = strrchr(cg_path, '/')) != cg_path) {
67+
*last_slash = '\0'; // strip path
68+
// update to shortened path and try again
69+
mem->set_subsystem_path(cg_path);
70+
limit = mem->read_memory_limit_in_bytes(phys_mem);
71+
if (limit >= 0 && limit < lowest_limit) {
72+
lowest_limit = limit;
73+
os::free(limit_cg_path); // handles nullptr
74+
limit_cg_path = os::strdup(cg_path);
75+
}
76+
}
77+
// need to check limit at mount point
78+
mem->set_subsystem_path("/");
79+
limit = mem->read_memory_limit_in_bytes(phys_mem);
80+
if (limit >= 0 && limit < lowest_limit) {
81+
lowest_limit = limit;
82+
os::free(limit_cg_path); // handles nullptr
83+
limit_cg_path = os::strdup("/");
84+
}
85+
assert(lowest_limit >= 0, "limit must be positive");
86+
if ((julong)lowest_limit != phys_mem) {
87+
// we've found a lower limit anywhere in the hierarchy,
88+
// set the path to the limit path
89+
assert(limit_cg_path != nullptr, "limit path must be set");
90+
mem->set_subsystem_path(limit_cg_path);
91+
log_trace(os, container)("Adjusted controller path for memory to: %s. "
92+
"Lowest limit was: " JLONG_FORMAT,
93+
mem->subsystem_path(),
94+
lowest_limit);
95+
} else {
96+
log_trace(os, container)("No lower limit found for memory in hierarchy %s, "
97+
"adjusting to original path %s",
98+
mem->mount_point(), orig);
99+
mem->set_subsystem_path(orig);
100+
}
101+
os::free(cg_path);
102+
os::free(orig);
103+
os::free(limit_cg_path);
104+
}
105+
106+
void CgroupUtil::adjust_controller(CgroupCpuController* cpu) {
107+
if (!cpu->needs_hierarchy_adjustment()) {
108+
// nothing to do
109+
return;
110+
}
111+
log_trace(os, container)("Adjusting controller path for cpu: %s", cpu->subsystem_path());
112+
assert(cpu->cgroup_path() != nullptr, "invariant");
113+
char* orig = os::strdup(cpu->cgroup_path());
114+
char* cg_path = os::strdup(orig);
115+
char* last_slash;
116+
assert(cg_path[0] == '/', "cgroup path must start with '/'");
117+
int host_cpus = os::Linux::active_processor_count();
118+
int cpus = CgroupUtil::processor_count(cpu, host_cpus);
119+
int lowest_limit = host_cpus;
120+
char* limit_cg_path = nullptr;
121+
while ((last_slash = strrchr(cg_path, '/')) != cg_path) {
122+
*last_slash = '\0'; // strip path
123+
// update to shortened path and try again
124+
cpu->set_subsystem_path(cg_path);
125+
cpus = CgroupUtil::processor_count(cpu, host_cpus);
126+
if (cpus != host_cpus && cpus < lowest_limit) {
127+
lowest_limit = cpus;
128+
os::free(limit_cg_path); // handles nullptr
129+
limit_cg_path = os::strdup(cg_path);
130+
}
131+
}
132+
// need to check limit at mount point
133+
cpu->set_subsystem_path("/");
134+
cpus = CgroupUtil::processor_count(cpu, host_cpus);
135+
if (cpus != host_cpus && cpus < lowest_limit) {
136+
lowest_limit = cpus;
137+
os::free(limit_cg_path); // handles nullptr
138+
limit_cg_path = os::strdup(cg_path);
139+
}
140+
assert(lowest_limit >= 0, "limit must be positive");
141+
if (lowest_limit != host_cpus) {
142+
// we've found a lower limit anywhere in the hierarchy,
143+
// set the path to the limit path
144+
assert(limit_cg_path != nullptr, "limit path must be set");
145+
cpu->set_subsystem_path(limit_cg_path);
146+
log_trace(os, container)("Adjusted controller path for cpu to: %s. "
147+
"Lowest limit was: %d",
148+
cpu->subsystem_path(),
149+
lowest_limit);
150+
} else {
151+
log_trace(os, container)("No lower limit found for cpu in hierarchy %s, "
152+
"adjusting to original path %s",
153+
cpu->mount_point(), orig);
154+
cpu->set_subsystem_path(orig);
155+
}
156+
os::free(cg_path);
157+
os::free(orig);
158+
os::free(limit_cg_path);
159+
}

‎src/hotspot/os/linux/cgroupUtil_linux.hpp

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ class CgroupUtil: AllStatic {
3232

3333
public:
3434
static int processor_count(CgroupCpuController* cpu, int host_cpus);
35+
// Given a memory controller, adjust its path to a point in the hierarchy
36+
// that represents the closest memory limit.
37+
static void adjust_controller(CgroupMemoryController* m);
38+
// Given a cpu controller, adjust its path to a point in the hierarchy
39+
// that represents the closest cpu limit.
40+
static void adjust_controller(CgroupCpuController* c);
3541
};
3642

3743
#endif // CGROUP_UTIL_LINUX_HPP

‎src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp

+34-53
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,15 @@
3838
* Set directory to subsystem specific files based
3939
* on the contents of the mountinfo and cgroup files.
4040
*/
41-
void CgroupV1Controller::set_subsystem_path(char *cgroup_path) {
41+
void CgroupV1Controller::set_subsystem_path(const char* cgroup_path) {
42+
if (_cgroup_path != nullptr) {
43+
os::free(_cgroup_path);
44+
}
45+
if (_path != nullptr) {
46+
os::free(_path);
47+
_path = nullptr;
48+
}
49+
_cgroup_path = os::strdup(cgroup_path);
4250
stringStream ss;
4351
if (_root != nullptr && cgroup_path != nullptr) {
4452
if (strcmp(_root, "/") == 0) {
@@ -52,7 +60,7 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) {
5260
ss.print_raw(_mount_point);
5361
_path = os::strdup(ss.base());
5462
} else {
55-
char *p = strstr(cgroup_path, _root);
63+
char *p = strstr((char*)cgroup_path, _root);
5664
if (p != nullptr && p == _root) {
5765
if (strlen(cgroup_path) > strlen(_root)) {
5866
ss.print_raw(_mount_point);
@@ -66,27 +74,15 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) {
6674
}
6775
}
6876

69-
/* uses_mem_hierarchy
70-
*
71-
* Return whether or not hierarchical cgroup accounting is being
72-
* done.
73-
*
74-
* return:
75-
* A number > 0 if true, or
76-
* OSCONTAINER_ERROR for not supported
77+
/*
78+
* The common case, containers, we have _root == _cgroup_path, and thus set the
79+
* controller path to the _mount_point. This is where the limits are exposed in
80+
* the cgroup pseudo filesystem (at the leaf) and adjustment of the path won't
81+
* be needed for that reason.
7782
*/
78-
jlong CgroupV1MemoryController::uses_mem_hierarchy() {
79-
julong use_hierarchy;
80-
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.use_hierarchy", "Use Hierarchy", use_hierarchy);
81-
return (jlong)use_hierarchy;
82-
}
83-
84-
void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) {
85-
reader()->set_subsystem_path(cgroup_path);
86-
jlong hierarchy = uses_mem_hierarchy();
87-
if (hierarchy > 0) {
88-
set_hierarchical(true);
89-
}
83+
bool CgroupV1Controller::needs_hierarchy_adjustment() {
84+
assert(_cgroup_path != nullptr, "sanity");
85+
return strcmp(_root, _cgroup_path) != 0;
9086
}
9187

9288
static inline
@@ -115,20 +111,6 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) {
115111
julong memlimit;
116112
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.limit_in_bytes", "Memory Limit", memlimit);
117113
if (memlimit >= phys_mem) {
118-
log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited");
119-
if (is_hierarchical()) {
120-
julong hier_memlimit;
121-
bool is_ok = reader()->read_numerical_key_value("/memory.stat", "hierarchical_memory_limit", &hier_memlimit);
122-
if (!is_ok) {
123-
return OSCONTAINER_ERROR;
124-
}
125-
log_trace(os, container)("Hierarchical Memory Limit is: " JULONG_FORMAT, hier_memlimit);
126-
if (hier_memlimit < phys_mem) {
127-
verbose_log(hier_memlimit, phys_mem);
128-
return (jlong)hier_memlimit;
129-
}
130-
log_trace(os, container)("Hierarchical Memory Limit is: Unlimited");
131-
}
132114
verbose_log(memlimit, phys_mem);
133115
return (jlong)-1;
134116
} else {
@@ -150,26 +132,10 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) {
150132
* upper bound)
151133
*/
152134
jlong CgroupV1MemoryController::read_mem_swap(julong host_total_memsw) {
153-
julong hier_memswlimit;
154135
julong memswlimit;
155136
CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", memswlimit);
156137
if (memswlimit >= host_total_memsw) {
157-
log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited");
158-
if (is_hierarchical()) {
159-
const char* matchline = "hierarchical_memsw_limit";
160-
bool is_ok = reader()->read_numerical_key_value("/memory.stat",
161-
matchline,
162-
&hier_memswlimit);
163-
if (!is_ok) {
164-
return OSCONTAINER_ERROR;
165-
}
166-
log_trace(os, container)("Hierarchical Memory and Swap Limit is: " JULONG_FORMAT, hier_memswlimit);
167-
if (hier_memswlimit >= host_total_memsw) {
168-
log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited");
169-
} else {
170-
return (jlong)hier_memswlimit;
171-
}
172-
}
138+
log_trace(os, container)("Memory and Swap Limit is: Unlimited");
173139
return (jlong)-1;
174140
} else {
175141
return (jlong)memswlimit;
@@ -233,6 +199,21 @@ jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) {
233199
}
234200
}
235201

202+
// Constructor
203+
CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset,
204+
CgroupV1CpuController* cpu,
205+
CgroupV1Controller* cpuacct,
206+
CgroupV1Controller* pids,
207+
CgroupV1MemoryController* memory) :
208+
_cpuset(cpuset),
209+
_cpuacct(cpuacct),
210+
_pids(pids) {
211+
CgroupUtil::adjust_controller(memory);
212+
CgroupUtil::adjust_controller(cpu);
213+
_memory = new CachingCgroupController<CgroupMemoryController>(memory);
214+
_cpu = new CachingCgroupController<CgroupCpuController>(cpu);
215+
}
216+
236217
bool CgroupV1Subsystem::is_containerized() {
237218
// containerized iff all required controllers are mounted
238219
// read-only. See OSContainer::is_containerized() for

‎src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp

+35-28
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@
2828
#include "runtime/os.hpp"
2929
#include "memory/allocation.hpp"
3030
#include "cgroupSubsystem_linux.hpp"
31+
#include "cgroupUtil_linux.hpp"
3132

3233
// Cgroups version 1 specific implementation
3334

3435
class CgroupV1Controller: public CgroupController {
3536
private:
3637
/* mountinfo contents */
3738
char* _root;
38-
char* _mount_point;
3939
bool _read_only;
4040

4141
/* Constructed subsystem directory */
@@ -45,24 +45,27 @@ class CgroupV1Controller: public CgroupController {
4545
CgroupV1Controller(char *root,
4646
char *mountpoint,
4747
bool ro) : _root(os::strdup(root)),
48-
_mount_point(os::strdup(mountpoint)),
4948
_read_only(ro),
5049
_path(nullptr) {
50+
_cgroup_path = nullptr;
51+
_mount_point = os::strdup(mountpoint);
5152
}
5253
// Shallow copy constructor
5354
CgroupV1Controller(const CgroupV1Controller& o) : _root(o._root),
54-
_mount_point(o._mount_point),
5555
_read_only(o._read_only),
5656
_path(o._path) {
57+
_cgroup_path = o._cgroup_path;
58+
_mount_point = o._mount_point;
5759
}
5860
~CgroupV1Controller() {
5961
// At least one subsystem controller exists with paths to malloc'd path
6062
// names
6163
}
6264

63-
void set_subsystem_path(char *cgroup_path);
64-
char *subsystem_path() override { return _path; }
65+
void set_subsystem_path(const char *cgroup_path);
66+
const char* subsystem_path() override { return _path; }
6567
bool is_read_only() override { return _read_only; }
68+
bool needs_hierarchy_adjustment() override;
6669
};
6770

6871
class CgroupV1MemoryController final : public CgroupMemoryController {
@@ -71,8 +74,9 @@ class CgroupV1MemoryController final : public CgroupMemoryController {
7174
CgroupV1Controller _reader;
7275
CgroupV1Controller* reader() { return &_reader; }
7376
public:
74-
bool is_hierarchical() { return _uses_mem_hierarchy; }
75-
void set_subsystem_path(char *cgroup_path);
77+
void set_subsystem_path(const char *cgroup_path) override {
78+
reader()->set_subsystem_path(cgroup_path);
79+
}
7680
jlong read_memory_limit_in_bytes(julong upper_bound) override;
7781
jlong memory_usage_in_bytes() override;
7882
jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) override;
@@ -85,23 +89,22 @@ class CgroupV1MemoryController final : public CgroupMemoryController {
8589
jlong kernel_memory_limit_in_bytes(julong host_mem);
8690
jlong kernel_memory_max_usage_in_bytes();
8791
void print_version_specific_info(outputStream* st, julong host_mem) override;
92+
bool needs_hierarchy_adjustment() override {
93+
return reader()->needs_hierarchy_adjustment();
94+
}
8895
bool is_read_only() override {
8996
return reader()->is_read_only();
9097
}
98+
const char* subsystem_path() override { return reader()->subsystem_path(); }
99+
const char* mount_point() override { return reader()->mount_point(); }
100+
const char* cgroup_path() override { return reader()->cgroup_path(); }
91101
private:
92-
/* Some container runtimes set limits via cgroup
93-
* hierarchy. If set to true consider also memory.stat
94-
* file if everything else seems unlimited */
95-
bool _uses_mem_hierarchy;
96-
jlong uses_mem_hierarchy();
97-
void set_hierarchical(bool value) { _uses_mem_hierarchy = value; }
98102
jlong read_mem_swappiness();
99103
jlong read_mem_swap(julong host_total_memsw);
100104

101105
public:
102106
CgroupV1MemoryController(const CgroupV1Controller& reader)
103-
: _reader(reader),
104-
_uses_mem_hierarchy(false) {
107+
: _reader(reader) {
105108
}
106109

107110
};
@@ -115,12 +118,22 @@ class CgroupV1CpuController final : public CgroupCpuController {
115118
int cpu_quota() override;
116119
int cpu_period() override;
117120
int cpu_shares() override;
118-
void set_subsystem_path(char *cgroup_path) {
121+
void set_subsystem_path(const char *cgroup_path) override {
119122
reader()->set_subsystem_path(cgroup_path);
120123
}
121124
bool is_read_only() override {
122125
return reader()->is_read_only();
123126
}
127+
const char* subsystem_path() override {
128+
return reader()->subsystem_path();
129+
}
130+
const char* mount_point() override {
131+
return reader()->mount_point();
132+
}
133+
bool needs_hierarchy_adjustment() override {
134+
return reader()->needs_hierarchy_adjustment();
135+
}
136+
const char* cgroup_path() override { return reader()->cgroup_path(); }
124137

125138
public:
126139
CgroupV1CpuController(const CgroupV1Controller& reader) : _reader(reader) {
@@ -130,6 +143,12 @@ class CgroupV1CpuController final : public CgroupCpuController {
130143
class CgroupV1Subsystem: public CgroupSubsystem {
131144

132145
public:
146+
CgroupV1Subsystem(CgroupV1Controller* cpuset,
147+
CgroupV1CpuController* cpu,
148+
CgroupV1Controller* cpuacct,
149+
CgroupV1Controller* pids,
150+
CgroupV1MemoryController* memory);
151+
133152
jlong kernel_memory_usage_in_bytes();
134153
jlong kernel_memory_limit_in_bytes();
135154
jlong kernel_memory_max_usage_in_bytes();
@@ -155,18 +174,6 @@ class CgroupV1Subsystem: public CgroupSubsystem {
155174
CgroupV1Controller* _cpuacct = nullptr;
156175
CgroupV1Controller* _pids = nullptr;
157176

158-
public:
159-
CgroupV1Subsystem(CgroupV1Controller* cpuset,
160-
CgroupV1CpuController* cpu,
161-
CgroupV1Controller* cpuacct,
162-
CgroupV1Controller* pids,
163-
CgroupV1MemoryController* memory) :
164-
_memory(new CachingCgroupController<CgroupMemoryController>(memory)),
165-
_cpuset(cpuset),
166-
_cpu(new CachingCgroupController<CgroupCpuController>(cpu)),
167-
_cpuacct(cpuacct),
168-
_pids(pids) {
169-
}
170177
};
171178

172179
#endif // CGROUP_V1_SUBSYSTEM_LINUX_HPP

‎src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp

+40-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@
2525
#include "cgroupV2Subsystem_linux.hpp"
2626
#include "cgroupUtil_linux.hpp"
2727

28+
// Constructor
29+
CgroupV2Controller::CgroupV2Controller(char* mount_path,
30+
char *cgroup_path,
31+
bool ro) : _read_only(ro),
32+
_path(construct_path(mount_path, cgroup_path)) {
33+
_cgroup_path = os::strdup(cgroup_path);
34+
_mount_point = os::strdup(mount_path);
35+
}
36+
// Shallow copy constructor
37+
CgroupV2Controller::CgroupV2Controller(const CgroupV2Controller& o) :
38+
_read_only(o._read_only),
39+
_path(o._path) {
40+
_cgroup_path = o._cgroup_path;
41+
_mount_point = o._mount_point;
42+
}
43+
2844
/* cpu_shares
2945
*
3046
* Return the amount of cpu shares available to the process
@@ -95,6 +111,17 @@ int CgroupV2CpuController::cpu_quota() {
95111
return limit;
96112
}
97113

114+
// Constructor
115+
CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory,
116+
CgroupV2CpuController* cpu,
117+
CgroupV2Controller unified) :
118+
_unified(unified) {
119+
CgroupUtil::adjust_controller(memory);
120+
CgroupUtil::adjust_controller(cpu);
121+
_memory = new CachingCgroupController<CgroupMemoryController>(memory);
122+
_cpu = new CachingCgroupController<CgroupCpuController>(cpu);
123+
}
124+
98125
bool CgroupV2Subsystem::is_containerized() {
99126
return _unified.is_read_only() &&
100127
_memory->controller()->is_read_only() &&
@@ -264,6 +291,18 @@ jlong memory_swap_limit_value(CgroupV2Controller* ctrl) {
264291
return swap_limit;
265292
}
266293

294+
void CgroupV2Controller::set_subsystem_path(const char* cgroup_path) {
295+
if (_path != nullptr) {
296+
os::free(_path);
297+
}
298+
_path = construct_path(_mount_point, cgroup_path);
299+
}
300+
301+
// For cgv2 we only need hierarchy walk if the cgroup path isn't '/' (root)
302+
bool CgroupV2Controller::needs_hierarchy_adjustment() {
303+
return strcmp(_cgroup_path, "/") != 0;
304+
}
305+
267306
void CgroupV2MemoryController::print_version_specific_info(outputStream* st, julong phys_mem) {
268307
jlong swap_current = memory_swap_current_value(reader());
269308
jlong swap_limit = memory_swap_limit_value(reader());
@@ -272,7 +311,7 @@ void CgroupV2MemoryController::print_version_specific_info(outputStream* st, jul
272311
OSContainer::print_container_helper(st, swap_limit, "memory_swap_max_limit_in_bytes");
273312
}
274313

275-
char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) {
314+
char* CgroupV2Controller::construct_path(char* mount_path, const char* cgroup_path) {
276315
stringStream ss;
277316
ss.print_raw(mount_path);
278317
if (strcmp(cgroup_path, "/") != 0) {

‎src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp

+32-25
Original file line numberDiff line numberDiff line change
@@ -26,39 +26,28 @@
2626
#define CGROUP_V2_SUBSYSTEM_LINUX_HPP
2727

2828
#include "cgroupSubsystem_linux.hpp"
29+
#include "cgroupUtil_linux.hpp"
2930

3031
class CgroupV2Controller: public CgroupController {
3132
private:
32-
/* the mount path of the cgroup v2 hierarchy */
33-
char *_mount_path;
34-
/* The cgroup path for the controller */
35-
char *_cgroup_path;
3633
bool _read_only;
3734

3835
/* Constructed full path to the subsystem directory */
3936
char *_path;
40-
static char* construct_path(char* mount_path, char *cgroup_path);
37+
static char* construct_path(char* mount_path, const char *cgroup_path);
4138

4239
public:
43-
CgroupV2Controller(char* mount_path,
44-
char *cgroup_path,
45-
bool ro) : _mount_path(os::strdup(mount_path)),
46-
_cgroup_path(os::strdup(cgroup_path)),
47-
_read_only(ro),
48-
_path(construct_path(mount_path, cgroup_path)) {
49-
}
40+
CgroupV2Controller(char* mount_path, char *cgroup_path, bool ro);
5041
// Shallow copy constructor
51-
CgroupV2Controller(const CgroupV2Controller& o) :
52-
_mount_path(o._mount_path),
53-
_cgroup_path(o._cgroup_path),
54-
_read_only(o._read_only),
55-
_path(o._path) {
56-
}
42+
CgroupV2Controller(const CgroupV2Controller& o);
5743
~CgroupV2Controller() {
5844
// At least one controller exists with references to the paths
5945
}
6046

61-
char *subsystem_path() override { return _path; }
47+
const char* subsystem_path() override { return _path; }
48+
bool needs_hierarchy_adjustment() override;
49+
// Allow for optional updates of the subsystem path
50+
void set_subsystem_path(const char* cgroup_path);
6251
bool is_read_only() override { return _read_only; }
6352
};
6453

@@ -75,6 +64,17 @@ class CgroupV2CpuController: public CgroupCpuController {
7564
bool is_read_only() override {
7665
return reader()->is_read_only();
7766
}
67+
const char* subsystem_path() {
68+
return reader()->subsystem_path();
69+
}
70+
bool needs_hierarchy_adjustment() override {
71+
return reader()->needs_hierarchy_adjustment();
72+
}
73+
void set_subsystem_path(const char* cgroup_path) {
74+
reader()->set_subsystem_path(cgroup_path);
75+
}
76+
const char* mount_point() { return reader()->mount_point(); }
77+
const char* cgroup_path() override { return reader()->cgroup_path(); }
7878
};
7979

8080
class CgroupV2MemoryController final: public CgroupMemoryController {
@@ -97,6 +97,17 @@ class CgroupV2MemoryController final: public CgroupMemoryController {
9797
bool is_read_only() override {
9898
return reader()->is_read_only();
9999
}
100+
const char* subsystem_path() {
101+
return reader()->subsystem_path();
102+
}
103+
bool needs_hierarchy_adjustment() override {
104+
return reader()->needs_hierarchy_adjustment();
105+
}
106+
void set_subsystem_path(const char* cgroup_path) {
107+
reader()->set_subsystem_path(cgroup_path);
108+
}
109+
const char* mount_point() { return reader()->mount_point(); }
110+
const char* cgroup_path() override { return reader()->cgroup_path(); }
100111
};
101112

102113
class CgroupV2Subsystem: public CgroupSubsystem {
@@ -110,13 +121,9 @@ class CgroupV2Subsystem: public CgroupSubsystem {
110121
CgroupV2Controller* unified() { return &_unified; }
111122

112123
public:
113-
CgroupV2Subsystem(CgroupV2MemoryController* memory,
124+
CgroupV2Subsystem(CgroupV2MemoryController * memory,
114125
CgroupV2CpuController* cpu,
115-
CgroupV2Controller unified) :
116-
_unified(unified),
117-
_memory(new CachingCgroupController<CgroupMemoryController>(memory)),
118-
_cpu(new CachingCgroupController<CgroupCpuController>(cpu)) {
119-
}
126+
CgroupV2Controller unified);
120127

121128
char * cpu_cpuset_cpus() override;
122129
char * cpu_cpuset_memory_nodes() override;

‎test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp

+85-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class TestController : public CgroupController {
7575
char* _path;
7676
public:
7777
TestController(char* p): _path(p) {}
78-
char* subsystem_path() override {
78+
const char* subsystem_path() override {
7979
return _path;
8080
};
8181
bool is_read_only() override {
@@ -470,4 +470,88 @@ TEST(cgroupTest, set_cgroupv2_subsystem_path) {
470470
}
471471
}
472472

473+
TEST(cgroupTest, cgroupv2_is_hierarchy_walk_needed) {
474+
bool controller_read_only = false; // value irrelevant;
475+
CgroupV2Controller* test = new CgroupV2Controller((char*)"/sys/fs/cgroup",
476+
(char*)"/" /* cgroup_path */,
477+
controller_read_only);
478+
EXPECT_FALSE(test->needs_hierarchy_adjustment());
479+
test = new CgroupV2Controller((char*)"/sys/fs/cgroup",
480+
(char*)"/bar" /* cgroup_path */,
481+
controller_read_only);
482+
EXPECT_TRUE(test->needs_hierarchy_adjustment());
483+
test = new CgroupV2Controller((char*)"/sys/fs/cgroup/b",
484+
(char*)"/a/b" /* cgroup_path */,
485+
controller_read_only);
486+
EXPECT_TRUE(test->needs_hierarchy_adjustment());
487+
488+
CgroupCpuController* test2 = new CgroupV2CpuController(CgroupV2Controller((char*)"/sys/fs/cgroup",
489+
(char*)"/" /* cgroup_path */,
490+
controller_read_only));
491+
EXPECT_FALSE(test2->needs_hierarchy_adjustment());
492+
test2 = new CgroupV2CpuController(CgroupV2Controller((char*)"/sys/fs/cgroup",
493+
(char*)"/bar" /* cgroup_path */,
494+
controller_read_only));
495+
EXPECT_TRUE(test2->needs_hierarchy_adjustment());
496+
test2 = new CgroupV2CpuController(CgroupV2Controller((char*)"/sys/fs/cgroup/b",
497+
(char*)"/a/b" /* cgroup_path */,
498+
controller_read_only));
499+
EXPECT_TRUE(test2->needs_hierarchy_adjustment());
500+
501+
CgroupMemoryController* test3 = new CgroupV2MemoryController(CgroupV2Controller((char*)"/sys/fs/cgroup",
502+
(char*)"/" /* cgroup_path */,
503+
controller_read_only));
504+
EXPECT_FALSE(test3->needs_hierarchy_adjustment());
505+
test3 = new CgroupV2MemoryController(CgroupV2Controller((char*)"/sys/fs/cgroup",
506+
(char*)"/bar" /* cgroup_path */,
507+
controller_read_only));
508+
EXPECT_TRUE(test3->needs_hierarchy_adjustment());
509+
test3 = new CgroupV2MemoryController(CgroupV2Controller((char*)"/sys/fs/cgroup/b",
510+
(char*)"/a/b" /* cgroup_path */,
511+
controller_read_only));
512+
EXPECT_TRUE(test3->needs_hierarchy_adjustment());
513+
}
514+
515+
TEST(cgroupTest, cgroupv1_is_hierarchy_walk_needed) {
516+
bool controller_read_only = true; // shouldn't matter;
517+
CgroupV1Controller* test = new CgroupV1Controller((char*)"/a/b/c" /* root */,
518+
(char*)"/sys/fs/cgroup/memory" /* mount_path */,
519+
controller_read_only);
520+
test->set_subsystem_path((char*)"/a/b/c");
521+
EXPECT_FALSE(test->needs_hierarchy_adjustment());
522+
test->set_subsystem_path((char*)"/");
523+
EXPECT_TRUE(test->needs_hierarchy_adjustment());
524+
test = new CgroupV1Controller((char*)"/a/b/c" /* root */,
525+
(char*)"/"/* mount_path */,
526+
controller_read_only);
527+
test->set_subsystem_path((char*)"/");
528+
EXPECT_TRUE(test->needs_hierarchy_adjustment());
529+
530+
CgroupCpuController* test2 = new CgroupV1CpuController(CgroupV1Controller((char*)"/a/b/c" /* root */,
531+
(char*)"/sys/fs/cgroup/memory" /* mount_path */,
532+
controller_read_only));
533+
static_cast<CgroupV1CpuController*>(test2)->set_subsystem_path((char*)"/a/b/c");
534+
EXPECT_FALSE(test2->needs_hierarchy_adjustment());
535+
static_cast<CgroupV1CpuController*>(test2)->set_subsystem_path((char*)"/");
536+
EXPECT_TRUE(test2->needs_hierarchy_adjustment());
537+
test2 = new CgroupV1CpuController(CgroupV1Controller((char*)"/a/b/c" /* root */,
538+
(char*)"/"/* mount_path */,
539+
controller_read_only));
540+
static_cast<CgroupV1CpuController*>(test2)->set_subsystem_path((char*)"/");
541+
EXPECT_TRUE(test2->needs_hierarchy_adjustment());
542+
543+
CgroupMemoryController* test3 = new CgroupV1MemoryController(CgroupV1Controller((char*)"/a/b/c" /* root */,
544+
(char*)"/sys/fs/cgroup/memory" /* mount_path */,
545+
controller_read_only));
546+
static_cast<CgroupV1MemoryController*>(test3)->set_subsystem_path((char*)"/a/b/c");
547+
EXPECT_FALSE(test3->needs_hierarchy_adjustment());
548+
static_cast<CgroupV1MemoryController*>(test3)->set_subsystem_path((char*)"/");
549+
EXPECT_TRUE(test3->needs_hierarchy_adjustment());
550+
test3 = new CgroupV1MemoryController(CgroupV1Controller((char*)"/a/b/c" /* root */,
551+
(char*)"/"/* mount_path */,
552+
controller_read_only));
553+
static_cast<CgroupV1MemoryController*>(test3)->set_subsystem_path((char*)"/");
554+
EXPECT_TRUE(test3->needs_hierarchy_adjustment());
555+
}
556+
473557
#endif // LINUX

0 commit comments

Comments
 (0)
Please sign in to comment.