Skip to content

Commit 153b12b

Browse files
committed
8331560: Refactor Hotspot container detection code so that subsystem delegates to controllers
Reviewed-by: jsjolen, stuefe
1 parent 685e587 commit 153b12b

File tree

8 files changed

+511
-264
lines changed

8 files changed

+511
-264
lines changed

src/hotspot/os/linux/cgroupSubsystem_linux.cpp

Lines changed: 72 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "cgroupSubsystem_linux.hpp"
2929
#include "cgroupV1Subsystem_linux.hpp"
3030
#include "cgroupV2Subsystem_linux.hpp"
31+
#include "cgroupUtil_linux.hpp"
3132
#include "logging/log.hpp"
3233
#include "memory/allocation.hpp"
3334
#include "os_linux.hpp"
@@ -41,7 +42,7 @@ static const char* cg_controller_name[] = { "cpu", "cpuset", "cpuacct", "memory"
4142
CgroupSubsystem* CgroupSubsystemFactory::create() {
4243
CgroupV1MemoryController* memory = nullptr;
4344
CgroupV1Controller* cpuset = nullptr;
44-
CgroupV1Controller* cpu = nullptr;
45+
CgroupV1CpuController* cpu = nullptr;
4546
CgroupV1Controller* cpuacct = nullptr;
4647
CgroupV1Controller* pids = nullptr;
4748
CgroupInfo cg_infos[CG_INFO_LENGTH];
@@ -61,14 +62,18 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
6162
if (is_cgroup_v2(&cg_type_flags)) {
6263
// Cgroups v2 case, we have all the info we need.
6364
// Construct the subsystem, free resources and return
64-
// Note: any index in cg_infos will do as the path is the same for
65-
// all controllers.
66-
CgroupController* unified = new CgroupV2Controller(cg_infos[MEMORY_IDX]._mount_path,
67-
cg_infos[MEMORY_IDX]._cgroup_path,
68-
cg_infos[MEMORY_IDX]._read_only);
65+
// Note: We use the memory for non-cpu non-memory controller look-ups.
66+
// Perhaps we ought to have separate controllers for all.
67+
CgroupV2Controller mem_other = CgroupV2Controller(cg_infos[MEMORY_IDX]._mount_path,
68+
cg_infos[MEMORY_IDX]._cgroup_path,
69+
cg_infos[MEMORY_IDX]._read_only);
70+
CgroupV2MemoryController* memory = new CgroupV2MemoryController(mem_other);
71+
CgroupV2CpuController* cpu = new CgroupV2CpuController(CgroupV2Controller(cg_infos[CPU_IDX]._mount_path,
72+
cg_infos[CPU_IDX]._cgroup_path,
73+
cg_infos[CPU_IDX]._read_only));
6974
log_debug(os, container)("Detected cgroups v2 unified hierarchy");
7075
cleanup(cg_infos);
71-
return new CgroupV2Subsystem(unified);
76+
return new CgroupV2Subsystem(memory, cpu, mem_other);
7277
}
7378

7479
/*
@@ -102,13 +107,13 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
102107
CgroupInfo info = cg_infos[i];
103108
if (info._data_complete) { // pids controller might have incomplete data
104109
if (strcmp(info._name, "memory") == 0) {
105-
memory = new CgroupV1MemoryController(info._root_mount_path, info._mount_path, info._read_only);
110+
memory = new CgroupV1MemoryController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only));
106111
memory->set_subsystem_path(info._cgroup_path);
107112
} else if (strcmp(info._name, "cpuset") == 0) {
108113
cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
109114
cpuset->set_subsystem_path(info._cgroup_path);
110115
} else if (strcmp(info._name, "cpu") == 0) {
111-
cpu = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
116+
cpu = new CgroupV1CpuController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only));
112117
cpu->set_subsystem_path(info._cgroup_path);
113118
} else if (strcmp(info._name, "cpuacct") == 0) {
114119
cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
@@ -556,37 +561,22 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) {
556561
*/
557562
int CgroupSubsystem::active_processor_count() {
558563
int quota_count = 0;
559-
int cpu_count, limit_count;
564+
int cpu_count;
560565
int result;
561566

562567
// We use a cache with a timeout to avoid performing expensive
563568
// computations in the event this function is called frequently.
564569
// [See 8227006].
565-
CachingCgroupController* contrl = cpu_controller();
570+
CachingCgroupController<CgroupCpuController>* contrl = cpu_controller();
566571
CachedMetric* cpu_limit = contrl->metrics_cache();
567572
if (!cpu_limit->should_check_metric()) {
568573
int val = (int)cpu_limit->value();
569574
log_trace(os, container)("CgroupSubsystem::active_processor_count (cached): %d", val);
570575
return val;
571576
}
572577

573-
cpu_count = limit_count = os::Linux::active_processor_count();
574-
int quota = cpu_quota();
575-
int period = cpu_period();
576-
577-
if (quota > -1 && period > 0) {
578-
quota_count = ceilf((float)quota / (float)period);
579-
log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count);
580-
}
581-
582-
// Use quotas
583-
if (quota_count != 0) {
584-
limit_count = quota_count;
585-
}
586-
587-
result = MIN2(cpu_count, limit_count);
588-
log_trace(os, container)("OSContainer::active_processor_count: %d", result);
589-
578+
cpu_count = os::Linux::active_processor_count();
579+
result = CgroupUtil::processor_count(contrl->controller(), cpu_count);
590580
// Update cached metric to avoid re-reading container settings too often
591581
cpu_limit->set_value(result, OSCONTAINER_CACHE_TIMEOUT);
592582

@@ -603,35 +593,14 @@ int CgroupSubsystem::active_processor_count() {
603593
* OSCONTAINER_ERROR for not supported
604594
*/
605595
jlong CgroupSubsystem::memory_limit_in_bytes() {
606-
CachingCgroupController* contrl = memory_controller();
596+
CachingCgroupController<CgroupMemoryController>* contrl = memory_controller();
607597
CachedMetric* memory_limit = contrl->metrics_cache();
608598
if (!memory_limit->should_check_metric()) {
609599
return memory_limit->value();
610600
}
611601
jlong phys_mem = os::Linux::physical_memory();
612602
log_trace(os, container)("total physical memory: " JLONG_FORMAT, phys_mem);
613-
jlong mem_limit = read_memory_limit_in_bytes();
614-
615-
if (mem_limit <= 0 || mem_limit >= phys_mem) {
616-
jlong read_mem_limit = mem_limit;
617-
const char *reason;
618-
if (mem_limit >= phys_mem) {
619-
// Exceeding physical memory is treated as unlimited. Cg v1's implementation
620-
// of read_memory_limit_in_bytes() caps this at phys_mem since Cg v1 has no
621-
// value to represent 'max'. Cg v2 may return a value >= phys_mem if e.g. the
622-
// container engine was started with a memory flag exceeding it.
623-
reason = "ignored";
624-
mem_limit = -1;
625-
} else if (OSCONTAINER_ERROR == mem_limit) {
626-
reason = "failed";
627-
} else {
628-
assert(mem_limit == -1, "Expected unlimited");
629-
reason = "unlimited";
630-
}
631-
log_debug(os, container)("container memory limit %s: " JLONG_FORMAT ", using host value " JLONG_FORMAT,
632-
reason, read_mem_limit, phys_mem);
633-
}
634-
603+
jlong mem_limit = contrl->controller()->read_memory_limit_in_bytes(phys_mem);
635604
// Update cached metric to avoid re-reading container settings too often
636605
memory_limit->set_value(mem_limit, OSCONTAINER_CACHE_TIMEOUT);
637606
return mem_limit;
@@ -796,3 +765,55 @@ jlong CgroupController::limit_from_str(char* limit_str) {
796765
}
797766
return (jlong)limit;
798767
}
768+
769+
// CgroupSubsystem implementations
770+
771+
jlong CgroupSubsystem::memory_and_swap_limit_in_bytes() {
772+
julong phys_mem = os::Linux::physical_memory();
773+
julong host_swap = os::Linux::host_swap();
774+
return memory_controller()->controller()->memory_and_swap_limit_in_bytes(phys_mem, host_swap);
775+
}
776+
777+
jlong CgroupSubsystem::memory_and_swap_usage_in_bytes() {
778+
julong phys_mem = os::Linux::physical_memory();
779+
julong host_swap = os::Linux::host_swap();
780+
return memory_controller()->controller()->memory_and_swap_usage_in_bytes(phys_mem, host_swap);
781+
}
782+
783+
jlong CgroupSubsystem::memory_soft_limit_in_bytes() {
784+
julong phys_mem = os::Linux::physical_memory();
785+
return memory_controller()->controller()->memory_soft_limit_in_bytes(phys_mem);
786+
}
787+
788+
jlong CgroupSubsystem::memory_usage_in_bytes() {
789+
return memory_controller()->controller()->memory_usage_in_bytes();
790+
}
791+
792+
jlong CgroupSubsystem::memory_max_usage_in_bytes() {
793+
return memory_controller()->controller()->memory_max_usage_in_bytes();
794+
}
795+
796+
jlong CgroupSubsystem::rss_usage_in_bytes() {
797+
return memory_controller()->controller()->rss_usage_in_bytes();
798+
}
799+
800+
jlong CgroupSubsystem::cache_usage_in_bytes() {
801+
return memory_controller()->controller()->cache_usage_in_bytes();
802+
}
803+
804+
int CgroupSubsystem::cpu_quota() {
805+
return cpu_controller()->controller()->cpu_quota();
806+
}
807+
808+
int CgroupSubsystem::cpu_period() {
809+
return cpu_controller()->controller()->cpu_period();
810+
}
811+
812+
int CgroupSubsystem::cpu_shares() {
813+
return cpu_controller()->controller()->cpu_shares();
814+
}
815+
816+
void CgroupSubsystem::print_version_specific_info(outputStream* st) {
817+
julong phys_mem = os::Linux::physical_memory();
818+
memory_controller()->controller()->print_version_specific_info(st, phys_mem);
819+
}

src/hotspot/os/linux/cgroupSubsystem_linux.hpp

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -180,48 +180,73 @@ class CachedMetric : public CHeapObj<mtInternal>{
180180
}
181181
};
182182

183+
template <class T>
183184
class CachingCgroupController : public CHeapObj<mtInternal> {
184185
private:
185-
CgroupController* _controller;
186+
T* _controller;
186187
CachedMetric* _metrics_cache;
187188

188189
public:
189-
CachingCgroupController(CgroupController* cont) {
190+
CachingCgroupController(T* cont) {
190191
_controller = cont;
191192
_metrics_cache = new CachedMetric();
192193
}
193194

194195
CachedMetric* metrics_cache() { return _metrics_cache; }
195-
CgroupController* controller() { return _controller; }
196+
T* controller() { return _controller; }
196197
};
197198

198-
class CgroupSubsystem: public CHeapObj<mtInternal> {
199+
// Pure virtual class representing version agnostic CPU controllers
200+
class CgroupCpuController: public CHeapObj<mtInternal> {
199201
public:
200-
jlong memory_limit_in_bytes();
201-
int active_processor_count();
202-
203202
virtual int cpu_quota() = 0;
204203
virtual int cpu_period() = 0;
205204
virtual int cpu_shares() = 0;
206-
virtual jlong pids_max() = 0;
207-
virtual jlong pids_current() = 0;
205+
virtual bool is_read_only() = 0;
206+
};
207+
208+
// Pure virtual class representing version agnostic memory controllers
209+
class CgroupMemoryController: public CHeapObj<mtInternal> {
210+
public:
211+
virtual jlong read_memory_limit_in_bytes(julong upper_bound) = 0;
208212
virtual jlong memory_usage_in_bytes() = 0;
209-
virtual jlong memory_and_swap_limit_in_bytes() = 0;
210-
virtual jlong memory_and_swap_usage_in_bytes() = 0;
211-
virtual jlong memory_soft_limit_in_bytes() = 0;
213+
virtual jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) = 0;
214+
virtual jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) = 0;
215+
virtual jlong memory_soft_limit_in_bytes(julong upper_bound) = 0;
212216
virtual jlong memory_max_usage_in_bytes() = 0;
213217
virtual jlong rss_usage_in_bytes() = 0;
214218
virtual jlong cache_usage_in_bytes() = 0;
219+
virtual void print_version_specific_info(outputStream* st, julong host_mem) = 0;
220+
virtual bool is_read_only() = 0;
221+
};
222+
223+
class CgroupSubsystem: public CHeapObj<mtInternal> {
224+
public:
225+
jlong memory_limit_in_bytes();
226+
int active_processor_count();
227+
228+
virtual jlong pids_max() = 0;
229+
virtual jlong pids_current() = 0;
215230
virtual bool is_containerized() = 0;
216231

217232
virtual char * cpu_cpuset_cpus() = 0;
218233
virtual char * cpu_cpuset_memory_nodes() = 0;
219-
virtual jlong read_memory_limit_in_bytes() = 0;
220234
virtual const char * container_type() = 0;
221-
virtual CachingCgroupController* memory_controller() = 0;
222-
virtual CachingCgroupController* cpu_controller() = 0;
223-
224-
virtual void print_version_specific_info(outputStream* st) = 0;
235+
virtual CachingCgroupController<CgroupMemoryController>* memory_controller() = 0;
236+
virtual CachingCgroupController<CgroupCpuController>* cpu_controller() = 0;
237+
238+
int cpu_quota();
239+
int cpu_period();
240+
int cpu_shares();
241+
242+
jlong memory_usage_in_bytes();
243+
jlong memory_and_swap_limit_in_bytes();
244+
jlong memory_and_swap_usage_in_bytes();
245+
jlong memory_soft_limit_in_bytes();
246+
jlong memory_max_usage_in_bytes();
247+
jlong rss_usage_in_bytes();
248+
jlong cache_usage_in_bytes();
249+
void print_version_specific_info(outputStream* st);
225250
};
226251

227252
// Utility class for storing info retrieved from /proc/cgroups,
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2024, Red Hat, Inc.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#include "cgroupUtil_linux.hpp"
26+
27+
int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) {
28+
assert(host_cpus > 0, "physical host cpus must be positive");
29+
int limit_count = host_cpus;
30+
int quota = cpu_ctrl->cpu_quota();
31+
int period = cpu_ctrl->cpu_period();
32+
int quota_count = 0;
33+
int result = 0;
34+
35+
if (quota > -1 && period > 0) {
36+
quota_count = ceilf((float)quota / (float)period);
37+
log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count);
38+
}
39+
40+
// Use quotas
41+
if (quota_count != 0) {
42+
limit_count = quota_count;
43+
}
44+
45+
result = MIN2(host_cpus, limit_count);
46+
log_trace(os, container)("OSContainer::active_processor_count: %d", result);
47+
return result;
48+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2024, Red Hat, Inc.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#ifndef CGROUP_UTIL_LINUX_HPP
26+
#define CGROUP_UTIL_LINUX_HPP
27+
28+
#include "utilities/globalDefinitions.hpp"
29+
#include "cgroupSubsystem_linux.hpp"
30+
31+
class CgroupUtil: AllStatic {
32+
33+
public:
34+
static int processor_count(CgroupCpuController* cpu, int host_cpus);
35+
};
36+
37+
#endif // CGROUP_UTIL_LINUX_HPP

0 commit comments

Comments
 (0)