diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 0dbd6ffd52be7..1da0e44dbf48e 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -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" @@ -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]; @@ -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); } /* @@ -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); @@ -556,13 +561,13 @@ 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* contrl = cpu_controller(); CachedMetric* cpu_limit = contrl->metrics_cache(); if (!cpu_limit->should_check_metric()) { int val = (int)cpu_limit->value(); @@ -570,23 +575,8 @@ int CgroupSubsystem::active_processor_count() { 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); @@ -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* 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; @@ -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); +} diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 00419c77570ae..4d5fa5d487900 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -180,48 +180,73 @@ class CachedMetric : public CHeapObj{ } }; +template class CachingCgroupController : public CHeapObj { 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 { +// Pure virtual class representing version agnostic CPU controllers +class CgroupCpuController: public CHeapObj { 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 { + 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 { + 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* memory_controller() = 0; + virtual CachingCgroupController* 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, diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp new file mode 100644 index 0000000000000..24046991905ee --- /dev/null +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -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; +} diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp new file mode 100644 index 0000000000000..fdcc4806c3b93 --- /dev/null +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -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 diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 3f6ae813810f2..d7f9918afdad8 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -26,6 +26,7 @@ #include #include #include "cgroupV1Subsystem_linux.hpp" +#include "cgroupUtil_linux.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" #include "runtime/globals.hpp" @@ -76,42 +77,62 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { */ jlong CgroupV1MemoryController::uses_mem_hierarchy() { julong use_hierarchy; - CONTAINER_READ_NUMBER_CHECKED(this, "/memory.use_hierarchy", "Use Hierarchy", use_hierarchy); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.use_hierarchy", "Use Hierarchy", use_hierarchy); return (jlong)use_hierarchy; } void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) { - CgroupV1Controller::set_subsystem_path(cgroup_path); + reader()->set_subsystem_path(cgroup_path); jlong hierarchy = uses_mem_hierarchy(); if (hierarchy > 0) { set_hierarchical(true); } } -jlong CgroupV1Subsystem::read_memory_limit_in_bytes() { +static inline +void verbose_log(julong read_mem_limit, julong host_mem) { + if (log_is_enabled(Debug, os, container)) { + jlong mem_limit = (jlong)read_mem_limit; // account for negative values + if (mem_limit < 0 || read_mem_limit >= host_mem) { + const char *reason; + if (mem_limit == OSCONTAINER_ERROR) { + reason = "failed"; + } else if (mem_limit == -1) { + reason = "unlimited"; + } else { + assert(read_mem_limit >= host_mem, "Expected read value exceeding host_mem"); + // Exceeding physical memory is treated as unlimited. This implementation + // caps it at host_mem since Cg v1 has no value to represent 'max'. + reason = "ignored"; + } + log_debug(os, container)("container memory limit %s: " JLONG_FORMAT ", using host value " JLONG_FORMAT, + reason, mem_limit, host_mem); + } + } +} + +jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { julong memlimit; - CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.limit_in_bytes", "Memory Limit", memlimit); - if (memlimit >= os::Linux::physical_memory()) { + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.limit_in_bytes", "Memory Limit", memlimit); + if (memlimit >= phys_mem) { log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); - CgroupV1MemoryController* mem_controller = reinterpret_cast(_memory->controller()); - if (mem_controller->is_hierarchical()) { + if (is_hierarchical()) { julong hier_memlimit; - bool is_ok = _memory->controller()->read_numerical_key_value("/memory.stat", - "hierarchical_memory_limit", - &hier_memlimit); + bool is_ok = reader()->read_numerical_key_value("/memory.stat", "hierarchical_memory_limit", &hier_memlimit); if (!is_ok) { return OSCONTAINER_ERROR; } log_trace(os, container)("Hierarchical Memory Limit is: " JULONG_FORMAT, hier_memlimit); - if (hier_memlimit >= os::Linux::physical_memory()) { - log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); - } else { + if (hier_memlimit < phys_mem) { + verbose_log(hier_memlimit, phys_mem); return (jlong)hier_memlimit; } + log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); } + verbose_log(memlimit, phys_mem); return (jlong)-1; - } - else { + } else { + verbose_log(memlimit, phys_mem); return (jlong)memlimit; } } @@ -128,20 +149,17 @@ jlong CgroupV1Subsystem::read_memory_limit_in_bytes() { * * -1 if there isn't any limit in place (note: includes values which exceed a physical * upper bound) */ -jlong CgroupV1Subsystem::read_mem_swap() { - julong host_total_memsw; +jlong CgroupV1MemoryController::read_mem_swap(julong host_total_memsw) { julong hier_memswlimit; julong memswlimit; - CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", memswlimit); - host_total_memsw = os::Linux::host_swap() + os::Linux::physical_memory(); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", memswlimit); if (memswlimit >= host_total_memsw) { log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited"); - CgroupV1MemoryController* mem_controller = reinterpret_cast(_memory->controller()); - if (mem_controller->is_hierarchical()) { + if (is_hierarchical()) { const char* matchline = "hierarchical_memsw_limit"; - bool is_ok = _memory->controller()->read_numerical_key_value("/memory.stat", - matchline, - &hier_memswlimit); + bool is_ok = reader()->read_numerical_key_value("/memory.stat", + matchline, + &hier_memswlimit); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -158,8 +176,8 @@ jlong CgroupV1Subsystem::read_mem_swap() { } } -jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() { - jlong memory_swap = read_mem_swap(); +jlong CgroupV1MemoryController::memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) { + jlong memory_swap = read_mem_swap(host_mem + host_swap); if (memory_swap == -1) { return memory_swap; } @@ -168,7 +186,7 @@ jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() { // supported. jlong swappiness = read_mem_swappiness(); if (swappiness == 0 || memory_swap == OSCONTAINER_ERROR) { - jlong memlimit = read_memory_limit_in_bytes(); + jlong memlimit = read_memory_limit_in_bytes(host_mem); if (memory_swap == OSCONTAINER_ERROR) { log_trace(os, container)("Memory and Swap Limit has been reset to " JLONG_FORMAT " because swap is not supported", memlimit); } else { @@ -186,28 +204,28 @@ jlong memory_swap_usage_impl(CgroupController* ctrl) { return (jlong)memory_swap_usage; } -jlong CgroupV1Subsystem::memory_and_swap_usage_in_bytes() { - jlong memory_sw_limit = memory_and_swap_limit_in_bytes(); - jlong memory_limit = CgroupSubsystem::memory_limit_in_bytes(); +jlong CgroupV1MemoryController::memory_and_swap_usage_in_bytes(julong phys_mem, julong host_swap) { + jlong memory_sw_limit = memory_and_swap_limit_in_bytes(phys_mem, host_swap); + jlong memory_limit = read_memory_limit_in_bytes(phys_mem); if (memory_sw_limit > 0 && memory_limit > 0) { jlong delta_swap = memory_sw_limit - memory_limit; if (delta_swap > 0) { - return memory_swap_usage_impl(_memory->controller()); + return memory_swap_usage_impl(reader()); } } return memory_usage_in_bytes(); } -jlong CgroupV1Subsystem::read_mem_swappiness() { +jlong CgroupV1MemoryController::read_mem_swappiness() { julong swappiness; - CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.swappiness", "Swappiness", swappiness); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.swappiness", "Swappiness", swappiness); return (jlong)swappiness; } -jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() { +jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { julong memsoftlimit; - CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.soft_limit_in_bytes", "Memory Soft Limit", memsoftlimit); - if (memsoftlimit >= os::Linux::physical_memory()) { + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.soft_limit_in_bytes", "Memory Soft Limit", memsoftlimit); + if (memsoftlimit >= phys_mem) { log_trace(os, container)("Memory Soft Limit is: Unlimited"); return (jlong)-1; } else { @@ -235,9 +253,9 @@ bool CgroupV1Subsystem::is_containerized() { * -1 for unlimited * OSCONTAINER_ERROR for not supported */ -jlong CgroupV1Subsystem::memory_usage_in_bytes() { +jlong CgroupV1MemoryController::memory_usage_in_bytes() { julong memusage; - CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.usage_in_bytes", "Memory Usage", memusage); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.usage_in_bytes", "Memory Usage", memusage); return (jlong)memusage; } @@ -249,17 +267,15 @@ jlong CgroupV1Subsystem::memory_usage_in_bytes() { * max memory usage in bytes or * OSCONTAINER_ERROR for not supported */ -jlong CgroupV1Subsystem::memory_max_usage_in_bytes() { +jlong CgroupV1MemoryController::memory_max_usage_in_bytes() { julong memmaxusage; - CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.max_usage_in_bytes", "Maximum Memory Usage", memmaxusage); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.max_usage_in_bytes", "Maximum Memory Usage", memmaxusage); return (jlong)memmaxusage; } -jlong CgroupV1Subsystem::rss_usage_in_bytes() { +jlong CgroupV1MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = _memory->controller()->read_numerical_key_value("/memory.stat", - "rss", - &rss); + bool is_ok = reader()->read_numerical_key_value("/memory.stat", "rss", &rss); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -267,11 +283,9 @@ jlong CgroupV1Subsystem::rss_usage_in_bytes() { return (jlong)rss; } -jlong CgroupV1Subsystem::cache_usage_in_bytes() { +jlong CgroupV1MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = _memory->controller()->read_numerical_key_value("/memory.stat", - "cache", - &cache); + bool is_ok = reader()->read_numerical_key_value("/memory.stat", "cache", &cache); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -279,30 +293,30 @@ jlong CgroupV1Subsystem::cache_usage_in_bytes() { return cache; } -jlong CgroupV1Subsystem::kernel_memory_usage_in_bytes() { +jlong CgroupV1MemoryController::kernel_memory_usage_in_bytes() { julong kmem_usage; - CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.kmem.usage_in_bytes", "Kernel Memory Usage", kmem_usage); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.usage_in_bytes", "Kernel Memory Usage", kmem_usage); return (jlong)kmem_usage; } -jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() { +jlong CgroupV1MemoryController::kernel_memory_limit_in_bytes(julong phys_mem) { julong kmem_limit; - CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", kmem_limit); - if (kmem_limit >= os::Linux::physical_memory()) { + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", kmem_limit); + if (kmem_limit >= phys_mem) { return (jlong)-1; } return (jlong)kmem_limit; } -jlong CgroupV1Subsystem::kernel_memory_max_usage_in_bytes() { +jlong CgroupV1MemoryController::kernel_memory_max_usage_in_bytes() { julong kmem_max_usage; - CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.kmem.max_usage_in_bytes", "Maximum Kernel Memory Usage", kmem_max_usage); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.max_usage_in_bytes", "Maximum Kernel Memory Usage", kmem_max_usage); return (jlong)kmem_max_usage; } -void CgroupV1Subsystem::print_version_specific_info(outputStream* st) { +void CgroupV1MemoryController::print_version_specific_info(outputStream* st, julong phys_mem) { jlong kmem_usage = kernel_memory_usage_in_bytes(); - jlong kmem_limit = kernel_memory_limit_in_bytes(); + jlong kmem_limit = kernel_memory_limit_in_bytes(phys_mem); jlong kmem_max_usage = kernel_memory_max_usage_in_bytes(); OSContainer::print_container_helper(st, kmem_usage, "kernel_memory_usage_in_bytes"); @@ -332,10 +346,9 @@ char* CgroupV1Subsystem::cpu_cpuset_memory_nodes() { * -1 for no quota * OSCONTAINER_ERROR for not supported */ -int CgroupV1Subsystem::cpu_quota() { +int CgroupV1CpuController::cpu_quota() { julong quota; - bool is_ok = _cpu->controller()-> - read_number("/cpu.cfs_quota_us", "a); + bool is_ok = reader()->read_number("/cpu.cfs_quota_us", "a); if (!is_ok) { log_trace(os, container)("CPU Quota failed: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -347,9 +360,9 @@ int CgroupV1Subsystem::cpu_quota() { return quota_int; } -int CgroupV1Subsystem::cpu_period() { +int CgroupV1CpuController::cpu_period() { julong period; - CONTAINER_READ_NUMBER_CHECKED(_cpu->controller(), "/cpu.cfs_period_us", "CPU Period", period); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpu.cfs_period_us", "CPU Period", period); return (int)period; } @@ -363,9 +376,9 @@ int CgroupV1Subsystem::cpu_period() { * -1 for no share setup * OSCONTAINER_ERROR for not supported */ -int CgroupV1Subsystem::cpu_shares() { +int CgroupV1CpuController::cpu_shares() { julong shares; - CONTAINER_READ_NUMBER_CHECKED(_cpu->controller(), "/cpu.shares", "CPU Shares", shares); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpu.shares", "CPU Shares", shares); int shares_int = (int)shares; // Convert 1024 to no shares setup if (shares_int == 1024) return -1; diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 2b67678c2e8da..251fbde85f0bb 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -34,31 +34,60 @@ class CgroupV1Controller: public CgroupController { private: /* mountinfo contents */ - char *_root; - char *_mount_point; + char* _root; + char* _mount_point; bool _read_only; /* Constructed subsystem directory */ - char *_path; + char* _path; public: - CgroupV1Controller(char *root, char *mountpoint, bool ro) { - _root = os::strdup(root); - _mount_point = os::strdup(mountpoint); - _path = nullptr; - _read_only = ro; + CgroupV1Controller(char *root, + char *mountpoint, + bool ro) : _root(os::strdup(root)), + _mount_point(os::strdup(mountpoint)), + _read_only(ro), + _path(nullptr) { + } + // Shallow copy constructor + CgroupV1Controller(const CgroupV1Controller& o) : _root(o._root), + _mount_point(o._mount_point), + _read_only(o._read_only), + _path(o._path) { + } + ~CgroupV1Controller() { + // At least one subsystem controller exists with paths to malloc'd path + // names } - virtual void set_subsystem_path(char *cgroup_path); - char *subsystem_path() { return _path; } + void set_subsystem_path(char *cgroup_path); + char *subsystem_path() override { return _path; } bool is_read_only() { return _read_only; } }; -class CgroupV1MemoryController: public CgroupV1Controller { +class CgroupV1MemoryController final : public CgroupMemoryController { + private: + CgroupV1Controller _reader; + CgroupV1Controller* reader() { return &_reader; } public: bool is_hierarchical() { return _uses_mem_hierarchy; } void set_subsystem_path(char *cgroup_path); + jlong read_memory_limit_in_bytes(julong upper_bound) override; + jlong memory_usage_in_bytes() override; + jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) override; + jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) override; + jlong memory_soft_limit_in_bytes(julong upper_bound) override; + jlong memory_max_usage_in_bytes() override; + jlong rss_usage_in_bytes() override; + jlong cache_usage_in_bytes() override; + jlong kernel_memory_usage_in_bytes(); + jlong kernel_memory_limit_in_bytes(julong host_mem); + jlong kernel_memory_max_usage_in_bytes(); + void print_version_specific_info(outputStream* st, julong host_mem) override; + bool is_read_only() override { + return reader()->is_read_only(); + } private: /* Some container runtimes set limits via cgroup * hierarchy. If set to true consider also memory.stat @@ -66,26 +95,41 @@ class CgroupV1MemoryController: public CgroupV1Controller { bool _uses_mem_hierarchy; jlong uses_mem_hierarchy(); void set_hierarchical(bool value) { _uses_mem_hierarchy = value; } + jlong read_mem_swappiness(); + jlong read_mem_swap(julong host_total_memsw); public: - CgroupV1MemoryController(char *root, char *mountpoint, bool ro) : CgroupV1Controller(root, mountpoint, ro) { - _uses_mem_hierarchy = false; + CgroupV1MemoryController(const CgroupV1Controller& reader) + : _reader(reader), + _uses_mem_hierarchy(false) { } }; -class CgroupV1Subsystem: public CgroupSubsystem { +class CgroupV1CpuController final : public CgroupCpuController { + + private: + CgroupV1Controller _reader; + CgroupV1Controller* reader() { return &_reader; } + public: + int cpu_quota() override; + int cpu_period() override; + int cpu_shares() override; + void set_subsystem_path(char *cgroup_path) { + reader()->set_subsystem_path(cgroup_path); + } + bool is_read_only() override { + return reader()->is_read_only(); + } public: - jlong read_memory_limit_in_bytes(); - jlong memory_and_swap_limit_in_bytes(); - jlong memory_and_swap_usage_in_bytes(); - jlong memory_soft_limit_in_bytes(); - jlong memory_usage_in_bytes(); - jlong memory_max_usage_in_bytes(); - jlong rss_usage_in_bytes(); - jlong cache_usage_in_bytes(); + CgroupV1CpuController(const CgroupV1Controller& reader) : _reader(reader) { + } +}; + +class CgroupV1Subsystem: public CgroupSubsystem { + public: jlong kernel_memory_usage_in_bytes(); jlong kernel_memory_limit_in_bytes(); jlong kernel_memory_max_usage_in_bytes(); @@ -93,45 +137,35 @@ class CgroupV1Subsystem: public CgroupSubsystem { char * cpu_cpuset_cpus(); char * cpu_cpuset_memory_nodes(); - int cpu_quota(); - int cpu_period(); - - int cpu_shares(); - jlong pids_max(); jlong pids_current(); bool is_containerized(); - void print_version_specific_info(outputStream* st); - const char * container_type() { return "cgroupv1"; } - CachingCgroupController * memory_controller() { return _memory; } - CachingCgroupController * cpu_controller() { return _cpu; } + CachingCgroupController* memory_controller() { return _memory; } + CachingCgroupController* cpu_controller() { return _cpu; } private: /* controllers */ - CachingCgroupController* _memory = nullptr; + CachingCgroupController* _memory = nullptr; CgroupV1Controller* _cpuset = nullptr; - CachingCgroupController* _cpu = nullptr; + CachingCgroupController* _cpu = nullptr; CgroupV1Controller* _cpuacct = nullptr; CgroupV1Controller* _pids = nullptr; - jlong read_mem_swappiness(); - jlong read_mem_swap(); - public: CgroupV1Subsystem(CgroupV1Controller* cpuset, - CgroupV1Controller* cpu, + CgroupV1CpuController* cpu, CgroupV1Controller* cpuacct, CgroupV1Controller* pids, - CgroupV1MemoryController* memory) { - _cpuset = cpuset; - _cpu = new CachingCgroupController(cpu); - _cpuacct = cpuacct; - _pids = pids; - _memory = new CachingCgroupController(memory); + CgroupV1MemoryController* memory) : + _memory(new CachingCgroupController(memory)), + _cpuset(cpuset), + _cpu(new CachingCgroupController(cpu)), + _cpuacct(cpuacct), + _pids(pids) { } }; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 1f97b0212395c..8f7e12d095474 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -23,6 +23,7 @@ */ #include "cgroupV2Subsystem_linux.hpp" +#include "cgroupUtil_linux.hpp" /* cpu_shares * @@ -34,9 +35,9 @@ * -1 for no share setup * OSCONTAINER_ERROR for not supported */ -int CgroupV2Subsystem::cpu_shares() { +int CgroupV2CpuController::cpu_shares() { julong shares; - CONTAINER_READ_NUMBER_CHECKED(_unified, "/cpu.weight", "Raw value for CPU Shares", shares); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpu.weight", "Raw value for CPU Shares", shares); int shares_int = (int)shares; // Convert default value of 100 to no shares setup if (shares_int == 100) { @@ -83,9 +84,9 @@ int CgroupV2Subsystem::cpu_shares() { * -1 for no quota * OSCONTAINER_ERROR for not supported */ -int CgroupV2Subsystem::cpu_quota() { +int CgroupV2CpuController::cpu_quota() { jlong quota_val; - bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", true /* use_first */, "a_val); + bool is_ok = reader()->read_numerical_tuple_value("/cpu.max", true /* use_first */, "a_val); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -95,24 +96,26 @@ int CgroupV2Subsystem::cpu_quota() { } bool CgroupV2Subsystem::is_containerized() { - return _unified->is_read_only(); + return _unified.is_read_only() && + _memory->controller()->is_read_only() && + _cpu->controller()->is_read_only(); } char* CgroupV2Subsystem::cpu_cpuset_cpus() { char cpus[1024]; - CONTAINER_READ_STRING_CHECKED(_unified, "/cpuset.cpus", "cpuset.cpus", cpus, 1024); + CONTAINER_READ_STRING_CHECKED(unified(), "/cpuset.cpus", "cpuset.cpus", cpus, 1024); return os::strdup(cpus); } char* CgroupV2Subsystem::cpu_cpuset_memory_nodes() { char mems[1024]; - CONTAINER_READ_STRING_CHECKED(_unified, "/cpuset.mems", "cpuset.mems", mems, 1024); + CONTAINER_READ_STRING_CHECKED(unified(), "/cpuset.mems", "cpuset.mems", mems, 1024); return os::strdup(mems); } -int CgroupV2Subsystem::cpu_period() { +int CgroupV2CpuController::cpu_period() { jlong period_val; - bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", false /* use_first */, &period_val); + bool is_ok = reader()->read_numerical_tuple_value("/cpu.max", false /* use_first */, &period_val); if (!is_ok) { log_trace(os, container)("CPU Period failed: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -131,28 +134,27 @@ int CgroupV2Subsystem::cpu_period() { * -1 for unlimited * OSCONTAINER_ERROR for not supported */ -jlong CgroupV2Subsystem::memory_usage_in_bytes() { +jlong CgroupV2MemoryController::memory_usage_in_bytes() { julong memusage; - CONTAINER_READ_NUMBER_CHECKED(_unified, "/memory.current", "Memory Usage", memusage); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.current", "Memory Usage", memusage); return (jlong)memusage; } -jlong CgroupV2Subsystem::memory_soft_limit_in_bytes() { +jlong CgroupV2MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { jlong mem_soft_limit; - CONTAINER_READ_NUMBER_CHECKED_MAX(_unified, "/memory.low", "Memory Soft Limit", mem_soft_limit); + CONTAINER_READ_NUMBER_CHECKED_MAX(reader(), "/memory.low", "Memory Soft Limit", mem_soft_limit); return mem_soft_limit; } -jlong CgroupV2Subsystem::memory_max_usage_in_bytes() { +jlong CgroupV2MemoryController::memory_max_usage_in_bytes() { // Log this string at trace level so as to make tests happy. log_trace(os, container)("Maximum Memory Usage is not supported."); return OSCONTAINER_ERROR; // not supported } -jlong CgroupV2Subsystem::rss_usage_in_bytes() { +jlong CgroupV2MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = _memory->controller()-> - read_numerical_key_value("/memory.stat", "anon", &rss); + bool is_ok = reader()->read_numerical_key_value("/memory.stat", "anon", &rss); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -160,10 +162,9 @@ jlong CgroupV2Subsystem::rss_usage_in_bytes() { return (jlong)rss; } -jlong CgroupV2Subsystem::cache_usage_in_bytes() { +jlong CgroupV2MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = _memory->controller()-> - read_numerical_key_value("/memory.stat", "file", &cache); + bool is_ok = reader()->read_numerical_key_value("/memory.stat", "file", &cache); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -176,18 +177,19 @@ jlong CgroupV2Subsystem::cache_usage_in_bytes() { // respectively. In order to properly report a cgroup v1 like // compound value we need to sum the two values. Setting a swap limit // without also setting a memory limit is not allowed. -jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() { +jlong CgroupV2MemoryController::memory_and_swap_limit_in_bytes(julong phys_mem, + julong host_swap /* unused in cg v2 */) { jlong swap_limit; - bool is_ok = _memory->controller()->read_number_handle_max("/memory.swap.max", &swap_limit); + bool is_ok = reader()->read_number_handle_max("/memory.swap.max", &swap_limit); if (!is_ok) { // Some container tests rely on this trace logging to happen. log_trace(os, container)("Swap Limit failed: %d", OSCONTAINER_ERROR); // swap disabled at kernel level, treat it as no swap - return read_memory_limit_in_bytes(); + return read_memory_limit_in_bytes(phys_mem); } log_trace(os, container)("Swap Limit is: " JLONG_FORMAT, swap_limit); if (swap_limit >= 0) { - jlong memory_limit = read_memory_limit_in_bytes(); + jlong memory_limit = read_memory_limit_in_bytes(phys_mem); assert(memory_limit >= 0, "swap limit without memory limit?"); return memory_limit + swap_limit; } @@ -195,29 +197,31 @@ jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() { return swap_limit; } -jlong CgroupV2Subsystem::memory_and_swap_usage_in_bytes() { - jlong memory_usage = memory_usage_in_bytes(); - if (memory_usage >= 0) { - jlong swap_current = mem_swp_current_val(); - return memory_usage + (swap_current >= 0 ? swap_current : 0); - } - return memory_usage; // not supported or unlimited case +// memory.swap.current : total amount of swap currently used by the cgroup and its descendants +static +jlong memory_swap_current_value(CgroupV2Controller* ctrl) { + julong swap_current; + CONTAINER_READ_NUMBER_CHECKED(ctrl, "/memory.swap.current", "Swap currently used", swap_current); + return (jlong)swap_current; } -jlong CgroupV2Subsystem::mem_swp_limit_val() { - jlong swap_limit; - CONTAINER_READ_NUMBER_CHECKED_MAX(_unified, "/memory.swap.max", "Swap Limit", swap_limit); - return swap_limit; +jlong CgroupV2MemoryController::memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) { + jlong memory_usage = memory_usage_in_bytes(); + if (memory_usage >= 0) { + jlong swap_current = memory_swap_current_value(reader()); + return memory_usage + (swap_current >= 0 ? swap_current : 0); + } + return memory_usage; // not supported or unlimited case } -// memory.swap.current : total amount of swap currently used by the cgroup and its descendants -jlong CgroupV2Subsystem::mem_swp_current_val() { - julong swap_current; - CONTAINER_READ_NUMBER_CHECKED(_unified, "/memory.swap.current", "Swap currently used", swap_current); - return (jlong)swap_current; +static +jlong memory_limit_value(CgroupV2Controller* ctrl) { + jlong memory_limit; + CONTAINER_READ_NUMBER_CHECKED_MAX(ctrl, "/memory.max", "Memory Limit", memory_limit); + return memory_limit; } -/* memory_limit_in_bytes +/* read_memory_limit_in_bytes * * Return the limit of available memory for this process. * @@ -225,15 +229,44 @@ jlong CgroupV2Subsystem::mem_swp_current_val() { * memory limit in bytes or * -1 for unlimited, OSCONTAINER_ERROR for an error */ -jlong CgroupV2Subsystem::read_memory_limit_in_bytes() { - jlong memory_limit; - CONTAINER_READ_NUMBER_CHECKED_MAX(_unified, "/memory.max", "Memory Limit", memory_limit); - return memory_limit; +jlong CgroupV2MemoryController::read_memory_limit_in_bytes(julong phys_mem) { + jlong limit = memory_limit_value(reader()); + if (log_is_enabled(Trace, os, container)) { + if (limit == -1) { + log_trace(os, container)("Memory Limit is: Unlimited"); + } else { + log_trace(os, container)("Memory Limit is: " JLONG_FORMAT, limit); + } + } + if (log_is_enabled(Debug, os, container)) { + julong read_limit = (julong)limit; // avoid signed/unsigned compare + if (limit < 0 || read_limit >= phys_mem) { + const char* reason; + if (limit == -1) { + reason = "unlimited"; + } else if (limit == OSCONTAINER_ERROR) { + reason = "failed"; + } else { + assert(read_limit >= phys_mem, "Expected mem limit to exceed host memory"); + reason = "ignored"; + } + log_debug(os, container)("container memory limit %s: " JLONG_FORMAT ", using host value " JLONG_FORMAT, + reason, limit, phys_mem); + } + } + return limit; +} + +static +jlong memory_swap_limit_value(CgroupV2Controller* ctrl) { + jlong swap_limit; + CONTAINER_READ_NUMBER_CHECKED_MAX(ctrl, "/memory.swap.max", "Swap Limit", swap_limit); + return swap_limit; } -void CgroupV2Subsystem::print_version_specific_info(outputStream* st) { - jlong swap_current = mem_swp_current_val(); - jlong swap_limit = mem_swp_limit_val(); +void CgroupV2MemoryController::print_version_specific_info(outputStream* st, julong phys_mem) { + jlong swap_current = memory_swap_current_value(reader()); + jlong swap_limit = memory_swap_limit_value(reader()); OSContainer::print_container_helper(st, swap_current, "memory_swap_current_in_bytes"); OSContainer::print_container_helper(st, swap_limit, "memory_swap_max_limit_in_bytes"); @@ -259,7 +292,7 @@ char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { */ jlong CgroupV2Subsystem::pids_max() { jlong pids_max; - CONTAINER_READ_NUMBER_CHECKED_MAX(_unified, "/pids.max", "Maximum number of tasks", pids_max); + CONTAINER_READ_NUMBER_CHECKED_MAX(unified(), "/pids.max", "Maximum number of tasks", pids_max); return pids_max; } @@ -273,6 +306,6 @@ jlong CgroupV2Subsystem::pids_max() { */ jlong CgroupV2Subsystem::pids_current() { julong pids_current; - CONTAINER_READ_NUMBER_CHECKED(_unified, "/pids.current", "Current number of tasks", pids_current); + CONTAINER_READ_NUMBER_CHECKED(unified(), "/pids.current", "Current number of tasks", pids_current); return pids_current; } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 8e06466a138a5..02774fb70aec8 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -40,60 +40,96 @@ class CgroupV2Controller: public CgroupController { static char* construct_path(char* mount_path, char *cgroup_path); public: - CgroupV2Controller(char * mount_path, char *cgroup_path, bool ro) { - _mount_path = mount_path; - _cgroup_path = os::strdup(cgroup_path); - _path = construct_path(mount_path, cgroup_path); - _read_only = ro; + CgroupV2Controller(char* mount_path, + char *cgroup_path, + bool ro) : _mount_path(os::strdup(mount_path)), + _cgroup_path(os::strdup(cgroup_path)), + _read_only(ro), + _path(construct_path(mount_path, cgroup_path)) { + } + // Shallow copy constructor + CgroupV2Controller(const CgroupV2Controller& o) : + _mount_path(o._mount_path), + _cgroup_path(o._cgroup_path), + _read_only(o._read_only), + _path(o._path) { + } + ~CgroupV2Controller() { + // At least one controller exists with references to the paths } - char *subsystem_path() { return _path; } - bool is_read_only() { return _read_only; } + char *subsystem_path() override { return _path; } + bool is_read_only() override { return _read_only; } +}; + +class CgroupV2CpuController: public CgroupCpuController { + private: + CgroupV2Controller _reader; + CgroupV2Controller* reader() { return &_reader; } + public: + CgroupV2CpuController(const CgroupV2Controller& reader) : _reader(reader) { + } + int cpu_quota() override; + int cpu_period() override; + int cpu_shares() override; + bool is_read_only() override { + return reader()->is_read_only(); + } +}; + +class CgroupV2MemoryController final: public CgroupMemoryController { + private: + CgroupV2Controller _reader; + CgroupV2Controller* reader() { return &_reader; } + public: + CgroupV2MemoryController(const CgroupV2Controller& reader) : _reader(reader) { + } + + jlong read_memory_limit_in_bytes(julong upper_bound) override; + jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swp) override; + jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swp) override; + jlong memory_soft_limit_in_bytes(julong upper_bound) override; + jlong memory_usage_in_bytes() override; + jlong memory_max_usage_in_bytes() override; + jlong rss_usage_in_bytes() override; + jlong cache_usage_in_bytes() override; + void print_version_specific_info(outputStream* st, julong host_mem) override; + bool is_read_only() override { + return reader()->is_read_only(); + } }; class CgroupV2Subsystem: public CgroupSubsystem { private: /* One unified controller */ - CgroupController* _unified = nullptr; + CgroupV2Controller _unified; /* Caching wrappers for cpu/memory metrics */ - CachingCgroupController* _memory = nullptr; - CachingCgroupController* _cpu = nullptr; + CachingCgroupController* _memory = nullptr; + CachingCgroupController* _cpu = nullptr; - jlong mem_swp_limit_val(); - jlong mem_swp_current_val(); + CgroupV2Controller* unified() { return &_unified; } public: - CgroupV2Subsystem(CgroupController * unified) { - _unified = unified; - _memory = new CachingCgroupController(unified); - _cpu = new CachingCgroupController(unified); + CgroupV2Subsystem(CgroupV2MemoryController* memory, + CgroupV2CpuController* cpu, + CgroupV2Controller unified) : + _unified(unified), + _memory(new CachingCgroupController(memory)), + _cpu(new CachingCgroupController(cpu)) { } - jlong read_memory_limit_in_bytes(); - int cpu_quota(); - int cpu_period(); - int cpu_shares(); - jlong memory_and_swap_limit_in_bytes(); - jlong memory_and_swap_usage_in_bytes(); - jlong memory_soft_limit_in_bytes(); - jlong memory_usage_in_bytes(); - jlong memory_max_usage_in_bytes(); - jlong rss_usage_in_bytes(); - jlong cache_usage_in_bytes(); - - char * cpu_cpuset_cpus(); - char * cpu_cpuset_memory_nodes(); - jlong pids_max(); - jlong pids_current(); - - bool is_containerized(); - void print_version_specific_info(outputStream* st); - - const char * container_type() { + char * cpu_cpuset_cpus() override; + char * cpu_cpuset_memory_nodes() override; + jlong pids_max() override; + jlong pids_current() override; + + bool is_containerized() override; + + const char * container_type() override { return "cgroupv2"; } - CachingCgroupController * memory_controller() { return _memory; } - CachingCgroupController * cpu_controller() { return _cpu; } + CachingCgroupController* memory_controller() { return _memory; } + CachingCgroupController* cpu_controller() { return _cpu; } }; #endif // CGROUP_V2_SUBSYSTEM_LINUX_HPP