8322420: [Linux] cgroup v2: Limits in parent nested control groups are not detected
Reviewed-by: stuefe, asmehra
This commit is contained in:
parent
5977888500
commit
55a7cf1445
@ -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;
|
||||
|
@ -103,9 +103,15 @@
|
||||
}
|
||||
|
||||
class CgroupController: public CHeapObj<mtInternal> {
|
||||
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<mtInternal> {
|
||||
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<mtInternal> {
|
||||
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<mtInternal> {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<CgroupMemoryController>(memory);
|
||||
_cpu = new CachingCgroupController<CgroupCpuController>(cpu);
|
||||
}
|
||||
|
||||
bool CgroupV1Subsystem::is_containerized() {
|
||||
// containerized iff all required controllers are mounted
|
||||
// read-only. See OSContainer::is_containerized() for
|
||||
|
@ -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<CgroupMemoryController>(memory)),
|
||||
_cpuset(cpuset),
|
||||
_cpu(new CachingCgroupController<CgroupCpuController>(cpu)),
|
||||
_cpuacct(cpuacct),
|
||||
_pids(pids) {
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CGROUP_V1_SUBSYSTEM_LINUX_HPP
|
||||
|
@ -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<CgroupMemoryController>(memory);
|
||||
_cpu = new CachingCgroupController<CgroupCpuController>(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) {
|
||||
|
@ -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<CgroupMemoryController>(memory)),
|
||||
_cpu(new CachingCgroupController<CgroupCpuController>(cpu)) {
|
||||
}
|
||||
CgroupV2Controller unified);
|
||||
|
||||
char * cpu_cpuset_cpus() override;
|
||||
char * cpu_cpuset_memory_nodes() override;
|
||||
|
@ -75,7 +75,7 @@ private:
|
||||
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<CgroupV1CpuController*>(test2)->set_subsystem_path((char*)"/a/b/c");
|
||||
EXPECT_FALSE(test2->needs_hierarchy_adjustment());
|
||||
static_cast<CgroupV1CpuController*>(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<CgroupV1CpuController*>(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<CgroupV1MemoryController*>(test3)->set_subsystem_path((char*)"/a/b/c");
|
||||
EXPECT_FALSE(test3->needs_hierarchy_adjustment());
|
||||
static_cast<CgroupV1MemoryController*>(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<CgroupV1MemoryController*>(test3)->set_subsystem_path((char*)"/");
|
||||
EXPECT_TRUE(test3->needs_hierarchy_adjustment());
|
||||
}
|
||||
|
||||
#endif // LINUX
|
||||
|
Loading…
x
Reference in New Issue
Block a user