8322420: [Linux] cgroup v2: Limits in parent nested control groups are not detected

Reviewed-by: stuefe, asmehra
This commit is contained in:
Severin Gehwolf 2024-09-11 13:51:31 +00:00
parent 5977888500
commit 55a7cf1445
9 changed files with 362 additions and 111 deletions

View File

@ -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;

View File

@ -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> {

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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