From eda2aa97dd6161c3c452ea7ee3644ae738fd8b55 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 12 Apr 2024 15:42:17 +0200 Subject: [PATCH 01/38] 8302744: Refactor Hotspot container detection code --- .../os/linux/cgroupSubsystem_linux.hpp | 180 ++++----- .../os/linux/cgroupV1Subsystem_linux.cpp | 208 +++++++--- .../os/linux/cgroupV2Subsystem_linux.cpp | 124 ++++-- .../os/linux/test_cgroupSubsystem_linux.cpp | 204 ---------- .../runtime/test_cgroupSubsystem_linux.cpp | 361 ++++++++++++++++++ .../gtest/runtime/test_os_linux_cgroups.cpp | 86 ----- 6 files changed, 712 insertions(+), 451 deletions(-) delete mode 100644 test/hotspot/gtest/os/linux/test_cgroupSubsystem_linux.cpp create mode 100644 test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp delete mode 100644 test/hotspot/gtest/runtime/test_os_linux_cgroups.cpp diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 7943d2fe9de45..e3d7dab80f269 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -69,8 +69,6 @@ #define MEMORY_IDX 3 #define PIDS_IDX 4 -typedef char * cptr; - class CgroupController: public CHeapObj { public: virtual char *subsystem_path() = 0; @@ -78,23 +76,51 @@ class CgroupController: public CHeapObj { PRAGMA_DIAG_PUSH PRAGMA_FORMAT_NONLITERAL_IGNORED -// Parses a subsystem's file, looking for a matching line. -// If key is null, then the first line will be matched with scan_fmt. -// If key isn't null, then each line will be matched, looking for something that matches "$key $scan_fmt". -// The matching value will be assigned to returnval. -// scan_fmt uses scanf() syntax. -// Return value: 0 on match, OSCONTAINER_ERROR on error. -template int subsystem_file_line_contents(CgroupController* c, - const char *filename, - const char *key, - const char *scan_fmt, - T returnval) { +template int __cg_file_contents_impl(const char *absolute_path, + const char *scan_fmt, + T returnval) { + FILE* fp = os::fopen(absolute_path, "r"); + if (fp == nullptr) { + log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); + return OSCONTAINER_ERROR; + } + + const int buf_len = MAXPATHLEN+1; + char buf[buf_len]; + char* line = fgets(buf, buf_len, fp); + if (line == nullptr) { + log_debug(os, container)("Empty file %s", absolute_path); + fclose(fp); + return OSCONTAINER_ERROR; + } + fclose(fp); + + int matched = sscanf(line, scan_fmt, returnval); + if (matched == 1) { + return 0; + } else { + log_debug(os, container)("Type %s not found in file %s", scan_fmt, absolute_path); + } + return OSCONTAINER_ERROR; +} +PRAGMA_DIAG_POP + +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED +template int cg_file_contents_ctrl(CgroupController* c, + const char *filename, + const char *scan_fmt, + T returnval) { if (c == nullptr) { - log_debug(os, container)("subsystem_file_line_contents: CgroupController* is null"); + log_debug(os, container)("cg_file_contents_ctrl: CgroupController* is null"); return OSCONTAINER_ERROR; } if (c->subsystem_path() == nullptr) { - log_debug(os, container)("subsystem_file_line_contents: subsystem path is null"); + log_debug(os, container)("cg_file_contents_ctrl: subsystem path is null"); + return OSCONTAINER_ERROR; + } + if (scan_fmt == nullptr || returnval == nullptr) { + log_debug(os, container)("cg_file_contents_ctrl: scan_fmt or return pointer is null"); return OSCONTAINER_ERROR; } @@ -102,13 +128,22 @@ template int subsystem_file_line_contents(CgroupController* c, file_path.print_raw(c->subsystem_path()); file_path.print_raw(filename); - if (file_path.size() > (MAXPATHLEN-1)) { + if (file_path.size() > MAXPATHLEN) { log_debug(os, container)("File path too long %s, %s", file_path.base(), filename); return OSCONTAINER_ERROR; } const char* absolute_path = file_path.freeze(); log_trace(os, container)("Path to %s is %s", filename, absolute_path); + return __cg_file_contents_impl(absolute_path, scan_fmt, returnval); +} +PRAGMA_DIAG_POP +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED +template int __cg_file_multi_line_impl(const char *absolute_path, + const char *key, + const char *scan_fmt, + T returnval) { FILE* fp = os::fopen(absolute_path, "r"); if (fp == nullptr) { log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); @@ -125,27 +160,21 @@ template int subsystem_file_line_contents(CgroupController* c, } bool found_match = false; - if (key == nullptr) { - // File consists of a single line according to caller, with only a value - int matched = sscanf(line, scan_fmt, returnval); - found_match = matched == 1; - } else { - // File consists of multiple lines in a "key value" - // fashion, we have to find the key. - const int key_len = (int)strlen(key); - for (; line != nullptr; line = fgets(buf, buf_len, fp)) { - char* key_substr = strstr(line, key); - char after_key = line[key_len]; - if (key_substr == line + // File consists of multiple lines in a "key value" + // fashion, we have to find the key. + const int key_len = (int)strlen(key); + for (; line != nullptr; line = fgets(buf, buf_len, fp)) { + char* key_substr = strstr(line, key); + char after_key = line[key_len]; + if (key_substr == line && isspace(after_key) != 0 && after_key != '\n') { - // Skip key, skip space - const char* value_substr = line + key_len + 1; - int matched = sscanf(value_substr, scan_fmt, returnval); - found_match = matched == 1; - if (found_match) { - break; - } + // Skip key, skip space + const char* value_substr = line + key_len + 1; + int matched = sscanf(value_substr, scan_fmt, returnval); + found_match = matched == 1; + if (found_match) { + break; } } } @@ -154,63 +183,44 @@ template int subsystem_file_line_contents(CgroupController* c, return 0; } log_debug(os, container)("Type %s (key == %s) not found in file %s", scan_fmt, - (key == nullptr ? "null" : key), absolute_path); + key, absolute_path); return OSCONTAINER_ERROR; } PRAGMA_DIAG_POP -// log_fmt can be different than scan_fmt. For example -// cpu_period() for cgv2 uses log_fmt='%d' and scan_fmt='%*s %d' -#define GET_CONTAINER_INFO(return_type, subsystem, filename, \ - logstring, log_fmt, scan_fmt, variable) \ - return_type variable; \ -{ \ - int err; \ - err = subsystem_file_line_contents(subsystem, \ - filename, \ - nullptr, \ - scan_fmt, \ - &variable); \ - if (err != 0) { \ - log_trace(os, container)(logstring "%d", OSCONTAINER_ERROR); \ - return (return_type) OSCONTAINER_ERROR; \ - } \ - \ - log_trace(os, container)(logstring log_fmt, variable); \ -} +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED +template int cg_file_multi_line_ctrl(CgroupController* c, + const char *filename, + const char *key, + const char *scan_fmt, + T returnval) { + if (c == nullptr) { + log_debug(os, container)("cg_file_multi_line_ctrl: CgroupController* is null"); + return OSCONTAINER_ERROR; + } + if (c->subsystem_path() == nullptr) { + log_debug(os, container)("cg_file_multi_line_ctrl: subsystem path is null"); + return OSCONTAINER_ERROR; + } + if (key == nullptr || scan_fmt == nullptr || returnval == nullptr) { + log_debug(os, container)("cg_file_multi_line_ctrl: key, scan_fmt or return pointer is null"); + return OSCONTAINER_ERROR; + } -#define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename, \ - logstring, scan_fmt, variable, bufsize) \ - char variable[bufsize]; \ -{ \ - int err; \ - err = subsystem_file_line_contents(subsystem, \ - filename, \ - nullptr, \ - scan_fmt, \ - variable); \ - if (err != 0) \ - return (return_type) nullptr; \ - \ - log_trace(os, container)(logstring, variable); \ -} + stringStream file_path; + file_path.print_raw(c->subsystem_path()); + file_path.print_raw(filename); -#define GET_CONTAINER_INFO_LINE(return_type, controller, filename, \ - matchline, logstring, scan_fmt, variable) \ - return_type variable; \ -{ \ - int err; \ - err = subsystem_file_line_contents(controller, \ - filename, \ - matchline, \ - scan_fmt, \ - &variable); \ - if (err != 0) \ - return (return_type) OSCONTAINER_ERROR; \ - \ - log_trace(os, container)(logstring, variable); \ + if (file_path.size() > MAXPATHLEN) { + log_debug(os, container)("File path too long %s, %s", file_path.base(), filename); + return OSCONTAINER_ERROR; + } + const char* absolute_path = file_path.freeze(); + log_trace(os, container)("Path to %s is %s", filename, absolute_path); + return __cg_file_multi_line_impl(absolute_path, key, scan_fmt, returnval); } - +PRAGMA_DIAG_POP class CachedMetric : public CHeapObj{ private: diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index b83da9099f80d..1b9e8d1714687 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -75,8 +75,13 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { * OSCONTAINER_ERROR for not supported */ jlong CgroupV1MemoryController::uses_mem_hierarchy() { - GET_CONTAINER_INFO(jlong, this, "/memory.use_hierarchy", - "Use Hierarchy is: ", JLONG_FORMAT, JLONG_FORMAT, use_hierarchy); + jlong use_hierarchy; + int err = cg_file_contents_ctrl(this, "/memory.use_hierarchy", JLONG_FORMAT, &use_hierarchy); + if (err != 0) { + log_trace(os, container)("Use Hierarchy is: %d", OSCONTAINER_ERROR); + return (jlong)OSCONTAINER_ERROR; + } + log_trace(os, container)("Use Hierarchy is: " JLONG_FORMAT, use_hierarchy); return use_hierarchy; } @@ -89,15 +94,25 @@ void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) { } jlong CgroupV1Subsystem::read_memory_limit_in_bytes() { - GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.limit_in_bytes", - "Memory Limit is: ", JULONG_FORMAT, JULONG_FORMAT, memlimit); + julong memlimit; + int err = cg_file_contents_ctrl(_memory->controller(), "/memory.limit_in_bytes", JULONG_FORMAT, &memlimit); + if (err != 0) { + log_trace(os, container)("Memory Limit is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Memory Limit is: " JULONG_FORMAT, memlimit); if (memlimit >= os::Linux::physical_memory()) { log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); CgroupV1MemoryController* mem_controller = reinterpret_cast(_memory->controller()); if (mem_controller->is_hierarchical()) { - GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", "hierarchical_memory_limit", - "Hierarchical Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, hier_memlimit) + julong hier_memlimit; + err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", + "hierarchical_memory_limit", JULONG_FORMAT, &hier_memlimit); + if (err != 0) { + 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 { @@ -125,16 +140,25 @@ jlong CgroupV1Subsystem::read_memory_limit_in_bytes() { */ jlong CgroupV1Subsystem::read_mem_swap() { julong host_total_memsw; - GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.memsw.limit_in_bytes", - "Memory and Swap Limit is: ", JULONG_FORMAT, JULONG_FORMAT, memswlimit); + julong hier_memswlimit; + julong memswlimit; + int err = cg_file_contents_ctrl(_memory->controller(), "/memory.memsw.limit_in_bytes", JULONG_FORMAT, &memswlimit); + if (err != 0) { + log_trace(os, container)("Memory and Swap Limit is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Memory and Swap Limit is: " JULONG_FORMAT, memswlimit); host_total_memsw = os::Linux::host_swap() + os::Linux::physical_memory(); 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()) { const char* matchline = "hierarchical_memsw_limit"; - GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", matchline, - "Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, JULONG_FORMAT, hier_memswlimit) + err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", matchline, JULONG_FORMAT, &hier_memswlimit); + if (err != 0) { + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, hier_memswlimit); if (hier_memswlimit >= host_total_memsw) { log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited"); } else { @@ -168,29 +192,51 @@ jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() { return memory_swap; } +static inline +jlong memory_swap_usage_impl(CgroupController* ctrl) { + julong memory_swap_usage; + int err = cg_file_contents_ctrl(ctrl, "/memory.memsw.usage_in_bytes", JULONG_FORMAT, &memory_swap_usage); + if (err != 0) { + log_trace(os, container)("mem swap usage is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("mem swap usage is: " JULONG_FORMAT, memory_swap_usage); + 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(); if (memory_sw_limit > 0 && memory_limit > 0) { jlong delta_swap = memory_sw_limit - memory_limit; if (delta_swap > 0) { - GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.memsw.usage_in_bytes", - "mem swap usage is: ", JULONG_FORMAT, JULONG_FORMAT, memory_swap_usage); - return (jlong)memory_swap_usage; + return memory_swap_usage_impl(_memory->controller()); } } return memory_usage_in_bytes(); } jlong CgroupV1Subsystem::read_mem_swappiness() { - GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.swappiness", - "Swappiness is: ", JULONG_FORMAT, JULONG_FORMAT, swappiness); - return swappiness; + julong swappiness; + int err = cg_file_contents_ctrl(_memory->controller(), "/memory.swappiness", + JULONG_FORMAT, &swappiness); + if (err != 0) { + log_trace(os, container)("Swappiness is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Swappiness is: " JULONG_FORMAT, swappiness); + return (jlong)swappiness; } jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() { - GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.soft_limit_in_bytes", - "Memory Soft Limit is: ", JULONG_FORMAT, JULONG_FORMAT, memsoftlimit); + julong memsoftlimit; + int err = cg_file_contents_ctrl(_memory->controller(), "/memory.soft_limit_in_bytes", + JULONG_FORMAT, &memsoftlimit); + if (err != 0) { + log_trace(os, container)("Memory Soft Limit is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Memory Soft Limit is: " JULONG_FORMAT, memsoftlimit); if (memsoftlimit >= os::Linux::physical_memory()) { log_trace(os, container)("Memory Soft Limit is: Unlimited"); return (jlong)-1; @@ -209,8 +255,14 @@ jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV1Subsystem::memory_usage_in_bytes() { - GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.usage_in_bytes", - "Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, memusage); + jlong memusage; + int err = cg_file_contents_ctrl(_memory->controller(), "/memory.usage_in_bytes", + JLONG_FORMAT, &memusage); + if (err != 0) { + log_trace(os, container)("Memory Usage is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Memory Usage is: " JLONG_FORMAT, memusage); return memusage; } @@ -223,32 +275,58 @@ jlong CgroupV1Subsystem::memory_usage_in_bytes() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV1Subsystem::memory_max_usage_in_bytes() { - GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.max_usage_in_bytes", - "Maximum Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, memmaxusage); + jlong memmaxusage; + int err = cg_file_contents_ctrl(_memory->controller(), "/memory.max_usage_in_bytes", + JLONG_FORMAT, &memmaxusage); + if (err != 0) { + log_trace(os, container)("Maximum Memory Usage is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Maximum Memory Usage is: " JLONG_FORMAT, memmaxusage); return memmaxusage; } jlong CgroupV1Subsystem::rss_usage_in_bytes() { - GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", - "rss", JULONG_FORMAT, JULONG_FORMAT, rss); + julong rss; + int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", "rss", JULONG_FORMAT, &rss); + if (err != 0) { + return OSCONTAINER_ERROR; + } + log_trace(os, container)("RSS usage is: " JULONG_FORMAT, rss); return rss; } jlong CgroupV1Subsystem::cache_usage_in_bytes() { - GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", - "cache", JULONG_FORMAT, JULONG_FORMAT, cache); + julong cache; + int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", "cache", JULONG_FORMAT, &cache); + if (err != 0) { + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Cache usage is: " JULONG_FORMAT, cache); return cache; } jlong CgroupV1Subsystem::kernel_memory_usage_in_bytes() { - GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.kmem.usage_in_bytes", - "Kernel Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, kmem_usage); + jlong kmem_usage; + int err = cg_file_contents_ctrl(_memory->controller(), "/memory.kmem.usage_in_bytes", + JLONG_FORMAT, &kmem_usage); + if (err != 0) { + log_trace(os, container)("Kernel Memory Usage is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Kernel Memory Usage is: " JLONG_FORMAT, kmem_usage); return kmem_usage; } jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() { - GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.kmem.limit_in_bytes", - "Kernel Memory Limit is: ", JULONG_FORMAT, JULONG_FORMAT, kmem_limit); + julong kmem_limit; + int err = cg_file_contents_ctrl(_memory->controller(), "/memory.kmem.limit_in_bytes", + JULONG_FORMAT, &kmem_limit); + if (err != 0) { + log_trace(os, container)("Kernel Memory Limit is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Kernel Memory Limit is: " JULONG_FORMAT, kmem_limit); if (kmem_limit >= os::Linux::physical_memory()) { return (jlong)-1; } @@ -256,8 +334,14 @@ jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() { } jlong CgroupV1Subsystem::kernel_memory_max_usage_in_bytes() { - GET_CONTAINER_INFO(jlong, _memory->controller(), "/memory.kmem.max_usage_in_bytes", - "Maximum Kernel Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, kmem_max_usage); + jlong kmem_max_usage; + int err = cg_file_contents_ctrl(_memory->controller(), "/memory.kmem.max_usage_in_bytes", + JLONG_FORMAT, &kmem_max_usage); + if (err != 0) { + log_trace(os, container)("Maximum Kernel Memory Usage is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Maximum Kernel Memory Usage is: " JLONG_FORMAT, kmem_max_usage); return kmem_max_usage; } @@ -272,14 +356,22 @@ void CgroupV1Subsystem::print_version_specific_info(outputStream* st) { } char * CgroupV1Subsystem::cpu_cpuset_cpus() { - GET_CONTAINER_INFO_CPTR(cptr, _cpuset, "/cpuset.cpus", - "cpuset.cpus is: %s", "%1023s", cpus, 1024); + char cpus[1024]; + int err = cg_file_contents_ctrl(_cpuset, "/cpuset.cpus", "%1023s", cpus); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("cpuset.cpus is: %s", cpus); return os::strdup(cpus); } char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() { - GET_CONTAINER_INFO_CPTR(cptr, _cpuset, "/cpuset.mems", - "cpuset.mems is: %s", "%1023s", mems, 1024); + char mems[1024]; + int err = cg_file_contents_ctrl(_cpuset, "/cpuset.mems", "%1023s", mems); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("cpuset.mems is: %s", mems); return os::strdup(mems); } @@ -294,14 +386,24 @@ char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() { * OSCONTAINER_ERROR for not supported */ int CgroupV1Subsystem::cpu_quota() { - GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.cfs_quota_us", - "CPU Quota is: ", "%d", "%d", quota); + int quota; + int err = cg_file_contents_ctrl(_cpu->controller(), "/cpu.cfs_quota_us", "%d", "a); + if (err != 0) { + log_trace(os, container)("CPU Quota is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("CPU Quota is: %d", quota); return quota; } int CgroupV1Subsystem::cpu_period() { - GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.cfs_period_us", - "CPU Period is: ", "%d", "%d", period); + int period; + int err = cg_file_contents_ctrl(_cpu->controller(), "/cpu.cfs_period_us", "%d", &period); + if (err != 0) { + log_trace(os, container)("CPU Period is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("CPU Period is: %d", period); return period; } @@ -316,8 +418,13 @@ int CgroupV1Subsystem::cpu_period() { * OSCONTAINER_ERROR for not supported */ int CgroupV1Subsystem::cpu_shares() { - GET_CONTAINER_INFO(int, _cpu->controller(), "/cpu.shares", - "CPU Shares is: ", "%d", "%d", shares); + int shares; + int err = cg_file_contents_ctrl(_cpu->controller(), "/cpu.shares", "%d", &shares); + if (err != 0) { + log_trace(os, container)("CPU Shares is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("CPU Shares is: %d", shares); // Convert 1024 to no shares setup if (shares == 1024) return -1; @@ -326,8 +433,12 @@ int CgroupV1Subsystem::cpu_shares() { char* CgroupV1Subsystem::pids_max_val() { - GET_CONTAINER_INFO_CPTR(cptr, _pids, "/pids.max", - "Maximum number of tasks is: %s", "%1023s", pidsmax, 1024); + char pidsmax[1024]; + int err = cg_file_contents_ctrl(_pids, "/pids.max", "%1023s", pidsmax); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("Maximum number of tasks is: %s", pidsmax); return os::strdup(pidsmax); } @@ -356,7 +467,12 @@ jlong CgroupV1Subsystem::pids_max() { */ jlong CgroupV1Subsystem::pids_current() { if (_pids == nullptr) return OSCONTAINER_ERROR; - GET_CONTAINER_INFO(jlong, _pids, "/pids.current", - "Current number of tasks is: ", JLONG_FORMAT, JLONG_FORMAT, pids_current); + jlong pids_current; + int err = cg_file_contents_ctrl(_pids, "/pids.current", JLONG_FORMAT, &pids_current); + if (err != 0) { + log_trace(os, container)("Current number of tasks is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Current number of tasks is: " JLONG_FORMAT, pids_current); return pids_current; } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index d7c6464caa0fa..6239ba461e365 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -35,8 +35,13 @@ * OSCONTAINER_ERROR for not supported */ int CgroupV2Subsystem::cpu_shares() { - GET_CONTAINER_INFO(int, _unified, "/cpu.weight", - "Raw value for CPU Shares is: ", "%d", "%d", shares); + int shares; + int err = cg_file_contents_ctrl(_unified, "/cpu.weight", "%d", &shares); + if (err != 0) { + log_trace(os, container)("Raw value for CPU Shares is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Raw value for CPU Shares is: %d", shares); // Convert default value of 100 to no shares setup if (shares == 100) { log_debug(os, container)("CPU Shares is: %d", -1); @@ -90,26 +95,43 @@ int CgroupV2Subsystem::cpu_quota() { } char * CgroupV2Subsystem::cpu_cpuset_cpus() { - GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpuset.cpus", - "cpuset.cpus is: %s", "%1023s", cpus, 1024); + char cpus[1024]; + int err = cg_file_contents_ctrl(_unified, "/cpuset.cpus", "%1023s", cpus); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("cpuset.cpus is: %s", cpus); return os::strdup(cpus); } char* CgroupV2Subsystem::cpu_quota_val() { - GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpu.max", - "Raw value for CPU quota is: %s", "%1023s %*d", quota, 1024); + char quota[1024]; + int err = cg_file_contents_ctrl(_unified, "/cpu.max", "%1023s %*d", quota); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("Raw value for CPU quota is: %s", quota); return os::strdup(quota); } char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() { - GET_CONTAINER_INFO_CPTR(cptr, _unified, "/cpuset.mems", - "cpuset.mems is: %s", "%1023s", mems, 1024); + char mems[1024]; + int err = cg_file_contents_ctrl(_unified, "/cpuset.mems", "%1023s", mems); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("cpuset.mems is: %s", mems); return os::strdup(mems); } int CgroupV2Subsystem::cpu_period() { - GET_CONTAINER_INFO(int, _unified, "/cpu.max", - "CPU Period is: ", "%d", "%*s %d", period); + int period; + int err = cg_file_contents_ctrl(_unified, "/cpu.max", "%*s %d", &period); + if (err != 0) { + log_trace(os, container)("CPU Period is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("CPU Period is: %d", period); return period; } @@ -123,8 +145,13 @@ int CgroupV2Subsystem::cpu_period() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV2Subsystem::memory_usage_in_bytes() { - GET_CONTAINER_INFO(jlong, _unified, "/memory.current", - "Memory Usage is: ", JLONG_FORMAT, JLONG_FORMAT, memusage); + jlong memusage; + int err = cg_file_contents_ctrl(_unified, "/memory.current", JLONG_FORMAT, &memusage); + if (err != 0) { + log_trace(os, container)("Memory Usage is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Memory Usage is: " JLONG_FORMAT, memusage); return memusage; } @@ -140,20 +167,34 @@ jlong CgroupV2Subsystem::memory_max_usage_in_bytes() { } jlong CgroupV2Subsystem::rss_usage_in_bytes() { - GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", - "anon", JULONG_FORMAT, JULONG_FORMAT, rss); - return rss; + julong rss; + int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", + "anon", JULONG_FORMAT, &rss); + if (err != 0) { + return OSCONTAINER_ERROR; + } + log_trace(os, container)("RSS usage is: " JULONG_FORMAT, rss); + return (jlong)rss; } jlong CgroupV2Subsystem::cache_usage_in_bytes() { - GET_CONTAINER_INFO_LINE(julong, _memory->controller(), "/memory.stat", - "file", JULONG_FORMAT, JULONG_FORMAT, cache); - return cache; + julong cache; + int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", + "file", JULONG_FORMAT, &cache); + if (err != 0) { + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Cache usage is: " JULONG_FORMAT, cache); + return (jlong)cache; } char* CgroupV2Subsystem::mem_soft_limit_val() { - GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.low", - "Memory Soft Limit is: %s", "%1023s", mem_soft_limit_str, 1024); + char mem_soft_limit_str[1024]; + int err = cg_file_contents_ctrl(_unified, "/memory.low", "%1023s", mem_soft_limit_str); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("Memory Soft Limit is: %s", mem_soft_limit_str); return os::strdup(mem_soft_limit_str); } @@ -191,15 +232,25 @@ jlong CgroupV2Subsystem::memory_and_swap_usage_in_bytes() { } char* CgroupV2Subsystem::mem_swp_limit_val() { - GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.swap.max", - "Memory and Swap Limit is: %s", "%1023s", mem_swp_limit_str, 1024); + char mem_swp_limit_str[1024]; + int err = cg_file_contents_ctrl(_unified, "/memory.swap.max", "%1023s", mem_swp_limit_str); + if (err != 0) { + return nullptr; + } + // FIXME: This log-line is misleading, since it reads the swap limit only, not memory *and* + // swap limit. + log_trace(os, container)("Memory and Swap Limit is: %s", mem_swp_limit_str); return os::strdup(mem_swp_limit_str); } // memory.swap.current : total amount of swap currently used by the cgroup and its descendants char* CgroupV2Subsystem::mem_swp_current_val() { - GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.swap.current", - "Swap currently used is: %s", "%1023s", mem_swp_current_str, 1024); + char mem_swp_current_str[1024]; + int err = cg_file_contents_ctrl(_unified, "/memory.swap.current", "%1023s", mem_swp_current_str); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("Swap currently used is: %s", mem_swp_current_str); return os::strdup(mem_swp_current_str); } @@ -225,8 +276,12 @@ jlong CgroupV2Subsystem::read_memory_limit_in_bytes() { } char* CgroupV2Subsystem::mem_limit_val() { - GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.max", - "Raw value for memory limit is: %s", "%1023s", mem_limit_str, 1024); + char mem_limit_str[1024]; + int err = cg_file_contents_ctrl(_unified, "/memory.max", "%1023s", mem_limit_str); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("Raw value for memory limit is: %s", mem_limit_str); return os::strdup(mem_limit_str); } @@ -251,8 +306,12 @@ char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { } char* CgroupV2Subsystem::pids_max_val() { - GET_CONTAINER_INFO_CPTR(cptr, _unified, "/pids.max", - "Maximum number of tasks is: %s", "%1023s", pidsmax, 1024); + char pidsmax[1024]; + int err = cg_file_contents_ctrl(_unified, "/pids.max", "%1023s", pidsmax); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("Maximum number of tasks is: %s", pidsmax); return os::strdup(pidsmax); } @@ -279,7 +338,12 @@ jlong CgroupV2Subsystem::pids_max() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV2Subsystem::pids_current() { - GET_CONTAINER_INFO(jlong, _unified, "/pids.current", - "Current number of tasks is: ", JLONG_FORMAT, JLONG_FORMAT, pids_current); + jlong pids_current; + int err = cg_file_contents_ctrl(_unified, "/pids.current", JLONG_FORMAT, &pids_current); + if (err != 0) { + log_trace(os, container)("Current number of tasks is: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("Current number of tasks is: " JLONG_FORMAT, pids_current); return pids_current; } diff --git a/test/hotspot/gtest/os/linux/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/os/linux/test_cgroupSubsystem_linux.cpp deleted file mode 100644 index 463955fd4012f..0000000000000 --- a/test/hotspot/gtest/os/linux/test_cgroupSubsystem_linux.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. - * 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 "precompiled.hpp" - -#ifdef LINUX - -#include "runtime/os.hpp" -#include "cgroupSubsystem_linux.hpp" -#include "unittest.hpp" -#include "utilities/globalDefinitions.hpp" - -#include - - -// Utilities -static bool file_exists(const char* filename) { - struct stat st; - return os::stat(filename, &st) == 0; -} - -static char* temp_file(const char* prefix) { - const testing::TestInfo* test_info = ::testing::UnitTest::GetInstance()->current_test_info(); - stringStream path; - path.print_raw(os::get_temp_directory()); - path.print_raw(os::file_separator()); - path.print("%s-test-jdk.pid%d.%s.%s", prefix, os::current_process_id(), - test_info->test_case_name(), test_info->name()); - return path.as_string(true); -} - -static void delete_file(const char* filename) { - if (!file_exists(filename)) { - return; - } - int ret = remove(filename); - EXPECT_TRUE(ret == 0 || errno == ENOENT) << "failed to remove file '" << filename << "': " - << os::strerror(errno) << " (" << errno << ")"; -} - -class TestController : public CgroupController { -public: - char* subsystem_path() override { - // The real subsystem is in /tmp/, generated by temp_file() - return (char*)"/"; - }; -}; - -static void fill_file(const char* path, const char* content) { - delete_file(path); - FILE* fp = os::fopen(path, "w"); - if (fp == nullptr) { - return; - } - if (content != nullptr) { - fprintf(fp, "%s", content); - } - fclose(fp); -} - -TEST(cgroupTest, SubSystemFileLineContentsMultipleLinesErrorCases) { - TestController my_controller{}; - const char* test_file = temp_file("cgroups"); - int x = 0; - char s[1024]; - int err = 0; - - s[0] = '\0'; - fill_file(test_file, "foo "); - err = subsystem_file_line_contents(&my_controller, test_file, "foo", "%s", &s); - EXPECT_NE(err, 0) << "Value must not be missing in key/value case"; - - s[0] = '\0'; - fill_file(test_file, "faulty_start foo bar"); - err = subsystem_file_line_contents(&my_controller, test_file, "foo", "%s", &s); - EXPECT_NE(err, 0) << "Key must be at start"; - - s[0] = '\0'; - fill_file(test_file, "foof bar"); - err = subsystem_file_line_contents(&my_controller, test_file, "foo", "%s", &s); - EXPECT_NE(err, 0) << "Key must be exact match"; - - // Cleanup - delete_file(test_file); -} - -TEST(cgroupTest, SubSystemFileLineContentsMultipleLinesSuccessCases) { - TestController my_controller{}; - const char* test_file = temp_file("cgroups"); - int x = 0; - char s[1024]; - int err = 0; - - s[0] = '\0'; - fill_file(test_file, "foo bar"); - err = subsystem_file_line_contents(&my_controller, test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "bar") << "Incorrect!"; - - s[0] = '\0'; - fill_file(test_file, "foo\tbar"); - err = subsystem_file_line_contents(&my_controller, test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "bar") << "Incorrect!"; - - s[0] = '\0'; - fill_file(test_file, "foof bar\nfoo car"); - err = subsystem_file_line_contents(&my_controller, test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "car"); - - s[0] = '\0'; - fill_file(test_file, "foo\ttest\nfoot car"); - err = subsystem_file_line_contents(&my_controller, test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "test"); - - s[0] = '\0'; - fill_file(test_file, "foo 1\nfoo car"); - err = subsystem_file_line_contents(&my_controller, test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "1"); - - s[0] = '\0'; - fill_file(test_file, "max 10000"); - err = subsystem_file_line_contents(&my_controller, test_file, nullptr, "%s %*d", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "max"); - - x = -3; - fill_file(test_file, "max 10001"); - err = subsystem_file_line_contents(&my_controller, test_file, nullptr, "%*s %d", &x); - EXPECT_EQ(err, 0); - EXPECT_EQ(x, 10001); - - // Cleanup - delete_file(test_file); -} - -TEST(cgroupTest, SubSystemFileLineContentsSingleLine) { - TestController my_controller{}; - const char* test_file = temp_file("cgroups"); - int x = 0; - char s[1024]; - int err = 0; - - fill_file(test_file, "foo"); - err = subsystem_file_line_contents(&my_controller, test_file, nullptr, "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "foo"); - - fill_file(test_file, "1337"); - err = subsystem_file_line_contents(&my_controller, test_file, nullptr, "%d", &x); - EXPECT_EQ(err, 0); - EXPECT_EQ(x, 1337) << "Wrong value for x"; - - s[0] = '\0'; - fill_file(test_file, "1337"); - err = subsystem_file_line_contents(&my_controller, test_file, nullptr, "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "1337"); - - x = -1; - fill_file(test_file, nullptr); - err = subsystem_file_line_contents(&my_controller, test_file, nullptr, "%d", &x); - EXPECT_NE(err, 0) << "Empty file should've failed"; - EXPECT_EQ(x, -1) << "x was altered"; - - jlong y; - fill_file(test_file, "1337"); - err = subsystem_file_line_contents(&my_controller, test_file, nullptr, JLONG_FORMAT, &y); - EXPECT_EQ(err, 0); - EXPECT_EQ(y, 1337) << "Wrong value for y"; - julong z; - fill_file(test_file, "1337"); - err = subsystem_file_line_contents(&my_controller, test_file, nullptr, JULONG_FORMAT, &z); - EXPECT_EQ(err, 0); - EXPECT_EQ(z, (julong)1337) << "Wrong value for z"; - - // Cleanup - delete_file(test_file); -} - -#endif // LINUX diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp new file mode 100644 index 0000000000000..307894f7d19b5 --- /dev/null +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * 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 "precompiled.hpp" + +#ifdef LINUX + +#include "runtime/os.hpp" +#include "cgroupSubsystem_linux.hpp" +#include "cgroupV1Subsystem_linux.hpp" +#include "cgroupV2Subsystem_linux.hpp" +#include "unittest.hpp" +#include "utilities/globalDefinitions.hpp" + +#include + +typedef struct { + const char* mount_path; + const char* root_path; + const char* cgroup_path; + const char* expected_path; +} TestCase; + +// Utilities +static bool file_exists(const char* filename) { + struct stat st; + return os::stat(filename, &st) == 0; +} + +static char* temp_file(const char* prefix) { + const testing::TestInfo* test_info = ::testing::UnitTest::GetInstance()->current_test_info(); + stringStream path; + path.print_raw(os::get_temp_directory()); + path.print_raw(os::file_separator()); + path.print("%s-test-jdk.pid%d.%s.%s", prefix, os::current_process_id(), + test_info->test_case_name(), test_info->name()); + return path.as_string(true); +} + +static void delete_file(const char* filename) { + if (!file_exists(filename)) { + return; + } + int ret = remove(filename); + EXPECT_TRUE(ret == 0 || errno == ENOENT) << "failed to remove file '" << filename << "': " + << os::strerror(errno) << " (" << errno << ")"; +} + +class TestController : public CgroupController { +private: + char* _path; +public: + TestController(char *p) { + _path = p; + } + char* subsystem_path() override { + return _path; + }; +}; + +static void fill_file(const char* path, const char* content) { + delete_file(path); + FILE* fp = os::fopen(path, "w"); + if (fp == nullptr) { + return; + } + if (content != nullptr) { + fprintf(fp, "%s", content); + } + fclose(fp); +} + +TEST(cgroupTest, cg_file_multi_line_impl_failure_cases) { + const char* test_file = temp_file("cgroups"); + int x = 0; + char s[1024]; + int err = 0; + + s[0] = '\0'; + fill_file(test_file, "foo "); + err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); + EXPECT_NE(err, 0) << "Value must not be missing in key/value case"; + + s[0] = '\0'; + fill_file(test_file, "faulty_start foo bar"); + err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); + EXPECT_NE(err, 0) << "Key must be at start"; + + s[0] = '\0'; + fill_file(test_file, "foof bar"); + err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); + EXPECT_NE(err, 0) << "Key must be exact match"; + + // Cleanup + delete_file(test_file); +} + +TEST(cgroupTest, cg_file_multi_line_impl_success_cases) { + const char* test_file = temp_file("cgroups"); + int x = 0; + char s[1024]; + int err = 0; + + s[0] = '\0'; + fill_file(test_file, "foo bar"); + err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); + EXPECT_EQ(err, 0); + EXPECT_STREQ(s, "bar") << "Incorrect!"; + + s[0] = '\0'; + fill_file(test_file, "foo\tbar"); + err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); + EXPECT_EQ(err, 0); + EXPECT_STREQ(s, "bar") << "Incorrect!"; + + s[0] = '\0'; + fill_file(test_file, "foof bar\nfoo car"); + err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); + EXPECT_EQ(err, 0); + EXPECT_STREQ(s, "car"); + + s[0] = '\0'; + fill_file(test_file, "foo\ttest\nfoot car"); + err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); + EXPECT_EQ(err, 0); + EXPECT_STREQ(s, "test"); + + s[0] = '\0'; + fill_file(test_file, "foo 1\nfoo car"); + err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); + EXPECT_EQ(err, 0); + EXPECT_STREQ(s, "1"); + + // Cleanup + delete_file(test_file); +} + +TEST(cgroupTest, cg_file_contents_impl) { + const char* test_file = temp_file("cgroups"); + int x = 0; + char s[1024]; + int err = 0; + + fill_file(test_file, "foo"); + err = __cg_file_contents_impl(test_file, "%s", &s); + EXPECT_EQ(err, 0); + EXPECT_STREQ(s, "foo"); + + err = __cg_file_contents_impl(test_file, "%d", &x); + EXPECT_NE(err, 0) << "'foo' cannot be read as int"; + EXPECT_EQ(x, 0); + + fill_file(test_file, "1337"); + err = __cg_file_contents_impl(test_file, "%d", &x); + EXPECT_EQ(err, 0); + EXPECT_EQ(x, 1337) << "Wrong value for x"; + + s[0] = '\0'; + fill_file(test_file, "1337"); + err = __cg_file_contents_impl(test_file, "%s", &s); + EXPECT_EQ(err, 0); + EXPECT_STREQ(s, "1337"); + + x = -1; + fill_file(test_file, nullptr); + err = __cg_file_contents_impl(test_file, "%d", &x); + EXPECT_NE(err, 0) << "Empty file should've failed"; + EXPECT_EQ(x, -1) << "x was altered"; + + jlong y; + fill_file(test_file, "1337"); + err = __cg_file_contents_impl(test_file, JLONG_FORMAT, &y); + EXPECT_EQ(err, 0); + EXPECT_EQ(y, 1337) << "Wrong value for y"; + julong z; + fill_file(test_file, "1337"); + err = __cg_file_contents_impl(test_file, JULONG_FORMAT, &z); + EXPECT_EQ(err, 0); + EXPECT_EQ(z, (julong)1337) << "Wrong value for z"; + + s[0] = '\0'; + fill_file(test_file, "max 10000"); + err = __cg_file_contents_impl(test_file, "%s %*d", &s); + EXPECT_EQ(err, 0); + EXPECT_STREQ(s, "max"); + + x = -3; + fill_file(test_file, "max 10001"); + err = __cg_file_contents_impl(test_file, "%*s %d", &x); + EXPECT_EQ(err, 0); + EXPECT_EQ(x, 10001); + + // Cleanup + delete_file(test_file); +} + +TEST(cgroupTest, cg_file_contents_ctrl_null) { + TestController* null_path_controller = new TestController((char*)nullptr); + const char* test_file_path = "/not-used"; + const char* scan_fmt = "%d"; + int a = -1; + // null subsystem_path() case + int err = cg_file_contents_ctrl(null_path_controller, test_file_path, scan_fmt, &a); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null subsystem path should be an error"; + EXPECT_EQ(-1, a) << "Expected untouched scan value"; + // null controller + err = cg_file_contents_ctrl(nullptr, test_file_path, scan_fmt, &a); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null subsystem path should be an error"; + EXPECT_EQ(-1, a) << "Expected untouched scan value"; + // null scan_fmt, null return pointer + TestController* test_controller = new TestController((char*)"/something"); + err = cg_file_contents_ctrl(test_controller, test_file_path, nullptr, &a); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null scan format should be an error"; + err = cg_file_contents_ctrl(test_controller, test_file_path, scan_fmt, nullptr); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null return pointer should be an error"; +} + +TEST(cgroupTest, cg_file_contents_ctrl_beyond_max_path) { + char larger_than_max[MAXPATHLEN + 1]; + for (int i = 0; i < (MAXPATHLEN); i++) { + larger_than_max[i] = 'A' + (i % 26); + } + larger_than_max[MAXPATHLEN] = '\0'; + TestController* too_large_path_controller = new TestController(larger_than_max); + const char* test_file_path = "/file-not-found"; + const char* scan_fmt = "%d"; + int foo = -1; + int err = cg_file_contents_ctrl(too_large_path_controller, test_file_path, scan_fmt, &foo); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Too long path should be an error"; + EXPECT_EQ(-1, foo) << "Expected untouched scan value"; +} + +TEST(cgroupTest, cg_file_contents_ctrl_file_not_exist) { + TestController* unknown_path_ctrl = new TestController((char*)"/do/not/exist"); + const char* test_file_path = "/file-not-found"; + const char* scan_fmt = "/not-used"; + const char* ret_val[2] = { "/one", "/two" }; + int err = cg_file_contents_ctrl(unknown_path_ctrl, test_file_path, scan_fmt, ret_val[0]); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "File not found should be an error"; + EXPECT_EQ("/one", ret_val[0]) << "Expected untouched scan value"; +} + +TEST(cgroupTest, cg_file_multi_line_ctrl_null) { + TestController* null_path_controller = new TestController((char*)nullptr); + const char* test_file_path = "/not-used"; + const char* scan_fmt = "%d"; + const char* key = "something"; + int a = -1; + // null subsystem_path() case + int err = cg_file_multi_line_ctrl(null_path_controller, test_file_path, key, scan_fmt, &a); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null subsystem path should be an error"; + EXPECT_EQ(-1, a) << "Expected untouched scan value"; + // null controller + err = cg_file_multi_line_ctrl(nullptr, test_file_path, key, scan_fmt, &a); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null subsystem path should be an error"; + EXPECT_EQ(-1, a) << "Expected untouched scan value"; + // null key, null scan_fmt, null return pointer + TestController* test_controller = new TestController((char*)"/something"); + err = cg_file_multi_line_ctrl(test_controller, test_file_path, nullptr, scan_fmt, &a); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null key should be an error"; + err = cg_file_multi_line_ctrl(test_controller, test_file_path, key, nullptr, &a); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null scan format should be an error"; + err = cg_file_multi_line_ctrl(test_controller, test_file_path, key, scan_fmt, nullptr); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null return pointer should be an error"; +} + +TEST(cgroupTest, cg_file_multi_line_ctrl_beyond_max_path) { + char larger_than_max[MAXPATHLEN + 1]; + for (int i = 0; i < (MAXPATHLEN); i++) { + larger_than_max[i] = 'A' + (i % 26); + } + larger_than_max[MAXPATHLEN] = '\0'; + TestController* too_large_path_controller = new TestController(larger_than_max); + const char* test_file_path = "/file-not-found"; + const char* scan_fmt = "%d"; + const char* key = "something"; + int foo = -1; + int err = cg_file_multi_line_ctrl(too_large_path_controller, test_file_path, key, scan_fmt, &foo); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "Too long path should be an error"; + EXPECT_EQ(-1, foo) << "Expected untouched scan value"; +} + +TEST(cgroupTest, cg_file_multi_line_ctrl_file_not_exist) { + TestController* unknown_path_ctrl = new TestController((char*)"/do/not/exist"); + const char* test_file_path = "/file-not-found"; + const char* scan_fmt = "/not-used"; + const char* key = "something"; + const char* ret_val[2] = { "/one", "/two" }; + int err = cg_file_multi_line_ctrl(unknown_path_ctrl, test_file_path, key, scan_fmt, ret_val[0]); + EXPECT_EQ(err, OSCONTAINER_ERROR) << "File not found should be an error"; + EXPECT_EQ("/one", ret_val[0]) << "Expected untouched scan value"; +} + +TEST(cgroupTest, set_cgroupv1_subsystem_path) { + TestCase host = { + "/sys/fs/cgroup/memory", // mount_path + "/", // root_path + "/user.slice/user-1000.slice/user@1000.service", // cgroup_path + "/sys/fs/cgroup/memory/user.slice/user-1000.slice/user@1000.service" // expected_path + }; + TestCase container_engine = { + "/sys/fs/cgroup/mem", // mount_path + "/user.slice/user-1000.slice/user@1000.service", // root_path + "/user.slice/user-1000.slice/user@1000.service", // cgroup_path + "/sys/fs/cgroup/mem" // expected_path + }; + int length = 2; + TestCase* testCases[] = { &host, + &container_engine }; + for (int i = 0; i < length; i++) { + CgroupV1Controller* ctrl = new CgroupV1Controller( (char*)testCases[i]->root_path, + (char*)testCases[i]->mount_path); + ctrl->set_subsystem_path((char*)testCases[i]->cgroup_path); + ASSERT_STREQ(testCases[i]->expected_path, ctrl->subsystem_path()); + } +} + +TEST(cgroupTest, set_cgroupv2_subsystem_path) { + TestCase at_mount_root = { + "/sys/fs/cgroup", // mount_path + nullptr, // root_path, ignored + "/", // cgroup_path + "/sys/fs/cgroup" // expected_path + }; + TestCase sub_path = { + "/sys/fs/cgroup", // mount_path + nullptr, // root_path, ignored + "/foobar", // cgroup_path + "/sys/fs/cgroup/foobar" // expected_path + }; + int length = 2; + TestCase* testCases[] = { &at_mount_root, + &sub_path }; + for (int i = 0; i < length; i++) { + CgroupV2Controller* ctrl = new CgroupV2Controller( (char*)testCases[i]->mount_path, + (char*)testCases[i]->cgroup_path); + ASSERT_STREQ(testCases[i]->expected_path, ctrl->subsystem_path()); + } +} + +#endif // LINUX diff --git a/test/hotspot/gtest/runtime/test_os_linux_cgroups.cpp b/test/hotspot/gtest/runtime/test_os_linux_cgroups.cpp deleted file mode 100644 index 21e0152a43d49..0000000000000 --- a/test/hotspot/gtest/runtime/test_os_linux_cgroups.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2022, 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 "precompiled.hpp" - -#ifdef LINUX - -#include "cgroupV1Subsystem_linux.hpp" -#include "cgroupV2Subsystem_linux.hpp" -#include "unittest.hpp" - -typedef struct { - const char* mount_path; - const char* root_path; - const char* cgroup_path; - const char* expected_path; -} TestCase; - -TEST(cgroupTest, set_cgroupv1_subsystem_path) { - TestCase host = { - "/sys/fs/cgroup/memory", // mount_path - "/", // root_path - "/user.slice/user-1000.slice/user@1000.service", // cgroup_path - "/sys/fs/cgroup/memory/user.slice/user-1000.slice/user@1000.service" // expected_path - }; - TestCase container_engine = { - "/sys/fs/cgroup/mem", // mount_path - "/user.slice/user-1000.slice/user@1000.service", // root_path - "/user.slice/user-1000.slice/user@1000.service", // cgroup_path - "/sys/fs/cgroup/mem" // expected_path - }; - int length = 2; - TestCase* testCases[] = { &host, - &container_engine }; - for (int i = 0; i < length; i++) { - CgroupV1Controller* ctrl = new CgroupV1Controller( (char*)testCases[i]->root_path, - (char*)testCases[i]->mount_path); - ctrl->set_subsystem_path((char*)testCases[i]->cgroup_path); - ASSERT_STREQ(testCases[i]->expected_path, ctrl->subsystem_path()); - } -} - -TEST(cgroupTest, set_cgroupv2_subsystem_path) { - TestCase at_mount_root = { - "/sys/fs/cgroup", // mount_path - nullptr, // root_path, ignored - "/", // cgroup_path - "/sys/fs/cgroup" // expected_path - }; - TestCase sub_path = { - "/sys/fs/cgroup", // mount_path - nullptr, // root_path, ignored - "/foobar", // cgroup_path - "/sys/fs/cgroup/foobar" // expected_path - }; - int length = 2; - TestCase* testCases[] = { &at_mount_root, - &sub_path }; - for (int i = 0; i < length; i++) { - CgroupV2Controller* ctrl = new CgroupV2Controller( (char*)testCases[i]->mount_path, - (char*)testCases[i]->cgroup_path); - ASSERT_STREQ(testCases[i]->expected_path, ctrl->subsystem_path()); - } -} - -#endif From e659a67c6c92e7f1a7fed4c496eb17af34839034 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Wed, 17 Apr 2024 15:43:59 +0200 Subject: [PATCH 02/38] Refactor cgroup controller code --- .../os/linux/cgroupSubsystem_linux.cpp | 120 ++++++----- .../os/linux/cgroupSubsystem_linux.hpp | 55 +++-- src/hotspot/os/linux/cgroupUtil_linux.cpp | 67 +++++++ src/hotspot/os/linux/cgroupUtil_linux.hpp | 38 ++++ .../os/linux/cgroupV1Subsystem_linux.cpp | 133 ++++++++----- .../os/linux/cgroupV1Subsystem_linux.hpp | 61 +++--- .../os/linux/cgroupV2Subsystem_linux.cpp | 188 ++++++++++-------- .../os/linux/cgroupV2Subsystem_linux.hpp | 52 +++-- 8 files changed, 457 insertions(+), 257 deletions(-) create mode 100644 src/hotspot/os/linux/cgroupUtil_linux.cpp create mode 100644 src/hotspot/os/linux/cgroupUtil_linux.hpp diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 9053e8eac302a..7c52b55eb0feb 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]; @@ -63,10 +64,11 @@ CgroupSubsystem* CgroupSubsystemFactory::create() { // 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); + CgroupV2MemoryController* memory = new CgroupV2MemoryController(cg_infos[MEMORY_IDX]._mount_path, cg_infos[MEMORY_IDX]._cgroup_path); + CgroupV2CpuController* cpu = new CgroupV2CpuController(cg_infos[CPU_IDX]._mount_path, cg_infos[CPU_IDX]._cgroup_path); log_debug(os, container)("Detected cgroups v2 unified hierarchy"); cleanup(cg_infos); - return new CgroupV2Subsystem(unified); + return new CgroupV2Subsystem(memory, cpu); } /* @@ -106,7 +108,7 @@ CgroupSubsystem* CgroupSubsystemFactory::create() { cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path); cpuset->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "cpu") == 0) { - cpu = new CgroupV1Controller(info._root_mount_path, info._mount_path); + cpu = new CgroupV1CpuController(info._root_mount_path, info._mount_path); cpu->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "cpuacct") == 0) { cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path); @@ -475,13 +477,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(); @@ -489,23 +491,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); @@ -522,55 +509,62 @@ 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; } -jlong CgroupSubsystem::limit_from_str(char* limit_str) { - if (limit_str == nullptr) { - return OSCONTAINER_ERROR; - } - // Unlimited memory in cgroups is the literal string 'max' for - // some controllers, for example the pids controller. - if (strcmp("max", limit_str) == 0) { - os::free(limit_str); - return (jlong)-1; - } - julong limit; - if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) { - os::free(limit_str); - return OSCONTAINER_ERROR; - } - os::free(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(); } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index e3d7dab80f269..468f04f9db660 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -246,47 +246,68 @@ 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 { +class CgroupCpuController: public CgroupController { public: - jlong memory_limit_in_bytes(); - int active_processor_count(); - jlong limit_from_str(char* limit_str); - 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 char *subsystem_path() = 0; +}; + +class CgroupMemoryController: public CgroupController { + 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 char *subsystem_path() = 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; + + int cpu_quota(); + int cpu_period(); + int cpu_shares(); 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 CachingCgroupController* memory_controller() = 0; + virtual CachingCgroupController* cpu_controller() = 0; + + 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(); virtual void print_version_specific_info(outputStream* st) = 0; }; diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp new file mode 100644 index 0000000000000..8ccb3e8177fd6 --- /dev/null +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -0,0 +1,67 @@ +/* + * 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; +} + +jlong CgroupUtil::limit_from_str(char* limit_str) { + if (limit_str == nullptr) { + return OSCONTAINER_ERROR; + } + // Unlimited memory in cgroups is the literal string 'max' for + // some controllers, for example the pids controller. + if (strcmp("max", limit_str) == 0) { + os::free(limit_str); + return (jlong)-1; + } + julong limit; + if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) { + os::free(limit_str); + return OSCONTAINER_ERROR; + } + os::free(limit_str); + return (jlong)limit; +} diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp new file mode 100644 index 0000000000000..6434242c92999 --- /dev/null +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -0,0 +1,38 @@ +/* + * 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); + static jlong limit_from_str(char* limit_str); +}; + +#endif // CGROUP_UTIL_LINUX_HPP diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 1b9e8d1714687..991b0888ea2a3 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,7 +77,7 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { */ jlong CgroupV1MemoryController::uses_mem_hierarchy() { jlong use_hierarchy; - int err = cg_file_contents_ctrl(this, "/memory.use_hierarchy", JLONG_FORMAT, &use_hierarchy); + int err = cg_file_contents_ctrl(static_cast(this), "/memory.use_hierarchy", JLONG_FORMAT, &use_hierarchy); if (err != 0) { log_trace(os, container)("Use Hierarchy is: %d", OSCONTAINER_ERROR); return (jlong)OSCONTAINER_ERROR; @@ -93,35 +94,60 @@ void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) { } } -jlong CgroupV1Subsystem::read_memory_limit_in_bytes() { +static inline +void do_trace_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; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.limit_in_bytes", JULONG_FORMAT, &memlimit); + int err = cg_file_contents_ctrl(static_cast(this), "/memory.limit_in_bytes", JULONG_FORMAT, &memlimit); if (err != 0) { log_trace(os, container)("Memory Limit is: %d", OSCONTAINER_ERROR); + do_trace_log(OSCONTAINER_ERROR, phys_mem); return OSCONTAINER_ERROR; } log_trace(os, container)("Memory Limit is: " JULONG_FORMAT, memlimit); - if (memlimit >= os::Linux::physical_memory()) { + 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; - err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", + err = cg_file_multi_line_ctrl(static_cast(this), "/memory.stat", "hierarchical_memory_limit", JULONG_FORMAT, &hier_memlimit); if (err != 0) { + do_trace_log(OSCONTAINER_ERROR, phys_mem); return OSCONTAINER_ERROR; } log_trace(os, container)("Hierarchical Memory Limit is: " JULONG_FORMAT, hier_memlimit); - if (hier_memlimit >= os::Linux::physical_memory()) { + if (hier_memlimit >= phys_mem) { log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); } else { + do_trace_log(hier_memlimit, phys_mem); return (jlong)hier_memlimit; } } + do_trace_log(memlimit, phys_mem); return (jlong)-1; - } - else { + } else { + do_trace_log(memlimit, phys_mem); return (jlong)memlimit; } } @@ -138,23 +164,20 @@ 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; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.memsw.limit_in_bytes", JULONG_FORMAT, &memswlimit); + int err = cg_file_contents_ctrl(static_cast(this), "/memory.memsw.limit_in_bytes", JULONG_FORMAT, &memswlimit); if (err != 0) { log_trace(os, container)("Memory and Swap Limit is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; } log_trace(os, container)("Memory and Swap Limit is: " JULONG_FORMAT, memswlimit); - host_total_memsw = os::Linux::host_swap() + os::Linux::physical_memory(); 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"; - err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", matchline, JULONG_FORMAT, &hier_memswlimit); + err = cg_file_multi_line_ctrl(static_cast(this), "/memory.stat", matchline, JULONG_FORMAT, &hier_memswlimit); if (err != 0) { return OSCONTAINER_ERROR; } @@ -171,8 +194,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; } @@ -181,7 +204,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 { @@ -204,21 +227,21 @@ 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(static_cast(this)); } } return memory_usage_in_bytes(); } -jlong CgroupV1Subsystem::read_mem_swappiness() { +jlong CgroupV1MemoryController::read_mem_swappiness() { julong swappiness; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.swappiness", + int err = cg_file_contents_ctrl(static_cast(this), "/memory.swappiness", JULONG_FORMAT, &swappiness); if (err != 0) { log_trace(os, container)("Swappiness is: %d", OSCONTAINER_ERROR); @@ -228,16 +251,16 @@ jlong CgroupV1Subsystem::read_mem_swappiness() { return (jlong)swappiness; } -jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() { +jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong upper_bound) { julong memsoftlimit; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.soft_limit_in_bytes", + int err = cg_file_contents_ctrl(static_cast(this), "/memory.soft_limit_in_bytes", JULONG_FORMAT, &memsoftlimit); if (err != 0) { log_trace(os, container)("Memory Soft Limit is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; } log_trace(os, container)("Memory Soft Limit is: " JULONG_FORMAT, memsoftlimit); - if (memsoftlimit >= os::Linux::physical_memory()) { + if (memsoftlimit >= upper_bound) { log_trace(os, container)("Memory Soft Limit is: Unlimited"); return (jlong)-1; } else { @@ -254,9 +277,9 @@ jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() { * -1 for unlimited * OSCONTAINER_ERROR for not supported */ -jlong CgroupV1Subsystem::memory_usage_in_bytes() { +jlong CgroupV1MemoryController::memory_usage_in_bytes() { jlong memusage; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.usage_in_bytes", + int err = cg_file_contents_ctrl(static_cast(this), "/memory.usage_in_bytes", JLONG_FORMAT, &memusage); if (err != 0) { log_trace(os, container)("Memory Usage is: %d", OSCONTAINER_ERROR); @@ -274,9 +297,9 @@ 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() { jlong memmaxusage; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.max_usage_in_bytes", + int err = cg_file_contents_ctrl(static_cast(this), "/memory.max_usage_in_bytes", JLONG_FORMAT, &memmaxusage); if (err != 0) { log_trace(os, container)("Maximum Memory Usage is: %d", OSCONTAINER_ERROR); @@ -286,9 +309,9 @@ jlong CgroupV1Subsystem::memory_max_usage_in_bytes() { return memmaxusage; } -jlong CgroupV1Subsystem::rss_usage_in_bytes() { +jlong CgroupV1MemoryController::rss_usage_in_bytes() { julong rss; - int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", "rss", JULONG_FORMAT, &rss); + int err = cg_file_multi_line_ctrl(static_cast(this), "/memory.stat", "rss", JULONG_FORMAT, &rss); if (err != 0) { return OSCONTAINER_ERROR; } @@ -296,9 +319,9 @@ jlong CgroupV1Subsystem::rss_usage_in_bytes() { return rss; } -jlong CgroupV1Subsystem::cache_usage_in_bytes() { +jlong CgroupV1MemoryController::cache_usage_in_bytes() { julong cache; - int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", "cache", JULONG_FORMAT, &cache); + int err = cg_file_multi_line_ctrl(static_cast(this), "/memory.stat", "cache", JULONG_FORMAT, &cache); if (err != 0) { return OSCONTAINER_ERROR; } @@ -306,9 +329,9 @@ jlong CgroupV1Subsystem::cache_usage_in_bytes() { return cache; } -jlong CgroupV1Subsystem::kernel_memory_usage_in_bytes() { +jlong CgroupV1MemoryController::kernel_memory_usage_in_bytes() { jlong kmem_usage; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.kmem.usage_in_bytes", + int err = cg_file_contents_ctrl(static_cast(this), "/memory.kmem.usage_in_bytes", JLONG_FORMAT, &kmem_usage); if (err != 0) { log_trace(os, container)("Kernel Memory Usage is: %d", OSCONTAINER_ERROR); @@ -318,24 +341,24 @@ jlong CgroupV1Subsystem::kernel_memory_usage_in_bytes() { return kmem_usage; } -jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() { +jlong CgroupV1MemoryController::kernel_memory_limit_in_bytes(julong phys_mem) { julong kmem_limit; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.kmem.limit_in_bytes", + int err = cg_file_contents_ctrl(static_cast(this), "/memory.kmem.limit_in_bytes", JULONG_FORMAT, &kmem_limit); if (err != 0) { log_trace(os, container)("Kernel Memory Limit is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; } log_trace(os, container)("Kernel Memory Limit is: " JULONG_FORMAT, kmem_limit); - if (kmem_limit >= os::Linux::physical_memory()) { + 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() { jlong kmem_max_usage; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.kmem.max_usage_in_bytes", + int err = cg_file_contents_ctrl(static_cast(this), "/memory.kmem.max_usage_in_bytes", JLONG_FORMAT, &kmem_max_usage); if (err != 0) { log_trace(os, container)("Maximum Kernel Memory Usage is: %d", OSCONTAINER_ERROR); @@ -346,9 +369,11 @@ jlong CgroupV1Subsystem::kernel_memory_max_usage_in_bytes() { } void CgroupV1Subsystem::print_version_specific_info(outputStream* st) { - jlong kmem_usage = kernel_memory_usage_in_bytes(); - jlong kmem_limit = kernel_memory_limit_in_bytes(); - jlong kmem_max_usage = kernel_memory_max_usage_in_bytes(); + julong phys_mem = os::Linux::physical_memory(); + CgroupV1MemoryController* ctrl = reinterpret_cast(memory_controller()->controller()); + jlong kmem_usage = ctrl->kernel_memory_usage_in_bytes(); + jlong kmem_limit = ctrl->kernel_memory_limit_in_bytes(phys_mem); + jlong kmem_max_usage = ctrl->kernel_memory_max_usage_in_bytes(); OSContainer::print_container_helper(st, kmem_usage, "kernel_memory_usage_in_bytes"); OSContainer::print_container_helper(st, kmem_limit, "kernel_memory_max_usage_in_bytes"); @@ -385,9 +410,9 @@ char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() { * -1 for no quota * OSCONTAINER_ERROR for not supported */ -int CgroupV1Subsystem::cpu_quota() { +int CgroupV1CpuController::cpu_quota() { int quota; - int err = cg_file_contents_ctrl(_cpu->controller(), "/cpu.cfs_quota_us", "%d", "a); + int err = cg_file_contents_ctrl(static_cast(this), "/cpu.cfs_quota_us", "%d", "a); if (err != 0) { log_trace(os, container)("CPU Quota is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -396,9 +421,9 @@ int CgroupV1Subsystem::cpu_quota() { return quota; } -int CgroupV1Subsystem::cpu_period() { +int CgroupV1CpuController::cpu_period() { int period; - int err = cg_file_contents_ctrl(_cpu->controller(), "/cpu.cfs_period_us", "%d", &period); + int err = cg_file_contents_ctrl(static_cast(this), "/cpu.cfs_period_us", "%d", &period); if (err != 0) { log_trace(os, container)("CPU Period is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -417,9 +442,9 @@ int CgroupV1Subsystem::cpu_period() { * -1 for no share setup * OSCONTAINER_ERROR for not supported */ -int CgroupV1Subsystem::cpu_shares() { +int CgroupV1CpuController::cpu_shares() { int shares; - int err = cg_file_contents_ctrl(_cpu->controller(), "/cpu.shares", "%d", &shares); + int err = cg_file_contents_ctrl(static_cast(this), "/cpu.shares", "%d", &shares); if (err != 0) { log_trace(os, container)("CPU Shares is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -454,7 +479,7 @@ char* CgroupV1Subsystem::pids_max_val() { jlong CgroupV1Subsystem::pids_max() { if (_pids == nullptr) return OSCONTAINER_ERROR; char * pidsmax_str = pids_max_val(); - return limit_from_str(pidsmax_str); + return CgroupUtil::limit_from_str(pidsmax_str); } /* pids_current diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 3205973961a92..a0716cb6f6b0c 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -36,7 +36,6 @@ class CgroupV1Controller: public CgroupController { /* mountinfo contents */ char *_root; char *_mount_point; - /* Constructed subsystem directory */ char *_path; @@ -51,11 +50,23 @@ class CgroupV1Controller: public CgroupController { char *subsystem_path() { return _path; } }; -class CgroupV1MemoryController: public CgroupV1Controller { +class CgroupV1MemoryController: public CgroupV1Controller, public CgroupMemoryController { public: bool is_hierarchical() { return _uses_mem_hierarchy; } void set_subsystem_path(char *cgroup_path); + jlong read_memory_limit_in_bytes(julong upper_bound); + jlong memory_usage_in_bytes(); + jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap); + jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap); + jlong memory_soft_limit_in_bytes(julong upper_bound); + jlong memory_max_usage_in_bytes(); + jlong rss_usage_in_bytes(); + jlong cache_usage_in_bytes(); + jlong kernel_memory_usage_in_bytes(); + jlong kernel_memory_limit_in_bytes(julong host_mem); + jlong kernel_memory_max_usage_in_bytes(); + char *subsystem_path() override { return CgroupV1Controller::subsystem_path(); } private: /* Some container runtimes set limits via cgroup * hierarchy. If set to true consider also memory.stat @@ -63,6 +74,8 @@ 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) : CgroupV1Controller(root, mountpoint) { @@ -71,18 +84,22 @@ class CgroupV1MemoryController: public CgroupV1Controller { }; -class CgroupV1Subsystem: public CgroupSubsystem { +class CgroupV1CpuController: public CgroupV1Controller, public CgroupCpuController { 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(); + int cpu_quota(); + int cpu_period(); + int cpu_shares(); + + public: + CgroupV1CpuController(char *root, char *mountpoint) : CgroupV1Controller(root, mountpoint) { + } + char *subsystem_path() override { return CgroupV1Controller::subsystem_path(); } +}; + +class CgroupV1Subsystem: public CgroupSubsystem { + public: jlong kernel_memory_usage_in_bytes(); jlong kernel_memory_limit_in_bytes(); jlong kernel_memory_max_usage_in_bytes(); @@ -90,11 +107,6 @@ 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(); @@ -103,33 +115,30 @@ class CgroupV1Subsystem: public CgroupSubsystem { 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; char * pids_max_val(); - 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); + _cpu = new CachingCgroupController(cpu); _cpuacct = cpuacct; _pids = pids; - _memory = new CachingCgroupController(memory); + _memory = new CachingCgroupController(memory); } }; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 6239ba461e365..44a152b491a08 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() { int shares; - int err = cg_file_contents_ctrl(_unified, "/cpu.weight", "%d", &shares); + int err = cg_file_contents_ctrl(static_cast(this), "/cpu.weight", "%d", &shares); if (err != 0) { log_trace(os, container)("Raw value for CPU Shares is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -77,6 +78,17 @@ int CgroupV2Subsystem::cpu_shares() { return x; } +static +char* cpu_quota_val(CgroupV2Controller* ctrl) { + char quota[1024]; + int err = cg_file_contents_ctrl(ctrl, "/cpu.max", "%1023s %*d", quota); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("Raw value for CPU quota is: %s", quota); + return os::strdup(quota); +} + /* cpu_quota * * Return the number of microseconds per period @@ -87,16 +99,16 @@ int CgroupV2Subsystem::cpu_shares() { * -1 for no quota * OSCONTAINER_ERROR for not supported */ -int CgroupV2Subsystem::cpu_quota() { - char * cpu_quota_str = cpu_quota_val(); - int limit = (int)limit_from_str(cpu_quota_str); +int CgroupV2CpuController::cpu_quota() { + char * cpu_quota_str = cpu_quota_val(static_cast(this)); + int limit = (int)CgroupUtil::limit_from_str(cpu_quota_str); log_trace(os, container)("CPU Quota is: %d", limit); return limit; } char * CgroupV2Subsystem::cpu_cpuset_cpus() { char cpus[1024]; - int err = cg_file_contents_ctrl(_unified, "/cpuset.cpus", "%1023s", cpus); + int err = cg_file_contents_ctrl(static_cast(_unified), "/cpuset.cpus", "%1023s", cpus); if (err != 0) { return nullptr; } @@ -104,19 +116,9 @@ char * CgroupV2Subsystem::cpu_cpuset_cpus() { return os::strdup(cpus); } -char* CgroupV2Subsystem::cpu_quota_val() { - char quota[1024]; - int err = cg_file_contents_ctrl(_unified, "/cpu.max", "%1023s %*d", quota); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("Raw value for CPU quota is: %s", quota); - return os::strdup(quota); -} - char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() { char mems[1024]; - int err = cg_file_contents_ctrl(_unified, "/cpuset.mems", "%1023s", mems); + int err = cg_file_contents_ctrl(static_cast(_unified), "/cpuset.mems", "%1023s", mems); if (err != 0) { return nullptr; } @@ -124,9 +126,9 @@ char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() { return os::strdup(mems); } -int CgroupV2Subsystem::cpu_period() { +int CgroupV2CpuController::cpu_period() { int period; - int err = cg_file_contents_ctrl(_unified, "/cpu.max", "%*s %d", &period); + int err = cg_file_contents_ctrl(static_cast(this), "/cpu.max", "%*s %d", &period); if (err != 0) { log_trace(os, container)("CPU Period is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -144,9 +146,9 @@ int CgroupV2Subsystem::cpu_period() { * -1 for unlimited * OSCONTAINER_ERROR for not supported */ -jlong CgroupV2Subsystem::memory_usage_in_bytes() { +jlong CgroupV2MemoryController::memory_usage_in_bytes() { jlong memusage; - int err = cg_file_contents_ctrl(_unified, "/memory.current", JLONG_FORMAT, &memusage); + int err = cg_file_contents_ctrl(static_cast(this), "/memory.current", JLONG_FORMAT, &memusage); if (err != 0) { log_trace(os, container)("Memory Usage is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -155,20 +157,31 @@ jlong CgroupV2Subsystem::memory_usage_in_bytes() { return memusage; } -jlong CgroupV2Subsystem::memory_soft_limit_in_bytes() { - char* mem_soft_limit_str = mem_soft_limit_val(); - return limit_from_str(mem_soft_limit_str); +static +char* mem_soft_limit_val(CgroupController* ctrl) { + char mem_soft_limit_str[1024]; + int err = cg_file_contents_ctrl(ctrl, "/memory.low", "%1023s", mem_soft_limit_str); + if (err != 0) { + return nullptr; + } + log_trace(os, container)("Memory Soft Limit is: %s", mem_soft_limit_str); + return os::strdup(mem_soft_limit_str); +} + +jlong CgroupV2MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { + char* mem_soft_limit_str = mem_soft_limit_val(static_cast(this)); + return CgroupUtil::limit_from_str(mem_soft_limit_str); } -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; - int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", + int err = cg_file_multi_line_ctrl(static_cast(this), "/memory.stat", "anon", JULONG_FORMAT, &rss); if (err != 0) { return OSCONTAINER_ERROR; @@ -177,9 +190,9 @@ jlong CgroupV2Subsystem::rss_usage_in_bytes() { return (jlong)rss; } -jlong CgroupV2Subsystem::cache_usage_in_bytes() { +jlong CgroupV2MemoryController::cache_usage_in_bytes() { julong cache; - int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", + int err = cg_file_multi_line_ctrl(static_cast(this), "/memory.stat", "file", JULONG_FORMAT, &cache); if (err != 0) { return OSCONTAINER_ERROR; @@ -188,14 +201,17 @@ jlong CgroupV2Subsystem::cache_usage_in_bytes() { return (jlong)cache; } -char* CgroupV2Subsystem::mem_soft_limit_val() { - char mem_soft_limit_str[1024]; - int err = cg_file_contents_ctrl(_unified, "/memory.low", "%1023s", mem_soft_limit_str); +static +char* mem_swp_limit_val(CgroupController* ctrl) { + char mem_swp_limit_str[1024]; + int err = cg_file_contents_ctrl(ctrl, "/memory.swap.max", "%1023s", mem_swp_limit_str); if (err != 0) { return nullptr; } - log_trace(os, container)("Memory Soft Limit is: %s", mem_soft_limit_str); - return os::strdup(mem_soft_limit_str); + // FIXME: This log-line is misleading, since it reads the swap limit only, not memory *and* + // swap limit. + log_trace(os, container)("Memory and Swap Limit is: %s", mem_swp_limit_str); + return os::strdup(mem_swp_limit_str); } // Note that for cgroups v2 the actual limits set for swap and @@ -203,17 +219,17 @@ char* CgroupV2Subsystem::mem_soft_limit_val() { // 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() { - char* mem_swp_limit_str = mem_swp_limit_val(); +jlong CgroupV2MemoryController::memory_and_swap_limit_in_bytes(julong phys_mem, julong host_swap) { + char* mem_swp_limit_str = mem_swp_limit_val(static_cast(this)); if (mem_swp_limit_str == nullptr) { // Some container tests rely on this trace logging to happen. log_trace(os, container)("Memory and Swap Limit is: %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); } - jlong swap_limit = limit_from_str(mem_swp_limit_str); + jlong swap_limit = CgroupUtil::limit_from_str(mem_swp_limit_str); 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; } @@ -221,40 +237,41 @@ 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) { - char* mem_swp_current_str = mem_swp_current_val(); - jlong swap_current = limit_from_str(mem_swp_current_str); - return memory_usage + (swap_current >= 0 ? swap_current : 0); - } - return memory_usage; // not supported or unlimited case -} - -char* CgroupV2Subsystem::mem_swp_limit_val() { - char mem_swp_limit_str[1024]; - int err = cg_file_contents_ctrl(_unified, "/memory.swap.max", "%1023s", mem_swp_limit_str); +// memory.swap.current : total amount of swap currently used by the cgroup and its descendants +static +char* mem_swp_current_val(CgroupV2Controller* ctrl) { + char mem_swp_current_str[1024]; + int err = cg_file_contents_ctrl(ctrl, "/memory.swap.current", "%1023s", mem_swp_current_str); if (err != 0) { return nullptr; } - // FIXME: This log-line is misleading, since it reads the swap limit only, not memory *and* - // swap limit. - log_trace(os, container)("Memory and Swap Limit is: %s", mem_swp_limit_str); - return os::strdup(mem_swp_limit_str); + log_trace(os, container)("Swap currently used is: %s", mem_swp_current_str); + return os::strdup(mem_swp_current_str); } -// memory.swap.current : total amount of swap currently used by the cgroup and its descendants -char* CgroupV2Subsystem::mem_swp_current_val() { - char mem_swp_current_str[1024]; - int err = cg_file_contents_ctrl(_unified, "/memory.swap.current", "%1023s", mem_swp_current_str); + +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) { + char* mem_swp_current_str = mem_swp_current_val(static_cast(this)); + jlong swap_current = CgroupUtil::limit_from_str(mem_swp_current_str); + return memory_usage + (swap_current >= 0 ? swap_current : 0); + } + return memory_usage; // not supported or unlimited case +} + +static +char* mem_limit_val(CgroupV2Controller* ctrl) { + char mem_limit_str[1024]; + int err = cg_file_contents_ctrl(ctrl, "/memory.max", "%1023s", mem_limit_str); if (err != 0) { return nullptr; } - log_trace(os, container)("Swap currently used is: %s", mem_swp_current_str); - return os::strdup(mem_swp_current_str); + log_trace(os, container)("Raw value for memory limit is: %s", mem_limit_str); + return os::strdup(mem_limit_str); } -/* memory_limit_in_bytes +/* read_memory_limit_in_bytes * * Return the limit of available memory for this process. * @@ -262,9 +279,9 @@ char* 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() { - char * mem_limit_str = mem_limit_val(); - jlong limit = limit_from_str(mem_limit_str); +jlong CgroupV2MemoryController::read_memory_limit_in_bytes(julong phys_mem) { + char * mem_limit_str = mem_limit_val(static_cast(this)); + jlong limit = CgroupUtil::limit_from_str(mem_limit_str); if (log_is_enabled(Trace, os, container)) { if (limit == -1) { log_trace(os, container)("Memory Limit is: Unlimited"); @@ -272,25 +289,32 @@ jlong CgroupV2Subsystem::read_memory_limit_in_bytes() { 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; } -char* CgroupV2Subsystem::mem_limit_val() { - char mem_limit_str[1024]; - int err = cg_file_contents_ctrl(_unified, "/memory.max", "%1023s", mem_limit_str); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("Raw value for memory limit is: %s", mem_limit_str); - return os::strdup(mem_limit_str); -} void CgroupV2Subsystem::print_version_specific_info(outputStream* st) { - char* mem_swp_current_str = mem_swp_current_val(); - jlong swap_current = limit_from_str(mem_swp_current_str); + char* mem_swp_current_str = mem_swp_current_val(static_cast(_unified)); + jlong swap_current = CgroupUtil::limit_from_str(mem_swp_current_str); - char* mem_swp_limit_str = mem_swp_limit_val(); - jlong swap_limit = limit_from_str(mem_swp_limit_str); + char* mem_swp_limit_str = mem_swp_limit_val(static_cast(_unified)); + jlong swap_limit = CgroupUtil::limit_from_str(mem_swp_limit_str); 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"); @@ -307,7 +331,7 @@ char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { char* CgroupV2Subsystem::pids_max_val() { char pidsmax[1024]; - int err = cg_file_contents_ctrl(_unified, "/pids.max", "%1023s", pidsmax); + int err = cg_file_contents_ctrl(static_cast(_unified), "/pids.max", "%1023s", pidsmax); if (err != 0) { return nullptr; } @@ -326,7 +350,7 @@ char* CgroupV2Subsystem::pids_max_val() { */ jlong CgroupV2Subsystem::pids_max() { char * pidsmax_str = pids_max_val(); - return limit_from_str(pidsmax_str); + return CgroupUtil::limit_from_str(pidsmax_str); } /* pids_current @@ -339,7 +363,7 @@ jlong CgroupV2Subsystem::pids_max() { */ jlong CgroupV2Subsystem::pids_current() { jlong pids_current; - int err = cg_file_contents_ctrl(_unified, "/pids.current", JLONG_FORMAT, &pids_current); + int err = cg_file_contents_ctrl(static_cast(_unified), "/pids.current", JLONG_FORMAT, &pids_current); if (err != 0) { log_trace(os, container)("Current number of tasks is: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 978c193c6027e..1d81d3b0ab6c5 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -48,26 +48,48 @@ class CgroupV2Controller: public CgroupController { char *subsystem_path() { return _path; } }; +class CgroupV2CpuController: public CgroupV2Controller, public CgroupCpuController { + public: + CgroupV2CpuController(char * mount_path, char *cgroup_path) : CgroupV2Controller(mount_path, cgroup_path) { + } + int cpu_quota(); + int cpu_period(); + int cpu_shares(); + char *subsystem_path() { return CgroupV2Controller::subsystem_path(); } +}; + +class CgroupV2MemoryController: public CgroupV2Controller, public CgroupMemoryController { + public: + CgroupV2MemoryController(char * mount_path, char *cgroup_path) : CgroupV2Controller(mount_path, cgroup_path) { + } + + jlong read_memory_limit_in_bytes(julong upper_bound); + jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swp); + jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swp); + jlong memory_soft_limit_in_bytes(julong upper_bound); + jlong memory_usage_in_bytes(); + jlong memory_max_usage_in_bytes(); + jlong rss_usage_in_bytes(); + jlong cache_usage_in_bytes(); + char *subsystem_path() { return CgroupV2Controller::subsystem_path(); } +}; + class CgroupV2Subsystem: public CgroupSubsystem { private: /* One unified controller */ - CgroupController* _unified = nullptr; + CgroupV2MemoryController* _unified = nullptr; /* Caching wrappers for cpu/memory metrics */ - CachingCgroupController* _memory = nullptr; - CachingCgroupController* _cpu = nullptr; - - char *mem_limit_val(); - char *mem_swp_limit_val(); - char *mem_swp_current_val(); - char *mem_soft_limit_val(); - char *cpu_quota_val(); + CachingCgroupController* _memory = nullptr; + CachingCgroupController* _cpu = nullptr; + char *pids_max_val(); public: - CgroupV2Subsystem(CgroupController * unified) { - _unified = unified; - _memory = new CachingCgroupController(unified); - _cpu = new CachingCgroupController(unified); + CgroupV2Subsystem(CgroupV2MemoryController * memory, + CgroupV2CpuController* cpu) { + _unified = memory; // Use memory for now, should have all separate later + _memory = new CachingCgroupController(memory); + _cpu = new CachingCgroupController(cpu); } jlong read_memory_limit_in_bytes(); @@ -92,8 +114,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { const char * container_type() { 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 From 3769ef38ed1fe97b85852f1ecb8dfab7efa8b463 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 3 May 2024 14:27:49 +0200 Subject: [PATCH 03/38] Fix whitespace --- src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 1d81d3b0ab6c5..8610bfb4787ff 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -86,7 +86,7 @@ class CgroupV2Subsystem: public CgroupSubsystem { public: CgroupV2Subsystem(CgroupV2MemoryController * memory, - CgroupV2CpuController* cpu) { + CgroupV2CpuController* cpu) { _unified = memory; // Use memory for now, should have all separate later _memory = new CachingCgroupController(memory); _cpu = new CachingCgroupController(cpu); From 3358c5bfa8051a1eaadb9a4b8a0f72af1b4f00c0 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Wed, 15 May 2024 21:25:25 +0200 Subject: [PATCH 04/38] Add generic julong/char* read functions --- .../os/linux/cgroupSubsystem_linux.cpp | 64 +++++++++++++++++++ .../os/linux/cgroupSubsystem_linux.hpp | 5 ++ 2 files changed, 69 insertions(+) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 9053e8eac302a..714fa17c41461 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -556,6 +556,70 @@ jlong CgroupSubsystem::memory_limit_in_bytes() { return mem_limit; } +bool CgroupController::read_string_from_file(const char* filename, char** result) { + char res[1024]; + bool ok = read_from_file(filename, "%1023s", res); + if (!ok) { + return false; + } + *result = os::strdup(res); + return true; +} + +bool CgroupController::read_number_from_file(const char* filename, julong* result) { + return read_from_file(filename, JULONG_FORMAT, result); +} + +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED +template +bool CgroupController::read_from_file(const char* filename, const char* scan_fmt, T result) { + if (result == nullptr) { + log_debug(os, container)("read_from_file: return pointer is null"); + return false; + } + char* s_path = subsystem_path(); + if (s_path == nullptr) { + log_debug(os, container)("read_from_file: subsystem path is null"); + return false; + } + + stringStream file_path; + file_path.print_raw(s_path); + file_path.print_raw(filename); + + if (file_path.size() > MAXPATHLEN) { + log_debug(os, container)("File path too long %s, %s", file_path.base(), filename); + return false; + } + const char* absolute_path = file_path.freeze(); + log_trace(os, container)("Path to %s is %s", filename, absolute_path); + + FILE* fp = os::fopen(absolute_path, "r"); + if (fp == nullptr) { + log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); + return false; + } + + const int buf_len = MAXPATHLEN+1; + char buf[buf_len]; + char* line = fgets(buf, buf_len, fp); + fclose(fp); + if (line == nullptr) { + log_debug(os, container)("Empty file %s", absolute_path); + return false; + } + + int matched = sscanf(line, scan_fmt, result); + if (matched == 1) { + return true; + } else { + log_debug(os, container)("Type %s not found in file %s", scan_fmt, absolute_path); + } + return false; +} +PRAGMA_DIAG_POP + jlong CgroupSubsystem::limit_from_str(char* limit_str) { if (limit_str == nullptr) { return OSCONTAINER_ERROR; diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index e3d7dab80f269..4374ed32e7c0d 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -72,6 +72,11 @@ class CgroupController: public CHeapObj { public: virtual char *subsystem_path() = 0; + bool read_number_from_file(const char* filename, julong* result); + bool read_string_from_file(const char* filename, char** result); + private: + template + bool read_from_file(const char* filename, const char* scan_fmt, T result); }; PRAGMA_DIAG_PUSH From 09521e58d1c9e19b2a8bf9523e5e2dc0e7bd3c49 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Thu, 16 May 2024 15:29:53 +0200 Subject: [PATCH 05/38] Add basic testing for read functions --- .../runtime/test_cgroupSubsystem_linux.cpp | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index 307894f7d19b5..bdad10830f9f7 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -284,6 +284,43 @@ TEST(cgroupTest, cg_file_multi_line_ctrl_null) { EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null return pointer should be an error"; } +TEST(cgroupTest, cg_read_number_tests) { + const char* test_file = temp_file("cgroups"); + const char* b = basename(test_file); + EXPECT_TRUE(b != nullptr) << "basename was null"; + stringStream path; + path.print_raw(os::file_separator()); + path.print_raw(b); + const char* base_with_slash = path.as_string(true); + fill_file(test_file, "8888"); + + TestController* controller = new TestController((char*)os::get_temp_directory()); + julong foo = 0xBAD; + bool ok = controller->read_number_from_file(base_with_slash, &foo); + EXPECT_TRUE(ok) << "Number parsing should have been successful"; + EXPECT_EQ((julong)8888, foo) << "Expected julongs to be equal (and not: " << 0xBAD << " == 0xBAD)"; + delete_file(test_file); +} + +TEST(cgroupTest, cg_read_string_tests) { + const char* test_file = temp_file("cgroups"); + const char* b = basename(test_file); + EXPECT_TRUE(b != nullptr) << "basename was null"; + stringStream path; + path.print_raw(os::file_separator()); + path.print_raw(b); + const char* base_with_slash = path.as_string(true); + fill_file(test_file, "foo-bar"); + + TestController* controller = new TestController((char*)os::get_temp_directory()); + char* result = nullptr; + bool ok = controller->read_string_from_file(base_with_slash, &result); + EXPECT_TRUE(ok) << "String parsing should have been successful"; + EXPECT_TRUE(result != nullptr) << "Expected non-null result"; + EXPECT_STREQ("foo-bar", result) << "Expected strings to be equal"; + delete_file(test_file); +} + TEST(cgroupTest, cg_file_multi_line_ctrl_beyond_max_path) { char larger_than_max[MAXPATHLEN + 1]; for (int i = 0; i < (MAXPATHLEN); i++) { From 6a81344956b7abe843b10e5316b8254d3b4c0050 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Thu, 16 May 2024 18:55:33 +0200 Subject: [PATCH 06/38] Add key-value read function --- .../os/linux/cgroupSubsystem_linux.cpp | 71 +++++++++++++++++++ .../os/linux/cgroupSubsystem_linux.hpp | 3 + 2 files changed, 74 insertions(+) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 714fa17c41461..afea7ce7c0a04 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -570,6 +570,10 @@ bool CgroupController::read_number_from_file(const char* filename, julong* resul return read_from_file(filename, JULONG_FORMAT, result); } +bool CgroupController::read_numerical_key_value(const char* filename, const char* key, julong* result) { + return read_key_from_file(filename, key, JULONG_FORMAT, result); +} + PRAGMA_DIAG_PUSH PRAGMA_FORMAT_NONLITERAL_IGNORED template @@ -620,6 +624,73 @@ bool CgroupController::read_from_file(const char* filename, const char* scan_fmt } PRAGMA_DIAG_POP +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED +template +bool CgroupController::read_key_from_file(const char* filename, const char* key, const char* scan_fmt, T result) { + char* s_path = subsystem_path(); + if (s_path == nullptr) { + log_debug(os, container)("read_key_from_file: subsystem path is null"); + return false; + } + if (key == nullptr || result == nullptr) { + log_debug(os, container)("read_key_from_file: key or return pointer is null"); + return false; + } + + stringStream file_path; + file_path.print_raw(s_path); + file_path.print_raw(filename); + + if (file_path.size() > MAXPATHLEN) { + log_debug(os, container)("File path too long %s, %s", file_path.base(), filename); + return false; + } + const char* absolute_path = file_path.freeze(); + log_trace(os, container)("Path to %s is %s", filename, absolute_path); + FILE* fp = os::fopen(absolute_path, "r"); + if (fp == nullptr) { + log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); + return false; + } + + const int buf_len = MAXPATHLEN+1; + char buf[buf_len]; + char* line = fgets(buf, buf_len, fp); + if (line == nullptr) { + log_debug(os, container)("Empty file %s", absolute_path); + fclose(fp); + return false; + } + + bool found_match = false; + // File consists of multiple lines in a "key value" + // fashion, we have to find the key. + const int key_len = (int)strlen(key); + for (; line != nullptr; line = fgets(buf, buf_len, fp)) { + char* key_substr = strstr(line, key); + char after_key = line[key_len]; + if (key_substr == line + && isspace(after_key) != 0 + && after_key != '\n') { + // Skip key, skip space + const char* value_substr = line + key_len + 1; + int matched = sscanf(value_substr, scan_fmt, result); + found_match = matched == 1; + if (found_match) { + break; + } + } + } + fclose(fp); + if (found_match) { + return true; + } + log_debug(os, container)("Type %s (key == %s) not found in file %s", scan_fmt, + key, absolute_path); + return false; +} + jlong CgroupSubsystem::limit_from_str(char* limit_str) { if (limit_str == nullptr) { return OSCONTAINER_ERROR; diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 4374ed32e7c0d..c546946400961 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -74,9 +74,12 @@ class CgroupController: public CHeapObj { virtual char *subsystem_path() = 0; bool read_number_from_file(const char* filename, julong* result); bool read_string_from_file(const char* filename, char** result); + bool read_numerical_key_value(const char* filename, const char* key, julong* result); private: template bool read_from_file(const char* filename, const char* scan_fmt, T result); + template + bool read_key_from_file(const char* filename, const char* key, const char* scan_fmt, T result); }; PRAGMA_DIAG_PUSH From 6ce5c550d8600f8d9f3b721fc8ed81d4a928acc5 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 17 May 2024 20:47:52 +0200 Subject: [PATCH 07/38] Try a different approach. Hybrid MACRO usage and locked down reading primitives. --- .../os/linux/cgroupSubsystem_linux.cpp | 117 +++--- .../os/linux/cgroupSubsystem_linux.hpp | 189 ++-------- .../os/linux/cgroupV1Subsystem_linux.cpp | 207 +++-------- .../os/linux/cgroupV2Subsystem_linux.cpp | 145 +++----- .../os/linux/cgroupV2Subsystem_linux.hpp | 1 - .../runtime/test_cgroupSubsystem_linux.cpp | 347 +++++++++--------- 6 files changed, 386 insertions(+), 620 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index afea7ce7c0a04..095273e271794 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -556,7 +556,7 @@ jlong CgroupSubsystem::memory_limit_in_bytes() { return mem_limit; } -bool CgroupController::read_string_from_file(const char* filename, char** result) { +bool CgroupController::read_string(const char* filename, char** result) { char res[1024]; bool ok = read_from_file(filename, "%1023s", res); if (!ok) { @@ -566,25 +566,22 @@ bool CgroupController::read_string_from_file(const char* filename, char** result return true; } -bool CgroupController::read_number_from_file(const char* filename, julong* result) { +bool CgroupController::read_number(const char* filename, julong* result) { return read_from_file(filename, JULONG_FORMAT, result); } bool CgroupController::read_numerical_key_value(const char* filename, const char* key, julong* result) { - return read_key_from_file(filename, key, JULONG_FORMAT, result); -} - -PRAGMA_DIAG_PUSH -PRAGMA_FORMAT_NONLITERAL_IGNORED -template -bool CgroupController::read_from_file(const char* filename, const char* scan_fmt, T result) { - if (result == nullptr) { - log_debug(os, container)("read_from_file: return pointer is null"); + if (filename == nullptr) { + log_debug(os, container)("read_numerical_key_value: filename is null"); return false; } char* s_path = subsystem_path(); if (s_path == nullptr) { - log_debug(os, container)("read_from_file: subsystem path is null"); + log_debug(os, container)("read_numerical_key_value: subsystem path is null"); + return false; + } + if (key == nullptr || result == nullptr) { + log_debug(os, container)("read_numerical_key_value: key or return pointer is null"); return false; } @@ -598,7 +595,6 @@ bool CgroupController::read_from_file(const char* filename, const char* scan_fmt } const char* absolute_path = file_path.freeze(); log_trace(os, container)("Path to %s is %s", filename, absolute_path); - FILE* fp = os::fopen(absolute_path, "r"); if (fp == nullptr) { log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); @@ -608,33 +604,71 @@ bool CgroupController::read_from_file(const char* filename, const char* scan_fmt const int buf_len = MAXPATHLEN+1; char buf[buf_len]; char* line = fgets(buf, buf_len, fp); - fclose(fp); if (line == nullptr) { log_debug(os, container)("Empty file %s", absolute_path); + fclose(fp); return false; } - int matched = sscanf(line, scan_fmt, result); - if (matched == 1) { + bool found_match = false; + // File consists of multiple lines in a "key value" + // fashion, we have to find the key. + const int key_len = (int)strlen(key); + for (; line != nullptr; line = fgets(buf, buf_len, fp)) { + char* key_substr = strstr(line, key); + char after_key = line[key_len]; + if (key_substr == line + && isspace(after_key) != 0 + && after_key != '\n') { + // Skip key, skip space + const char* value_substr = line + key_len + 1; + int matched = sscanf(value_substr, JULONG_FORMAT, result); + found_match = matched == 1; + if (found_match) { + break; + } + } + } + fclose(fp); + if (found_match) { return true; - } else { - log_debug(os, container)("Type %s not found in file %s", scan_fmt, absolute_path); } + log_debug(os, container)("Type %s (key == %s) not found in file %s", JULONG_FORMAT, + key, absolute_path); return false; } -PRAGMA_DIAG_POP + +bool CgroupController::read_numerical_tuple_value(const char* filename, TupleValue tup, jlong* result) { + char token[1024]; + bool is_ok = read_from_file(filename, tuple_format(tup), token); + if (!is_ok) { + return false; + } + char* t = os::strdup(token); + jlong val = CgroupSubsystem::limit_from_str(t); + if (val == OSCONTAINER_ERROR) { + return false; + } + *result = val; + return true; +} PRAGMA_DIAG_PUSH -PRAGMA_FORMAT_NONLITERAL_IGNORED +PRAGMA_FORMAT_NONLITERAL_IGNORED // Only string/number literal formats used. See read_*() functions. template -bool CgroupController::read_key_from_file(const char* filename, const char* key, const char* scan_fmt, T result) { - char* s_path = subsystem_path(); - if (s_path == nullptr) { - log_debug(os, container)("read_key_from_file: subsystem path is null"); +bool CgroupController::read_from_file(const char* filename, const char* scan_fmt, T result) { + assert(scan_fmt != nullptr, "invariant"); + if (filename == nullptr) { + log_debug(os, container)("read_from_file: filename is null"); return false; } - if (key == nullptr || result == nullptr) { - log_debug(os, container)("read_key_from_file: key or return pointer is null"); + if (result == nullptr) { + log_debug(os, container)("read_from_file: return pointer is null"); + return false; + } + char* s_path = subsystem_path(); + if (s_path == nullptr) { + log_debug(os, container)("read_from_file: subsystem path is null"); return false; } @@ -648,6 +682,7 @@ bool CgroupController::read_key_from_file(const char* filename, const char* key, } const char* absolute_path = file_path.freeze(); log_trace(os, container)("Path to %s is %s", filename, absolute_path); + FILE* fp = os::fopen(absolute_path, "r"); if (fp == nullptr) { log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); @@ -657,39 +692,21 @@ bool CgroupController::read_key_from_file(const char* filename, const char* key, const int buf_len = MAXPATHLEN+1; char buf[buf_len]; char* line = fgets(buf, buf_len, fp); + fclose(fp); if (line == nullptr) { log_debug(os, container)("Empty file %s", absolute_path); - fclose(fp); return false; } - bool found_match = false; - // File consists of multiple lines in a "key value" - // fashion, we have to find the key. - const int key_len = (int)strlen(key); - for (; line != nullptr; line = fgets(buf, buf_len, fp)) { - char* key_substr = strstr(line, key); - char after_key = line[key_len]; - if (key_substr == line - && isspace(after_key) != 0 - && after_key != '\n') { - // Skip key, skip space - const char* value_substr = line + key_len + 1; - int matched = sscanf(value_substr, scan_fmt, result); - found_match = matched == 1; - if (found_match) { - break; - } - } - } - fclose(fp); - if (found_match) { + int matched = sscanf(line, scan_fmt, result); + if (matched == 1) { return true; + } else { + log_debug(os, container)("Type %s not found in file %s", scan_fmt, absolute_path); } - log_debug(os, container)("Type %s (key == %s) not found in file %s", scan_fmt, - key, absolute_path); return false; } +PRAGMA_DIAG_POP jlong CgroupSubsystem::limit_from_str(char* limit_str) { if (limit_str == nullptr) { diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index c546946400961..370b6d5ea99d8 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -69,166 +69,49 @@ #define MEMORY_IDX 3 #define PIDS_IDX 4 -class CgroupController: public CHeapObj { - public: - virtual char *subsystem_path() = 0; - bool read_number_from_file(const char* filename, julong* result); - bool read_string_from_file(const char* filename, char** result); - bool read_numerical_key_value(const char* filename, const char* key, julong* result); - private: - template - bool read_from_file(const char* filename, const char* scan_fmt, T result); - template - bool read_key_from_file(const char* filename, const char* key, const char* scan_fmt, T result); -}; - -PRAGMA_DIAG_PUSH -PRAGMA_FORMAT_NONLITERAL_IGNORED -template int __cg_file_contents_impl(const char *absolute_path, - const char *scan_fmt, - T returnval) { - FILE* fp = os::fopen(absolute_path, "r"); - if (fp == nullptr) { - log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); - return OSCONTAINER_ERROR; - } - - const int buf_len = MAXPATHLEN+1; - char buf[buf_len]; - char* line = fgets(buf, buf_len, fp); - if (line == nullptr) { - log_debug(os, container)("Empty file %s", absolute_path); - fclose(fp); - return OSCONTAINER_ERROR; - } - fclose(fp); - - int matched = sscanf(line, scan_fmt, returnval); - if (matched == 1) { - return 0; - } else { - log_debug(os, container)("Type %s not found in file %s", scan_fmt, absolute_path); - } - return OSCONTAINER_ERROR; +#define CONTAINER_READ_NUMBER_CHECKED(controller, filename, log_string, retval) \ +{ \ + bool is_ok; \ + is_ok = controller->read_number(filename, &retval); \ + if (!is_ok) { \ + log_trace(os, container)(log_string " failed: %d", OSCONTAINER_ERROR); \ + return OSCONTAINER_ERROR; \ + } \ + log_trace(os, container)(log_string " is: " JULONG_FORMAT, retval); \ } -PRAGMA_DIAG_POP - -PRAGMA_DIAG_PUSH -PRAGMA_FORMAT_NONLITERAL_IGNORED -template int cg_file_contents_ctrl(CgroupController* c, - const char *filename, - const char *scan_fmt, - T returnval) { - if (c == nullptr) { - log_debug(os, container)("cg_file_contents_ctrl: CgroupController* is null"); - return OSCONTAINER_ERROR; - } - if (c->subsystem_path() == nullptr) { - log_debug(os, container)("cg_file_contents_ctrl: subsystem path is null"); - return OSCONTAINER_ERROR; - } - if (scan_fmt == nullptr || returnval == nullptr) { - log_debug(os, container)("cg_file_contents_ctrl: scan_fmt or return pointer is null"); - return OSCONTAINER_ERROR; - } - - stringStream file_path; - file_path.print_raw(c->subsystem_path()); - file_path.print_raw(filename); - if (file_path.size() > MAXPATHLEN) { - log_debug(os, container)("File path too long %s, %s", file_path.base(), filename); - return OSCONTAINER_ERROR; - } - const char* absolute_path = file_path.freeze(); - log_trace(os, container)("Path to %s is %s", filename, absolute_path); - return __cg_file_contents_impl(absolute_path, scan_fmt, returnval); +#define CONTAINER_READ_STRING_CHECKED(controller, filename, log_string, retval) \ +{ \ + bool is_ok; \ + is_ok = controller->read_string(filename, &retval); \ + if (!is_ok) { \ + log_trace(os, container)(log_string " failed: %d", OSCONTAINER_ERROR); \ + return nullptr; \ + } \ + log_trace(os, container)(log_string " is: %s", retval); \ } -PRAGMA_DIAG_POP -PRAGMA_DIAG_PUSH -PRAGMA_FORMAT_NONLITERAL_IGNORED -template int __cg_file_multi_line_impl(const char *absolute_path, - const char *key, - const char *scan_fmt, - T returnval) { - FILE* fp = os::fopen(absolute_path, "r"); - if (fp == nullptr) { - log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); - return OSCONTAINER_ERROR; - } - const int buf_len = MAXPATHLEN+1; - char buf[buf_len]; - char* line = fgets(buf, buf_len, fp); - if (line == nullptr) { - log_debug(os, container)("Empty file %s", absolute_path); - fclose(fp); - return OSCONTAINER_ERROR; - } +enum TupleValue { FIRST, SECOND }; - bool found_match = false; - // File consists of multiple lines in a "key value" - // fashion, we have to find the key. - const int key_len = (int)strlen(key); - for (; line != nullptr; line = fgets(buf, buf_len, fp)) { - char* key_substr = strstr(line, key); - char after_key = line[key_len]; - if (key_substr == line - && isspace(after_key) != 0 - && after_key != '\n') { - // Skip key, skip space - const char* value_substr = line + key_len + 1; - int matched = sscanf(value_substr, scan_fmt, returnval); - found_match = matched == 1; - if (found_match) { - break; +class CgroupController: public CHeapObj { + public: + virtual char *subsystem_path() = 0; + bool read_number(const char* filename, julong* result); + bool read_string(const char* filename, char** result); + bool read_numerical_tuple_value(const char* filename, TupleValue val, jlong* result); + bool read_numerical_key_value(const char* filename, const char* key, julong* result); + private: + inline static const char* tuple_format(TupleValue val) { + switch(val) { + case FIRST: return "%1023s %*s"; + case SECOND: return "%*s %1023s"; } + return nullptr; } - } - fclose(fp); - if (found_match) { - return 0; - } - log_debug(os, container)("Type %s (key == %s) not found in file %s", scan_fmt, - key, absolute_path); - return OSCONTAINER_ERROR; -} -PRAGMA_DIAG_POP - -PRAGMA_DIAG_PUSH -PRAGMA_FORMAT_NONLITERAL_IGNORED -template int cg_file_multi_line_ctrl(CgroupController* c, - const char *filename, - const char *key, - const char *scan_fmt, - T returnval) { - if (c == nullptr) { - log_debug(os, container)("cg_file_multi_line_ctrl: CgroupController* is null"); - return OSCONTAINER_ERROR; - } - if (c->subsystem_path() == nullptr) { - log_debug(os, container)("cg_file_multi_line_ctrl: subsystem path is null"); - return OSCONTAINER_ERROR; - } - if (key == nullptr || scan_fmt == nullptr || returnval == nullptr) { - log_debug(os, container)("cg_file_multi_line_ctrl: key, scan_fmt or return pointer is null"); - return OSCONTAINER_ERROR; - } - - stringStream file_path; - file_path.print_raw(c->subsystem_path()); - file_path.print_raw(filename); - - if (file_path.size() > MAXPATHLEN) { - log_debug(os, container)("File path too long %s, %s", file_path.base(), filename); - return OSCONTAINER_ERROR; - } - const char* absolute_path = file_path.freeze(); - log_trace(os, container)("Path to %s is %s", filename, absolute_path); - return __cg_file_multi_line_impl(absolute_path, key, scan_fmt, returnval); -} -PRAGMA_DIAG_POP + template + bool read_from_file(const char* filename, const char* scan_fmt, T result); +}; class CachedMetric : public CHeapObj{ private: @@ -273,7 +156,7 @@ class CgroupSubsystem: public CHeapObj { public: jlong memory_limit_in_bytes(); int active_processor_count(); - jlong limit_from_str(char* limit_str); + static jlong limit_from_str(char* limit_str); virtual int cpu_quota() = 0; virtual int cpu_period() = 0; diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 1b9e8d1714687..eadeaf9743532 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -75,14 +75,9 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { * OSCONTAINER_ERROR for not supported */ jlong CgroupV1MemoryController::uses_mem_hierarchy() { - jlong use_hierarchy; - int err = cg_file_contents_ctrl(this, "/memory.use_hierarchy", JLONG_FORMAT, &use_hierarchy); - if (err != 0) { - log_trace(os, container)("Use Hierarchy is: %d", OSCONTAINER_ERROR); - return (jlong)OSCONTAINER_ERROR; - } - log_trace(os, container)("Use Hierarchy is: " JLONG_FORMAT, use_hierarchy); - return use_hierarchy; + julong use_hierarchy; + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.use_hierarchy", "Use Hierarchy", use_hierarchy); + return (jlong)use_hierarchy; } void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) { @@ -95,21 +90,15 @@ void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) { jlong CgroupV1Subsystem::read_memory_limit_in_bytes() { julong memlimit; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.limit_in_bytes", JULONG_FORMAT, &memlimit); - if (err != 0) { - log_trace(os, container)("Memory Limit is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Memory Limit is: " JULONG_FORMAT, memlimit); - + CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.limit_in_bytes", "Memory Limit", memlimit); if (memlimit >= os::Linux::physical_memory()) { log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); CgroupV1MemoryController* mem_controller = reinterpret_cast(_memory->controller()); if (mem_controller->is_hierarchical()) { julong hier_memlimit; - err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", - "hierarchical_memory_limit", JULONG_FORMAT, &hier_memlimit); - if (err != 0) { + bool is_ok = _memory->controller()-> + 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); @@ -142,23 +131,19 @@ jlong CgroupV1Subsystem::read_mem_swap() { julong host_total_memsw; julong hier_memswlimit; julong memswlimit; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.memsw.limit_in_bytes", JULONG_FORMAT, &memswlimit); - if (err != 0) { - log_trace(os, container)("Memory and Swap Limit is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Memory and Swap Limit is: " JULONG_FORMAT, 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(); 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()) { const char* matchline = "hierarchical_memsw_limit"; - err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", matchline, JULONG_FORMAT, &hier_memswlimit); - if (err != 0) { + bool is_ok = _memory->controller()-> + read_numerical_key_value("/memory.stat", matchline, &hier_memswlimit); + if (!is_ok) { return OSCONTAINER_ERROR; } - log_trace(os, container)("Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, hier_memswlimit); + log_trace(os, container)("Hierarchical Memory and Swap Limit is: " JULONG_FORMAT, hier_memswlimit); if (hier_memswlimit >= host_total_memsw) { log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited"); } else { @@ -195,12 +180,7 @@ jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() { static inline jlong memory_swap_usage_impl(CgroupController* ctrl) { julong memory_swap_usage; - int err = cg_file_contents_ctrl(ctrl, "/memory.memsw.usage_in_bytes", JULONG_FORMAT, &memory_swap_usage); - if (err != 0) { - log_trace(os, container)("mem swap usage is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("mem swap usage is: " JULONG_FORMAT, memory_swap_usage); + CONTAINER_READ_NUMBER_CHECKED(ctrl, "/memory.memsw.usage_in_bytes", "mem swap usage", memory_swap_usage); return (jlong)memory_swap_usage; } @@ -218,25 +198,13 @@ jlong CgroupV1Subsystem::memory_and_swap_usage_in_bytes() { jlong CgroupV1Subsystem::read_mem_swappiness() { julong swappiness; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.swappiness", - JULONG_FORMAT, &swappiness); - if (err != 0) { - log_trace(os, container)("Swappiness is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Swappiness is: " JULONG_FORMAT, swappiness); + CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.swappiness", "Swappiness", swappiness); return (jlong)swappiness; } jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() { julong memsoftlimit; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.soft_limit_in_bytes", - JULONG_FORMAT, &memsoftlimit); - if (err != 0) { - log_trace(os, container)("Memory Soft Limit is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Memory Soft Limit is: " JULONG_FORMAT, memsoftlimit); + CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.soft_limit_in_bytes", "Memory Soft Limit", memsoftlimit); if (memsoftlimit >= os::Linux::physical_memory()) { log_trace(os, container)("Memory Soft Limit is: Unlimited"); return (jlong)-1; @@ -255,15 +223,9 @@ jlong CgroupV1Subsystem::memory_soft_limit_in_bytes() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV1Subsystem::memory_usage_in_bytes() { - jlong memusage; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.usage_in_bytes", - JLONG_FORMAT, &memusage); - if (err != 0) { - log_trace(os, container)("Memory Usage is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Memory Usage is: " JLONG_FORMAT, memusage); - return memusage; + julong memusage; + CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.usage_in_bytes", "Memory Usage", memusage); + return (jlong)memusage; } /* memory_max_usage_in_bytes @@ -275,31 +237,27 @@ jlong CgroupV1Subsystem::memory_usage_in_bytes() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV1Subsystem::memory_max_usage_in_bytes() { - jlong memmaxusage; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.max_usage_in_bytes", - JLONG_FORMAT, &memmaxusage); - if (err != 0) { - log_trace(os, container)("Maximum Memory Usage is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Maximum Memory Usage is: " JLONG_FORMAT, memmaxusage); - return memmaxusage; + julong memmaxusage; + CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.max_usage_in_bytes", "Maximum Memory Usage", memmaxusage); + return (jlong)memmaxusage; } jlong CgroupV1Subsystem::rss_usage_in_bytes() { julong rss; - int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", "rss", JULONG_FORMAT, &rss); - if (err != 0) { + bool is_ok = _memory->controller()-> + read_numerical_key_value("/memory.stat", "rss", &rss); + if (!is_ok) { return OSCONTAINER_ERROR; } log_trace(os, container)("RSS usage is: " JULONG_FORMAT, rss); - return rss; + return (jlong)rss; } jlong CgroupV1Subsystem::cache_usage_in_bytes() { julong cache; - int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", "cache", JULONG_FORMAT, &cache); - if (err != 0) { + bool is_ok = _memory->controller()-> + read_numerical_key_value("/memory.stat", "cache", &cache); + if (!is_ok) { return OSCONTAINER_ERROR; } log_trace(os, container)("Cache usage is: " JULONG_FORMAT, cache); @@ -307,26 +265,14 @@ jlong CgroupV1Subsystem::cache_usage_in_bytes() { } jlong CgroupV1Subsystem::kernel_memory_usage_in_bytes() { - jlong kmem_usage; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.kmem.usage_in_bytes", - JLONG_FORMAT, &kmem_usage); - if (err != 0) { - log_trace(os, container)("Kernel Memory Usage is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Kernel Memory Usage is: " JLONG_FORMAT, kmem_usage); - return kmem_usage; + julong kmem_usage; + CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/memory.kmem.usage_in_bytes", "Kernel Memory Usage", kmem_usage); + return (jlong)kmem_usage; } jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() { julong kmem_limit; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.kmem.limit_in_bytes", - JULONG_FORMAT, &kmem_limit); - if (err != 0) { - log_trace(os, container)("Kernel Memory Limit is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Kernel Memory Limit is: " JULONG_FORMAT, 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()) { return (jlong)-1; } @@ -334,15 +280,9 @@ jlong CgroupV1Subsystem::kernel_memory_limit_in_bytes() { } jlong CgroupV1Subsystem::kernel_memory_max_usage_in_bytes() { - jlong kmem_max_usage; - int err = cg_file_contents_ctrl(_memory->controller(), "/memory.kmem.max_usage_in_bytes", - JLONG_FORMAT, &kmem_max_usage); - if (err != 0) { - log_trace(os, container)("Maximum Kernel Memory Usage is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Maximum Kernel Memory Usage is: " JLONG_FORMAT, kmem_max_usage); - return kmem_max_usage; + julong kmem_max_usage; + CONTAINER_READ_NUMBER_CHECKED(_memory->controller(), "/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) { @@ -356,23 +296,15 @@ void CgroupV1Subsystem::print_version_specific_info(outputStream* st) { } char * CgroupV1Subsystem::cpu_cpuset_cpus() { - char cpus[1024]; - int err = cg_file_contents_ctrl(_cpuset, "/cpuset.cpus", "%1023s", cpus); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("cpuset.cpus is: %s", cpus); - return os::strdup(cpus); + char* cpus = nullptr; + CONTAINER_READ_STRING_CHECKED(_cpuset, "/cpuset.cpus", "cpuset.cpus", cpus); + return cpus; } char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() { - char mems[1024]; - int err = cg_file_contents_ctrl(_cpuset, "/cpuset.mems", "%1023s", mems); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("cpuset.mems is: %s", mems); - return os::strdup(mems); + char* mems = nullptr; + CONTAINER_READ_STRING_CHECKED(_cpuset, "/cpuset.mems", "cpuset.mems", mems); + return mems; } /* cpu_quota @@ -386,25 +318,15 @@ char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() { * OSCONTAINER_ERROR for not supported */ int CgroupV1Subsystem::cpu_quota() { - int quota; - int err = cg_file_contents_ctrl(_cpu->controller(), "/cpu.cfs_quota_us", "%d", "a); - if (err != 0) { - log_trace(os, container)("CPU Quota is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("CPU Quota is: %d", quota); - return quota; + julong quota; + CONTAINER_READ_NUMBER_CHECKED(_cpu->controller(), "/cpu.cfs_quota_us", "CPU Quota", quota); + return (int)quota; } int CgroupV1Subsystem::cpu_period() { - int period; - int err = cg_file_contents_ctrl(_cpu->controller(), "/cpu.cfs_period_us", "%d", &period); - if (err != 0) { - log_trace(os, container)("CPU Period is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("CPU Period is: %d", period); - return period; + julong period; + CONTAINER_READ_NUMBER_CHECKED(_cpu->controller(), "/cpu.cfs_period_us", "CPU Period", period); + return (int)period; } /* cpu_shares @@ -418,28 +340,20 @@ int CgroupV1Subsystem::cpu_period() { * OSCONTAINER_ERROR for not supported */ int CgroupV1Subsystem::cpu_shares() { - int shares; - int err = cg_file_contents_ctrl(_cpu->controller(), "/cpu.shares", "%d", &shares); - if (err != 0) { - log_trace(os, container)("CPU Shares is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("CPU Shares is: %d", shares); + julong shares; + CONTAINER_READ_NUMBER_CHECKED(_cpu->controller(), "/cpu.shares", "CPU Shares", shares); + int shares_int = (int)shares; // Convert 1024 to no shares setup - if (shares == 1024) return -1; + if (shares_int == 1024) return -1; - return shares; + return shares_int; } char* CgroupV1Subsystem::pids_max_val() { - char pidsmax[1024]; - int err = cg_file_contents_ctrl(_pids, "/pids.max", "%1023s", pidsmax); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("Maximum number of tasks is: %s", pidsmax); - return os::strdup(pidsmax); + char* pidsmax = nullptr; + CONTAINER_READ_STRING_CHECKED(_pids, "/pids.max", "Maximum number of tasks", pidsmax); + return pidsmax; } /* pids_max @@ -467,12 +381,7 @@ jlong CgroupV1Subsystem::pids_max() { */ jlong CgroupV1Subsystem::pids_current() { if (_pids == nullptr) return OSCONTAINER_ERROR; - jlong pids_current; - int err = cg_file_contents_ctrl(_pids, "/pids.current", JLONG_FORMAT, &pids_current); - if (err != 0) { - log_trace(os, container)("Current number of tasks is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Current number of tasks is: " JLONG_FORMAT, pids_current); - return pids_current; + julong pids_current; + CONTAINER_READ_NUMBER_CHECKED(_pids, "/pids.current", "Current number of tasks", pids_current); + return (jlong)pids_current; } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 6239ba461e365..c4637f9519c78 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -35,15 +35,11 @@ * OSCONTAINER_ERROR for not supported */ int CgroupV2Subsystem::cpu_shares() { - int shares; - int err = cg_file_contents_ctrl(_unified, "/cpu.weight", "%d", &shares); - if (err != 0) { - log_trace(os, container)("Raw value for CPU Shares is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Raw value for CPU Shares is: %d", shares); + julong shares; + CONTAINER_READ_NUMBER_CHECKED(_unified, "/cpu.weight", "Raw value for CPU Shares", shares); + int shares_int = (int)shares; // Convert default value of 100 to no shares setup - if (shares == 100) { + if (shares_int == 100) { log_debug(os, container)("CPU Shares is: %d", -1); return -1; } @@ -55,7 +51,7 @@ int CgroupV2Subsystem::cpu_shares() { // Use the inverse of (x == OCI value, y == cgroupsv2 value): // ((262142 * y - 1)/9999) + 2 = x // - int x = 262142 * shares - 1; + int x = 262142 * shares_int - 1; double frac = x/9999.0; x = ((int)frac) + 2; log_trace(os, container)("Scaled CPU shares value is: %d", x); @@ -88,49 +84,36 @@ int CgroupV2Subsystem::cpu_shares() { * OSCONTAINER_ERROR for not supported */ int CgroupV2Subsystem::cpu_quota() { - char * cpu_quota_str = cpu_quota_val(); - int limit = (int)limit_from_str(cpu_quota_str); + jlong quota_val; + bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", FIRST, "a_val); + if (!is_ok) { + return OSCONTAINER_ERROR; + } + int limit = (int)quota_val; log_trace(os, container)("CPU Quota is: %d", limit); return limit; } char * CgroupV2Subsystem::cpu_cpuset_cpus() { - char cpus[1024]; - int err = cg_file_contents_ctrl(_unified, "/cpuset.cpus", "%1023s", cpus); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("cpuset.cpus is: %s", cpus); - return os::strdup(cpus); -} - -char* CgroupV2Subsystem::cpu_quota_val() { - char quota[1024]; - int err = cg_file_contents_ctrl(_unified, "/cpu.max", "%1023s %*d", quota); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("Raw value for CPU quota is: %s", quota); - return os::strdup(quota); + char* cpus = nullptr; + CONTAINER_READ_STRING_CHECKED(_unified, "/cpuset.cpus", "cpuset.cpus", cpus); + return cpus; } char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() { - char mems[1024]; - int err = cg_file_contents_ctrl(_unified, "/cpuset.mems", "%1023s", mems); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("cpuset.mems is: %s", mems); - return os::strdup(mems); + char* mems; + CONTAINER_READ_STRING_CHECKED(_unified, "/cpuset.mems", "cpuset.mems", mems); + return mems; } int CgroupV2Subsystem::cpu_period() { - int period; - int err = cg_file_contents_ctrl(_unified, "/cpu.max", "%*s %d", &period); - if (err != 0) { - log_trace(os, container)("CPU Period is: %d", OSCONTAINER_ERROR); + jlong period_val; + bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", SECOND, &period_val); + if (!is_ok) { + log_trace(os, container)("CPU Period failed: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; } + int period = (int)period_val; log_trace(os, container)("CPU Period is: %d", period); return period; } @@ -145,14 +128,9 @@ int CgroupV2Subsystem::cpu_period() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV2Subsystem::memory_usage_in_bytes() { - jlong memusage; - int err = cg_file_contents_ctrl(_unified, "/memory.current", JLONG_FORMAT, &memusage); - if (err != 0) { - log_trace(os, container)("Memory Usage is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Memory Usage is: " JLONG_FORMAT, memusage); - return memusage; + julong memusage; + CONTAINER_READ_NUMBER_CHECKED(_unified, "/memory.current", "Memory Usage", memusage); + return (jlong)memusage; } jlong CgroupV2Subsystem::memory_soft_limit_in_bytes() { @@ -168,9 +146,9 @@ jlong CgroupV2Subsystem::memory_max_usage_in_bytes() { jlong CgroupV2Subsystem::rss_usage_in_bytes() { julong rss; - int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", - "anon", JULONG_FORMAT, &rss); - if (err != 0) { + bool is_ok = _memory->controller()-> + read_numerical_key_value("/memory.stat", "anon", &rss); + if (!is_ok) { return OSCONTAINER_ERROR; } log_trace(os, container)("RSS usage is: " JULONG_FORMAT, rss); @@ -179,9 +157,9 @@ jlong CgroupV2Subsystem::rss_usage_in_bytes() { jlong CgroupV2Subsystem::cache_usage_in_bytes() { julong cache; - int err = cg_file_multi_line_ctrl(_memory->controller(), "/memory.stat", - "file", JULONG_FORMAT, &cache); - if (err != 0) { + bool is_ok = _memory->controller()-> + read_numerical_key_value("/memory.stat", "file", &cache); + if (!is_ok) { return OSCONTAINER_ERROR; } log_trace(os, container)("Cache usage is: " JULONG_FORMAT, cache); @@ -189,13 +167,9 @@ jlong CgroupV2Subsystem::cache_usage_in_bytes() { } char* CgroupV2Subsystem::mem_soft_limit_val() { - char mem_soft_limit_str[1024]; - int err = cg_file_contents_ctrl(_unified, "/memory.low", "%1023s", mem_soft_limit_str); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("Memory Soft Limit is: %s", mem_soft_limit_str); - return os::strdup(mem_soft_limit_str); + char* mem_soft_limit_str; + CONTAINER_READ_STRING_CHECKED(_unified, "/memory.low", "Memory Soft Limit", mem_soft_limit_str); + return mem_soft_limit_str; } // Note that for cgroups v2 the actual limits set for swap and @@ -232,26 +206,16 @@ jlong CgroupV2Subsystem::memory_and_swap_usage_in_bytes() { } char* CgroupV2Subsystem::mem_swp_limit_val() { - char mem_swp_limit_str[1024]; - int err = cg_file_contents_ctrl(_unified, "/memory.swap.max", "%1023s", mem_swp_limit_str); - if (err != 0) { - return nullptr; - } - // FIXME: This log-line is misleading, since it reads the swap limit only, not memory *and* - // swap limit. - log_trace(os, container)("Memory and Swap Limit is: %s", mem_swp_limit_str); - return os::strdup(mem_swp_limit_str); + char* mem_swp_limit_str; + CONTAINER_READ_STRING_CHECKED(_unified, "/memory.swap.max", "Swap Limit", mem_swp_limit_str); + return mem_swp_limit_str; } // memory.swap.current : total amount of swap currently used by the cgroup and its descendants char* CgroupV2Subsystem::mem_swp_current_val() { - char mem_swp_current_str[1024]; - int err = cg_file_contents_ctrl(_unified, "/memory.swap.current", "%1023s", mem_swp_current_str); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("Swap currently used is: %s", mem_swp_current_str); - return os::strdup(mem_swp_current_str); + char* mem_swp_current_str; + CONTAINER_READ_STRING_CHECKED(_unified, "/memory.swap.current", "Swap currently used", mem_swp_current_str); + return mem_swp_current_str; } /* memory_limit_in_bytes @@ -276,13 +240,9 @@ jlong CgroupV2Subsystem::read_memory_limit_in_bytes() { } char* CgroupV2Subsystem::mem_limit_val() { - char mem_limit_str[1024]; - int err = cg_file_contents_ctrl(_unified, "/memory.max", "%1023s", mem_limit_str); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("Raw value for memory limit is: %s", mem_limit_str); - return os::strdup(mem_limit_str); + char* mem_limit_str; + CONTAINER_READ_STRING_CHECKED(_unified, "/memory.max", "Raw value for memory limit", mem_limit_str); + return mem_limit_str; } void CgroupV2Subsystem::print_version_specific_info(outputStream* st) { @@ -306,13 +266,9 @@ char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { } char* CgroupV2Subsystem::pids_max_val() { - char pidsmax[1024]; - int err = cg_file_contents_ctrl(_unified, "/pids.max", "%1023s", pidsmax); - if (err != 0) { - return nullptr; - } - log_trace(os, container)("Maximum number of tasks is: %s", pidsmax); - return os::strdup(pidsmax); + char* pidsmax; + CONTAINER_READ_STRING_CHECKED(_unified, "/pids.max", "Maximum number of tasks", pidsmax); + return pidsmax; } /* pids_max @@ -338,12 +294,7 @@ jlong CgroupV2Subsystem::pids_max() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV2Subsystem::pids_current() { - jlong pids_current; - int err = cg_file_contents_ctrl(_unified, "/pids.current", JLONG_FORMAT, &pids_current); - if (err != 0) { - log_trace(os, container)("Current number of tasks is: %d", OSCONTAINER_ERROR); - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Current number of tasks is: " JLONG_FORMAT, pids_current); + julong 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 978c193c6027e..1fd5dd9cf845a 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -60,7 +60,6 @@ class CgroupV2Subsystem: public CgroupSubsystem { char *mem_swp_limit_val(); char *mem_swp_current_val(); char *mem_soft_limit_val(); - char *cpu_quota_val(); char *pids_max_val(); public: diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index bdad10830f9f7..4a60e7dd95ce9 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -90,152 +90,102 @@ static void fill_file(const char* path, const char* content) { fclose(fp); } -TEST(cgroupTest, cg_file_multi_line_impl_failure_cases) { +TEST(cgroupTest, read_numerical_key_value_failure_cases) { const char* test_file = temp_file("cgroups"); - int x = 0; - char s[1024]; - int err = 0; - - s[0] = '\0'; - fill_file(test_file, "foo "); - err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); - EXPECT_NE(err, 0) << "Value must not be missing in key/value case"; - - s[0] = '\0'; - fill_file(test_file, "faulty_start foo bar"); - err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); - EXPECT_NE(err, 0) << "Key must be at start"; - - s[0] = '\0'; - fill_file(test_file, "foof bar"); - err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); - EXPECT_NE(err, 0) << "Key must be exact match"; + const char* b = basename(test_file); + EXPECT_TRUE(b != nullptr) << "basename was null"; + stringStream path; + path.print_raw(os::file_separator()); + path.print_raw(b); + const char* base_with_slash = path.as_string(true); - // Cleanup - delete_file(test_file); -} + TestController* controller = new TestController((char*)os::get_temp_directory()); + julong x = 0xBAD; -TEST(cgroupTest, cg_file_multi_line_impl_success_cases) { - const char* test_file = temp_file("cgroups"); - int x = 0; - char s[1024]; - int err = 0; - - s[0] = '\0'; - fill_file(test_file, "foo bar"); - err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "bar") << "Incorrect!"; - - s[0] = '\0'; - fill_file(test_file, "foo\tbar"); - err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "bar") << "Incorrect!"; - - s[0] = '\0'; - fill_file(test_file, "foof bar\nfoo car"); - err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "car"); - - s[0] = '\0'; - fill_file(test_file, "foo\ttest\nfoot car"); - err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "test"); - - s[0] = '\0'; - fill_file(test_file, "foo 1\nfoo car"); - err = __cg_file_multi_line_impl(test_file, "foo", "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "1"); + fill_file(test_file, "foo "); + bool is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x); + EXPECT_FALSE(is_ok) << "Value must not be missing in key/value case"; + EXPECT_EQ((julong)0xBAD, x) << "x must be unchanged"; + + x = 0xBAD; + fill_file(test_file, "faulty_start foo 101"); + is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x); + EXPECT_FALSE(is_ok) << "key must be at the start"; + EXPECT_EQ((julong)0xBAD, x) << "x must be unchanged"; + + x = 0xBAD; + fill_file(test_file, "foof 1002"); + is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x); + EXPECT_FALSE(is_ok) << "key must be exact match"; + EXPECT_EQ((julong)0xBAD, x) << "x must be unchanged"; // Cleanup delete_file(test_file); } -TEST(cgroupTest, cg_file_contents_impl) { +TEST(cgroupTest, read_numerical_key_value_success_cases) { const char* test_file = temp_file("cgroups"); - int x = 0; - char s[1024]; - int err = 0; - - fill_file(test_file, "foo"); - err = __cg_file_contents_impl(test_file, "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "foo"); - - err = __cg_file_contents_impl(test_file, "%d", &x); - EXPECT_NE(err, 0) << "'foo' cannot be read as int"; - EXPECT_EQ(x, 0); - - fill_file(test_file, "1337"); - err = __cg_file_contents_impl(test_file, "%d", &x); - EXPECT_EQ(err, 0); - EXPECT_EQ(x, 1337) << "Wrong value for x"; - - s[0] = '\0'; - fill_file(test_file, "1337"); - err = __cg_file_contents_impl(test_file, "%s", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "1337"); - - x = -1; - fill_file(test_file, nullptr); - err = __cg_file_contents_impl(test_file, "%d", &x); - EXPECT_NE(err, 0) << "Empty file should've failed"; - EXPECT_EQ(x, -1) << "x was altered"; - - jlong y; - fill_file(test_file, "1337"); - err = __cg_file_contents_impl(test_file, JLONG_FORMAT, &y); - EXPECT_EQ(err, 0); - EXPECT_EQ(y, 1337) << "Wrong value for y"; - julong z; - fill_file(test_file, "1337"); - err = __cg_file_contents_impl(test_file, JULONG_FORMAT, &z); - EXPECT_EQ(err, 0); - EXPECT_EQ(z, (julong)1337) << "Wrong value for z"; - - s[0] = '\0'; - fill_file(test_file, "max 10000"); - err = __cg_file_contents_impl(test_file, "%s %*d", &s); - EXPECT_EQ(err, 0); - EXPECT_STREQ(s, "max"); + const char* b = basename(test_file); + EXPECT_TRUE(b != nullptr) << "basename was null"; + stringStream path; + path.print_raw(os::file_separator()); + path.print_raw(b); + const char* base_with_slash = path.as_string(true); - x = -3; - fill_file(test_file, "max 10001"); - err = __cg_file_contents_impl(test_file, "%*s %d", &x); - EXPECT_EQ(err, 0); - EXPECT_EQ(x, 10001); + TestController* controller = new TestController((char*)os::get_temp_directory()); + julong x = 0xBAD; + + fill_file(test_file, "foo 100"); + bool is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x); + EXPECT_TRUE(is_ok); + EXPECT_EQ((julong)100, x) << "Incorrect!"; + + x = 0xBAD; + fill_file(test_file, "foo\t111"); + is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x); + EXPECT_TRUE(is_ok); + EXPECT_EQ((julong)111, x) << "Incorrect!"; + + x = 0xBAD; + fill_file(test_file, "foof 100\nfoo 133"); + is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x); + EXPECT_TRUE(is_ok); + EXPECT_EQ((julong)133, x) << "Incorrect!"; + + x = 0xBAD; + fill_file(test_file, "foo\t333\nfoot 999"); + is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x); + EXPECT_TRUE(is_ok); + EXPECT_EQ((julong)333, x) << "Incorrect!"; + + x = 0xBAD; + fill_file(test_file, "foo 1\nfoo car"); + is_ok = controller->read_numerical_key_value(base_with_slash, "foo", &x); + EXPECT_TRUE(is_ok); + EXPECT_EQ((julong)1, x) << "Incorrect!"; // Cleanup delete_file(test_file); } -TEST(cgroupTest, cg_file_contents_ctrl_null) { +TEST(cgroupTest, read_number_null) { TestController* null_path_controller = new TestController((char*)nullptr); const char* test_file_path = "/not-used"; - const char* scan_fmt = "%d"; - int a = -1; + julong a = 0xBAD; // null subsystem_path() case - int err = cg_file_contents_ctrl(null_path_controller, test_file_path, scan_fmt, &a); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null subsystem path should be an error"; - EXPECT_EQ(-1, a) << "Expected untouched scan value"; - // null controller - err = cg_file_contents_ctrl(nullptr, test_file_path, scan_fmt, &a); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null subsystem path should be an error"; - EXPECT_EQ(-1, a) << "Expected untouched scan value"; - // null scan_fmt, null return pointer + bool is_ok = null_path_controller->read_number(test_file_path, &a); + EXPECT_FALSE(is_ok) << "Null subsystem path should be an error"; + EXPECT_EQ((julong)0xBAD, a) << "Expected untouched scan value"; + // null file, null return pointer TestController* test_controller = new TestController((char*)"/something"); - err = cg_file_contents_ctrl(test_controller, test_file_path, nullptr, &a); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null scan format should be an error"; - err = cg_file_contents_ctrl(test_controller, test_file_path, scan_fmt, nullptr); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null return pointer should be an error"; + is_ok = test_controller->read_number(nullptr, &a); + EXPECT_FALSE(is_ok) << "Null file should be an error"; + EXPECT_EQ((julong)0xBAD, a) << "Expected untouched scan value"; + is_ok = test_controller->read_number(test_file_path, nullptr); + EXPECT_FALSE(is_ok) << "Null return pointer should be an error"; } -TEST(cgroupTest, cg_file_contents_ctrl_beyond_max_path) { +TEST(cgroupTest, read_string_beyond_max_path) { char larger_than_max[MAXPATHLEN + 1]; for (int i = 0; i < (MAXPATHLEN); i++) { larger_than_max[i] = 'A' + (i % 26); @@ -243,48 +193,41 @@ TEST(cgroupTest, cg_file_contents_ctrl_beyond_max_path) { larger_than_max[MAXPATHLEN] = '\0'; TestController* too_large_path_controller = new TestController(larger_than_max); const char* test_file_path = "/file-not-found"; - const char* scan_fmt = "%d"; - int foo = -1; - int err = cg_file_contents_ctrl(too_large_path_controller, test_file_path, scan_fmt, &foo); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Too long path should be an error"; - EXPECT_EQ(-1, foo) << "Expected untouched scan value"; + char* foo = nullptr; + bool is_ok = too_large_path_controller->read_string(test_file_path, &foo); + EXPECT_FALSE(is_ok) << "Too long path should be an error"; + EXPECT_TRUE(nullptr == foo) << "Expected untouched scan value"; } -TEST(cgroupTest, cg_file_contents_ctrl_file_not_exist) { +TEST(cgroupTest, read_number_file_not_exist) { TestController* unknown_path_ctrl = new TestController((char*)"/do/not/exist"); const char* test_file_path = "/file-not-found"; - const char* scan_fmt = "/not-used"; - const char* ret_val[2] = { "/one", "/two" }; - int err = cg_file_contents_ctrl(unknown_path_ctrl, test_file_path, scan_fmt, ret_val[0]); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "File not found should be an error"; - EXPECT_EQ("/one", ret_val[0]) << "Expected untouched scan value"; + julong result = 0xBAD; + bool is_ok = unknown_path_ctrl->read_number(test_file_path, &result); + EXPECT_FALSE(is_ok) << "File not found should be an error"; + EXPECT_EQ((julong)0xBAD, result) << "Expected untouched scan value"; } -TEST(cgroupTest, cg_file_multi_line_ctrl_null) { +TEST(cgroupTest, read_numerical_key_value_null) { TestController* null_path_controller = new TestController((char*)nullptr); const char* test_file_path = "/not-used"; - const char* scan_fmt = "%d"; const char* key = "something"; - int a = -1; + julong a = 0xBAD; // null subsystem_path() case - int err = cg_file_multi_line_ctrl(null_path_controller, test_file_path, key, scan_fmt, &a); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null subsystem path should be an error"; - EXPECT_EQ(-1, a) << "Expected untouched scan value"; - // null controller - err = cg_file_multi_line_ctrl(nullptr, test_file_path, key, scan_fmt, &a); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null subsystem path should be an error"; - EXPECT_EQ(-1, a) << "Expected untouched scan value"; - // null key, null scan_fmt, null return pointer + bool is_ok = null_path_controller->read_numerical_key_value(test_file_path, key, &a); + EXPECT_FALSE(is_ok) << "Null subsystem path should be an error"; + EXPECT_EQ((julong)0xBAD, a) << "Expected untouched scan value"; + // null key, null file, null return pointer TestController* test_controller = new TestController((char*)"/something"); - err = cg_file_multi_line_ctrl(test_controller, test_file_path, nullptr, scan_fmt, &a); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null key should be an error"; - err = cg_file_multi_line_ctrl(test_controller, test_file_path, key, nullptr, &a); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null scan format should be an error"; - err = cg_file_multi_line_ctrl(test_controller, test_file_path, key, scan_fmt, nullptr); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Null return pointer should be an error"; + is_ok = test_controller->read_numerical_key_value(test_file_path, nullptr, &a); + EXPECT_FALSE(is_ok) << "Null key should be an error"; + is_ok = test_controller->read_numerical_key_value(nullptr, key, &a); + EXPECT_FALSE(is_ok) << "Null file should be an error"; + is_ok = test_controller->read_numerical_key_value(test_file_path, key, nullptr); + EXPECT_FALSE(is_ok) << "Null return pointer should be an error"; } -TEST(cgroupTest, cg_read_number_tests) { +TEST(cgroupTest, read_number_tests) { const char* test_file = temp_file("cgroups"); const char* b = basename(test_file); EXPECT_TRUE(b != nullptr) << "basename was null"; @@ -296,13 +239,20 @@ TEST(cgroupTest, cg_read_number_tests) { TestController* controller = new TestController((char*)os::get_temp_directory()); julong foo = 0xBAD; - bool ok = controller->read_number_from_file(base_with_slash, &foo); + bool ok = controller->read_number(base_with_slash, &foo); EXPECT_TRUE(ok) << "Number parsing should have been successful"; - EXPECT_EQ((julong)8888, foo) << "Expected julongs to be equal (and not: " << 0xBAD << " == 0xBAD)"; + EXPECT_EQ((julong)8888, foo) << "Wrong value for 'foo' (NOTE: 0xBAD == " << 0xBAD << ")"; + + foo = 0xBAD; + fill_file(test_file, nullptr); + ok = controller->read_number(base_with_slash, &foo); + EXPECT_FALSE(ok) << "Empty file should have failed"; + EXPECT_EQ((julong)0xBAD, foo) << "foo was altered"; + delete_file(test_file); } -TEST(cgroupTest, cg_read_string_tests) { +TEST(cgroupTest, read_string_tests) { const char* test_file = temp_file("cgroups"); const char* b = basename(test_file); EXPECT_TRUE(b != nullptr) << "basename was null"; @@ -314,14 +264,73 @@ TEST(cgroupTest, cg_read_string_tests) { TestController* controller = new TestController((char*)os::get_temp_directory()); char* result = nullptr; - bool ok = controller->read_string_from_file(base_with_slash, &result); + bool ok = controller->read_string(base_with_slash, &result); EXPECT_TRUE(ok) << "String parsing should have been successful"; EXPECT_TRUE(result != nullptr) << "Expected non-null result"; EXPECT_STREQ("foo-bar", result) << "Expected strings to be equal"; + os::free(result); + + result = nullptr; + fill_file(test_file, "1234"); + ok = controller->read_string(base_with_slash, &result); + EXPECT_TRUE(ok) << "String parsing should have been successful"; + EXPECT_TRUE(result != nullptr) << "Expected non-null result"; + EXPECT_STREQ("1234", result) << "Expected strings to be equal"; + os::free(result); + + // values with a space only read in the first token + result = nullptr; + fill_file(test_file, "abc def"); + ok = controller->read_string(base_with_slash, &result); + EXPECT_TRUE(ok) << "String parsing should have been successful"; + EXPECT_TRUE(result != nullptr) << "Expected non-null result"; + EXPECT_STREQ("abc", result) << "Expected strings to be equal"; + os::free(result); + + result = nullptr; + fill_file(test_file, nullptr); + ok = controller->read_string(base_with_slash, &result); + EXPECT_FALSE(ok) << "Empty file should have failed"; + EXPECT_TRUE(result == nullptr) << "Expected untouched result"; delete_file(test_file); } -TEST(cgroupTest, cg_file_multi_line_ctrl_beyond_max_path) { +TEST(cgroupTest, read_number_tuple_test) { + const char* test_file = temp_file("cgroups"); + const char* b = basename(test_file); + EXPECT_TRUE(b != nullptr) << "basename was null"; + stringStream path; + path.print_raw(os::file_separator()); + path.print_raw(b); + const char* base_with_slash = path.as_string(true); + fill_file(test_file, "max 10000"); + + TestController* controller = new TestController((char*)os::get_temp_directory()); + jlong result = -10; + bool ok = controller->read_numerical_tuple_value(base_with_slash, FIRST, &result); + EXPECT_TRUE(ok) << "Should be OK to read value"; + EXPECT_EQ((jlong)-1, result) << "max should be unlimited (-1)"; + + result = -10; + ok = controller->read_numerical_tuple_value(base_with_slash, SECOND, &result); + EXPECT_TRUE(ok) << "Should be OK to read the value"; + EXPECT_EQ((jlong)10000, result) << "result value incorrect"; + + // non-max strings + fill_file(test_file, "abc 10000"); + result = -10; + ok = controller->read_numerical_tuple_value(base_with_slash, FIRST, &result); + EXPECT_FALSE(ok) << "abc should not be parsable"; + EXPECT_EQ((jlong)-10, result) << "result value should be unchanged"; + + fill_file(test_file, nullptr); + result = -10; + ok = controller->read_numerical_tuple_value(base_with_slash, FIRST, &result); + EXPECT_FALSE(ok) << "Empty file should be an error"; + EXPECT_EQ((jlong)-10, result) << "result value should be unchanged"; +} + +TEST(cgroupTest, read_numerical_key_beyond_max_path) { char larger_than_max[MAXPATHLEN + 1]; for (int i = 0; i < (MAXPATHLEN); i++) { larger_than_max[i] = 'A' + (i % 26); @@ -329,23 +338,21 @@ TEST(cgroupTest, cg_file_multi_line_ctrl_beyond_max_path) { larger_than_max[MAXPATHLEN] = '\0'; TestController* too_large_path_controller = new TestController(larger_than_max); const char* test_file_path = "/file-not-found"; - const char* scan_fmt = "%d"; const char* key = "something"; - int foo = -1; - int err = cg_file_multi_line_ctrl(too_large_path_controller, test_file_path, key, scan_fmt, &foo); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "Too long path should be an error"; - EXPECT_EQ(-1, foo) << "Expected untouched scan value"; + julong a = 0xBAD; + bool is_ok = too_large_path_controller->read_numerical_key_value(test_file_path, key, &a); + EXPECT_FALSE(is_ok) << "Too long path should be an error"; + EXPECT_EQ((julong)0xBAD, a) << "Expected untouched scan value"; } -TEST(cgroupTest, cg_file_multi_line_ctrl_file_not_exist) { +TEST(cgroupTest, read_numerical_key_file_not_exist) { TestController* unknown_path_ctrl = new TestController((char*)"/do/not/exist"); const char* test_file_path = "/file-not-found"; - const char* scan_fmt = "/not-used"; const char* key = "something"; - const char* ret_val[2] = { "/one", "/two" }; - int err = cg_file_multi_line_ctrl(unknown_path_ctrl, test_file_path, key, scan_fmt, ret_val[0]); - EXPECT_EQ(err, OSCONTAINER_ERROR) << "File not found should be an error"; - EXPECT_EQ("/one", ret_val[0]) << "Expected untouched scan value"; + julong a = 0xBAD; + bool is_ok = unknown_path_ctrl->read_numerical_key_value(test_file_path, key, &a); + EXPECT_FALSE(is_ok) << "File not found should be an error"; + EXPECT_EQ((julong)0xBAD, a) << "Expected untouched scan value"; } TEST(cgroupTest, set_cgroupv1_subsystem_path) { From f368d548c410047d0e25e22363a94e3ebd1ee58b Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 17 May 2024 21:10:29 +0200 Subject: [PATCH 08/38] Handle cpu quota for cgroups v1 specially --- src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp | 13 +++++++++++-- .../gtest/runtime/test_cgroupSubsystem_linux.cpp | 8 ++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index eadeaf9743532..1495b59484435 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -319,8 +319,17 @@ char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() { */ int CgroupV1Subsystem::cpu_quota() { julong quota; - CONTAINER_READ_NUMBER_CHECKED(_cpu->controller(), "/cpu.cfs_quota_us", "CPU Quota", quota); - return (int)quota; + bool is_ok = _cpu->controller()-> + read_number("/cpu.cfs_quota_us", "a); + if (!is_ok) { + log_trace(os, container)("CPU Quota failed: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + // cast to int since the read value might be negative + // and we want to avoid logging -1 as a large unsigned value. + int quota_int = (int)quota; + log_trace(os, container)("CPU Quota is: %d", quota_int); + return quota_int; } int CgroupV1Subsystem::cpu_period() { diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index 4a60e7dd95ce9..ad939ab1359b4 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -243,6 +243,14 @@ TEST(cgroupTest, read_number_tests) { EXPECT_TRUE(ok) << "Number parsing should have been successful"; EXPECT_EQ((julong)8888, foo) << "Wrong value for 'foo' (NOTE: 0xBAD == " << 0xBAD << ")"; + // Some interface files might have negative values, ensure we can read + // them and manually cast them as needed. + fill_file(test_file, "-1"); + foo = 0xBAD; + ok = controller->read_number(base_with_slash, &foo); + EXPECT_TRUE(ok) << "Number parsing should have been successful"; + EXPECT_EQ((jlong)-1, (jlong)foo) << "Wrong value for 'foo' (NOTE: 0xBAD == " << 0xBAD << ")"; + foo = 0xBAD; fill_file(test_file, nullptr); ok = controller->read_number(base_with_slash, &foo); From 4de2adba145135273381a1d84c1800d77f77e3cc Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 21 May 2024 15:03:04 +0200 Subject: [PATCH 09/38] Fix whitespace --- src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index c4637f9519c78..481a3da98ef66 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -84,7 +84,7 @@ int CgroupV2Subsystem::cpu_shares() { * OSCONTAINER_ERROR for not supported */ int CgroupV2Subsystem::cpu_quota() { - jlong quota_val; + jlong quota_val; bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", FIRST, "a_val); if (!is_ok) { return OSCONTAINER_ERROR; From 9ac1b407c0ef2523a8c194745295d28f41bd1224 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 21 May 2024 15:31:49 +0200 Subject: [PATCH 10/38] Fix TestMemoryAwareness for cgroup v2 It only reads the swap value, thus doesn't print: "Memory and Swap Limit is" but rather prints: "Swap Limit is". This is fine, since for cg v2 the memory limit and the swap limits are in different files. --- test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java index 553ba692ee73b..20354cf934d96 100644 --- a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java @@ -200,8 +200,8 @@ private static void testMemorySwapLimitSanity() throws Exception { .shouldMatch("Memory Limit is:.*" + expectedTraceValue) // Either for cgroup v1: a_1) same as memory limit, or b_1) -2 on systems with swapaccount=0 // Either for cgroup v2: a_2) 0, or b_2) -2 on systems with swapaccount=0 - .shouldMatch("Memory and Swap Limit is:.*(" + expectedTraceValue + "|-2|0)") - .shouldNotMatch("Memory and Swap Limit is:.*" + neg2InUnsignedLong); + .shouldMatch("(Memory and )?Swap Limit is:.*(" + expectedTraceValue + "|-2|0)") + .shouldNotMatch("(Memory and )?Swap Limit is:.*" + neg2InUnsignedLong); } From b0c3a72abc26cea2dd8d36bc0d39d9b5f175002a Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 21 May 2024 18:38:50 +0200 Subject: [PATCH 11/38] Fix limit_from_str usages --- src/hotspot/os/linux/cgroupSubsystem_linux.cpp | 2 +- src/hotspot/os/linux/cgroupSubsystem_linux.hpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 43e775f63a39b..c37a5e5663da3 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -611,7 +611,7 @@ bool CgroupController::read_numerical_tuple_value(const char* filename, TupleVal return false; } char* t = os::strdup(token); - jlong val = CgroupSubsystem::limit_from_str(t); + jlong val = CgroupUtil::limit_from_str(t); if (val == OSCONTAINER_ERROR) { return false; } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index abb3e1fbb704a..16c7fb2aedc70 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -179,7 +179,6 @@ class CgroupSubsystem: public CHeapObj { public: jlong memory_limit_in_bytes(); int active_processor_count(); - static jlong limit_from_str(char* limit_str); virtual jlong pids_max() = 0; virtual jlong pids_current() = 0; From 5d20a2d5616a908511f92179969109289a1e3b8b Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 21 May 2024 20:34:23 +0200 Subject: [PATCH 12/38] More fix-ups after merge --- .../os/linux/cgroupSubsystem_linux.cpp | 19 ------------------- .../os/linux/cgroupV1Subsystem_linux.cpp | 16 +++++++++------- .../os/linux/cgroupV2Subsystem_linux.cpp | 19 ++++++++++--------- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index c37a5e5663da3..8c94b298207e4 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -674,25 +674,6 @@ bool CgroupController::read_from_file(const char* filename, const char* scan_fmt } PRAGMA_DIAG_POP -jlong CgroupSubsystem::limit_from_str(char* limit_str) { - if (limit_str == nullptr) { - return OSCONTAINER_ERROR; - } - // Unlimited memory in cgroups is the literal string 'max' for - // some controllers, for example the pids controller. - if (strcmp("max", limit_str) == 0) { - os::free(limit_str); - return (jlong)-1; - } - julong limit; - if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) { - os::free(limit_str); - return OSCONTAINER_ERROR; - } - os::free(limit_str); - return (jlong)limit; -} - // CgroupSubsystem implementations jlong CgroupSubsystem::memory_and_swap_limit_in_bytes() { diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 7719ad7ca4a9e..a636da1fafed2 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -113,12 +113,13 @@ void do_trace_log(julong read_mem_limit, julong host_mem) { jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { julong memlimit; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.limit_in_bytes", "Memory Limit", memlimit); + CgroupV1Controller* v1_controller = static_cast(this); + CONTAINER_READ_NUMBER_CHECKED(v1_controller, "/memory.limit_in_bytes", "Memory Limit", memlimit); if (memlimit >= phys_mem) { log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); if (is_hierarchical()) { julong hier_memlimit; - bool is_ok = read_numerical_key_value("/memory.stat", "hierarchical_memory_limit", &hier_memlimit); + bool is_ok = v1_controller->read_numerical_key_value("/memory.stat", "hierarchical_memory_limit", &hier_memlimit); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -153,12 +154,13 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { jlong CgroupV1MemoryController::read_mem_swap(julong host_total_memsw) { julong hier_memswlimit; julong memswlimit; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", memswlimit); + CgroupV1Controller* v1_controller = static_cast(this); + CONTAINER_READ_NUMBER_CHECKED(v1_controller, "/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"); if (is_hierarchical()) { const char* matchline = "hierarchical_memsw_limit"; - bool is_ok = read_numerical_key_value("/memory.stat", matchline, &hier_memswlimit); + bool is_ok = v1_controller->read_numerical_key_value("/memory.stat", matchline, &hier_memswlimit); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -263,7 +265,7 @@ jlong CgroupV1MemoryController::memory_max_usage_in_bytes() { jlong CgroupV1MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = read_numerical_key_value("/memory.stat", "rss", &rss); + bool is_ok = static_cast(this)->read_numerical_key_value("/memory.stat", "rss", &rss); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -273,7 +275,7 @@ jlong CgroupV1MemoryController::rss_usage_in_bytes() { jlong CgroupV1MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = read_numerical_key_value("/memory.stat", "cache", &cache); + bool is_ok = static_cast(this)->read_numerical_key_value("/memory.stat", "cache", &cache); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -338,7 +340,7 @@ char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() { */ int CgroupV1CpuController::cpu_quota() { julong quota; - bool is_ok = read_number("/cpu.cfs_quota_us", "a); + bool is_ok = static_cast(this)->read_number("/cpu.cfs_quota_us", "a); if (!is_ok) { log_trace(os, container)("CPU Quota failed: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index d4ff7c03d94f0..fea188fd50d66 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -86,7 +86,7 @@ int CgroupV2CpuController::cpu_shares() { */ int CgroupV2CpuController::cpu_quota() { jlong quota_val; - bool is_ok = read_numerical_tuple_value("/cpu.max", FIRST, "a_val); + bool is_ok = static_cast(this)->read_numerical_tuple_value("/cpu.max", FIRST, "a_val); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -97,19 +97,19 @@ int CgroupV2CpuController::cpu_quota() { char * CgroupV2Subsystem::cpu_cpuset_cpus() { char* cpus = nullptr; - CONTAINER_READ_STRING_CHECKED(_unified, "/cpuset.cpus", "cpuset.cpus", cpus); + CONTAINER_READ_STRING_CHECKED(static_cast(_unified), "/cpuset.cpus", "cpuset.cpus", cpus); return cpus; } char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() { char* mems; - CONTAINER_READ_STRING_CHECKED(_unified, "/cpuset.mems", "cpuset.mems", mems); + CONTAINER_READ_STRING_CHECKED(static_cast(_unified), "/cpuset.mems", "cpuset.mems", mems); return mems; } int CgroupV2CpuController::cpu_period() { jlong period_val; - bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", SECOND, &period_val); + bool is_ok = static_cast(this)->read_numerical_tuple_value("/cpu.max", SECOND, &period_val); if (!is_ok) { log_trace(os, container)("CPU Period failed: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -154,7 +154,7 @@ jlong CgroupV2MemoryController::memory_max_usage_in_bytes() { jlong CgroupV2MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = read_numerical_key_value("/memory.stat", "anon", &rss); + bool is_ok = static_cast(this)->read_numerical_key_value("/memory.stat", "anon", &rss); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -164,7 +164,7 @@ jlong CgroupV2MemoryController::rss_usage_in_bytes() { jlong CgroupV2MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = read_numerical_key_value("/memory.stat", "file", &cache); + bool is_ok = static_cast(this)->read_numerical_key_value("/memory.stat", "file", &cache); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -286,9 +286,10 @@ char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { return os::strdup(ss.base()); } -char* CgroupV2Subsystem::pids_max_val() { +static +char* pids_max_val(CgroupController* ctrl) { char* pidsmax; - CONTAINER_READ_STRING_CHECKED(static_cast(_unified), "/pids.max", "Maximum number of tasks", pidsmax); + CONTAINER_READ_STRING_CHECKED(ctrl, "/pids.max", "Maximum number of tasks", pidsmax); return pidsmax; } @@ -302,7 +303,7 @@ char* CgroupV2Subsystem::pids_max_val() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV2Subsystem::pids_max() { - char * pidsmax_str = pids_max_val(); + char * pidsmax_str = pids_max_val(static_cast(_unified)); return CgroupUtil::limit_from_str(pidsmax_str); } From 7da8791acb90c3626f22039444688f6dcf0437fa Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Wed, 22 May 2024 16:31:41 +0200 Subject: [PATCH 13/38] Use enum class for TupleValue --- src/hotspot/os/linux/cgroupSubsystem_linux.hpp | 13 +++++++++---- src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 4 ++-- .../gtest/runtime/test_cgroupSubsystem_linux.cpp | 8 ++++---- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 370b6d5ea99d8..99e1b99c1a83e 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -91,8 +91,13 @@ log_trace(os, container)(log_string " is: %s", retval); \ } - -enum TupleValue { FIRST, SECOND }; +/* + * Used to model the tuple-valued cgroup interface files like cpu.max, which + * contains two values: . In this case the first tuple value + * is and the second tuple value is . We use this to map the + * tuple value to a constant string format for sscanf reading. + */ +enum class TupleValue { FIRST, SECOND }; class CgroupController: public CHeapObj { public: @@ -104,8 +109,8 @@ class CgroupController: public CHeapObj { private: inline static const char* tuple_format(TupleValue val) { switch(val) { - case FIRST: return "%1023s %*s"; - case SECOND: return "%*s %1023s"; + case TupleValue::FIRST: return "%1023s %*s"; + case TupleValue::SECOND: return "%*s %1023s"; } return nullptr; } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 481a3da98ef66..9a2b997f1b2e6 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -85,7 +85,7 @@ int CgroupV2Subsystem::cpu_shares() { */ int CgroupV2Subsystem::cpu_quota() { jlong quota_val; - bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", FIRST, "a_val); + bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", TupleValue::FIRST, "a_val); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -108,7 +108,7 @@ char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() { int CgroupV2Subsystem::cpu_period() { jlong period_val; - bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", SECOND, &period_val); + bool is_ok = _unified->read_numerical_tuple_value("/cpu.max", TupleValue::SECOND, &period_val); if (!is_ok) { log_trace(os, container)("CPU Period failed: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index ad939ab1359b4..04f49003999c7 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -315,25 +315,25 @@ TEST(cgroupTest, read_number_tuple_test) { TestController* controller = new TestController((char*)os::get_temp_directory()); jlong result = -10; - bool ok = controller->read_numerical_tuple_value(base_with_slash, FIRST, &result); + bool ok = controller->read_numerical_tuple_value(base_with_slash, TupleValue::FIRST, &result); EXPECT_TRUE(ok) << "Should be OK to read value"; EXPECT_EQ((jlong)-1, result) << "max should be unlimited (-1)"; result = -10; - ok = controller->read_numerical_tuple_value(base_with_slash, SECOND, &result); + ok = controller->read_numerical_tuple_value(base_with_slash, TupleValue::SECOND, &result); EXPECT_TRUE(ok) << "Should be OK to read the value"; EXPECT_EQ((jlong)10000, result) << "result value incorrect"; // non-max strings fill_file(test_file, "abc 10000"); result = -10; - ok = controller->read_numerical_tuple_value(base_with_slash, FIRST, &result); + ok = controller->read_numerical_tuple_value(base_with_slash, TupleValue::FIRST, &result); EXPECT_FALSE(ok) << "abc should not be parsable"; EXPECT_EQ((jlong)-10, result) << "result value should be unchanged"; fill_file(test_file, nullptr); result = -10; - ok = controller->read_numerical_tuple_value(base_with_slash, FIRST, &result); + ok = controller->read_numerical_tuple_value(base_with_slash, TupleValue::FIRST, &result); EXPECT_FALSE(ok) << "Empty file should be an error"; EXPECT_EQ((jlong)-10, result) << "result value should be unchanged"; } From d3e827f8d7acf0a810905b7128c697802eb6e5ea Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Wed, 22 May 2024 21:13:18 +0200 Subject: [PATCH 14/38] Get rid of the templated function --- .../os/linux/cgroupSubsystem_linux.cpp | 133 +++++++++--------- .../os/linux/cgroupSubsystem_linux.hpp | 14 +- .../os/linux/cgroupV1Subsystem_linux.cpp | 10 +- .../os/linux/cgroupV1Subsystem_linux.hpp | 2 +- .../os/linux/cgroupV2Subsystem_linux.cpp | 68 ++++----- .../os/linux/cgroupV2Subsystem_linux.hpp | 10 +- .../runtime/test_cgroupSubsystem_linux.cpp | 42 +++--- 7 files changed, 135 insertions(+), 144 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 095273e271794..d3c016608d0a1 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -556,18 +556,59 @@ jlong CgroupSubsystem::memory_limit_in_bytes() { return mem_limit; } -bool CgroupController::read_string(const char* filename, char** result) { - char res[1024]; - bool ok = read_from_file(filename, "%1023s", res); - if (!ok) { +bool CgroupController::read_string(const char* filename, char* buf) { + assert(buf != nullptr, "invariant"); + if (filename == nullptr) { + log_debug(os, container)("read_string: filename is null"); + return false; + } + char* s_path = subsystem_path(); + if (s_path == nullptr) { + log_debug(os, container)("read_string: subsystem path is null"); + return false; + } + + stringStream file_path; + file_path.print_raw(s_path); + file_path.print_raw(filename); + + if (file_path.size() > MAXPATHLEN) { + log_debug(os, container)("File path too long %s, %s", file_path.base(), filename); + return false; + } + const char* absolute_path = file_path.freeze(); + log_trace(os, container)("Path to %s is %s", filename, absolute_path); + + FILE* fp = os::fopen(absolute_path, "r"); + if (fp == nullptr) { + log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); return false; } - *result = os::strdup(res); + + char* line = fgets(buf, 1024, fp); + fclose(fp); + if (line == nullptr) { + log_debug(os, container)("Empty file %s", absolute_path); + return false; + } + int len = strlen(line); + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; // trim trailing new line + } return true; } bool CgroupController::read_number(const char* filename, julong* result) { - return read_from_file(filename, JULONG_FORMAT, result); + char buf[1024]; + bool is_ok = read_string(filename, buf); + if (!is_ok) { + return false; + } + int matched = sscanf(buf, JULONG_FORMAT, result); + if (matched == 1) { + return true; + } + return false; } bool CgroupController::read_numerical_key_value(const char* filename, const char* key, julong* result) { @@ -639,74 +680,33 @@ bool CgroupController::read_numerical_key_value(const char* filename, const char } bool CgroupController::read_numerical_tuple_value(const char* filename, TupleValue tup, jlong* result) { - char token[1024]; - bool is_ok = read_from_file(filename, tuple_format(tup), token); + char buf[1024]; + bool is_ok = read_string(filename, buf); if (!is_ok) { return false; } - char* t = os::strdup(token); - jlong val = CgroupSubsystem::limit_from_str(t); - if (val == OSCONTAINER_ERROR) { - return false; - } - *result = val; - return true; -} - -PRAGMA_DIAG_PUSH -PRAGMA_FORMAT_NONLITERAL_IGNORED // Only string/number literal formats used. See read_*() functions. -template -bool CgroupController::read_from_file(const char* filename, const char* scan_fmt, T result) { - assert(scan_fmt != nullptr, "invariant"); - if (filename == nullptr) { - log_debug(os, container)("read_from_file: filename is null"); - return false; - } - if (result == nullptr) { - log_debug(os, container)("read_from_file: return pointer is null"); - return false; - } - char* s_path = subsystem_path(); - if (s_path == nullptr) { - log_debug(os, container)("read_from_file: subsystem path is null"); - return false; + char token[1024]; + int matched = -1; + switch(tup) { + case TupleValue::FIRST: { + matched = sscanf(buf, "%1023s %*s", token); + break; + } + case TupleValue::SECOND: { + matched = sscanf(buf, "%*s %1023s", token); + break; + } } - - stringStream file_path; - file_path.print_raw(s_path); - file_path.print_raw(filename); - - if (file_path.size() > MAXPATHLEN) { - log_debug(os, container)("File path too long %s, %s", file_path.base(), filename); + if (matched != 1) { return false; } - const char* absolute_path = file_path.freeze(); - log_trace(os, container)("Path to %s is %s", filename, absolute_path); - - FILE* fp = os::fopen(absolute_path, "r"); - if (fp == nullptr) { - log_debug(os, container)("Open of file %s failed, %s", absolute_path, os::strerror(errno)); - return false; - } - - const int buf_len = MAXPATHLEN+1; - char buf[buf_len]; - char* line = fgets(buf, buf_len, fp); - fclose(fp); - if (line == nullptr) { - log_debug(os, container)("Empty file %s", absolute_path); + jlong val = CgroupSubsystem::limit_from_str(token); + if (val == OSCONTAINER_ERROR) { return false; } - - int matched = sscanf(line, scan_fmt, result); - if (matched == 1) { - return true; - } else { - log_debug(os, container)("Type %s not found in file %s", scan_fmt, absolute_path); - } - return false; + *result = val; + return true; } -PRAGMA_DIAG_POP jlong CgroupSubsystem::limit_from_str(char* limit_str) { if (limit_str == nullptr) { @@ -715,14 +715,11 @@ jlong CgroupSubsystem::limit_from_str(char* limit_str) { // Unlimited memory in cgroups is the literal string 'max' for // some controllers, for example the pids controller. if (strcmp("max", limit_str) == 0) { - os::free(limit_str); return (jlong)-1; } julong limit; if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) { - os::free(limit_str); return OSCONTAINER_ERROR; } - os::free(limit_str); return (jlong)limit; } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 99e1b99c1a83e..cffec361fb44a 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -83,7 +83,7 @@ #define CONTAINER_READ_STRING_CHECKED(controller, filename, log_string, retval) \ { \ bool is_ok; \ - is_ok = controller->read_string(filename, &retval); \ + is_ok = controller->read_string(filename, retval); \ if (!is_ok) { \ log_trace(os, container)(log_string " failed: %d", OSCONTAINER_ERROR); \ return nullptr; \ @@ -103,19 +103,9 @@ class CgroupController: public CHeapObj { public: virtual char *subsystem_path() = 0; bool read_number(const char* filename, julong* result); - bool read_string(const char* filename, char** result); + bool read_string(const char* filename, char* buf); bool read_numerical_tuple_value(const char* filename, TupleValue val, jlong* result); bool read_numerical_key_value(const char* filename, const char* key, julong* result); - private: - inline static const char* tuple_format(TupleValue val) { - switch(val) { - case TupleValue::FIRST: return "%1023s %*s"; - case TupleValue::SECOND: return "%*s %1023s"; - } - return nullptr; - } - template - bool read_from_file(const char* filename, const char* scan_fmt, T result); }; class CachedMetric : public CHeapObj{ diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 1495b59484435..03e3451215f29 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -359,10 +359,9 @@ int CgroupV1Subsystem::cpu_shares() { } -char* CgroupV1Subsystem::pids_max_val() { - char* pidsmax = nullptr; - CONTAINER_READ_STRING_CHECKED(_pids, "/pids.max", "Maximum number of tasks", pidsmax); - return pidsmax; +char* CgroupV1Subsystem::pids_max_val(char* buf) { + CONTAINER_READ_STRING_CHECKED(_pids, "/pids.max", "Maximum number of tasks", buf); + return buf; } /* pids_max @@ -376,7 +375,8 @@ char* CgroupV1Subsystem::pids_max_val() { */ jlong CgroupV1Subsystem::pids_max() { if (_pids == nullptr) return OSCONTAINER_ERROR; - char * pidsmax_str = pids_max_val(); + char buf[1024]; + char * pidsmax_str = pids_max_val(buf); return limit_from_str(pidsmax_str); } diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 3205973961a92..dd53969c6a3fe 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -114,7 +114,7 @@ class CgroupV1Subsystem: public CgroupSubsystem { CgroupV1Controller* _cpuacct = nullptr; CgroupV1Controller* _pids = nullptr; - char * pids_max_val(); + char * pids_max_val(char* buf); jlong read_mem_swappiness(); jlong read_mem_swap(); diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 9a2b997f1b2e6..b037fdea780ad 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -94,16 +94,16 @@ int CgroupV2Subsystem::cpu_quota() { return limit; } -char * CgroupV2Subsystem::cpu_cpuset_cpus() { - char* cpus = nullptr; +char* CgroupV2Subsystem::cpu_cpuset_cpus() { + char cpus[1024]; CONTAINER_READ_STRING_CHECKED(_unified, "/cpuset.cpus", "cpuset.cpus", cpus); - return cpus; + return os::strdup(cpus); } -char * CgroupV2Subsystem::cpu_cpuset_memory_nodes() { - char* mems; +char* CgroupV2Subsystem::cpu_cpuset_memory_nodes() { + char mems[1024]; CONTAINER_READ_STRING_CHECKED(_unified, "/cpuset.mems", "cpuset.mems", mems); - return mems; + return os::strdup(mems); } int CgroupV2Subsystem::cpu_period() { @@ -134,7 +134,8 @@ jlong CgroupV2Subsystem::memory_usage_in_bytes() { } jlong CgroupV2Subsystem::memory_soft_limit_in_bytes() { - char* mem_soft_limit_str = mem_soft_limit_val(); + char buf[1024]; + char* mem_soft_limit_str = mem_soft_limit_val(buf); return limit_from_str(mem_soft_limit_str); } @@ -166,10 +167,9 @@ jlong CgroupV2Subsystem::cache_usage_in_bytes() { return (jlong)cache; } -char* CgroupV2Subsystem::mem_soft_limit_val() { - char* mem_soft_limit_str; - CONTAINER_READ_STRING_CHECKED(_unified, "/memory.low", "Memory Soft Limit", mem_soft_limit_str); - return mem_soft_limit_str; +char* CgroupV2Subsystem::mem_soft_limit_val(char* buf) { + CONTAINER_READ_STRING_CHECKED(_unified, "/memory.low", "Memory Soft Limit", buf); + return buf; } // Note that for cgroups v2 the actual limits set for swap and @@ -178,7 +178,8 @@ char* CgroupV2Subsystem::mem_soft_limit_val() { // 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() { - char* mem_swp_limit_str = mem_swp_limit_val(); + char buf[1024]; + char* mem_swp_limit_str = mem_swp_limit_val(buf); if (mem_swp_limit_str == nullptr) { // Some container tests rely on this trace logging to happen. log_trace(os, container)("Memory and Swap Limit is: %d", OSCONTAINER_ERROR); @@ -198,24 +199,23 @@ jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() { jlong CgroupV2Subsystem::memory_and_swap_usage_in_bytes() { jlong memory_usage = memory_usage_in_bytes(); if (memory_usage >= 0) { - char* mem_swp_current_str = mem_swp_current_val(); + char buf[1024]; + char* mem_swp_current_str = mem_swp_current_val(buf); jlong swap_current = limit_from_str(mem_swp_current_str); return memory_usage + (swap_current >= 0 ? swap_current : 0); } return memory_usage; // not supported or unlimited case } -char* CgroupV2Subsystem::mem_swp_limit_val() { - char* mem_swp_limit_str; - CONTAINER_READ_STRING_CHECKED(_unified, "/memory.swap.max", "Swap Limit", mem_swp_limit_str); - return mem_swp_limit_str; +char* CgroupV2Subsystem::mem_swp_limit_val(char* buf) { + CONTAINER_READ_STRING_CHECKED(_unified, "/memory.swap.max", "Swap Limit", buf); + return buf; } // memory.swap.current : total amount of swap currently used by the cgroup and its descendants -char* CgroupV2Subsystem::mem_swp_current_val() { - char* mem_swp_current_str; - CONTAINER_READ_STRING_CHECKED(_unified, "/memory.swap.current", "Swap currently used", mem_swp_current_str); - return mem_swp_current_str; +char* CgroupV2Subsystem::mem_swp_current_val(char* buf) { + CONTAINER_READ_STRING_CHECKED(_unified, "/memory.swap.current", "Swap currently used", buf); + return buf; } /* memory_limit_in_bytes @@ -227,7 +227,8 @@ char* CgroupV2Subsystem::mem_swp_current_val() { * -1 for unlimited, OSCONTAINER_ERROR for an error */ jlong CgroupV2Subsystem::read_memory_limit_in_bytes() { - char * mem_limit_str = mem_limit_val(); + char buf[1024]; + char * mem_limit_str = mem_limit_val(buf); jlong limit = limit_from_str(mem_limit_str); if (log_is_enabled(Trace, os, container)) { if (limit == -1) { @@ -239,17 +240,18 @@ jlong CgroupV2Subsystem::read_memory_limit_in_bytes() { return limit; } -char* CgroupV2Subsystem::mem_limit_val() { - char* mem_limit_str; - CONTAINER_READ_STRING_CHECKED(_unified, "/memory.max", "Raw value for memory limit", mem_limit_str); - return mem_limit_str; +char* CgroupV2Subsystem::mem_limit_val(char* buf) { + CONTAINER_READ_STRING_CHECKED(_unified, "/memory.max", "Raw value for memory limit", buf); + return buf; } void CgroupV2Subsystem::print_version_specific_info(outputStream* st) { - char* mem_swp_current_str = mem_swp_current_val(); + char mem_swp_buf[1024]; + char* mem_swp_current_str = mem_swp_current_val(mem_swp_buf); jlong swap_current = limit_from_str(mem_swp_current_str); - char* mem_swp_limit_str = mem_swp_limit_val(); + char mem_swp_limit_buf[1024]; + char* mem_swp_limit_str = mem_swp_limit_val(mem_swp_limit_buf); jlong swap_limit = limit_from_str(mem_swp_limit_str); OSContainer::print_container_helper(st, swap_current, "memory_swap_current_in_bytes"); @@ -265,10 +267,9 @@ char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { return os::strdup(ss.base()); } -char* CgroupV2Subsystem::pids_max_val() { - char* pidsmax; - CONTAINER_READ_STRING_CHECKED(_unified, "/pids.max", "Maximum number of tasks", pidsmax); - return pidsmax; +char* CgroupV2Subsystem::pids_max_val(char* buf) { + CONTAINER_READ_STRING_CHECKED(_unified, "/pids.max", "Maximum number of tasks", buf); + return buf; } /* pids_max @@ -281,7 +282,8 @@ char* CgroupV2Subsystem::pids_max_val() { * OSCONTAINER_ERROR for not supported */ jlong CgroupV2Subsystem::pids_max() { - char * pidsmax_str = pids_max_val(); + char buf[1024]; + char* pidsmax_str = pids_max_val(buf); return limit_from_str(pidsmax_str); } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 1fd5dd9cf845a..d2134b18a8240 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -56,11 +56,11 @@ class CgroupV2Subsystem: public CgroupSubsystem { CachingCgroupController* _memory = nullptr; CachingCgroupController* _cpu = nullptr; - char *mem_limit_val(); - char *mem_swp_limit_val(); - char *mem_swp_current_val(); - char *mem_soft_limit_val(); - char *pids_max_val(); + char *mem_limit_val(char* buf); + char *mem_swp_limit_val(char* buf); + char *mem_swp_current_val(char* buf); + char *mem_soft_limit_val(char* buf); + char *pids_max_val(char* buf); public: CgroupV2Subsystem(CgroupController * unified) { diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index 04f49003999c7..692fec2191c5a 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -193,10 +193,11 @@ TEST(cgroupTest, read_string_beyond_max_path) { larger_than_max[MAXPATHLEN] = '\0'; TestController* too_large_path_controller = new TestController(larger_than_max); const char* test_file_path = "/file-not-found"; - char* foo = nullptr; - bool is_ok = too_large_path_controller->read_string(test_file_path, &foo); + char foo[1024]; + foo[0] = '\0'; + bool is_ok = too_large_path_controller->read_string(test_file_path, foo); EXPECT_FALSE(is_ok) << "Too long path should be an error"; - EXPECT_TRUE(nullptr == foo) << "Expected untouched scan value"; + EXPECT_STREQ("", foo) << "Expected untouched scan value"; } TEST(cgroupTest, read_number_file_not_exist) { @@ -271,35 +272,36 @@ TEST(cgroupTest, read_string_tests) { fill_file(test_file, "foo-bar"); TestController* controller = new TestController((char*)os::get_temp_directory()); - char* result = nullptr; - bool ok = controller->read_string(base_with_slash, &result); + char result[1024]; + bool ok = controller->read_string(base_with_slash, result); EXPECT_TRUE(ok) << "String parsing should have been successful"; - EXPECT_TRUE(result != nullptr) << "Expected non-null result"; EXPECT_STREQ("foo-bar", result) << "Expected strings to be equal"; - os::free(result); - result = nullptr; + result[0] = '\0'; fill_file(test_file, "1234"); - ok = controller->read_string(base_with_slash, &result); + ok = controller->read_string(base_with_slash, result); EXPECT_TRUE(ok) << "String parsing should have been successful"; - EXPECT_TRUE(result != nullptr) << "Expected non-null result"; EXPECT_STREQ("1234", result) << "Expected strings to be equal"; - os::free(result); - // values with a space only read in the first token - result = nullptr; + // values with a space + result[0] = '\0'; fill_file(test_file, "abc def"); - ok = controller->read_string(base_with_slash, &result); + ok = controller->read_string(base_with_slash, result); EXPECT_TRUE(ok) << "String parsing should have been successful"; - EXPECT_TRUE(result != nullptr) << "Expected non-null result"; - EXPECT_STREQ("abc", result) << "Expected strings to be equal"; - os::free(result); + EXPECT_STREQ("abc def", result) << "Expected strings to be equal"; - result = nullptr; + // only the first line are being returned + result[0] = '\0'; + fill_file(test_file, "test\nabc"); + ok = controller->read_string(base_with_slash, result); + EXPECT_TRUE(ok) << "String parsing should have been successful"; + EXPECT_STREQ("test", result) << "Expected strings to be equal"; + + result[0] = '\0'; fill_file(test_file, nullptr); - ok = controller->read_string(base_with_slash, &result); + ok = controller->read_string(base_with_slash, result); EXPECT_FALSE(ok) << "Empty file should have failed"; - EXPECT_TRUE(result == nullptr) << "Expected untouched result"; + EXPECT_STREQ("", result) << "Expected untouched result"; delete_file(test_file); } From 808ec1095cbc0d643481150d92333c7d7ffe8b30 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Thu, 23 May 2024 10:56:46 +0200 Subject: [PATCH 15/38] Fix general read string cases. --- src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 03e3451215f29..d2962e9ddb378 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -295,16 +295,16 @@ void CgroupV1Subsystem::print_version_specific_info(outputStream* st) { OSContainer::print_container_helper(st, kmem_max_usage, "kernel_memory_limit_in_bytes"); } -char * CgroupV1Subsystem::cpu_cpuset_cpus() { - char* cpus = nullptr; +char* CgroupV1Subsystem::cpu_cpuset_cpus() { + char cpus[1024]; CONTAINER_READ_STRING_CHECKED(_cpuset, "/cpuset.cpus", "cpuset.cpus", cpus); - return cpus; + return os::strdup(cpus); } -char * CgroupV1Subsystem::cpu_cpuset_memory_nodes() { - char* mems = nullptr; +char* CgroupV1Subsystem::cpu_cpuset_memory_nodes() { + char mems[1024]; CONTAINER_READ_STRING_CHECKED(_cpuset, "/cpuset.mems", "cpuset.mems", mems); - return mems; + return os::strdup(mems); } /* cpu_quota From b4fcf1d71309c9f397199cd4286c81acd6efaf07 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Thu, 23 May 2024 14:25:18 +0200 Subject: [PATCH 16/38] Add convenience function for 'max' handling This now makes limit_from_str a private method --- .../os/linux/cgroupSubsystem_linux.cpp | 18 ++++- .../os/linux/cgroupSubsystem_linux.hpp | 24 +++++- .../os/linux/cgroupV1Subsystem_linux.cpp | 12 +-- .../os/linux/cgroupV1Subsystem_linux.hpp | 2 - .../os/linux/cgroupV2Subsystem_linux.cpp | 78 ++++++------------- .../os/linux/cgroupV2Subsystem_linux.hpp | 7 +- .../runtime/test_cgroupSubsystem_linux.cpp | 20 +++++ 7 files changed, 89 insertions(+), 72 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index d3c016608d0a1..f09d880f728f8 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -611,6 +611,20 @@ bool CgroupController::read_number(const char* filename, julong* result) { return false; } +bool CgroupController::read_number_handle_max(const char* filename, jlong* result) { + char buf[1024]; + bool is_ok = read_string(filename, buf); + if (!is_ok) { + return false; + } + jlong val = limit_from_str(buf); + if (val == OSCONTAINER_ERROR) { + return false; + } + *result = val; + return true; +} + bool CgroupController::read_numerical_key_value(const char* filename, const char* key, julong* result) { if (filename == nullptr) { log_debug(os, container)("read_numerical_key_value: filename is null"); @@ -700,7 +714,7 @@ bool CgroupController::read_numerical_tuple_value(const char* filename, TupleVal if (matched != 1) { return false; } - jlong val = CgroupSubsystem::limit_from_str(token); + jlong val = limit_from_str(token); if (val == OSCONTAINER_ERROR) { return false; } @@ -708,7 +722,7 @@ bool CgroupController::read_numerical_tuple_value(const char* filename, TupleVal return true; } -jlong CgroupSubsystem::limit_from_str(char* limit_str) { +jlong CgroupController::limit_from_str(char* limit_str) { if (limit_str == nullptr) { return OSCONTAINER_ERROR; } diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index cffec361fb44a..db2c6a0b5f2f4 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -80,6 +80,17 @@ log_trace(os, container)(log_string " is: " JULONG_FORMAT, retval); \ } +#define CONTAINER_READ_NUMBER_CHECKED_MAX(controller, filename, log_string, retval) \ +{ \ + bool is_ok; \ + is_ok = controller->read_number_handle_max(filename, &retval); \ + if (!is_ok) { \ + log_trace(os, container)(log_string " failed: %d", OSCONTAINER_ERROR); \ + return OSCONTAINER_ERROR; \ + } \ + log_trace(os, container)(log_string " is: " JLONG_FORMAT, retval); \ +} + #define CONTAINER_READ_STRING_CHECKED(controller, filename, log_string, retval) \ { \ bool is_ok; \ @@ -102,10 +113,22 @@ enum class TupleValue { FIRST, SECOND }; class CgroupController: public CHeapObj { public: virtual char *subsystem_path() = 0; + // Read a numerical value as unsigned long bool read_number(const char* filename, julong* result); + // Convenience method to deal with numbers as well as the string 'max' + // in interface files. Otherwise same as read_number(). + bool read_number_handle_max(const char* filename, jlong* result); + // Read a string from the interface file. The provided buffer must be + // at least 1K (1024) in size. This is something the caller needs to ensure. bool read_string(const char* filename, char* buf); + // Read a tuple value as a number. Tuple is: ' '. + // Handles 'max' for unlimited. bool read_numerical_tuple_value(const char* filename, TupleValue val, jlong* result); + // Read a numerical value from a multi-line interface file. The matched line is + // determined by the provided 'key'. The associated value is returned as a number. bool read_numerical_key_value(const char* filename, const char* key, julong* result); + private: + static jlong limit_from_str(char* limit_str); }; class CachedMetric : public CHeapObj{ @@ -151,7 +174,6 @@ class CgroupSubsystem: public CHeapObj { public: jlong memory_limit_in_bytes(); int active_processor_count(); - static jlong limit_from_str(char* limit_str); virtual int cpu_quota() = 0; virtual int cpu_period() = 0; diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index d2962e9ddb378..214bfd882069c 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -358,12 +358,6 @@ int CgroupV1Subsystem::cpu_shares() { return shares_int; } - -char* CgroupV1Subsystem::pids_max_val(char* buf) { - CONTAINER_READ_STRING_CHECKED(_pids, "/pids.max", "Maximum number of tasks", buf); - return buf; -} - /* pids_max * * Return the maximum number of tasks available to the process @@ -375,9 +369,9 @@ char* CgroupV1Subsystem::pids_max_val(char* buf) { */ jlong CgroupV1Subsystem::pids_max() { if (_pids == nullptr) return OSCONTAINER_ERROR; - char buf[1024]; - char * pidsmax_str = pids_max_val(buf); - return limit_from_str(pidsmax_str); + jlong pids_max; + CONTAINER_READ_NUMBER_CHECKED_MAX(_pids, "/pids.max", "Maximum number of tasks", pids_max); + return pids_max; } /* pids_current diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index dd53969c6a3fe..254b17de0ba8c 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -114,8 +114,6 @@ class CgroupV1Subsystem: public CgroupSubsystem { CgroupV1Controller* _cpuacct = nullptr; CgroupV1Controller* _pids = nullptr; - char * pids_max_val(char* buf); - jlong read_mem_swappiness(); jlong read_mem_swap(); diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index b037fdea780ad..d672105f16dd9 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -134,9 +134,9 @@ jlong CgroupV2Subsystem::memory_usage_in_bytes() { } jlong CgroupV2Subsystem::memory_soft_limit_in_bytes() { - char buf[1024]; - char* mem_soft_limit_str = mem_soft_limit_val(buf); - return limit_from_str(mem_soft_limit_str); + jlong mem_soft_limit; + CONTAINER_READ_NUMBER_CHECKED_MAX(_unified, "/memory.low", "Memory Soft Limit", mem_soft_limit); + return mem_soft_limit; } jlong CgroupV2Subsystem::memory_max_usage_in_bytes() { @@ -167,26 +167,21 @@ jlong CgroupV2Subsystem::cache_usage_in_bytes() { return (jlong)cache; } -char* CgroupV2Subsystem::mem_soft_limit_val(char* buf) { - CONTAINER_READ_STRING_CHECKED(_unified, "/memory.low", "Memory Soft Limit", buf); - return buf; -} - // Note that for cgroups v2 the actual limits set for swap and // memory live in two different files, memory.swap.max and memory.max // 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() { - char buf[1024]; - char* mem_swp_limit_str = mem_swp_limit_val(buf); - if (mem_swp_limit_str == nullptr) { + jlong swap_limit; + bool is_ok = _memory->controller()->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)("Memory and Swap Limit is: %d", OSCONTAINER_ERROR); + 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(); } - jlong swap_limit = limit_from_str(mem_swp_limit_str); + log_trace(os, container)("Swap Limit is: " JLONG_FORMAT, swap_limit); if (swap_limit >= 0) { jlong memory_limit = read_memory_limit_in_bytes(); assert(memory_limit >= 0, "swap limit without memory limit?"); @@ -199,23 +194,23 @@ jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() { jlong CgroupV2Subsystem::memory_and_swap_usage_in_bytes() { jlong memory_usage = memory_usage_in_bytes(); if (memory_usage >= 0) { - char buf[1024]; - char* mem_swp_current_str = mem_swp_current_val(buf); - jlong swap_current = limit_from_str(mem_swp_current_str); + jlong swap_current = mem_swp_current_val(); return memory_usage + (swap_current >= 0 ? swap_current : 0); } return memory_usage; // not supported or unlimited case } -char* CgroupV2Subsystem::mem_swp_limit_val(char* buf) { - CONTAINER_READ_STRING_CHECKED(_unified, "/memory.swap.max", "Swap Limit", buf); - return buf; +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; } // memory.swap.current : total amount of swap currently used by the cgroup and its descendants -char* CgroupV2Subsystem::mem_swp_current_val(char* buf) { - CONTAINER_READ_STRING_CHECKED(_unified, "/memory.swap.current", "Swap currently used", buf); - return buf; +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; } /* memory_limit_in_bytes @@ -227,32 +222,14 @@ char* CgroupV2Subsystem::mem_swp_current_val(char* buf) { * -1 for unlimited, OSCONTAINER_ERROR for an error */ jlong CgroupV2Subsystem::read_memory_limit_in_bytes() { - char buf[1024]; - char * mem_limit_str = mem_limit_val(buf); - jlong limit = limit_from_str(mem_limit_str); - 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); - } - } - return limit; -} - -char* CgroupV2Subsystem::mem_limit_val(char* buf) { - CONTAINER_READ_STRING_CHECKED(_unified, "/memory.max", "Raw value for memory limit", buf); - return buf; + jlong memory_limit; + CONTAINER_READ_NUMBER_CHECKED_MAX(_unified, "/memory.max", "Memory Limit", memory_limit); + return memory_limit; } void CgroupV2Subsystem::print_version_specific_info(outputStream* st) { - char mem_swp_buf[1024]; - char* mem_swp_current_str = mem_swp_current_val(mem_swp_buf); - jlong swap_current = limit_from_str(mem_swp_current_str); - - char mem_swp_limit_buf[1024]; - char* mem_swp_limit_str = mem_swp_limit_val(mem_swp_limit_buf); - jlong swap_limit = limit_from_str(mem_swp_limit_str); + jlong swap_current = mem_swp_current_val(); + jlong swap_limit = mem_swp_limit_val(); 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"); @@ -267,11 +244,6 @@ char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { return os::strdup(ss.base()); } -char* CgroupV2Subsystem::pids_max_val(char* buf) { - CONTAINER_READ_STRING_CHECKED(_unified, "/pids.max", "Maximum number of tasks", buf); - return buf; -} - /* pids_max * * Return the maximum number of tasks available to the process @@ -282,9 +254,9 @@ char* CgroupV2Subsystem::pids_max_val(char* buf) { * OSCONTAINER_ERROR for not supported */ jlong CgroupV2Subsystem::pids_max() { - char buf[1024]; - char* pidsmax_str = pids_max_val(buf); - return limit_from_str(pidsmax_str); + jlong pids_max; + CONTAINER_READ_NUMBER_CHECKED_MAX(_unified, "/pids.max", "Maximum number of tasks", pids_max); + return pids_max; } /* pids_current diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index d2134b18a8240..a004c8f75b984 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -56,11 +56,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { CachingCgroupController* _memory = nullptr; CachingCgroupController* _cpu = nullptr; - char *mem_limit_val(char* buf); - char *mem_swp_limit_val(char* buf); - char *mem_swp_current_val(char* buf); - char *mem_soft_limit_val(char* buf); - char *pids_max_val(char* buf); + jlong mem_swp_limit_val(); + jlong mem_swp_current_val(); public: CgroupV2Subsystem(CgroupController * unified) { diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index 692fec2191c5a..71f207e686d57 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -258,6 +258,26 @@ TEST(cgroupTest, read_number_tests) { EXPECT_FALSE(ok) << "Empty file should have failed"; EXPECT_EQ((julong)0xBAD, foo) << "foo was altered"; + // Some interface files have numbers as well as the string + // 'max', which means unlimited. + jlong result = -10; + fill_file(test_file, "max\n"); + ok = controller->read_number_handle_max(base_with_slash, &result); + EXPECT_TRUE(ok) << "Number parsing for 'max' string should have been successful"; + EXPECT_EQ((jlong)-1, result) << "'max' means unlimited (-1)"; + + result = -10; + fill_file(test_file, "11114\n"); + ok = controller->read_number_handle_max(base_with_slash, &result); + EXPECT_TRUE(ok) << "Number parsing for should have been successful"; + EXPECT_EQ((jlong)11114, result) << "Incorrect result"; + + result = -10; + fill_file(test_file, "-51114\n"); + ok = controller->read_number_handle_max(base_with_slash, &result); + EXPECT_TRUE(ok) << "Number parsing for should have been successful"; + EXPECT_EQ((jlong)-51114, result) << "Incorrect result"; + delete_file(test_file); } From 328390d1019ec2ad1aff2dcd0c12edf4c6bb84a7 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Thu, 23 May 2024 14:48:23 +0200 Subject: [PATCH 17/38] Add a test for a large string read --- src/hotspot/os/linux/cgroupSubsystem_linux.cpp | 2 ++ .../gtest/runtime/test_cgroupSubsystem_linux.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index f09d880f728f8..1855850c41d98 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -585,6 +585,7 @@ bool CgroupController::read_string(const char* filename, char* buf) { return false; } + // Read a single line into the provided buffer. At most 1023 characters. char* line = fgets(buf, 1024, fp); fclose(fp); if (line == nullptr) { @@ -592,6 +593,7 @@ bool CgroupController::read_string(const char* filename, char* buf) { return false; } int len = strlen(line); + assert(len <= 1023, "At most 1023 bytes can be read"); if (line[len - 1] == '\n') { line[len - 1] = '\0'; // trim trailing new line } diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index 71f207e686d57..4586e322b2c72 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -323,6 +323,22 @@ TEST(cgroupTest, read_string_tests) { EXPECT_FALSE(ok) << "Empty file should have failed"; EXPECT_STREQ("", result) << "Expected untouched result"; delete_file(test_file); + + // File contents larger than 1K + // We only read in the first 1K - 1 bytes + char too_large[2 * 1024]; + for (int i = 0; i < (2 * 1024); i++) { + too_large[i] = 'A' + (i % 26); + } + result[0] = '\0'; + fill_file(test_file, too_large); + ok = controller->read_string(base_with_slash, result); + EXPECT_TRUE(ok) << "String parsing should have been successful"; + EXPECT_TRUE(1023 == strlen(result)) << "Expected only the first 1023 chars to be read in"; + for (int i = 0; i < 1023; i++) { + EXPECT_EQ(too_large[i], result[i]) << "Expected item at idx " << i << " to match"; + } + EXPECT_EQ(result[1023], '\0') << "The last character must be the null character"; } TEST(cgroupTest, read_number_tuple_test) { From c41d31831f52bf553c18009e3a2c7ed28cfe520d Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Thu, 23 May 2024 15:26:34 +0200 Subject: [PATCH 18/38] Add proper comments for parsing utility functions --- .../os/linux/cgroupSubsystem_linux.hpp | 50 +++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index db2c6a0b5f2f4..3d619a097d785 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -113,20 +113,52 @@ enum class TupleValue { FIRST, SECOND }; class CgroupController: public CHeapObj { public: virtual char *subsystem_path() = 0; - // Read a numerical value as unsigned long + + /* Read a numerical value as unsigned long + * + * returns: false if any error occurred. true otherwise and + * the parsed value is set in the provided julong pointer. + */ bool read_number(const char* filename, julong* result); - // Convenience method to deal with numbers as well as the string 'max' - // in interface files. Otherwise same as read_number(). + + /* Convenience method to deal with numbers as well as the string 'max' + * in interface files. Otherwise same as read_number(). + * + * returns: false if any error occurred. true otherwise and + * the parsed value (which might be negative) is being set in + * the provided jlong pointer. + */ bool read_number_handle_max(const char* filename, jlong* result); - // Read a string from the interface file. The provided buffer must be - // at least 1K (1024) in size. This is something the caller needs to ensure. + + /* Read a string of at most 1K - 1 characters from the interface file. + * The provided buffer must be at least 1K (1024) in size so as to account + * for the null terminating character. Callers must ensure that the buffer + * is appropriately in-scope and of sufficient size. + * + * returns: false if any error occured. true otherwise and the passed + * in buffer will contain the first 1023 characters of the string or + * up to the first new line character ('\n') whichever comes first. + */ bool read_string(const char* filename, char* buf); - // Read a tuple value as a number. Tuple is: ' '. - // Handles 'max' for unlimited. + + /* Read a tuple value as a number. Tuple is: ' '. + * Handles 'max' (for unlimited) for any tuple value. This is handy for + * parsing interface files like cpu.max which contain such tuples. + * + * returns: false if any error occurred. true otherwise and the parsed + * value of the appropriate tuple entry set in the provided jlong pointer. + */ bool read_numerical_tuple_value(const char* filename, TupleValue val, jlong* result); - // Read a numerical value from a multi-line interface file. The matched line is - // determined by the provided 'key'. The associated value is returned as a number. + + /* Read a numerical value from a multi-line interface file. The matched line is + * determined by the provided 'key'. The associated numerical value is being set + * via the passed in julong pointer. Example interface file 'memory.stat' + * + * returns: false if any error occurred. true otherwise and the parsed value is + * being set in the provided julong pointer. + */ bool read_numerical_key_value(const char* filename, const char* key, julong* result); + private: static jlong limit_from_str(char* limit_str); }; From 9477fd210ff577a012fec58d6d23b895b2d2aeb9 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Thu, 23 May 2024 19:24:23 +0200 Subject: [PATCH 19/38] Remove limit_from_str from util class --- src/hotspot/os/linux/cgroupUtil_linux.cpp | 19 ------------------- src/hotspot/os/linux/cgroupUtil_linux.hpp | 1 - 2 files changed, 20 deletions(-) diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index 8ccb3e8177fd6..24046991905ee 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -46,22 +46,3 @@ int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) { log_trace(os, container)("OSContainer::active_processor_count: %d", result); return result; } - -jlong CgroupUtil::limit_from_str(char* limit_str) { - if (limit_str == nullptr) { - return OSCONTAINER_ERROR; - } - // Unlimited memory in cgroups is the literal string 'max' for - // some controllers, for example the pids controller. - if (strcmp("max", limit_str) == 0) { - os::free(limit_str); - return (jlong)-1; - } - julong limit; - if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) { - os::free(limit_str); - return OSCONTAINER_ERROR; - } - os::free(limit_str); - return (jlong)limit; -} diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp index 6434242c92999..fdcc4806c3b93 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.hpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -32,7 +32,6 @@ class CgroupUtil: AllStatic { public: static int processor_count(CgroupCpuController* cpu, int host_cpus); - static jlong limit_from_str(char* limit_str); }; #endif // CGROUP_UTIL_LINUX_HPP From b085574f237f2b72cd17ffd2eb183f32ec6deb48 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Mon, 27 May 2024 18:58:51 +0200 Subject: [PATCH 20/38] Resolve ambiguity with static_cast --- src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index d9c9910473918..78423a9a74b5b 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -285,7 +285,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(static_cast(_unified), "/pids.max", "Maximum number of tasks", pids_max); return pids_max; } From 93c52ec1924e94f0c02b207c32e175ce8da9986d Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Wed, 29 May 2024 13:46:42 +0200 Subject: [PATCH 21/38] Fixup after merge --- src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 5652b740a3e45..728fd1e21550f 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -160,9 +160,9 @@ jlong CgroupV1MemoryController::read_mem_swap(julong host_total_memsw) { log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited"); 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 = v1_controller->read_numerical_key_value("/memory.stat", + matchline, + &hier_memswlimit); if (!is_ok) { return OSCONTAINER_ERROR; } From 8b97280c5e4558355a1edb5ac414bf5e37607a1e Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Wed, 29 May 2024 15:50:34 +0200 Subject: [PATCH 22/38] Fix inheritance hierarchy --- .../os/linux/cgroupSubsystem_linux.hpp | 9 ++-- .../os/linux/cgroupV1Subsystem_linux.cpp | 42 ++++++++----------- .../os/linux/cgroupV2Subsystem_linux.cpp | 36 +++++++--------- 3 files changed, 38 insertions(+), 49 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index f9513a7f17e5f..716fe10318a8f 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -195,15 +195,16 @@ class CachingCgroupController : public CHeapObj { T controller() { return _controller; } }; -class CgroupCpuController: public CgroupController { +// Pure virtual class representing version agnostic CPU controllers +class CgroupCpuController { public: virtual int cpu_quota() = 0; virtual int cpu_period() = 0; virtual int cpu_shares() = 0; - virtual char *subsystem_path() = 0; }; -class CgroupMemoryController: public CgroupController { +// Pure virtual class representing version agnostic memory controllers +class CgroupMemoryController { public: virtual jlong read_memory_limit_in_bytes(julong upper_bound) = 0; virtual jlong memory_usage_in_bytes() = 0; @@ -213,10 +214,8 @@ class CgroupMemoryController: public CgroupController { virtual jlong memory_max_usage_in_bytes() = 0; virtual jlong rss_usage_in_bytes() = 0; virtual jlong cache_usage_in_bytes() = 0; - virtual char *subsystem_path() = 0; }; - class CgroupSubsystem: public CHeapObj { public: jlong memory_limit_in_bytes(); diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 728fd1e21550f..82435d0005208 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -77,7 +77,7 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { */ jlong CgroupV1MemoryController::uses_mem_hierarchy() { julong use_hierarchy; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.use_hierarchy", "Use Hierarchy", use_hierarchy); + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.use_hierarchy", "Use Hierarchy", use_hierarchy); return (jlong)use_hierarchy; } @@ -113,13 +113,12 @@ void do_trace_log(julong read_mem_limit, julong host_mem) { jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { julong memlimit; - CgroupV1Controller* v1_controller = static_cast(this); - CONTAINER_READ_NUMBER_CHECKED(v1_controller, "/memory.limit_in_bytes", "Memory Limit", memlimit); + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.limit_in_bytes", "Memory Limit", memlimit); if (memlimit >= phys_mem) { log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); if (is_hierarchical()) { julong hier_memlimit; - bool is_ok = v1_controller->read_numerical_key_value("/memory.stat", "hierarchical_memory_limit", &hier_memlimit); + bool is_ok = read_numerical_key_value("/memory.stat", "hierarchical_memory_limit", &hier_memlimit); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -154,13 +153,12 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { jlong CgroupV1MemoryController::read_mem_swap(julong host_total_memsw) { julong hier_memswlimit; julong memswlimit; - CgroupV1Controller* v1_controller = static_cast(this); - CONTAINER_READ_NUMBER_CHECKED(v1_controller, "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", memswlimit); + CONTAINER_READ_NUMBER_CHECKED(this, "/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"); if (is_hierarchical()) { const char* matchline = "hierarchical_memsw_limit"; - bool is_ok = v1_controller->read_numerical_key_value("/memory.stat", + bool is_ok = read_numerical_key_value("/memory.stat", matchline, &hier_memswlimit); if (!is_ok) { @@ -213,7 +211,7 @@ jlong CgroupV1MemoryController::memory_and_swap_usage_in_bytes(julong 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(static_cast(this)); + return memory_swap_usage_impl(this); } } return memory_usage_in_bytes(); @@ -221,13 +219,13 @@ jlong CgroupV1MemoryController::memory_and_swap_usage_in_bytes(julong phys_mem, jlong CgroupV1MemoryController::read_mem_swappiness() { julong swappiness; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.swappiness", "Swappiness", swappiness); + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.swappiness", "Swappiness", swappiness); return (jlong)swappiness; } jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { julong memsoftlimit; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.soft_limit_in_bytes", "Memory Soft Limit", memsoftlimit); + CONTAINER_READ_NUMBER_CHECKED(this, "/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; @@ -247,7 +245,7 @@ jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { */ jlong CgroupV1MemoryController::memory_usage_in_bytes() { julong memusage; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.usage_in_bytes", "Memory Usage", memusage); + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.usage_in_bytes", "Memory Usage", memusage); return (jlong)memusage; } @@ -261,15 +259,13 @@ jlong CgroupV1MemoryController::memory_usage_in_bytes() { */ jlong CgroupV1MemoryController::memory_max_usage_in_bytes() { julong memmaxusage; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.max_usage_in_bytes", "Maximum Memory Usage", memmaxusage); + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.max_usage_in_bytes", "Maximum Memory Usage", memmaxusage); return (jlong)memmaxusage; } jlong CgroupV1MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = static_cast(this)->read_numerical_key_value("/memory.stat", - "rss", - &rss); + bool is_ok = read_numerical_key_value("/memory.stat", "rss", &rss); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -279,9 +275,7 @@ jlong CgroupV1MemoryController::rss_usage_in_bytes() { jlong CgroupV1MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = static_cast(this)->read_numerical_key_value("/memory.stat", - "cache", - &cache); + bool is_ok = read_numerical_key_value("/memory.stat", "cache", &cache); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -291,13 +285,13 @@ jlong CgroupV1MemoryController::cache_usage_in_bytes() { jlong CgroupV1MemoryController::kernel_memory_usage_in_bytes() { julong kmem_usage; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.kmem.usage_in_bytes", "Kernel Memory Usage", kmem_usage); + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.kmem.usage_in_bytes", "Kernel Memory Usage", kmem_usage); return (jlong)kmem_usage; } jlong CgroupV1MemoryController::kernel_memory_limit_in_bytes(julong phys_mem) { julong kmem_limit; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", kmem_limit); + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", kmem_limit); if (kmem_limit >= phys_mem) { return (jlong)-1; } @@ -306,7 +300,7 @@ jlong CgroupV1MemoryController::kernel_memory_limit_in_bytes(julong phys_mem) { jlong CgroupV1MemoryController::kernel_memory_max_usage_in_bytes() { julong kmem_max_usage; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.kmem.max_usage_in_bytes", "Maximum Kernel Memory Usage", kmem_max_usage); + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.kmem.max_usage_in_bytes", "Maximum Kernel Memory Usage", kmem_max_usage); return (jlong)kmem_max_usage; } @@ -346,7 +340,7 @@ char* CgroupV1Subsystem::cpu_cpuset_memory_nodes() { */ int CgroupV1CpuController::cpu_quota() { julong quota; - bool is_ok = static_cast(this)->read_number("/cpu.cfs_quota_us", "a); + bool is_ok = read_number("/cpu.cfs_quota_us", "a); if (!is_ok) { log_trace(os, container)("CPU Quota failed: %d", OSCONTAINER_ERROR); return OSCONTAINER_ERROR; @@ -360,7 +354,7 @@ int CgroupV1CpuController::cpu_quota() { int CgroupV1CpuController::cpu_period() { julong period; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/cpu.cfs_period_us", "CPU Period", period); + CONTAINER_READ_NUMBER_CHECKED(this, "/cpu.cfs_period_us", "CPU Period", period); return (int)period; } @@ -376,7 +370,7 @@ int CgroupV1CpuController::cpu_period() { */ int CgroupV1CpuController::cpu_shares() { julong shares; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/cpu.shares", "CPU Shares", shares); + CONTAINER_READ_NUMBER_CHECKED(this, "/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/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 48ef13a7247f7..bacc9de92208b 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -37,7 +37,7 @@ */ int CgroupV2CpuController::cpu_shares() { julong shares; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/cpu.weight", "Raw value for CPU Shares", shares); + CONTAINER_READ_NUMBER_CHECKED(this, "/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) { @@ -86,7 +86,7 @@ int CgroupV2CpuController::cpu_shares() { */ int CgroupV2CpuController::cpu_quota() { jlong quota_val; - bool is_ok = static_cast(this)->read_numerical_tuple_value("/cpu.max", true /* use_first */, "a_val); + bool is_ok = read_numerical_tuple_value("/cpu.max", true /* use_first */, "a_val); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -97,19 +97,19 @@ int CgroupV2CpuController::cpu_quota() { char* CgroupV2Subsystem::cpu_cpuset_cpus() { char cpus[1024]; - CONTAINER_READ_STRING_CHECKED(static_cast(_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(static_cast(_unified), "/cpuset.mems", "cpuset.mems", mems, 1024); + CONTAINER_READ_STRING_CHECKED(_unified, "/cpuset.mems", "cpuset.mems", mems, 1024); return os::strdup(mems); } int CgroupV2CpuController::cpu_period() { jlong period_val; - bool is_ok = static_cast(this)->read_numerical_tuple_value("/cpu.max", false /* use_first */, &period_val); + bool is_ok = 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; @@ -130,13 +130,13 @@ int CgroupV2CpuController::cpu_period() { */ jlong CgroupV2MemoryController::memory_usage_in_bytes() { julong memusage; - CONTAINER_READ_NUMBER_CHECKED(static_cast(this), "/memory.current", "Memory Usage", memusage); + CONTAINER_READ_NUMBER_CHECKED(this, "/memory.current", "Memory Usage", memusage); return (jlong)memusage; } jlong CgroupV2MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { jlong mem_soft_limit; - CONTAINER_READ_NUMBER_CHECKED_MAX(static_cast(this), "/memory.low", "Memory Soft Limit", mem_soft_limit); + CONTAINER_READ_NUMBER_CHECKED_MAX(this, "/memory.low", "Memory Soft Limit", mem_soft_limit); return mem_soft_limit; } @@ -148,9 +148,7 @@ jlong CgroupV2MemoryController::memory_max_usage_in_bytes() { jlong CgroupV2MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = static_cast(this)->read_numerical_key_value("/memory.stat", - "anon", - &rss); + bool is_ok = read_numerical_key_value("/memory.stat", "anon", &rss); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -160,9 +158,7 @@ jlong CgroupV2MemoryController::rss_usage_in_bytes() { jlong CgroupV2MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = static_cast(this)->read_numerical_key_value("/memory.stat", - "file", - &cache); + bool is_ok = read_numerical_key_value("/memory.stat", "file", &cache); if (!is_ok) { return OSCONTAINER_ERROR; } @@ -177,7 +173,7 @@ jlong CgroupV2MemoryController::cache_usage_in_bytes() { // without also setting a memory limit is not allowed. jlong CgroupV2MemoryController::memory_and_swap_limit_in_bytes(julong phys_mem, julong host_swap) { jlong swap_limit; - bool is_ok = static_cast(this)->read_number_handle_max("/memory.swap.max", &swap_limit); + bool is_ok = 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); @@ -205,7 +201,7 @@ jlong mem_swp_current_val(CgroupV2Controller* ctrl) { 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 = mem_swp_current_val(static_cast(this)); + jlong swap_current = mem_swp_current_val(this); return memory_usage + (swap_current >= 0 ? swap_current : 0); } return memory_usage; // not supported or unlimited case @@ -227,7 +223,7 @@ jlong mem_limit_val(CgroupV2Controller* ctrl) { * -1 for unlimited, OSCONTAINER_ERROR for an error */ jlong CgroupV2MemoryController::read_memory_limit_in_bytes(julong phys_mem) { - jlong limit = mem_limit_val(static_cast(this)); + jlong limit = mem_limit_val(this); if (log_is_enabled(Trace, os, container)) { if (limit == -1) { log_trace(os, container)("Memory Limit is: Unlimited"); @@ -262,8 +258,8 @@ jlong mem_swp_limit_val(CgroupV2Controller* ctrl) { } void CgroupV2Subsystem::print_version_specific_info(outputStream* st) { - jlong swap_current = mem_swp_current_val(static_cast(_unified)); - jlong swap_limit = mem_swp_limit_val(static_cast(_unified)); + jlong swap_current = mem_swp_current_val(_unified); + jlong swap_limit = mem_swp_limit_val(_unified); 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"); @@ -289,7 +285,7 @@ char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { */ jlong CgroupV2Subsystem::pids_max() { jlong pids_max; - CONTAINER_READ_NUMBER_CHECKED_MAX(static_cast(_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; } @@ -303,6 +299,6 @@ jlong CgroupV2Subsystem::pids_max() { */ jlong CgroupV2Subsystem::pids_current() { julong pids_current; - CONTAINER_READ_NUMBER_CHECKED(static_cast(_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; } From cb91f4cb2f9996fbfa5228022b6fb48aeba26bb8 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Wed, 29 May 2024 18:44:36 +0200 Subject: [PATCH 23/38] Appropriately handle version specific printing --- src/hotspot/os/linux/cgroupSubsystem_linux.cpp | 5 +++++ src/hotspot/os/linux/cgroupSubsystem_linux.hpp | 3 ++- src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp | 10 ++++------ src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp | 1 + src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 6 +++--- src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 1 + 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index e3a3b8896c66e..669037a80a341 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -728,3 +728,8 @@ int CgroupSubsystem::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 716fe10318a8f..975cbc0dd66fd 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -214,6 +214,7 @@ class CgroupMemoryController { 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); }; class CgroupSubsystem: public CHeapObj { @@ -240,7 +241,7 @@ class CgroupSubsystem: public CHeapObj { jlong memory_max_usage_in_bytes(); jlong rss_usage_in_bytes(); jlong cache_usage_in_bytes(); - virtual void print_version_specific_info(outputStream* st) = 0; + void print_version_specific_info(outputStream* st); }; // Utility class for storing info retrieved from /proc/cgroups, diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 82435d0005208..8f7014f56a797 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -304,12 +304,10 @@ jlong CgroupV1MemoryController::kernel_memory_max_usage_in_bytes() { return (jlong)kmem_max_usage; } -void CgroupV1Subsystem::print_version_specific_info(outputStream* st) { - julong phys_mem = os::Linux::physical_memory(); - CgroupV1MemoryController* ctrl = reinterpret_cast(memory_controller()->controller()); - jlong kmem_usage = ctrl->kernel_memory_usage_in_bytes(); - jlong kmem_limit = ctrl->kernel_memory_limit_in_bytes(phys_mem); - jlong kmem_max_usage = ctrl->kernel_memory_max_usage_in_bytes(); +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(phys_mem); + jlong kmem_max_usage = kernel_memory_max_usage_in_bytes(); OSContainer::print_container_helper(st, kmem_usage, "kernel_memory_usage_in_bytes"); OSContainer::print_container_helper(st, kmem_limit, "kernel_memory_max_usage_in_bytes"); diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index b9d59b129f772..2b73dd2c2908c 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -66,6 +66,7 @@ class CgroupV1MemoryController: public CgroupV1Controller, public CgroupMemoryCo 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); char *subsystem_path() override { return CgroupV1Controller::subsystem_path(); } private: /* Some container runtimes set limits via cgroup diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index bacc9de92208b..17b9b84da589d 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -257,9 +257,9 @@ jlong mem_swp_limit_val(CgroupV2Controller* ctrl) { return swap_limit; } -void CgroupV2Subsystem::print_version_specific_info(outputStream* st) { - jlong swap_current = mem_swp_current_val(_unified); - jlong swap_limit = mem_swp_limit_val(_unified); +void CgroupV2MemoryController::print_version_specific_info(outputStream* st, julong phys_mem) { + jlong swap_current = mem_swp_current_val(this); + jlong swap_limit = mem_swp_limit_val(this); 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"); diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 893b4e5cba13e..cdd604a508b3f 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -71,6 +71,7 @@ class CgroupV2MemoryController: public CgroupV2Controller, public CgroupMemoryCo jlong memory_max_usage_in_bytes(); jlong rss_usage_in_bytes(); jlong cache_usage_in_bytes(); + void print_version_specific_info(outputStream* st, julong host_mem); char *subsystem_path() { return CgroupV2Controller::subsystem_path(); } }; From ad62e1642217ddf960eb9a8fc4eb8228769edcf3 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Wed, 29 May 2024 18:51:01 +0200 Subject: [PATCH 24/38] Clean up --- src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp | 2 -- src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 2b73dd2c2908c..7860be5b9a7e3 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -67,7 +67,6 @@ class CgroupV1MemoryController: public CgroupV1Controller, public CgroupMemoryCo 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); - char *subsystem_path() override { return CgroupV1Controller::subsystem_path(); } private: /* Some container runtimes set limits via cgroup * hierarchy. If set to true consider also memory.stat @@ -95,7 +94,6 @@ class CgroupV1CpuController: public CgroupV1Controller, public CgroupCpuControll public: CgroupV1CpuController(char *root, char *mountpoint) : CgroupV1Controller(root, mountpoint) { } - char *subsystem_path() override { return CgroupV1Controller::subsystem_path(); } }; class CgroupV1Subsystem: public CgroupSubsystem { diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index cdd604a508b3f..c64a54b01ee67 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -55,7 +55,6 @@ class CgroupV2CpuController: public CgroupV2Controller, public CgroupCpuControll int cpu_quota(); int cpu_period(); int cpu_shares(); - char *subsystem_path() { return CgroupV2Controller::subsystem_path(); } }; class CgroupV2MemoryController: public CgroupV2Controller, public CgroupMemoryController { @@ -72,7 +71,6 @@ class CgroupV2MemoryController: public CgroupV2Controller, public CgroupMemoryCo jlong rss_usage_in_bytes(); jlong cache_usage_in_bytes(); void print_version_specific_info(outputStream* st, julong host_mem); - char *subsystem_path() { return CgroupV2Controller::subsystem_path(); } }; class CgroupV2Subsystem: public CgroupSubsystem { From e177a0863787eee481b46264e6b7612324ddec11 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Wed, 29 May 2024 19:04:47 +0200 Subject: [PATCH 25/38] Review feedback --- src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 8f7014f56a797..4124b23a1c20a 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -123,12 +123,11 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { return OSCONTAINER_ERROR; } log_trace(os, container)("Hierarchical Memory Limit is: " JULONG_FORMAT, hier_memlimit); - if (hier_memlimit >= phys_mem) { - log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); - } else { + if (hier_memlimit < phys_mem) { do_trace_log(hier_memlimit, phys_mem); return (jlong)hier_memlimit; } + log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); } do_trace_log(memlimit, phys_mem); return (jlong)-1; From cc819f2e178b9dcd3eae6379f62d25b08ef3fd4f Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 14 Jun 2024 16:38:42 +0200 Subject: [PATCH 26/38] Get rid of multiple inheritance --- .../os/linux/cgroupSubsystem_linux.cpp | 18 +++++---- .../os/linux/cgroupSubsystem_linux.hpp | 4 +- .../os/linux/cgroupV1Subsystem_linux.cpp | 38 +++++++++--------- .../os/linux/cgroupV1Subsystem_linux.hpp | 39 +++++++++++-------- .../os/linux/cgroupV2Subsystem_linux.cpp | 24 ++++++------ .../os/linux/cgroupV2Subsystem_linux.hpp | 37 ++++++++++-------- 6 files changed, 86 insertions(+), 74 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 672fc5cc97603..c3b170b6c6cb2 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -62,13 +62,14 @@ 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. - CgroupV2MemoryController* memory = new CgroupV2MemoryController(cg_infos[MEMORY_IDX]._mount_path, cg_infos[MEMORY_IDX]._cgroup_path); - CgroupV2CpuController* cpu = new CgroupV2CpuController(cg_infos[CPU_IDX]._mount_path, cg_infos[CPU_IDX]._cgroup_path); + // 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 = new CgroupV2Controller(cg_infos[MEMORY_IDX]._mount_path, cg_infos[MEMORY_IDX]._cgroup_path); + CgroupV2MemoryController* memory = new CgroupV2MemoryController(mem_other); + CgroupV2CpuController* cpu = new CgroupV2CpuController(new CgroupV2Controller(cg_infos[CPU_IDX]._mount_path, cg_infos[CPU_IDX]._cgroup_path)); log_debug(os, container)("Detected cgroups v2 unified hierarchy"); cleanup(cg_infos); - return new CgroupV2Subsystem(memory, cpu); + return new CgroupV2Subsystem(memory, cpu, mem_other); } /* @@ -102,14 +103,15 @@ 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); + memory = new CgroupV1MemoryController(new CgroupV1Controller(info._root_mount_path, info._mount_path)); memory->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "cpuset") == 0) { cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path); cpuset->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "cpu") == 0) { - cpu = new CgroupV1CpuController(info._root_mount_path, info._mount_path); - cpu->set_subsystem_path(info._cgroup_path); + CgroupV1Controller* c_r = new CgroupV1Controller(info._root_mount_path, info._mount_path); + cpu = new CgroupV1CpuController(c_r); + c_r->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "cpuacct") == 0) { cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path); cpuacct->set_subsystem_path(info._cgroup_path); diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 975cbc0dd66fd..8e37e54814692 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -196,7 +196,7 @@ class CachingCgroupController : public CHeapObj { }; // Pure virtual class representing version agnostic CPU controllers -class CgroupCpuController { +class CgroupCpuController: public CHeapObj { public: virtual int cpu_quota() = 0; virtual int cpu_period() = 0; @@ -204,7 +204,7 @@ class CgroupCpuController { }; // Pure virtual class representing version agnostic memory controllers -class CgroupMemoryController { +class CgroupMemoryController: public CHeapObj { public: virtual jlong read_memory_limit_in_bytes(julong upper_bound) = 0; virtual jlong memory_usage_in_bytes() = 0; diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 4124b23a1c20a..010d3fd5986b0 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -77,12 +77,12 @@ 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); @@ -113,12 +113,12 @@ void do_trace_log(julong read_mem_limit, julong host_mem) { jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { julong memlimit; - CONTAINER_READ_NUMBER_CHECKED(this, "/memory.limit_in_bytes", "Memory Limit", memlimit); + 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"); if (is_hierarchical()) { julong hier_memlimit; - bool is_ok = 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; } @@ -152,12 +152,12 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { jlong CgroupV1MemoryController::read_mem_swap(julong host_total_memsw) { julong hier_memswlimit; julong memswlimit; - CONTAINER_READ_NUMBER_CHECKED(this, "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", memswlimit); + 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"); if (is_hierarchical()) { const char* matchline = "hierarchical_memsw_limit"; - bool is_ok = read_numerical_key_value("/memory.stat", + bool is_ok = _reader->read_numerical_key_value("/memory.stat", matchline, &hier_memswlimit); if (!is_ok) { @@ -210,7 +210,7 @@ jlong CgroupV1MemoryController::memory_and_swap_usage_in_bytes(julong 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(this); + return memory_swap_usage_impl(_reader); } } return memory_usage_in_bytes(); @@ -218,13 +218,13 @@ jlong CgroupV1MemoryController::memory_and_swap_usage_in_bytes(julong phys_mem, jlong CgroupV1MemoryController::read_mem_swappiness() { julong swappiness; - CONTAINER_READ_NUMBER_CHECKED(this, "/memory.swappiness", "Swappiness", swappiness); + CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.swappiness", "Swappiness", swappiness); return (jlong)swappiness; } jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { julong memsoftlimit; - CONTAINER_READ_NUMBER_CHECKED(this, "/memory.soft_limit_in_bytes", "Memory Soft Limit", memsoftlimit); + 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; @@ -244,7 +244,7 @@ jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { */ jlong CgroupV1MemoryController::memory_usage_in_bytes() { julong memusage; - CONTAINER_READ_NUMBER_CHECKED(this, "/memory.usage_in_bytes", "Memory Usage", memusage); + CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.usage_in_bytes", "Memory Usage", memusage); return (jlong)memusage; } @@ -258,13 +258,13 @@ jlong CgroupV1MemoryController::memory_usage_in_bytes() { */ jlong CgroupV1MemoryController::memory_max_usage_in_bytes() { julong memmaxusage; - CONTAINER_READ_NUMBER_CHECKED(this, "/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 CgroupV1MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = 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; } @@ -274,7 +274,7 @@ jlong CgroupV1MemoryController::rss_usage_in_bytes() { jlong CgroupV1MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = 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; } @@ -284,13 +284,13 @@ jlong CgroupV1MemoryController::cache_usage_in_bytes() { jlong CgroupV1MemoryController::kernel_memory_usage_in_bytes() { julong kmem_usage; - CONTAINER_READ_NUMBER_CHECKED(this, "/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 CgroupV1MemoryController::kernel_memory_limit_in_bytes(julong phys_mem) { julong kmem_limit; - CONTAINER_READ_NUMBER_CHECKED(this, "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", kmem_limit); + CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", kmem_limit); if (kmem_limit >= phys_mem) { return (jlong)-1; } @@ -299,7 +299,7 @@ jlong CgroupV1MemoryController::kernel_memory_limit_in_bytes(julong phys_mem) { jlong CgroupV1MemoryController::kernel_memory_max_usage_in_bytes() { julong kmem_max_usage; - CONTAINER_READ_NUMBER_CHECKED(this, "/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; } @@ -337,7 +337,7 @@ char* CgroupV1Subsystem::cpu_cpuset_memory_nodes() { */ int CgroupV1CpuController::cpu_quota() { julong quota; - bool is_ok = 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; @@ -351,7 +351,7 @@ int CgroupV1CpuController::cpu_quota() { int CgroupV1CpuController::cpu_period() { julong period; - CONTAINER_READ_NUMBER_CHECKED(this, "/cpu.cfs_period_us", "CPU Period", period); + CONTAINER_READ_NUMBER_CHECKED(_reader, "/cpu.cfs_period_us", "CPU Period", period); return (int)period; } @@ -367,7 +367,7 @@ int CgroupV1CpuController::cpu_period() { */ int CgroupV1CpuController::cpu_shares() { julong shares; - CONTAINER_READ_NUMBER_CHECKED(this, "/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 7860be5b9a7e3..d940636ff0996 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -50,23 +50,25 @@ class CgroupV1Controller: public CgroupController { char *subsystem_path() { return _path; } }; -class CgroupV1MemoryController: public CgroupV1Controller, public CgroupMemoryController { +class CgroupV1MemoryController : public CgroupMemoryController { + private: + CgroupV1Controller* _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); - jlong memory_usage_in_bytes(); - jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap); - jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap); - jlong memory_soft_limit_in_bytes(julong upper_bound); - jlong memory_max_usage_in_bytes(); - jlong rss_usage_in_bytes(); - jlong cache_usage_in_bytes(); + 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); + void print_version_specific_info(outputStream* st, julong host_mem) override; private: /* Some container runtimes set limits via cgroup * hierarchy. If set to true consider also memory.stat @@ -78,21 +80,24 @@ class CgroupV1MemoryController: public CgroupV1Controller, public CgroupMemoryCo jlong read_mem_swap(julong host_total_memsw); public: - CgroupV1MemoryController(char *root, char *mountpoint) : CgroupV1Controller(root, mountpoint) { - _uses_mem_hierarchy = false; + CgroupV1MemoryController(CgroupV1Controller* reader) + : _reader(reader), + _uses_mem_hierarchy(false) { } }; -class CgroupV1CpuController: public CgroupV1Controller, public CgroupCpuController { +class CgroupV1CpuController: public CgroupCpuController { + private: + CgroupV1Controller* _reader; public: - int cpu_quota(); - int cpu_period(); - int cpu_shares(); + int cpu_quota() override; + int cpu_period() override; + int cpu_shares() override; public: - CgroupV1CpuController(char *root, char *mountpoint) : CgroupV1Controller(root, mountpoint) { + CgroupV1CpuController(CgroupV1Controller* reader) : _reader(reader) { } }; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 17b9b84da589d..619241d345ebc 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -37,7 +37,7 @@ */ int CgroupV2CpuController::cpu_shares() { julong shares; - CONTAINER_READ_NUMBER_CHECKED(this, "/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) { @@ -86,7 +86,7 @@ int CgroupV2CpuController::cpu_shares() { */ int CgroupV2CpuController::cpu_quota() { jlong quota_val; - bool is_ok = 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; } @@ -109,7 +109,7 @@ char* CgroupV2Subsystem::cpu_cpuset_memory_nodes() { int CgroupV2CpuController::cpu_period() { jlong period_val; - bool is_ok = 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; @@ -130,13 +130,13 @@ int CgroupV2CpuController::cpu_period() { */ jlong CgroupV2MemoryController::memory_usage_in_bytes() { julong memusage; - CONTAINER_READ_NUMBER_CHECKED(this, "/memory.current", "Memory Usage", memusage); + CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.current", "Memory Usage", memusage); return (jlong)memusage; } jlong CgroupV2MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { jlong mem_soft_limit; - CONTAINER_READ_NUMBER_CHECKED_MAX(this, "/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; } @@ -148,7 +148,7 @@ jlong CgroupV2MemoryController::memory_max_usage_in_bytes() { jlong CgroupV2MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = 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; } @@ -158,7 +158,7 @@ jlong CgroupV2MemoryController::rss_usage_in_bytes() { jlong CgroupV2MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = 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; } @@ -173,7 +173,7 @@ jlong CgroupV2MemoryController::cache_usage_in_bytes() { // without also setting a memory limit is not allowed. jlong CgroupV2MemoryController::memory_and_swap_limit_in_bytes(julong phys_mem, julong host_swap) { jlong swap_limit; - bool is_ok = 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); @@ -201,7 +201,7 @@ jlong mem_swp_current_val(CgroupV2Controller* ctrl) { 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 = mem_swp_current_val(this); + jlong swap_current = mem_swp_current_val(_reader); return memory_usage + (swap_current >= 0 ? swap_current : 0); } return memory_usage; // not supported or unlimited case @@ -223,7 +223,7 @@ jlong mem_limit_val(CgroupV2Controller* ctrl) { * -1 for unlimited, OSCONTAINER_ERROR for an error */ jlong CgroupV2MemoryController::read_memory_limit_in_bytes(julong phys_mem) { - jlong limit = mem_limit_val(this); + jlong limit = mem_limit_val(_reader); if (log_is_enabled(Trace, os, container)) { if (limit == -1) { log_trace(os, container)("Memory Limit is: Unlimited"); @@ -258,8 +258,8 @@ jlong mem_swp_limit_val(CgroupV2Controller* ctrl) { } void CgroupV2MemoryController::print_version_specific_info(outputStream* st, julong phys_mem) { - jlong swap_current = mem_swp_current_val(this); - jlong swap_limit = mem_swp_limit_val(this); + jlong swap_current = mem_swp_current_val(_reader); + jlong swap_limit = mem_swp_limit_val(_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"); diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index c64a54b01ee67..5e1905d310f8e 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -48,43 +48,48 @@ class CgroupV2Controller: public CgroupController { char *subsystem_path() { return _path; } }; -class CgroupV2CpuController: public CgroupV2Controller, public CgroupCpuController { +class CgroupV2CpuController: public CgroupCpuController { + private: + CgroupV2Controller* _reader; public: - CgroupV2CpuController(char * mount_path, char *cgroup_path) : CgroupV2Controller(mount_path, cgroup_path) { + CgroupV2CpuController(CgroupV2Controller* reader) : _reader(reader) { } int cpu_quota(); int cpu_period(); int cpu_shares(); }; -class CgroupV2MemoryController: public CgroupV2Controller, public CgroupMemoryController { +class CgroupV2MemoryController: public CgroupMemoryController { + private: + CgroupV2Controller* _reader; public: - CgroupV2MemoryController(char * mount_path, char *cgroup_path) : CgroupV2Controller(mount_path, cgroup_path) { + CgroupV2MemoryController(CgroupV2Controller* reader) : _reader(reader) { } - jlong read_memory_limit_in_bytes(julong upper_bound); - jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swp); - jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swp); - jlong memory_soft_limit_in_bytes(julong upper_bound); - jlong memory_usage_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, julong host_mem); + 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; }; class CgroupV2Subsystem: public CgroupSubsystem { private: /* One unified controller */ - CgroupV2MemoryController* _unified = nullptr; + CgroupV2Controller* _unified = nullptr; /* Caching wrappers for cpu/memory metrics */ CachingCgroupController* _memory = nullptr; CachingCgroupController* _cpu = nullptr; public: CgroupV2Subsystem(CgroupV2MemoryController * memory, - CgroupV2CpuController* cpu) { - _unified = memory; // Use memory for now, should have all separate later + CgroupV2CpuController* cpu, + CgroupV2Controller* unified) { + _unified = unified; _memory = new CachingCgroupController(memory); _cpu = new CachingCgroupController(cpu); } From 2c067ddaf2805fc7149f9de88c6c35894f48b303 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 14 Jun 2024 16:41:33 +0200 Subject: [PATCH 27/38] Style nit --- src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 5e1905d310f8e..9372ddce3d7f5 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -86,7 +86,7 @@ class CgroupV2Subsystem: public CgroupSubsystem { CachingCgroupController* _cpu = nullptr; public: - CgroupV2Subsystem(CgroupV2MemoryController * memory, + CgroupV2Subsystem(CgroupV2MemoryController* memory, CgroupV2CpuController* cpu, CgroupV2Controller* unified) { _unified = unified; From 5d7537fd559529a09b57af326387650de9a6c9d3 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 14 Jun 2024 16:48:03 +0200 Subject: [PATCH 28/38] initializer lists --- src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 9372ddce3d7f5..70219407a3282 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -88,10 +88,10 @@ class CgroupV2Subsystem: public CgroupSubsystem { public: CgroupV2Subsystem(CgroupV2MemoryController* memory, CgroupV2CpuController* cpu, - CgroupV2Controller* unified) { - _unified = unified; - _memory = new CachingCgroupController(memory); - _cpu = new CachingCgroupController(cpu); + CgroupV2Controller* unified) : + _unified(unified), + _memory(new CachingCgroupController(memory)), + _cpu(new CachingCgroupController(cpu)) { } jlong read_memory_limit_in_bytes(); From 2e2b4eb9517647f9e501831c325cdd64a565c471 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 14 Jun 2024 17:42:07 +0200 Subject: [PATCH 29/38] Templated class use references --- .../os/linux/cgroupSubsystem_linux.cpp | 4 ++-- .../os/linux/cgroupSubsystem_linux.hpp | 10 +++++----- .../os/linux/cgroupV1Subsystem_linux.hpp | 20 +++++++++---------- .../os/linux/cgroupV2Subsystem_linux.hpp | 12 +++++------ 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index c3b170b6c6cb2..47dd9a77a6a02 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -485,7 +485,7 @@ int CgroupSubsystem::active_processor_count() { // 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(); @@ -511,7 +511,7 @@ 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(); diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 8e37e54814692..baf97162c60a4 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -182,17 +182,17 @@ class CachedMetric : public CHeapObj{ template class CachingCgroupController : public CHeapObj { private: - T _controller; + T* _controller; CachedMetric* _metrics_cache; public: - CachingCgroupController(T cont) { + CachingCgroupController(T* cont) { _controller = cont; _metrics_cache = new CachedMetric(); } CachedMetric* metrics_cache() { return _metrics_cache; } - T controller() { return _controller; } + T* controller() { return _controller; } }; // Pure virtual class representing version agnostic CPU controllers @@ -231,8 +231,8 @@ class CgroupSubsystem: public CHeapObj { virtual char * cpu_cpuset_cpus() = 0; virtual char * cpu_cpuset_memory_nodes() = 0; virtual const char * container_type() = 0; - virtual CachingCgroupController* memory_controller() = 0; - virtual CachingCgroupController* cpu_controller() = 0; + virtual CachingCgroupController* memory_controller() = 0; + virtual CachingCgroupController* cpu_controller() = 0; jlong memory_usage_in_bytes(); jlong memory_and_swap_limit_in_bytes(); diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index d940636ff0996..d5d67b6db6c96 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -119,14 +119,14 @@ class CgroupV1Subsystem: public CgroupSubsystem { 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; @@ -135,12 +135,12 @@ class CgroupV1Subsystem: public CgroupSubsystem { 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.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 70219407a3282..0e60f20353293 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -82,16 +82,16 @@ class CgroupV2Subsystem: public CgroupSubsystem { /* One unified controller */ CgroupV2Controller* _unified = nullptr; /* Caching wrappers for cpu/memory metrics */ - CachingCgroupController* _memory = nullptr; - CachingCgroupController* _cpu = nullptr; + CachingCgroupController* _memory = nullptr; + CachingCgroupController* _cpu = nullptr; public: CgroupV2Subsystem(CgroupV2MemoryController* memory, CgroupV2CpuController* cpu, CgroupV2Controller* unified) : _unified(unified), - _memory(new CachingCgroupController(memory)), - _cpu(new CachingCgroupController(cpu)) { + _memory(new CachingCgroupController(memory)), + _cpu(new CachingCgroupController(cpu)) { } jlong read_memory_limit_in_bytes(); @@ -116,8 +116,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { const char * container_type() { 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 From 6aff2076495657843e09d84615b41b3111569cdf Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 14 Jun 2024 17:49:26 +0200 Subject: [PATCH 30/38] Tabs --- src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index d5d67b6db6c96..60c773b89b5ab 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -81,8 +81,8 @@ class CgroupV1MemoryController : public CgroupMemoryController { public: CgroupV1MemoryController(CgroupV1Controller* reader) - : _reader(reader), - _uses_mem_hierarchy(false) { + : _reader(reader), + _uses_mem_hierarchy(false) { } }; From 467018f56732f1428343c9d62606ac69fbcc94ea Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 18 Jun 2024 15:50:33 +0200 Subject: [PATCH 31/38] Review feedback --- .../os/linux/cgroupSubsystem_linux.cpp | 11 +++-- .../os/linux/cgroupSubsystem_linux.hpp | 2 +- .../os/linux/cgroupV1Subsystem_linux.cpp | 38 ++++++++-------- .../os/linux/cgroupV1Subsystem_linux.hpp | 43 ++++++++++++------- .../os/linux/cgroupV2Subsystem_linux.cpp | 32 +++++++------- .../os/linux/cgroupV2Subsystem_linux.hpp | 37 ++++++++++------ 6 files changed, 94 insertions(+), 69 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 47dd9a77a6a02..957dab3a05af1 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -64,9 +64,9 @@ CgroupSubsystem* CgroupSubsystemFactory::create() { // Construct the subsystem, free resources and return // 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 = new CgroupV2Controller(cg_infos[MEMORY_IDX]._mount_path, cg_infos[MEMORY_IDX]._cgroup_path); + CgroupV2Controller mem_other = CgroupV2Controller(cg_infos[MEMORY_IDX]._mount_path, cg_infos[MEMORY_IDX]._cgroup_path); CgroupV2MemoryController* memory = new CgroupV2MemoryController(mem_other); - CgroupV2CpuController* cpu = new CgroupV2CpuController(new CgroupV2Controller(cg_infos[CPU_IDX]._mount_path, cg_infos[CPU_IDX]._cgroup_path)); + CgroupV2CpuController* cpu = new CgroupV2CpuController(CgroupV2Controller(cg_infos[CPU_IDX]._mount_path, cg_infos[CPU_IDX]._cgroup_path)); log_debug(os, container)("Detected cgroups v2 unified hierarchy"); cleanup(cg_infos); return new CgroupV2Subsystem(memory, cpu, mem_other); @@ -103,15 +103,14 @@ 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(new CgroupV1Controller(info._root_mount_path, info._mount_path)); + memory = new CgroupV1MemoryController(CgroupV1Controller(info._root_mount_path, info._mount_path)); memory->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "cpuset") == 0) { cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path); cpuset->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "cpu") == 0) { - CgroupV1Controller* c_r = new CgroupV1Controller(info._root_mount_path, info._mount_path); - cpu = new CgroupV1CpuController(c_r); - c_r->set_subsystem_path(info._cgroup_path); + cpu = new CgroupV1CpuController(CgroupV1Controller(info._root_mount_path, info._mount_path)); + cpu->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "cpuacct") == 0) { cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path); cpuacct->set_subsystem_path(info._cgroup_path); diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index baf97162c60a4..24d7fad352ea6 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -214,7 +214,7 @@ class CgroupMemoryController: public CHeapObj { 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); + virtual void print_version_specific_info(outputStream* st, julong host_mem) = 0; }; class CgroupSubsystem: public CHeapObj { diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 010d3fd5986b0..95375bf452ca1 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -77,12 +77,12 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { */ jlong CgroupV1MemoryController::uses_mem_hierarchy() { julong use_hierarchy; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/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) { - _reader->set_subsystem_path(cgroup_path); + reader()->set_subsystem_path(cgroup_path); jlong hierarchy = uses_mem_hierarchy(); if (hierarchy > 0) { set_hierarchical(true); @@ -113,12 +113,12 @@ void do_trace_log(julong read_mem_limit, julong host_mem) { jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { julong memlimit; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.limit_in_bytes", "Memory Limit", memlimit); + 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"); if (is_hierarchical()) { julong hier_memlimit; - bool is_ok = _reader->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; } @@ -152,12 +152,12 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { jlong CgroupV1MemoryController::read_mem_swap(julong host_total_memsw) { julong hier_memswlimit; julong memswlimit; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.memsw.limit_in_bytes", "Memory and Swap Limit", memswlimit); + 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"); if (is_hierarchical()) { const char* matchline = "hierarchical_memsw_limit"; - bool is_ok = _reader->read_numerical_key_value("/memory.stat", + bool is_ok = reader()->read_numerical_key_value("/memory.stat", matchline, &hier_memswlimit); if (!is_ok) { @@ -210,7 +210,7 @@ jlong CgroupV1MemoryController::memory_and_swap_usage_in_bytes(julong 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(_reader); + return memory_swap_usage_impl(reader()); } } return memory_usage_in_bytes(); @@ -218,13 +218,13 @@ jlong CgroupV1MemoryController::memory_and_swap_usage_in_bytes(julong phys_mem, jlong CgroupV1MemoryController::read_mem_swappiness() { julong swappiness; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.swappiness", "Swappiness", swappiness); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.swappiness", "Swappiness", swappiness); return (jlong)swappiness; } jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { julong memsoftlimit; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.soft_limit_in_bytes", "Memory Soft Limit", memsoftlimit); + 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; @@ -244,7 +244,7 @@ jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { */ jlong CgroupV1MemoryController::memory_usage_in_bytes() { julong memusage; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.usage_in_bytes", "Memory Usage", memusage); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.usage_in_bytes", "Memory Usage", memusage); return (jlong)memusage; } @@ -258,13 +258,13 @@ jlong CgroupV1MemoryController::memory_usage_in_bytes() { */ jlong CgroupV1MemoryController::memory_max_usage_in_bytes() { julong memmaxusage; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/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 CgroupV1MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = _reader->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; } @@ -274,7 +274,7 @@ jlong CgroupV1MemoryController::rss_usage_in_bytes() { jlong CgroupV1MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = _reader->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; } @@ -284,13 +284,13 @@ jlong CgroupV1MemoryController::cache_usage_in_bytes() { jlong CgroupV1MemoryController::kernel_memory_usage_in_bytes() { julong kmem_usage; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/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 CgroupV1MemoryController::kernel_memory_limit_in_bytes(julong phys_mem) { julong kmem_limit; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", kmem_limit); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.kmem.limit_in_bytes", "Kernel Memory Limit", kmem_limit); if (kmem_limit >= phys_mem) { return (jlong)-1; } @@ -299,7 +299,7 @@ jlong CgroupV1MemoryController::kernel_memory_limit_in_bytes(julong phys_mem) { jlong CgroupV1MemoryController::kernel_memory_max_usage_in_bytes() { julong kmem_max_usage; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/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; } @@ -337,7 +337,7 @@ char* CgroupV1Subsystem::cpu_cpuset_memory_nodes() { */ int CgroupV1CpuController::cpu_quota() { julong quota; - bool is_ok = _reader->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; @@ -351,7 +351,7 @@ int CgroupV1CpuController::cpu_quota() { int CgroupV1CpuController::cpu_period() { julong period; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/cpu.cfs_period_us", "CPU Period", period); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpu.cfs_period_us", "CPU Period", period); return (int)period; } @@ -367,7 +367,7 @@ int CgroupV1CpuController::cpu_period() { */ int CgroupV1CpuController::cpu_shares() { julong shares; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/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 60c773b89b5ab..dd9f844abbf96 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -34,26 +34,35 @@ class CgroupV1Controller: public CgroupController { private: /* mountinfo contents */ - char *_root; - char *_mount_point; + char* _root; + char* _mount_point; /* Constructed subsystem directory */ - char *_path; + char* _path; public: - CgroupV1Controller(char *root, char *mountpoint) { - _root = os::strdup(root); - _mount_point = os::strdup(mountpoint); - _path = nullptr; + CgroupV1Controller(char *root, char *mountpoint) : _root(os::strdup(root)), + _mount_point(os::strdup(mountpoint)), + _path(nullptr) { + } + // Shallow copy constructor + CgroupV1Controller(const CgroupV1Controller& o) : _root(o._root), + _mount_point(o._mount_point), + _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; } }; -class CgroupV1MemoryController : public CgroupMemoryController { +class CgroupV1MemoryController final : public CgroupMemoryController { private: - CgroupV1Controller* _reader; + CgroupV1Controller _reader; + CgroupV1Controller* reader() { return &_reader; } public: bool is_hierarchical() { return _uses_mem_hierarchy; } void set_subsystem_path(char *cgroup_path); @@ -80,24 +89,28 @@ class CgroupV1MemoryController : public CgroupMemoryController { jlong read_mem_swap(julong host_total_memsw); public: - CgroupV1MemoryController(CgroupV1Controller* reader) + CgroupV1MemoryController(CgroupV1Controller reader) : _reader(reader), _uses_mem_hierarchy(false) { } }; -class CgroupV1CpuController: public CgroupCpuController { +class CgroupV1CpuController final : public CgroupCpuController { private: - CgroupV1Controller* _reader; + 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); + } public: - CgroupV1CpuController(CgroupV1Controller* reader) : _reader(reader) { + CgroupV1CpuController(CgroupV1Controller reader) : _reader(reader) { } }; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 619241d345ebc..46c40a4a15517 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -37,7 +37,7 @@ */ int CgroupV2CpuController::cpu_shares() { julong shares; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/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) { @@ -86,7 +86,7 @@ int CgroupV2CpuController::cpu_shares() { */ int CgroupV2CpuController::cpu_quota() { jlong quota_val; - bool is_ok = _reader->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; } @@ -97,19 +97,19 @@ int CgroupV2CpuController::cpu_quota() { 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 CgroupV2CpuController::cpu_period() { jlong period_val; - bool is_ok = _reader->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; @@ -130,13 +130,13 @@ int CgroupV2CpuController::cpu_period() { */ jlong CgroupV2MemoryController::memory_usage_in_bytes() { julong memusage; - CONTAINER_READ_NUMBER_CHECKED(_reader, "/memory.current", "Memory Usage", memusage); + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.current", "Memory Usage", memusage); return (jlong)memusage; } jlong CgroupV2MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { jlong mem_soft_limit; - CONTAINER_READ_NUMBER_CHECKED_MAX(_reader, "/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; } @@ -148,7 +148,7 @@ jlong CgroupV2MemoryController::memory_max_usage_in_bytes() { jlong CgroupV2MemoryController::rss_usage_in_bytes() { julong rss; - bool is_ok = _reader->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; } @@ -158,7 +158,7 @@ jlong CgroupV2MemoryController::rss_usage_in_bytes() { jlong CgroupV2MemoryController::cache_usage_in_bytes() { julong cache; - bool is_ok = _reader->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; } @@ -173,7 +173,7 @@ jlong CgroupV2MemoryController::cache_usage_in_bytes() { // without also setting a memory limit is not allowed. jlong CgroupV2MemoryController::memory_and_swap_limit_in_bytes(julong phys_mem, julong host_swap) { jlong swap_limit; - bool is_ok = _reader->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); @@ -201,7 +201,7 @@ jlong mem_swp_current_val(CgroupV2Controller* ctrl) { 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 = mem_swp_current_val(_reader); + jlong swap_current = mem_swp_current_val(reader()); return memory_usage + (swap_current >= 0 ? swap_current : 0); } return memory_usage; // not supported or unlimited case @@ -223,7 +223,7 @@ jlong mem_limit_val(CgroupV2Controller* ctrl) { * -1 for unlimited, OSCONTAINER_ERROR for an error */ jlong CgroupV2MemoryController::read_memory_limit_in_bytes(julong phys_mem) { - jlong limit = mem_limit_val(_reader); + jlong limit = mem_limit_val(reader()); if (log_is_enabled(Trace, os, container)) { if (limit == -1) { log_trace(os, container)("Memory Limit is: Unlimited"); @@ -258,8 +258,8 @@ jlong mem_swp_limit_val(CgroupV2Controller* ctrl) { } void CgroupV2MemoryController::print_version_specific_info(outputStream* st, julong phys_mem) { - jlong swap_current = mem_swp_current_val(_reader); - jlong swap_limit = mem_swp_limit_val(_reader); + jlong swap_current = mem_swp_current_val(reader()); + jlong swap_limit = mem_swp_limit_val(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"); @@ -285,7 +285,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; } @@ -299,6 +299,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 0e60f20353293..98e4fba71bb5e 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -39,31 +39,42 @@ class CgroupV2Controller: public CgroupController { static char* construct_path(char* mount_path, char *cgroup_path); public: - CgroupV2Controller(char * mount_path, char *cgroup_path) { - _mount_path = mount_path; - _cgroup_path = os::strdup(cgroup_path); - _path = construct_path(mount_path, cgroup_path); + CgroupV2Controller(char * mount_path, char *cgroup_path) : + _mount_path(os::strdup(mount_path)), + _cgroup_path(os::strdup(cgroup_path)), + _path(construct_path(mount_path, cgroup_path)) { + } + // Shallow copy constructor + CgroupV2Controller(const CgroupV2Controller& o) : + _mount_path(o._mount_path), + _cgroup_path(o._cgroup_path), + _path(o._path) { + } + ~CgroupV2Controller() { + // At least one controller exists with references to the paths } - char *subsystem_path() { return _path; } + char *subsystem_path() override { return _path; } }; class CgroupV2CpuController: public CgroupCpuController { private: - CgroupV2Controller* _reader; + CgroupV2Controller _reader; + CgroupV2Controller* reader() { return &_reader; } public: - CgroupV2CpuController(CgroupV2Controller* reader) : _reader(reader) { + CgroupV2CpuController(CgroupV2Controller reader) : _reader(reader) { } int cpu_quota(); int cpu_period(); int cpu_shares(); }; -class CgroupV2MemoryController: public CgroupMemoryController { +class CgroupV2MemoryController final: public CgroupMemoryController { private: - CgroupV2Controller* _reader; + CgroupV2Controller _reader; + CgroupV2Controller* reader() { return &_reader; } public: - CgroupV2MemoryController(CgroupV2Controller* reader) : _reader(reader) { + CgroupV2MemoryController(CgroupV2Controller reader) : _reader(reader) { } jlong read_memory_limit_in_bytes(julong upper_bound) override; @@ -80,15 +91,17 @@ class CgroupV2MemoryController: public CgroupMemoryController { class CgroupV2Subsystem: public CgroupSubsystem { private: /* One unified controller */ - CgroupV2Controller* _unified = nullptr; + CgroupV2Controller _unified; /* Caching wrappers for cpu/memory metrics */ CachingCgroupController* _memory = nullptr; CachingCgroupController* _cpu = nullptr; + CgroupV2Controller* unified() { return &_unified; } + public: CgroupV2Subsystem(CgroupV2MemoryController* memory, CgroupV2CpuController* cpu, - CgroupV2Controller* unified) : + CgroupV2Controller unified) : _unified(unified), _memory(new CachingCgroupController(memory)), _cpu(new CachingCgroupController(cpu)) { From abf0adc6d87df4418a93933f77fd29a921fa565a Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 18 Jun 2024 16:00:31 +0200 Subject: [PATCH 32/38] Rename log function --- src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index 95375bf452ca1..d4673066e5303 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -90,7 +90,7 @@ void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) { } static inline -void do_trace_log(julong read_mem_limit, julong host_mem) { +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) { @@ -124,15 +124,15 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { } log_trace(os, container)("Hierarchical Memory Limit is: " JULONG_FORMAT, hier_memlimit); if (hier_memlimit < phys_mem) { - do_trace_log(hier_memlimit, phys_mem); + verbose_log(hier_memlimit, phys_mem); return (jlong)hier_memlimit; } log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); } - do_trace_log(memlimit, phys_mem); + verbose_log(memlimit, phys_mem); return (jlong)-1; } else { - do_trace_log(memlimit, phys_mem); + verbose_log(memlimit, phys_mem); return (jlong)memlimit; } } From be3c15b7c01917926f911468e1e4bf51a1c737e0 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 25 Jun 2024 16:49:24 +0200 Subject: [PATCH 33/38] Use references when passing in readers --- src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp | 4 ++-- src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index dd9f844abbf96..33edeb2dceb19 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -89,7 +89,7 @@ class CgroupV1MemoryController final : public CgroupMemoryController { jlong read_mem_swap(julong host_total_memsw); public: - CgroupV1MemoryController(CgroupV1Controller reader) + CgroupV1MemoryController(const CgroupV1Controller& reader) : _reader(reader), _uses_mem_hierarchy(false) { } @@ -110,7 +110,7 @@ class CgroupV1CpuController final : public CgroupCpuController { } public: - CgroupV1CpuController(CgroupV1Controller reader) : _reader(reader) { + CgroupV1CpuController(const CgroupV1Controller& reader) : _reader(reader) { } }; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 98e4fba71bb5e..bc3d6b083f0fe 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -39,7 +39,7 @@ class CgroupV2Controller: public CgroupController { static char* construct_path(char* mount_path, char *cgroup_path); public: - CgroupV2Controller(char * mount_path, char *cgroup_path) : + CgroupV2Controller(char* mount_path, char *cgroup_path) : _mount_path(os::strdup(mount_path)), _cgroup_path(os::strdup(cgroup_path)), _path(construct_path(mount_path, cgroup_path)) { @@ -62,7 +62,7 @@ class CgroupV2CpuController: public CgroupCpuController { CgroupV2Controller _reader; CgroupV2Controller* reader() { return &_reader; } public: - CgroupV2CpuController(CgroupV2Controller reader) : _reader(reader) { + CgroupV2CpuController(const CgroupV2Controller& reader) : _reader(reader) { } int cpu_quota(); int cpu_period(); @@ -74,7 +74,7 @@ class CgroupV2MemoryController final: public CgroupMemoryController { CgroupV2Controller _reader; CgroupV2Controller* reader() { return &_reader; } public: - CgroupV2MemoryController(CgroupV2Controller reader) : _reader(reader) { + CgroupV2MemoryController(const CgroupV2Controller& reader) : _reader(reader) { } jlong read_memory_limit_in_bytes(julong upper_bound) override; From 20ad401df366e932f4286ad19321771447ff4954 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 25 Jun 2024 17:03:14 +0200 Subject: [PATCH 34/38] Remove declaration from sub-classes that are implemented in super class --- src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp | 2 -- src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 14 -------------- 2 files changed, 16 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 33edeb2dceb19..0a41532bd20e4 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -127,8 +127,6 @@ class CgroupV1Subsystem: public CgroupSubsystem { jlong pids_max(); jlong pids_current(); - void print_version_specific_info(outputStream* st); - const char * container_type() { return "cgroupv1"; } diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index bc3d6b083f0fe..15778926d6475 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -107,25 +107,11 @@ class CgroupV2Subsystem: public CgroupSubsystem { _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(); - void print_version_specific_info(outputStream* st); - const char * container_type() { return "cgroupv2"; } From 65a396dcbdfb45cad0a3e4a6c3d2214cacf184be Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 25 Jun 2024 17:08:56 +0200 Subject: [PATCH 35/38] Helper function naming fixes --- src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 46c40a4a15517..46243b29a19c6 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -192,7 +192,7 @@ jlong CgroupV2MemoryController::memory_and_swap_limit_in_bytes(julong phys_mem, // memory.swap.current : total amount of swap currently used by the cgroup and its descendants static -jlong mem_swp_current_val(CgroupV2Controller* ctrl) { +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; @@ -201,14 +201,14 @@ jlong mem_swp_current_val(CgroupV2Controller* ctrl) { 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 = mem_swp_current_val(reader()); + 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 } static -jlong mem_limit_val(CgroupV2Controller* ctrl) { +jlong memory_limit_value(CgroupV2Controller* ctrl) { jlong memory_limit; CONTAINER_READ_NUMBER_CHECKED_MAX(ctrl, "/memory.max", "Memory Limit", memory_limit); return memory_limit; @@ -223,7 +223,7 @@ jlong mem_limit_val(CgroupV2Controller* ctrl) { * -1 for unlimited, OSCONTAINER_ERROR for an error */ jlong CgroupV2MemoryController::read_memory_limit_in_bytes(julong phys_mem) { - jlong limit = mem_limit_val(reader()); + jlong limit = memory_limit_value(reader()); if (log_is_enabled(Trace, os, container)) { if (limit == -1) { log_trace(os, container)("Memory Limit is: Unlimited"); @@ -251,15 +251,15 @@ jlong CgroupV2MemoryController::read_memory_limit_in_bytes(julong phys_mem) { } static -jlong mem_swp_limit_val(CgroupV2Controller* ctrl) { +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 CgroupV2MemoryController::print_version_specific_info(outputStream* st, julong phys_mem) { - jlong swap_current = mem_swp_current_val(reader()); - jlong swap_limit = mem_swp_limit_val(reader()); + 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"); From 30fa6173bb752b4ff6e902a4700ef2b15e1662a8 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 25 Jun 2024 17:12:07 +0200 Subject: [PATCH 36/38] Add comment about un-used-ness --- src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 46243b29a19c6..63b37141f35f0 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -171,7 +171,8 @@ jlong CgroupV2MemoryController::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 CgroupV2MemoryController::memory_and_swap_limit_in_bytes(julong phys_mem, julong host_swap) { +jlong CgroupV2MemoryController::memory_and_swap_limit_in_bytes(julong phys_mem, + julong host_swap /* unused in cg v2 */) { jlong swap_limit; bool is_ok = reader()->read_number_handle_max("/memory.swap.max", &swap_limit); if (!is_ok) { From dcd0554b66cbdb57508b7fae79aa0de814e99a26 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Mon, 1 Jul 2024 14:17:43 +0200 Subject: [PATCH 37/38] Fixes after merge --- src/hotspot/os/linux/cgroupSubsystem_linux.hpp | 2 ++ src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp | 7 +++++++ src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 4 +++- src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 4 ++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 7f5883f958622..4d5fa5d487900 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -202,6 +202,7 @@ class CgroupCpuController: public CHeapObj { virtual int cpu_quota() = 0; virtual int cpu_period() = 0; virtual int cpu_shares() = 0; + virtual bool is_read_only() = 0; }; // Pure virtual class representing version agnostic memory controllers @@ -216,6 +217,7 @@ class CgroupMemoryController: public CHeapObj { 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 { diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 648ab6c663d52..251fbde85f0bb 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -52,6 +52,7 @@ class CgroupV1Controller: public CgroupController { // Shallow copy constructor CgroupV1Controller(const CgroupV1Controller& o) : _root(o._root), _mount_point(o._mount_point), + _read_only(o._read_only), _path(o._path) { } ~CgroupV1Controller() { @@ -84,6 +85,9 @@ class CgroupV1MemoryController final : public CgroupMemoryController { 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 @@ -114,6 +118,9 @@ class CgroupV1CpuController final : public CgroupCpuController { void set_subsystem_path(char *cgroup_path) { reader()->set_subsystem_path(cgroup_path); } + bool is_read_only() override { + return reader()->is_read_only(); + } public: CgroupV1CpuController(const CgroupV1Controller& reader) : _reader(reader) { diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index e2fccd1a88182..61f6dd6482398 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -96,7 +96,9 @@ int CgroupV2CpuController::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() { diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index c383059598aaf..02774fb70aec8 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -51,6 +51,7 @@ class CgroupV2Controller: public CgroupController { CgroupV2Controller(const CgroupV2Controller& o) : _mount_path(o._mount_path), _cgroup_path(o._cgroup_path), + _read_only(o._read_only), _path(o._path) { } ~CgroupV2Controller() { @@ -123,9 +124,8 @@ class CgroupV2Subsystem: public CgroupSubsystem { jlong pids_current() override; bool is_containerized() override; - void print_version_specific_info(outputStream* st) override; - const char * container_type() { + const char * container_type() override { return "cgroupv2"; } CachingCgroupController* memory_controller() { return _memory; } From 77cf2ea2e61b7ec5626a12f0a62bb1a285d3300b Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Tue, 2 Jul 2024 10:21:53 +0200 Subject: [PATCH 38/38] Fix tabs => spaces --- src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 61f6dd6482398..8f7e12d095474 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -97,8 +97,8 @@ int CgroupV2CpuController::cpu_quota() { bool CgroupV2Subsystem::is_containerized() { return _unified.is_read_only() && - _memory->controller()->is_read_only() && - _cpu->controller()->is_read_only(); + _memory->controller()->is_read_only() && + _cpu->controller()->is_read_only(); } char* CgroupV2Subsystem::cpu_cpuset_cpus() {