Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
eda2aa9
8302744: Refactor Hotspot container detection code
jerboaa Apr 12, 2024
e659a67
Refactor cgroup controller code
jerboaa Apr 17, 2024
3769ef3
Fix whitespace
jerboaa May 3, 2024
6c88f9f
Merge branch 'master' into jdk-8302744-cleanup-getcontainer-info
jerboaa May 15, 2024
3358c5b
Add generic julong/char* read functions
jerboaa May 15, 2024
09521e5
Add basic testing for read functions
jerboaa May 16, 2024
6a81344
Add key-value read function
jerboaa May 16, 2024
6ce5c55
Try a different approach.
jerboaa May 17, 2024
f368d54
Handle cpu quota for cgroups v1 specially
jerboaa May 17, 2024
4de2adb
Fix whitespace
jerboaa May 21, 2024
9ac1b40
Fix TestMemoryAwareness for cgroup v2
jerboaa May 21, 2024
8d5d0a4
Merge branch 'pr/19060' into jdk-8331560-cgroup-controller-delegation
jerboaa May 21, 2024
b0c3a72
Fix limit_from_str usages
jerboaa May 21, 2024
5d20a2d
More fix-ups after merge
jerboaa May 21, 2024
fec9d9a
Merge branch 'master' into jdk-8302744-cleanup-getcontainer-info
jerboaa May 22, 2024
7da8791
Use enum class for TupleValue
jerboaa May 22, 2024
d3e827f
Get rid of the templated function
jerboaa May 22, 2024
808ec10
Fix general read string cases.
jerboaa May 23, 2024
b4fcf1d
Add convenience function for 'max' handling
jerboaa May 23, 2024
328390d
Add a test for a large string read
jerboaa May 23, 2024
c41d318
Add proper comments for parsing utility functions
jerboaa May 23, 2024
e8603c2
Merge branch 'jdk-8302744-cleanup-getcontainer-info' into jdk-8331560…
jerboaa May 23, 2024
9477fd2
Remove limit_from_str from util class
jerboaa May 23, 2024
b085574
Resolve ambiguity with static_cast
jerboaa May 27, 2024
bd2ad62
Merge branch 'master' into jdk-8331560-cgroup-controller-delegation
jerboaa May 29, 2024
93c52ec
Fixup after merge
jerboaa May 29, 2024
8b97280
Fix inheritance hierarchy
jerboaa May 29, 2024
cb91f4c
Appropriately handle version specific printing
jerboaa May 29, 2024
ad62e16
Clean up
jerboaa May 29, 2024
e177a08
Review feedback
jerboaa May 29, 2024
b26f188
Merge branch 'master' into jdk-8331560-cgroup-controller-delegation
jerboaa Jun 14, 2024
cc819f2
Get rid of multiple inheritance
jerboaa Jun 14, 2024
2c067dd
Style nit
jerboaa Jun 14, 2024
5d7537f
initializer lists
jerboaa Jun 14, 2024
2e2b4eb
Templated class use references
jerboaa Jun 14, 2024
6aff207
Tabs
jerboaa Jun 14, 2024
467018f
Review feedback
jerboaa Jun 18, 2024
abf0adc
Rename log function
jerboaa Jun 18, 2024
48494f6
Merge branch 'master' into jdk-8331560-cgroup-controller-delegation
jerboaa Jun 20, 2024
883be8c
Merge branch 'master' into jdk-8331560-cgroup-controller-delegation
jerboaa Jun 25, 2024
be3c15b
Use references when passing in readers
jerboaa Jun 25, 2024
20ad401
Remove declaration from sub-classes that are implemented in super class
jerboaa Jun 25, 2024
65a396d
Helper function naming fixes
jerboaa Jun 25, 2024
30fa617
Add comment about un-used-ness
jerboaa Jun 25, 2024
35411e8
Merge branch 'master' into jdk-8331560-cgroup-controller-delegation
jerboaa Jul 1, 2024
dcd0554
Fixes after merge
jerboaa Jul 1, 2024
77cf2ea
Fix tabs => spaces
jerboaa Jul 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 72 additions & 51 deletions src/hotspot/os/linux/cgroupSubsystem_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "cgroupSubsystem_linux.hpp"
#include "cgroupV1Subsystem_linux.hpp"
#include "cgroupV2Subsystem_linux.hpp"
#include "cgroupUtil_linux.hpp"
#include "logging/log.hpp"
#include "memory/allocation.hpp"
#include "os_linux.hpp"
Expand All @@ -41,7 +42,7 @@ static const char* cg_controller_name[] = { "cpu", "cpuset", "cpuacct", "memory"
CgroupSubsystem* CgroupSubsystemFactory::create() {
CgroupV1MemoryController* memory = nullptr;
CgroupV1Controller* cpuset = nullptr;
CgroupV1Controller* cpu = nullptr;
CgroupV1CpuController* cpu = nullptr;
CgroupV1Controller* cpuacct = nullptr;
CgroupV1Controller* pids = nullptr;
CgroupInfo cg_infos[CG_INFO_LENGTH];
Expand All @@ -61,14 +62,18 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
if (is_cgroup_v2(&cg_type_flags)) {
// Cgroups v2 case, we have all the info we need.
// Construct the subsystem, free resources and return
// Note: any index in cg_infos will do as the path is the same for
// all controllers.
CgroupController* unified = new CgroupV2Controller(cg_infos[MEMORY_IDX]._mount_path,
cg_infos[MEMORY_IDX]._cgroup_path,
cg_infos[MEMORY_IDX]._read_only);
// Note: We use the memory for non-cpu non-memory controller look-ups.
// Perhaps we ought to have separate controllers for all.
CgroupV2Controller mem_other = CgroupV2Controller(cg_infos[MEMORY_IDX]._mount_path,
cg_infos[MEMORY_IDX]._cgroup_path,
cg_infos[MEMORY_IDX]._read_only);
CgroupV2MemoryController* memory = new CgroupV2MemoryController(mem_other);
CgroupV2CpuController* cpu = new CgroupV2CpuController(CgroupV2Controller(cg_infos[CPU_IDX]._mount_path,
cg_infos[CPU_IDX]._cgroup_path,
cg_infos[CPU_IDX]._read_only));
log_debug(os, container)("Detected cgroups v2 unified hierarchy");
cleanup(cg_infos);
return new CgroupV2Subsystem(unified);
return new CgroupV2Subsystem(memory, cpu, mem_other);
}

/*
Expand Down Expand Up @@ -102,13 +107,13 @@ CgroupSubsystem* CgroupSubsystemFactory::create() {
CgroupInfo info = cg_infos[i];
if (info._data_complete) { // pids controller might have incomplete data
if (strcmp(info._name, "memory") == 0) {
memory = new CgroupV1MemoryController(info._root_mount_path, info._mount_path, info._read_only);
memory = new CgroupV1MemoryController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only));
memory->set_subsystem_path(info._cgroup_path);
} else if (strcmp(info._name, "cpuset") == 0) {
cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
cpuset->set_subsystem_path(info._cgroup_path);
} else if (strcmp(info._name, "cpu") == 0) {
cpu = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
cpu = new CgroupV1CpuController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only));
cpu->set_subsystem_path(info._cgroup_path);
} else if (strcmp(info._name, "cpuacct") == 0) {
cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only);
Expand Down Expand Up @@ -556,37 +561,22 @@ void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) {
*/
int CgroupSubsystem::active_processor_count() {
int quota_count = 0;
int cpu_count, limit_count;
int cpu_count;
int result;

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

cpu_count = limit_count = os::Linux::active_processor_count();
int quota = cpu_quota();
int period = cpu_period();

if (quota > -1 && period > 0) {
quota_count = ceilf((float)quota / (float)period);
log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count);
}

// Use quotas
if (quota_count != 0) {
limit_count = quota_count;
}

result = MIN2(cpu_count, limit_count);
log_trace(os, container)("OSContainer::active_processor_count: %d", result);

cpu_count = os::Linux::active_processor_count();
result = CgroupUtil::processor_count(contrl->controller(), cpu_count);
// Update cached metric to avoid re-reading container settings too often
cpu_limit->set_value(result, OSCONTAINER_CACHE_TIMEOUT);

Expand All @@ -603,35 +593,14 @@ int CgroupSubsystem::active_processor_count() {
* OSCONTAINER_ERROR for not supported
*/
jlong CgroupSubsystem::memory_limit_in_bytes() {
CachingCgroupController* contrl = memory_controller();
CachingCgroupController<CgroupMemoryController>* contrl = memory_controller();
CachedMetric* memory_limit = contrl->metrics_cache();
if (!memory_limit->should_check_metric()) {
return memory_limit->value();
}
jlong phys_mem = os::Linux::physical_memory();
log_trace(os, container)("total physical memory: " JLONG_FORMAT, phys_mem);
jlong mem_limit = read_memory_limit_in_bytes();

if (mem_limit <= 0 || mem_limit >= phys_mem) {
jlong read_mem_limit = mem_limit;
const char *reason;
if (mem_limit >= phys_mem) {
// Exceeding physical memory is treated as unlimited. Cg v1's implementation
// of read_memory_limit_in_bytes() caps this at phys_mem since Cg v1 has no
// value to represent 'max'. Cg v2 may return a value >= phys_mem if e.g. the
// container engine was started with a memory flag exceeding it.
reason = "ignored";
mem_limit = -1;
} else if (OSCONTAINER_ERROR == mem_limit) {
reason = "failed";
} else {
assert(mem_limit == -1, "Expected unlimited");
reason = "unlimited";
}
log_debug(os, container)("container memory limit %s: " JLONG_FORMAT ", using host value " JLONG_FORMAT,
reason, read_mem_limit, phys_mem);
}

jlong mem_limit = contrl->controller()->read_memory_limit_in_bytes(phys_mem);
// Update cached metric to avoid re-reading container settings too often
memory_limit->set_value(mem_limit, OSCONTAINER_CACHE_TIMEOUT);
return mem_limit;
Expand Down Expand Up @@ -796,3 +765,55 @@ jlong CgroupController::limit_from_str(char* limit_str) {
}
return (jlong)limit;
}

// CgroupSubsystem implementations

jlong CgroupSubsystem::memory_and_swap_limit_in_bytes() {
julong phys_mem = os::Linux::physical_memory();
julong host_swap = os::Linux::host_swap();
return memory_controller()->controller()->memory_and_swap_limit_in_bytes(phys_mem, host_swap);
}

jlong CgroupSubsystem::memory_and_swap_usage_in_bytes() {
julong phys_mem = os::Linux::physical_memory();
julong host_swap = os::Linux::host_swap();
return memory_controller()->controller()->memory_and_swap_usage_in_bytes(phys_mem, host_swap);
}

jlong CgroupSubsystem::memory_soft_limit_in_bytes() {
julong phys_mem = os::Linux::physical_memory();
return memory_controller()->controller()->memory_soft_limit_in_bytes(phys_mem);
}

jlong CgroupSubsystem::memory_usage_in_bytes() {
return memory_controller()->controller()->memory_usage_in_bytes();
}

jlong CgroupSubsystem::memory_max_usage_in_bytes() {
return memory_controller()->controller()->memory_max_usage_in_bytes();
}

jlong CgroupSubsystem::rss_usage_in_bytes() {
return memory_controller()->controller()->rss_usage_in_bytes();
}

jlong CgroupSubsystem::cache_usage_in_bytes() {
return memory_controller()->controller()->cache_usage_in_bytes();
}

int CgroupSubsystem::cpu_quota() {
return cpu_controller()->controller()->cpu_quota();
}

int CgroupSubsystem::cpu_period() {
return cpu_controller()->controller()->cpu_period();
}

int CgroupSubsystem::cpu_shares() {
return cpu_controller()->controller()->cpu_shares();
}

void CgroupSubsystem::print_version_specific_info(outputStream* st) {
julong phys_mem = os::Linux::physical_memory();
memory_controller()->controller()->print_version_specific_info(st, phys_mem);
}
59 changes: 42 additions & 17 deletions src/hotspot/os/linux/cgroupSubsystem_linux.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,48 +180,73 @@ class CachedMetric : public CHeapObj<mtInternal>{
}
};

template <class T>
class CachingCgroupController : public CHeapObj<mtInternal> {
private:
CgroupController* _controller;
T* _controller;
CachedMetric* _metrics_cache;

public:
CachingCgroupController(CgroupController* cont) {
CachingCgroupController(T* cont) {
_controller = cont;
_metrics_cache = new CachedMetric();
}

CachedMetric* metrics_cache() { return _metrics_cache; }
CgroupController* controller() { return _controller; }
T* controller() { return _controller; }
};

class CgroupSubsystem: public CHeapObj<mtInternal> {
// Pure virtual class representing version agnostic CPU controllers
class CgroupCpuController: public CHeapObj<mtInternal> {
public:
jlong memory_limit_in_bytes();
int active_processor_count();

virtual int cpu_quota() = 0;
virtual int cpu_period() = 0;
virtual int cpu_shares() = 0;
virtual jlong pids_max() = 0;
virtual jlong pids_current() = 0;
virtual bool is_read_only() = 0;
};

// Pure virtual class representing version agnostic memory controllers
class CgroupMemoryController: public CHeapObj<mtInternal> {
public:
virtual jlong read_memory_limit_in_bytes(julong upper_bound) = 0;
virtual jlong memory_usage_in_bytes() = 0;
virtual jlong memory_and_swap_limit_in_bytes() = 0;
virtual jlong memory_and_swap_usage_in_bytes() = 0;
virtual jlong memory_soft_limit_in_bytes() = 0;
virtual jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) = 0;
virtual jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) = 0;
virtual jlong memory_soft_limit_in_bytes(julong upper_bound) = 0;
virtual jlong memory_max_usage_in_bytes() = 0;
virtual jlong rss_usage_in_bytes() = 0;
virtual jlong cache_usage_in_bytes() = 0;
virtual void print_version_specific_info(outputStream* st, julong host_mem) = 0;
virtual bool is_read_only() = 0;
};

class CgroupSubsystem: public CHeapObj<mtInternal> {
public:
jlong memory_limit_in_bytes();
int active_processor_count();

virtual jlong pids_max() = 0;
virtual jlong pids_current() = 0;
virtual bool is_containerized() = 0;

virtual char * cpu_cpuset_cpus() = 0;
virtual char * cpu_cpuset_memory_nodes() = 0;
virtual jlong read_memory_limit_in_bytes() = 0;
virtual const char * container_type() = 0;
virtual CachingCgroupController* memory_controller() = 0;
virtual CachingCgroupController* cpu_controller() = 0;

virtual void print_version_specific_info(outputStream* st) = 0;
virtual CachingCgroupController<CgroupMemoryController>* memory_controller() = 0;
virtual CachingCgroupController<CgroupCpuController>* cpu_controller() = 0;

int cpu_quota();
int cpu_period();
int cpu_shares();

jlong memory_usage_in_bytes();
jlong memory_and_swap_limit_in_bytes();
jlong memory_and_swap_usage_in_bytes();
jlong memory_soft_limit_in_bytes();
jlong memory_max_usage_in_bytes();
jlong rss_usage_in_bytes();
jlong cache_usage_in_bytes();
void print_version_specific_info(outputStream* st);
};

// Utility class for storing info retrieved from /proc/cgroups,
Expand Down
48 changes: 48 additions & 0 deletions src/hotspot/os/linux/cgroupUtil_linux.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024, Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#include "cgroupUtil_linux.hpp"

int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) {
assert(host_cpus > 0, "physical host cpus must be positive");
int limit_count = host_cpus;
int quota = cpu_ctrl->cpu_quota();
int period = cpu_ctrl->cpu_period();
int quota_count = 0;
int result = 0;

if (quota > -1 && period > 0) {
quota_count = ceilf((float)quota / (float)period);
log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count);
}

// Use quotas
if (quota_count != 0) {
limit_count = quota_count;
}

result = MIN2(host_cpus, limit_count);
log_trace(os, container)("OSContainer::active_processor_count: %d", result);
return result;
}
37 changes: 37 additions & 0 deletions src/hotspot/os/linux/cgroupUtil_linux.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024, Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#ifndef CGROUP_UTIL_LINUX_HPP
#define CGROUP_UTIL_LINUX_HPP

#include "utilities/globalDefinitions.hpp"
#include "cgroupSubsystem_linux.hpp"

class CgroupUtil: AllStatic {

public:
static int processor_count(CgroupCpuController* cpu, int host_cpus);
};

#endif // CGROUP_UTIL_LINUX_HPP
Loading