diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 1da0e44dbf48e..d51499611a8d1 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -609,7 +609,7 @@ jlong CgroupSubsystem::memory_limit_in_bytes() { bool CgroupController::read_string(const char* filename, char* buf, size_t buf_size) { assert(buf != nullptr, "buffer must not be null"); assert(filename != nullptr, "filename must be given"); - char* s_path = subsystem_path(); + const char* s_path = subsystem_path(); if (s_path == nullptr) { log_debug(os, container)("read_string: subsystem path is null"); return false; @@ -679,7 +679,7 @@ bool CgroupController::read_numerical_key_value(const char* filename, const char assert(key != nullptr, "key must be given"); assert(result != nullptr, "result pointer must not be null"); assert(filename != nullptr, "file to search in must be given"); - char* s_path = subsystem_path(); + const char* s_path = subsystem_path(); if (s_path == nullptr) { log_debug(os, container)("read_numerical_key_value: subsystem path is null"); return false; diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 4d5fa5d487900..40948dc5e2883 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -103,9 +103,15 @@ } class CgroupController: public CHeapObj { + protected: + char* _cgroup_path; + char* _mount_point; public: - virtual char* subsystem_path() = 0; + virtual const char* subsystem_path() = 0; virtual bool is_read_only() = 0; + const char* cgroup_path() { return _cgroup_path; } + const char* mount_point() { return _mount_point; } + virtual bool needs_hierarchy_adjustment() { return false; } /* Read a numerical value as unsigned long * @@ -202,7 +208,12 @@ class CgroupCpuController: public CHeapObj { virtual int cpu_quota() = 0; virtual int cpu_period() = 0; virtual int cpu_shares() = 0; + virtual bool needs_hierarchy_adjustment() = 0; virtual bool is_read_only() = 0; + virtual const char* subsystem_path() = 0; + virtual void set_subsystem_path(const char* cgroup_path) = 0; + virtual const char* mount_point() = 0; + virtual const char* cgroup_path() = 0; }; // Pure virtual class representing version agnostic memory controllers @@ -217,7 +228,12 @@ 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 needs_hierarchy_adjustment() = 0; virtual bool is_read_only() = 0; + virtual const char* subsystem_path() = 0; + virtual void set_subsystem_path(const char* cgroup_path) = 0; + virtual const char* mount_point() = 0; + virtual const char* cgroup_path() = 0; }; class CgroupSubsystem: public CHeapObj { diff --git a/src/hotspot/os/linux/cgroupUtil_linux.cpp b/src/hotspot/os/linux/cgroupUtil_linux.cpp index 24046991905ee..bc0e018d6be03 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.cpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.cpp @@ -22,6 +22,7 @@ * */ +#include "os_linux.hpp" #include "cgroupUtil_linux.hpp" int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) { @@ -46,3 +47,113 @@ int CgroupUtil::processor_count(CgroupCpuController* cpu_ctrl, int host_cpus) { log_trace(os, container)("OSContainer::active_processor_count: %d", result); return result; } + +void CgroupUtil::adjust_controller(CgroupMemoryController* mem) { + if (!mem->needs_hierarchy_adjustment()) { + // nothing to do + return; + } + log_trace(os, container)("Adjusting controller path for memory: %s", mem->subsystem_path()); + assert(mem->cgroup_path() != nullptr, "invariant"); + char* orig = os::strdup(mem->cgroup_path()); + char* cg_path = os::strdup(orig); + char* last_slash; + assert(cg_path[0] == '/', "cgroup path must start with '/'"); + julong phys_mem = os::Linux::physical_memory(); + char* limit_cg_path = nullptr; + jlong limit = mem->read_memory_limit_in_bytes(phys_mem); + jlong lowest_limit = phys_mem; + while ((last_slash = strrchr(cg_path, '/')) != cg_path) { + *last_slash = '\0'; // strip path + // update to shortened path and try again + mem->set_subsystem_path(cg_path); + limit = mem->read_memory_limit_in_bytes(phys_mem); + if (limit >= 0 && limit < lowest_limit) { + lowest_limit = limit; + os::free(limit_cg_path); // handles nullptr + limit_cg_path = os::strdup(cg_path); + } + } + // need to check limit at mount point + mem->set_subsystem_path("/"); + limit = mem->read_memory_limit_in_bytes(phys_mem); + if (limit >= 0 && limit < lowest_limit) { + lowest_limit = limit; + os::free(limit_cg_path); // handles nullptr + limit_cg_path = os::strdup("/"); + } + assert(lowest_limit >= 0, "limit must be positive"); + if ((julong)lowest_limit != phys_mem) { + // we've found a lower limit anywhere in the hierarchy, + // set the path to the limit path + assert(limit_cg_path != nullptr, "limit path must be set"); + mem->set_subsystem_path(limit_cg_path); + log_trace(os, container)("Adjusted controller path for memory to: %s. " + "Lowest limit was: " JLONG_FORMAT, + mem->subsystem_path(), + lowest_limit); + } else { + log_trace(os, container)("No lower limit found for memory in hierarchy %s, " + "adjusting to original path %s", + mem->mount_point(), orig); + mem->set_subsystem_path(orig); + } + os::free(cg_path); + os::free(orig); + os::free(limit_cg_path); +} + +void CgroupUtil::adjust_controller(CgroupCpuController* cpu) { + if (!cpu->needs_hierarchy_adjustment()) { + // nothing to do + return; + } + log_trace(os, container)("Adjusting controller path for cpu: %s", cpu->subsystem_path()); + assert(cpu->cgroup_path() != nullptr, "invariant"); + char* orig = os::strdup(cpu->cgroup_path()); + char* cg_path = os::strdup(orig); + char* last_slash; + assert(cg_path[0] == '/', "cgroup path must start with '/'"); + int host_cpus = os::Linux::active_processor_count(); + int cpus = CgroupUtil::processor_count(cpu, host_cpus); + int lowest_limit = host_cpus; + char* limit_cg_path = nullptr; + while ((last_slash = strrchr(cg_path, '/')) != cg_path) { + *last_slash = '\0'; // strip path + // update to shortened path and try again + cpu->set_subsystem_path(cg_path); + cpus = CgroupUtil::processor_count(cpu, host_cpus); + if (cpus != host_cpus && cpus < lowest_limit) { + lowest_limit = cpus; + os::free(limit_cg_path); // handles nullptr + limit_cg_path = os::strdup(cg_path); + } + } + // need to check limit at mount point + cpu->set_subsystem_path("/"); + cpus = CgroupUtil::processor_count(cpu, host_cpus); + if (cpus != host_cpus && cpus < lowest_limit) { + lowest_limit = cpus; + os::free(limit_cg_path); // handles nullptr + limit_cg_path = os::strdup(cg_path); + } + assert(lowest_limit >= 0, "limit must be positive"); + if (lowest_limit != host_cpus) { + // we've found a lower limit anywhere in the hierarchy, + // set the path to the limit path + assert(limit_cg_path != nullptr, "limit path must be set"); + cpu->set_subsystem_path(limit_cg_path); + log_trace(os, container)("Adjusted controller path for cpu to: %s. " + "Lowest limit was: %d", + cpu->subsystem_path(), + lowest_limit); + } else { + log_trace(os, container)("No lower limit found for cpu in hierarchy %s, " + "adjusting to original path %s", + cpu->mount_point(), orig); + cpu->set_subsystem_path(orig); + } + os::free(cg_path); + os::free(orig); + os::free(limit_cg_path); +} diff --git a/src/hotspot/os/linux/cgroupUtil_linux.hpp b/src/hotspot/os/linux/cgroupUtil_linux.hpp index fdcc4806c3b93..19220af317766 100644 --- a/src/hotspot/os/linux/cgroupUtil_linux.hpp +++ b/src/hotspot/os/linux/cgroupUtil_linux.hpp @@ -32,6 +32,12 @@ class CgroupUtil: AllStatic { public: static int processor_count(CgroupCpuController* cpu, int host_cpus); + // Given a memory controller, adjust its path to a point in the hierarchy + // that represents the closest memory limit. + static void adjust_controller(CgroupMemoryController* m); + // Given a cpu controller, adjust its path to a point in the hierarchy + // that represents the closest cpu limit. + static void adjust_controller(CgroupCpuController* c); }; #endif // CGROUP_UTIL_LINUX_HPP diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index d7f9918afdad8..388ee5c6ea093 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -38,7 +38,15 @@ * Set directory to subsystem specific files based * on the contents of the mountinfo and cgroup files. */ -void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { +void CgroupV1Controller::set_subsystem_path(const char* cgroup_path) { + if (_cgroup_path != nullptr) { + os::free(_cgroup_path); + } + if (_path != nullptr) { + os::free(_path); + _path = nullptr; + } + _cgroup_path = os::strdup(cgroup_path); stringStream ss; if (_root != nullptr && cgroup_path != nullptr) { if (strcmp(_root, "/") == 0) { @@ -52,7 +60,7 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { ss.print_raw(_mount_point); _path = os::strdup(ss.base()); } else { - char *p = strstr(cgroup_path, _root); + char *p = strstr((char*)cgroup_path, _root); if (p != nullptr && p == _root) { if (strlen(cgroup_path) > strlen(_root)) { ss.print_raw(_mount_point); @@ -66,27 +74,15 @@ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { } } -/* uses_mem_hierarchy - * - * Return whether or not hierarchical cgroup accounting is being - * done. - * - * return: - * A number > 0 if true, or - * OSCONTAINER_ERROR for not supported +/* + * The common case, containers, we have _root == _cgroup_path, and thus set the + * controller path to the _mount_point. This is where the limits are exposed in + * the cgroup pseudo filesystem (at the leaf) and adjustment of the path won't + * be needed for that reason. */ -jlong CgroupV1MemoryController::uses_mem_hierarchy() { - julong 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); - jlong hierarchy = uses_mem_hierarchy(); - if (hierarchy > 0) { - set_hierarchical(true); - } +bool CgroupV1Controller::needs_hierarchy_adjustment() { + assert(_cgroup_path != nullptr, "sanity"); + return strcmp(_root, _cgroup_path) != 0; } static inline @@ -115,20 +111,6 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { julong 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); - if (!is_ok) { - return OSCONTAINER_ERROR; - } - log_trace(os, container)("Hierarchical Memory Limit is: " JULONG_FORMAT, hier_memlimit); - if (hier_memlimit < phys_mem) { - verbose_log(hier_memlimit, phys_mem); - return (jlong)hier_memlimit; - } - log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); - } verbose_log(memlimit, phys_mem); return (jlong)-1; } else { @@ -150,26 +132,10 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { * upper bound) */ 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); 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", - matchline, - &hier_memswlimit); - if (!is_ok) { - 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 { - return (jlong)hier_memswlimit; - } - } + log_trace(os, container)("Memory and Swap Limit is: Unlimited"); return (jlong)-1; } else { return (jlong)memswlimit; @@ -233,6 +199,21 @@ jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { } } +// Constructor +CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset, + CgroupV1CpuController* cpu, + CgroupV1Controller* cpuacct, + CgroupV1Controller* pids, + CgroupV1MemoryController* memory) : + _cpuset(cpuset), + _cpuacct(cpuacct), + _pids(pids) { + CgroupUtil::adjust_controller(memory); + CgroupUtil::adjust_controller(cpu); + _memory = new CachingCgroupController(memory); + _cpu = new CachingCgroupController(cpu); +} + bool CgroupV1Subsystem::is_containerized() { // containerized iff all required controllers are mounted // read-only. See OSContainer::is_containerized() for diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 56af87881e757..0c191ab91c7f5 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -28,6 +28,7 @@ #include "runtime/os.hpp" #include "memory/allocation.hpp" #include "cgroupSubsystem_linux.hpp" +#include "cgroupUtil_linux.hpp" // Cgroups version 1 specific implementation @@ -35,7 +36,6 @@ class CgroupV1Controller: public CgroupController { private: /* mountinfo contents */ char* _root; - char* _mount_point; bool _read_only; /* Constructed subsystem directory */ @@ -45,24 +45,27 @@ class CgroupV1Controller: public CgroupController { CgroupV1Controller(char *root, char *mountpoint, bool ro) : _root(os::strdup(root)), - _mount_point(os::strdup(mountpoint)), _read_only(ro), _path(nullptr) { + _cgroup_path = nullptr; + _mount_point = os::strdup(mountpoint); } // Shallow copy constructor CgroupV1Controller(const CgroupV1Controller& o) : _root(o._root), - _mount_point(o._mount_point), _read_only(o._read_only), _path(o._path) { + _cgroup_path = o._cgroup_path; + _mount_point = o._mount_point; } ~CgroupV1Controller() { // At least one subsystem controller exists with paths to malloc'd path // names } - void set_subsystem_path(char *cgroup_path); - char *subsystem_path() override { return _path; } + void set_subsystem_path(const char *cgroup_path); + const char* subsystem_path() override { return _path; } bool is_read_only() override { return _read_only; } + bool needs_hierarchy_adjustment() override; }; class CgroupV1MemoryController final : public CgroupMemoryController { @@ -71,8 +74,9 @@ class CgroupV1MemoryController final : public CgroupMemoryController { CgroupV1Controller _reader; CgroupV1Controller* reader() { return &_reader; } public: - bool is_hierarchical() { return _uses_mem_hierarchy; } - void set_subsystem_path(char *cgroup_path); + void set_subsystem_path(const char *cgroup_path) override { + reader()->set_subsystem_path(cgroup_path); + } jlong read_memory_limit_in_bytes(julong upper_bound) override; jlong memory_usage_in_bytes() override; jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) override; @@ -85,23 +89,22 @@ 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 needs_hierarchy_adjustment() override { + return reader()->needs_hierarchy_adjustment(); + } bool is_read_only() override { return reader()->is_read_only(); } + const char* subsystem_path() override { return reader()->subsystem_path(); } + const char* mount_point() override { return reader()->mount_point(); } + const char* cgroup_path() override { return reader()->cgroup_path(); } private: - /* Some container runtimes set limits via cgroup - * hierarchy. If set to true consider also memory.stat - * file if everything else seems unlimited */ - 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(const CgroupV1Controller& reader) - : _reader(reader), - _uses_mem_hierarchy(false) { + : _reader(reader) { } }; @@ -115,12 +118,22 @@ class CgroupV1CpuController final : public CgroupCpuController { int cpu_quota() override; int cpu_period() override; int cpu_shares() override; - void set_subsystem_path(char *cgroup_path) { + void set_subsystem_path(const char *cgroup_path) override { reader()->set_subsystem_path(cgroup_path); } bool is_read_only() override { return reader()->is_read_only(); } + const char* subsystem_path() override { + return reader()->subsystem_path(); + } + const char* mount_point() override { + return reader()->mount_point(); + } + bool needs_hierarchy_adjustment() override { + return reader()->needs_hierarchy_adjustment(); + } + const char* cgroup_path() override { return reader()->cgroup_path(); } public: CgroupV1CpuController(const CgroupV1Controller& reader) : _reader(reader) { @@ -130,6 +143,12 @@ class CgroupV1CpuController final : public CgroupCpuController { class CgroupV1Subsystem: public CgroupSubsystem { public: + CgroupV1Subsystem(CgroupV1Controller* cpuset, + CgroupV1CpuController* cpu, + CgroupV1Controller* cpuacct, + CgroupV1Controller* pids, + CgroupV1MemoryController* memory); + jlong kernel_memory_usage_in_bytes(); jlong kernel_memory_limit_in_bytes(); jlong kernel_memory_max_usage_in_bytes(); @@ -155,18 +174,6 @@ class CgroupV1Subsystem: public CgroupSubsystem { CgroupV1Controller* _cpuacct = nullptr; CgroupV1Controller* _pids = nullptr; - public: - CgroupV1Subsystem(CgroupV1Controller* cpuset, - CgroupV1CpuController* cpu, - CgroupV1Controller* cpuacct, - CgroupV1Controller* pids, - CgroupV1MemoryController* memory) : - _memory(new CachingCgroupController(memory)), - _cpuset(cpuset), - _cpu(new CachingCgroupController(cpu)), - _cpuacct(cpuacct), - _pids(pids) { - } }; #endif // CGROUP_V1_SUBSYSTEM_LINUX_HPP diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 8f7e12d095474..62e8cac3a62b2 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -25,6 +25,22 @@ #include "cgroupV2Subsystem_linux.hpp" #include "cgroupUtil_linux.hpp" +// Constructor +CgroupV2Controller::CgroupV2Controller(char* mount_path, + char *cgroup_path, + bool ro) : _read_only(ro), + _path(construct_path(mount_path, cgroup_path)) { + _cgroup_path = os::strdup(cgroup_path); + _mount_point = os::strdup(mount_path); +} +// Shallow copy constructor +CgroupV2Controller::CgroupV2Controller(const CgroupV2Controller& o) : + _read_only(o._read_only), + _path(o._path) { + _cgroup_path = o._cgroup_path; + _mount_point = o._mount_point; +} + /* cpu_shares * * Return the amount of cpu shares available to the process @@ -95,6 +111,17 @@ int CgroupV2CpuController::cpu_quota() { return limit; } +// Constructor +CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory, + CgroupV2CpuController* cpu, + CgroupV2Controller unified) : + _unified(unified) { + CgroupUtil::adjust_controller(memory); + CgroupUtil::adjust_controller(cpu); + _memory = new CachingCgroupController(memory); + _cpu = new CachingCgroupController(cpu); +} + bool CgroupV2Subsystem::is_containerized() { return _unified.is_read_only() && _memory->controller()->is_read_only() && @@ -264,6 +291,18 @@ jlong memory_swap_limit_value(CgroupV2Controller* ctrl) { return swap_limit; } +void CgroupV2Controller::set_subsystem_path(const char* cgroup_path) { + if (_path != nullptr) { + os::free(_path); + } + _path = construct_path(_mount_point, cgroup_path); +} + +// For cgv2 we only need hierarchy walk if the cgroup path isn't '/' (root) +bool CgroupV2Controller::needs_hierarchy_adjustment() { + return strcmp(_cgroup_path, "/") != 0; +} + void CgroupV2MemoryController::print_version_specific_info(outputStream* st, julong phys_mem) { jlong swap_current = memory_swap_current_value(reader()); jlong swap_limit = memory_swap_limit_value(reader()); @@ -272,7 +311,7 @@ void CgroupV2MemoryController::print_version_specific_info(outputStream* st, jul OSContainer::print_container_helper(st, swap_limit, "memory_swap_max_limit_in_bytes"); } -char* CgroupV2Controller::construct_path(char* mount_path, char *cgroup_path) { +char* CgroupV2Controller::construct_path(char* mount_path, const char* cgroup_path) { stringStream ss; ss.print_raw(mount_path); if (strcmp(cgroup_path, "/") != 0) { diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index cd100f298747c..527573644a816 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -26,39 +26,28 @@ #define CGROUP_V2_SUBSYSTEM_LINUX_HPP #include "cgroupSubsystem_linux.hpp" +#include "cgroupUtil_linux.hpp" class CgroupV2Controller: public CgroupController { private: - /* the mount path of the cgroup v2 hierarchy */ - char *_mount_path; - /* The cgroup path for the controller */ - char *_cgroup_path; bool _read_only; /* Constructed full path to the subsystem directory */ char *_path; - static char* construct_path(char* mount_path, char *cgroup_path); + static char* construct_path(char* mount_path, const char *cgroup_path); public: - CgroupV2Controller(char* mount_path, - char *cgroup_path, - bool ro) : _mount_path(os::strdup(mount_path)), - _cgroup_path(os::strdup(cgroup_path)), - _read_only(ro), - _path(construct_path(mount_path, cgroup_path)) { - } + CgroupV2Controller(char* mount_path, char *cgroup_path, bool ro); // Shallow copy constructor - CgroupV2Controller(const CgroupV2Controller& o) : - _mount_path(o._mount_path), - _cgroup_path(o._cgroup_path), - _read_only(o._read_only), - _path(o._path) { - } + CgroupV2Controller(const CgroupV2Controller& o); ~CgroupV2Controller() { // At least one controller exists with references to the paths } - char *subsystem_path() override { return _path; } + const char* subsystem_path() override { return _path; } + bool needs_hierarchy_adjustment() override; + // Allow for optional updates of the subsystem path + void set_subsystem_path(const char* cgroup_path); bool is_read_only() override { return _read_only; } }; @@ -75,6 +64,17 @@ class CgroupV2CpuController: public CgroupCpuController { bool is_read_only() override { return reader()->is_read_only(); } + const char* subsystem_path() { + return reader()->subsystem_path(); + } + bool needs_hierarchy_adjustment() override { + return reader()->needs_hierarchy_adjustment(); + } + void set_subsystem_path(const char* cgroup_path) { + reader()->set_subsystem_path(cgroup_path); + } + const char* mount_point() { return reader()->mount_point(); } + const char* cgroup_path() override { return reader()->cgroup_path(); } }; class CgroupV2MemoryController final: public CgroupMemoryController { @@ -97,6 +97,17 @@ class CgroupV2MemoryController final: public CgroupMemoryController { bool is_read_only() override { return reader()->is_read_only(); } + const char* subsystem_path() { + return reader()->subsystem_path(); + } + bool needs_hierarchy_adjustment() override { + return reader()->needs_hierarchy_adjustment(); + } + void set_subsystem_path(const char* cgroup_path) { + reader()->set_subsystem_path(cgroup_path); + } + const char* mount_point() { return reader()->mount_point(); } + const char* cgroup_path() override { return reader()->cgroup_path(); } }; class CgroupV2Subsystem: public CgroupSubsystem { @@ -110,13 +121,9 @@ class CgroupV2Subsystem: public CgroupSubsystem { CgroupV2Controller* unified() { return &_unified; } public: - CgroupV2Subsystem(CgroupV2MemoryController* memory, + CgroupV2Subsystem(CgroupV2MemoryController * memory, CgroupV2CpuController* cpu, - CgroupV2Controller unified) : - _unified(unified), - _memory(new CachingCgroupController(memory)), - _cpu(new CachingCgroupController(cpu)) { - } + CgroupV2Controller unified); char * cpu_cpuset_cpus() override; char * cpu_cpuset_memory_nodes() override; diff --git a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp index 0f054a3bd7293..f2af6372aa49e 100644 --- a/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/runtime/test_cgroupSubsystem_linux.cpp @@ -75,7 +75,7 @@ class TestController : public CgroupController { char* _path; public: TestController(char* p): _path(p) {} - char* subsystem_path() override { + const char* subsystem_path() override { return _path; }; bool is_read_only() override { @@ -470,4 +470,88 @@ TEST(cgroupTest, set_cgroupv2_subsystem_path) { } } +TEST(cgroupTest, cgroupv2_is_hierarchy_walk_needed) { + bool controller_read_only = false; // value irrelevant; + CgroupV2Controller* test = new CgroupV2Controller((char*)"/sys/fs/cgroup", + (char*)"/" /* cgroup_path */, + controller_read_only); + EXPECT_FALSE(test->needs_hierarchy_adjustment()); + test = new CgroupV2Controller((char*)"/sys/fs/cgroup", + (char*)"/bar" /* cgroup_path */, + controller_read_only); + EXPECT_TRUE(test->needs_hierarchy_adjustment()); + test = new CgroupV2Controller((char*)"/sys/fs/cgroup/b", + (char*)"/a/b" /* cgroup_path */, + controller_read_only); + EXPECT_TRUE(test->needs_hierarchy_adjustment()); + + CgroupCpuController* test2 = new CgroupV2CpuController(CgroupV2Controller((char*)"/sys/fs/cgroup", + (char*)"/" /* cgroup_path */, + controller_read_only)); + EXPECT_FALSE(test2->needs_hierarchy_adjustment()); + test2 = new CgroupV2CpuController(CgroupV2Controller((char*)"/sys/fs/cgroup", + (char*)"/bar" /* cgroup_path */, + controller_read_only)); + EXPECT_TRUE(test2->needs_hierarchy_adjustment()); + test2 = new CgroupV2CpuController(CgroupV2Controller((char*)"/sys/fs/cgroup/b", + (char*)"/a/b" /* cgroup_path */, + controller_read_only)); + EXPECT_TRUE(test2->needs_hierarchy_adjustment()); + + CgroupMemoryController* test3 = new CgroupV2MemoryController(CgroupV2Controller((char*)"/sys/fs/cgroup", + (char*)"/" /* cgroup_path */, + controller_read_only)); + EXPECT_FALSE(test3->needs_hierarchy_adjustment()); + test3 = new CgroupV2MemoryController(CgroupV2Controller((char*)"/sys/fs/cgroup", + (char*)"/bar" /* cgroup_path */, + controller_read_only)); + EXPECT_TRUE(test3->needs_hierarchy_adjustment()); + test3 = new CgroupV2MemoryController(CgroupV2Controller((char*)"/sys/fs/cgroup/b", + (char*)"/a/b" /* cgroup_path */, + controller_read_only)); + EXPECT_TRUE(test3->needs_hierarchy_adjustment()); +} + +TEST(cgroupTest, cgroupv1_is_hierarchy_walk_needed) { + bool controller_read_only = true; // shouldn't matter; + CgroupV1Controller* test = new CgroupV1Controller((char*)"/a/b/c" /* root */, + (char*)"/sys/fs/cgroup/memory" /* mount_path */, + controller_read_only); + test->set_subsystem_path((char*)"/a/b/c"); + EXPECT_FALSE(test->needs_hierarchy_adjustment()); + test->set_subsystem_path((char*)"/"); + EXPECT_TRUE(test->needs_hierarchy_adjustment()); + test = new CgroupV1Controller((char*)"/a/b/c" /* root */, + (char*)"/"/* mount_path */, + controller_read_only); + test->set_subsystem_path((char*)"/"); + EXPECT_TRUE(test->needs_hierarchy_adjustment()); + + CgroupCpuController* test2 = new CgroupV1CpuController(CgroupV1Controller((char*)"/a/b/c" /* root */, + (char*)"/sys/fs/cgroup/memory" /* mount_path */, + controller_read_only)); + static_cast(test2)->set_subsystem_path((char*)"/a/b/c"); + EXPECT_FALSE(test2->needs_hierarchy_adjustment()); + static_cast(test2)->set_subsystem_path((char*)"/"); + EXPECT_TRUE(test2->needs_hierarchy_adjustment()); + test2 = new CgroupV1CpuController(CgroupV1Controller((char*)"/a/b/c" /* root */, + (char*)"/"/* mount_path */, + controller_read_only)); + static_cast(test2)->set_subsystem_path((char*)"/"); + EXPECT_TRUE(test2->needs_hierarchy_adjustment()); + + CgroupMemoryController* test3 = new CgroupV1MemoryController(CgroupV1Controller((char*)"/a/b/c" /* root */, + (char*)"/sys/fs/cgroup/memory" /* mount_path */, + controller_read_only)); + static_cast(test3)->set_subsystem_path((char*)"/a/b/c"); + EXPECT_FALSE(test3->needs_hierarchy_adjustment()); + static_cast(test3)->set_subsystem_path((char*)"/"); + EXPECT_TRUE(test3->needs_hierarchy_adjustment()); + test3 = new CgroupV1MemoryController(CgroupV1Controller((char*)"/a/b/c" /* root */, + (char*)"/"/* mount_path */, + controller_read_only)); + static_cast(test3)->set_subsystem_path((char*)"/"); + EXPECT_TRUE(test3->needs_hierarchy_adjustment()); +} + #endif // LINUX