From 4a22816ccb4d506954dca6cf708744ef59f08d23 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Mon, 29 Apr 2024 16:29:58 +0200 Subject: [PATCH] Finish the hierarchy walking experiment - Includes cgroups v1 code for cpu/memory - Includes cgroups v2 code for cpu/memory --- .../os/linux/cgroupSubsystem_linux.cpp | 16 ++ .../os/linux/cgroupSubsystem_linux.hpp | 13 +- .../os/linux/cgroupV1Subsystem_linux.cpp | 159 ++++++++++++------ .../os/linux/cgroupV1Subsystem_linux.hpp | 22 +-- .../os/linux/cgroupV2Subsystem_linux.cpp | 107 ++++++++++++ .../os/linux/cgroupV2Subsystem_linux.hpp | 19 ++- .../os/linux/test_cgroupSubsystem_linux.cpp | 52 ++++++ 7 files changed, 315 insertions(+), 73 deletions(-) diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 7c52b55eb0feb..d9158c080f976 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -522,6 +522,22 @@ jlong CgroupSubsystem::memory_limit_in_bytes() { return mem_limit; } +CgroupMemoryController* CgroupSubsystem::adjust_controller(CgroupMemoryController* mem) { + if (mem->needs_hierarchy_adjustment()) { + julong phys_mem = os::Linux::physical_memory(); + return mem->adjust_controller(phys_mem); + } + return mem; +} + +CgroupCpuController* CgroupSubsystem::adjust_controller(CgroupCpuController* cpu) { + if (cpu->needs_hierarchy_adjustment()) { + int cpu_total = os::Linux::active_processor_count(); + return cpu->adjust_controller(cpu_total); + } + return cpu; +} + // CgroupSubsystem implementations jlong CgroupSubsystem::memory_and_swap_limit_in_bytes() { diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index 468f04f9db660..1763c120d5166 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -70,8 +70,12 @@ #define PIDS_IDX 4 class CgroupController: public CHeapObj { + protected: + char* _cgroup_path; public: + char* cgroup_path() { return _cgroup_path; } virtual char *subsystem_path() = 0; + virtual bool needs_hierarchy_adjustment() { return false; } }; PRAGMA_DIAG_PUSH @@ -222,6 +226,7 @@ template int cg_file_multi_line_ctrl(CgroupController* c, } PRAGMA_DIAG_POP + class CachedMetric : public CHeapObj{ private: volatile jlong _metric; @@ -267,7 +272,8 @@ class CgroupCpuController: public CgroupController { virtual int cpu_quota() = 0; virtual int cpu_period() = 0; virtual int cpu_shares() = 0; - virtual char *subsystem_path() = 0; + virtual bool needs_hierarchy_adjustment() = 0; + virtual CgroupCpuController* adjust_controller(int host_cpus) = 0; }; class CgroupMemoryController: public CgroupController { @@ -281,9 +287,10 @@ class CgroupMemoryController: public CgroupController { virtual jlong rss_usage_in_bytes() = 0; virtual jlong cache_usage_in_bytes() = 0; virtual char *subsystem_path() = 0; + virtual CgroupMemoryController* adjust_controller(julong phys_mem) = 0; + virtual bool needs_hierarchy_adjustment() = 0; }; - class CgroupSubsystem: public CHeapObj { public: jlong memory_limit_in_bytes(); @@ -309,6 +316,8 @@ class CgroupSubsystem: public CHeapObj { jlong rss_usage_in_bytes(); jlong cache_usage_in_bytes(); virtual void print_version_specific_info(outputStream* st) = 0; + static CgroupMemoryController* adjust_controller(CgroupMemoryController* m); + static CgroupCpuController* adjust_controller(CgroupCpuController* c); }; // 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 991b0888ea2a3..2c9917eddb218 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -39,6 +39,13 @@ * on the contents of the mountinfo and cgroup files. */ void CgroupV1Controller::set_subsystem_path(char *cgroup_path) { + if (_cgroup_path != nullptr) { + os::free(_cgroup_path); + } + if (_path != nullptr) { + os::free(_path); + } + _cgroup_path = os::strdup(cgroup_path); stringStream ss; if (_root != nullptr && cgroup_path != nullptr) { if (strcmp(_root, "/") == 0) { @@ -66,32 +73,106 @@ 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 - */ -jlong CgroupV1MemoryController::uses_mem_hierarchy() { - jlong 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; +bool CgroupV1MemoryController::needs_hierarchy_adjustment() { + return CgroupV1Controller::needs_hierarchy_adjustment(); +} + +bool CgroupV1CpuController::needs_hierarchy_adjustment() { + return CgroupV1Controller::needs_hierarchy_adjustment(); +} + +CgroupV1MemoryController* CgroupV1MemoryController::adjust_controller(julong phys_mem) { + log_trace(os, container)("Adjusting v1 controller path for memory: %s", subsystem_path()); + CgroupV1Controller* base_ctrl = static_cast(this); + assert(base_ctrl->cgroup_path() != nullptr, "invariant"); + char* orig = os::strdup(base_ctrl->cgroup_path()); + char* cg_path = os::strdup(orig); + char* last_slash; + jlong limit = read_memory_limit_in_bytes(phys_mem); + bool path_iterated = false; + while (limit < 0 && (last_slash = strrchr(cg_path, '/')) != cg_path) { + *last_slash = '\0'; // strip path + // update to shortened path and try again + base_ctrl->set_subsystem_path(cg_path); + limit = read_memory_limit_in_bytes(phys_mem); + path_iterated = true; + if (limit > 0) { + log_trace(os, container)("Adjusted v1 controller path for memory to: %s", subsystem_path()); + os::free(cg_path); + os::free(orig); + return this; + } + } + // no lower limit found or limit at leaf + os::free(cg_path); + if (path_iterated) { + base_ctrl->set_subsystem_path((char*)"/"); + limit = read_memory_limit_in_bytes(phys_mem); + if (limit > 0) { + // handle limit set at mount point + log_trace(os, container)("Adjusted v1 controller path for memory to: %s", subsystem_path()); + os::free(orig); + return this; + } + log_trace(os, container)("No lower limit found in hierarchy %s, adjusting to original path %s", + base_ctrl->mount_point(), orig); + base_ctrl->set_subsystem_path(orig); + } else { + log_trace(os, container)("Lowest limit for memory at leaf: %s", + base_ctrl->subsystem_path()); } - log_trace(os, container)("Use Hierarchy is: " JLONG_FORMAT, use_hierarchy); - return use_hierarchy; + os::free(orig); + return this; } -void CgroupV1MemoryController::set_subsystem_path(char *cgroup_path) { - CgroupV1Controller::set_subsystem_path(cgroup_path); - jlong hierarchy = uses_mem_hierarchy(); - if (hierarchy > 0) { - set_hierarchical(true); +CgroupV1CpuController* CgroupV1CpuController::adjust_controller(int host_cpus) { + log_trace(os, container)("Adjusting v1 controller path for cpu: %s", subsystem_path()); + CgroupV1Controller* base_ctrl = static_cast(this); + assert(base_ctrl->cgroup_path() != nullptr, "invariant"); + assert(host_cpus > 0, "Negative host cpus?"); + char* orig = os::strdup(base_ctrl->cgroup_path()); + char* cg_path = os::strdup(orig); + char* last_slash; + int cpus = CgroupUtil::processor_count(this, host_cpus); + bool path_iterated = false; + while (cpus == host_cpus && (last_slash = strrchr(cg_path, '/')) != cg_path) { + *last_slash = '\0'; // strip path + // update to shortened path and try again + base_ctrl->set_subsystem_path((char*)cg_path); + cpus = CgroupUtil::processor_count(this, host_cpus); + path_iterated = true; + if (cpus != host_cpus) { + log_trace(os, container)("Adjusted v1 controller path for cpu to: %s", subsystem_path()); + os::free(cg_path); + os::free(orig); + return this; + } + } + // no lower limit found or limit at leaf + os::free(cg_path); + if (path_iterated) { + base_ctrl->set_subsystem_path((char*)"/"); + cpus = CgroupUtil::processor_count(this, host_cpus); + if (cpus != host_cpus) { + // handle limit set at mount point + log_trace(os, container)("Adjusted v1 controller path for cpu to: %s", subsystem_path()); + os::free(orig); + return this; + } + log_trace(os, container)("No lower limit found in hierarchy %s, adjusting to original path %s", + base_ctrl->mount_point(), orig); + base_ctrl->set_subsystem_path(orig); + } else { + log_trace(os, container)("Lowest limit for cpu at leaf: %s", + base_ctrl->subsystem_path()); } + os::free(orig); + return this; +} + +bool CgroupV1Controller::needs_hierarchy_adjustment() { + assert(_cgroup_path != nullptr, "sanity"); + return strcmp(_root, _cgroup_path) != 0; } static inline @@ -125,25 +206,7 @@ jlong CgroupV1MemoryController::read_memory_limit_in_bytes(julong phys_mem) { return OSCONTAINER_ERROR; } log_trace(os, container)("Memory Limit is: " JULONG_FORMAT, memlimit); - if (memlimit >= phys_mem) { - log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); - if (is_hierarchical()) { - julong hier_memlimit; - 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 >= 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 { @@ -165,7 +228,6 @@ 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; int err = cg_file_contents_ctrl(static_cast(this), "/memory.memsw.limit_in_bytes", JULONG_FORMAT, &memswlimit); if (err != 0) { @@ -174,20 +236,7 @@ jlong CgroupV1MemoryController::read_mem_swap(julong host_total_memsw) { } log_trace(os, container)("Memory and Swap Limit is: " JULONG_FORMAT, 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"; - err = cg_file_multi_line_ctrl(static_cast(this), "/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 { - return (jlong)hier_memswlimit; - } - } + log_trace(os, container)("Memory and Swap Limit is: Unlimited"); return (jlong)-1; } else { return (jlong)memswlimit; diff --git a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index a0716cb6f6b0c..186557de5ec6a 100644 --- a/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -44,17 +44,18 @@ class CgroupV1Controller: public CgroupController { _root = os::strdup(root); _mount_point = os::strdup(mountpoint); _path = nullptr; + _cgroup_path = nullptr; } virtual void set_subsystem_path(char *cgroup_path); char *subsystem_path() { return _path; } + bool needs_hierarchy_adjustment(); + char *mount_point() { return _mount_point; } }; 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); @@ -67,19 +68,14 @@ class CgroupV1MemoryController: public CgroupV1Controller, public CgroupMemoryCo jlong kernel_memory_limit_in_bytes(julong host_mem); jlong kernel_memory_max_usage_in_bytes(); char *subsystem_path() override { return CgroupV1Controller::subsystem_path(); } + bool needs_hierarchy_adjustment(); + CgroupV1MemoryController* adjust_controller(julong phys_mem); 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(char *root, char *mountpoint) : CgroupV1Controller(root, mountpoint) { - _uses_mem_hierarchy = false; } }; @@ -95,6 +91,8 @@ class CgroupV1CpuController: public CgroupV1Controller, public CgroupCpuControll CgroupV1CpuController(char *root, char *mountpoint) : CgroupV1Controller(root, mountpoint) { } char *subsystem_path() override { return CgroupV1Controller::subsystem_path(); } + bool needs_hierarchy_adjustment(); + CgroupV1CpuController* adjust_controller(int host_cpus); }; class CgroupV1Subsystem: public CgroupSubsystem { @@ -135,10 +133,12 @@ class CgroupV1Subsystem: public CgroupSubsystem { CgroupV1Controller* pids, CgroupV1MemoryController* memory) { _cpuset = cpuset; - _cpu = new CachingCgroupController(cpu); + CgroupCpuController* c = adjust_controller(cpu); + _cpu = new CachingCgroupController(c); _cpuacct = cpuacct; _pids = pids; - _memory = new CachingCgroupController(memory); + CgroupMemoryController* m = adjust_controller(memory); + _memory = new CachingCgroupController(m); } }; diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 44a152b491a08..5e859271c1e9d 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -308,6 +308,113 @@ jlong CgroupV2MemoryController::read_memory_limit_in_bytes(julong phys_mem) { return limit; } +void CgroupV2Controller::set_subsystem_path(char* cgroup_path) { + if (_path != nullptr) { + os::free(_path); + } + _path = construct_path(_mount_path, cgroup_path); +} + +bool CgroupV2MemoryController::needs_hierarchy_adjustment() { + return CgroupV2Controller::needs_hierarchy_adjustment(); +} + +bool CgroupV2CpuController::needs_hierarchy_adjustment() { + return CgroupV2Controller::needs_hierarchy_adjustment(); +} + +CgroupCpuController* CgroupV2CpuController::adjust_controller(int host_cpus) { + log_trace(os, container)("Adjusting v2 controller path for cpu: %s", subsystem_path()); + CgroupV2Controller* base_ctrl = static_cast(this); + assert(base_ctrl->cgroup_path() != nullptr, "invariant"); + assert(host_cpus > 0, "Negative host cpus?"); + char* orig = os::strdup(base_ctrl->cgroup_path()); + char* cg_path = os::strdup(orig); + char* last_slash; + int cpus = CgroupUtil::processor_count(this, host_cpus); + bool path_iterated = false; + while (cpus == host_cpus && (last_slash = strrchr(cg_path, '/')) != cg_path) { + *last_slash = '\0'; // strip path + // update to shortened path and try again + base_ctrl->set_subsystem_path(cg_path); + cpus = CgroupUtil::processor_count(this, host_cpus); + path_iterated = true; + if (cpus != host_cpus) { + log_trace(os, container)("Adjusted v2 controller path for cpu to: %s", subsystem_path()); + os::free(cg_path); + os::free(orig); + return this; + } + } + os::free(cg_path); + if (path_iterated) { + base_ctrl->set_subsystem_path((char*)"/"); + cpus = CgroupUtil::processor_count(this, host_cpus); + if (cpus != host_cpus) { + // handle limit set at mount point + log_trace(os, container)("Adjusted v2 controller path for cpu to: %s", subsystem_path()); + os::free(orig); + return this; + } + log_trace(os, container)("No lower limit found in hierarchy %s, adjusting to original path %s", + base_ctrl->mount_point(), orig); + base_ctrl->set_subsystem_path(orig); + } else { + log_trace(os, container)("Lowest limit for cpu at leaf: %s", + base_ctrl->subsystem_path()); + } + os::free(orig); + return this; +} + +CgroupMemoryController* CgroupV2MemoryController::adjust_controller(julong phys_mem) { + log_trace(os, container)("Adjusting v2 controller path for memory: %s", subsystem_path()); + CgroupV2Controller* base_ctrl = static_cast(this); + assert(base_ctrl->cgroup_path() != nullptr, "invariant"); + char* orig = os::strdup(base_ctrl->cgroup_path()); + char* cg_path = os::strdup(orig); + char* last_slash; + jlong limit = read_memory_limit_in_bytes(phys_mem); + bool path_iterated = false; + while (limit < 0 && (last_slash = strrchr(cg_path, '/')) != cg_path) { + *last_slash = '\0'; // strip path + // update to shortened path and try again + base_ctrl->set_subsystem_path(cg_path); + limit = read_memory_limit_in_bytes(phys_mem); + path_iterated = true; + if (limit > 0) { + log_trace(os, container)("Adjusted v2 controller path for memory to: %s", subsystem_path()); + os::free(cg_path); + os::free(orig); + return this; + } + } + // no lower limit found or limit at leaf + os::free(cg_path); + if (path_iterated) { + base_ctrl->set_subsystem_path((char*)"/"); + limit = read_memory_limit_in_bytes(phys_mem); + if (limit > 0) { + // handle limit set at mount point + log_trace(os, container)("Adjusted v2 controller path for memory to: %s", subsystem_path()); + os::free(orig); + return this; + } + log_trace(os, container)("No lower limit found in hierarchy %s, adjusting to original path %s", + base_ctrl->mount_point(), orig); + base_ctrl->set_subsystem_path(orig); + } else { + log_trace(os, container)("Lowest limit for memory at leaf: %s", + base_ctrl->subsystem_path()); + } + os::free(orig); + return this; +} + +// 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 CgroupV2Subsystem::print_version_specific_info(outputStream* st) { char* mem_swp_current_str = mem_swp_current_val(static_cast(_unified)); diff --git a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 1d81d3b0ab6c5..e5c651b7bcaa4 100644 --- a/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -31,8 +31,6 @@ 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; /* Constructed full path to the subsystem directory */ char *_path; @@ -40,12 +38,17 @@ class CgroupV2Controller: public CgroupController { public: CgroupV2Controller(char * mount_path, char *cgroup_path) { - _mount_path = mount_path; + _mount_path = os::strdup(mount_path); _cgroup_path = os::strdup(cgroup_path); _path = construct_path(mount_path, cgroup_path); } char *subsystem_path() { return _path; } + bool needs_hierarchy_adjustment(); + char * cgroup_path() { return _cgroup_path; } + char * mount_point() { return _mount_path; } + // Allow for optional updates of the subsystem path + void set_subsystem_path(char* cgroup_path); }; class CgroupV2CpuController: public CgroupV2Controller, public CgroupCpuController { @@ -55,7 +58,9 @@ class CgroupV2CpuController: public CgroupV2Controller, public CgroupCpuControll int cpu_quota(); int cpu_period(); int cpu_shares(); + bool needs_hierarchy_adjustment(); char *subsystem_path() { return CgroupV2Controller::subsystem_path(); } + CgroupCpuController* adjust_controller(int host_cpus); }; class CgroupV2MemoryController: public CgroupV2Controller, public CgroupMemoryController { @@ -71,7 +76,9 @@ class CgroupV2MemoryController: public CgroupV2Controller, public CgroupMemoryCo jlong memory_max_usage_in_bytes(); jlong rss_usage_in_bytes(); jlong cache_usage_in_bytes(); + bool needs_hierarchy_adjustment(); char *subsystem_path() { return CgroupV2Controller::subsystem_path(); } + CgroupMemoryController* adjust_controller(julong phys_mem); }; class CgroupV2Subsystem: public CgroupSubsystem { @@ -88,8 +95,10 @@ class CgroupV2Subsystem: public CgroupSubsystem { CgroupV2Subsystem(CgroupV2MemoryController * memory, CgroupV2CpuController* cpu) { _unified = memory; // Use memory for now, should have all separate later - _memory = new CachingCgroupController(memory); - _cpu = new CachingCgroupController(cpu); + CgroupMemoryController* m = adjust_controller(memory); + _memory = new CachingCgroupController(m); + CgroupCpuController* c = adjust_controller(cpu); + _cpu = new CachingCgroupController(c); } jlong read_memory_limit_in_bytes(); diff --git a/test/hotspot/gtest/os/linux/test_cgroupSubsystem_linux.cpp b/test/hotspot/gtest/os/linux/test_cgroupSubsystem_linux.cpp index 307894f7d19b5..2e54e0dc23cda 100644 --- a/test/hotspot/gtest/os/linux/test_cgroupSubsystem_linux.cpp +++ b/test/hotspot/gtest/os/linux/test_cgroupSubsystem_linux.cpp @@ -358,4 +358,56 @@ TEST(cgroupTest, set_cgroupv2_subsystem_path) { } } +TEST(cgroupTest, cgroupv2_is_hierarchy_walk_needed) { + CgroupV2Controller* test = new CgroupV2Controller( (char*)"/sys/fs/cgroup", (char*)"/" /* cgroup_path */); + ASSERT_FALSE(test->needs_hierarchy_adjustment()); + test = new CgroupV2Controller( (char*)"/sys/fs/cgroup", (char*)"/bar" /* cgroup_path */); + ASSERT_TRUE(test->needs_hierarchy_adjustment()); + test = new CgroupV2Controller( (char*)"/sys/fs/cgroup/b", (char*)"/a/b" /* cgroup_path */); + ASSERT_TRUE(test->needs_hierarchy_adjustment()); + + CgroupCpuController* test2 = new CgroupV2CpuController( (char*)"/sys/fs/cgroup", (char*)"/" /* cgroup_path */); + ASSERT_FALSE(test2->needs_hierarchy_adjustment()); + test2 = new CgroupV2CpuController( (char*)"/sys/fs/cgroup", (char*)"/bar" /* cgroup_path */); + ASSERT_TRUE(test2->needs_hierarchy_adjustment()); + test2 = new CgroupV2CpuController( (char*)"/sys/fs/cgroup/b", (char*)"/a/b" /* cgroup_path */); + ASSERT_TRUE(test2->needs_hierarchy_adjustment()); + + CgroupMemoryController* test3 = new CgroupV2MemoryController( (char*)"/sys/fs/cgroup", (char*)"/" /* cgroup_path */); + ASSERT_FALSE(test3->needs_hierarchy_adjustment()); + test3 = new CgroupV2MemoryController( (char*)"/sys/fs/cgroup", (char*)"/bar" /* cgroup_path */); + ASSERT_TRUE(test3->needs_hierarchy_adjustment()); + test3 = new CgroupV2MemoryController( (char*)"/sys/fs/cgroup/b", (char*)"/a/b" /* cgroup_path */); + ASSERT_TRUE(test3->needs_hierarchy_adjustment()); +} + +TEST(cgroupTest, cgroupv1_is_hierarchy_walk_needed) { + CgroupV1Controller* test = new CgroupV1Controller( (char*)"/a/b/c" /* root */, (char*)"/sys/fs/cgroup/memory" /* mount_path */); + test->set_subsystem_path((char*)"/a/b/c"); + ASSERT_FALSE(test->needs_hierarchy_adjustment()); + test->set_subsystem_path((char*)"/"); + ASSERT_TRUE(test->needs_hierarchy_adjustment()); + test = new CgroupV1Controller( (char*)"/a/b/c" /* root */, (char*)"/"/* mount_path */); + test->set_subsystem_path((char*)"/"); + ASSERT_TRUE(test->needs_hierarchy_adjustment()); + + CgroupCpuController* test2 = new CgroupV1CpuController( (char*)"/a/b/c" /* root */, (char*)"/sys/fs/cgroup/memory" /* mount_path */); + static_cast(test2)->set_subsystem_path((char*)"/a/b/c"); + ASSERT_FALSE(test2->needs_hierarchy_adjustment()); + static_cast(test2)->set_subsystem_path((char*)"/"); + ASSERT_TRUE(test2->needs_hierarchy_adjustment()); + test2 = new CgroupV1CpuController( (char*)"/a/b/c" /* root */, (char*)"/"/* mount_path */); + static_cast(test2)->set_subsystem_path((char*)"/"); + ASSERT_TRUE(test2->needs_hierarchy_adjustment()); + + CgroupMemoryController* test3 = new CgroupV1MemoryController( (char*)"/a/b/c" /* root */, (char*)"/sys/fs/cgroup/memory" /* mount_path */); + static_cast(test3)->set_subsystem_path((char*)"/a/b/c"); + ASSERT_FALSE(test3->needs_hierarchy_adjustment()); + static_cast(test3)->set_subsystem_path((char*)"/"); + ASSERT_TRUE(test3->needs_hierarchy_adjustment()); + test3 = new CgroupV1MemoryController( (char*)"/a/b/c" /* root */, (char*)"/"/* mount_path */); + static_cast(test3)->set_subsystem_path((char*)"/"); + ASSERT_TRUE(test3->needs_hierarchy_adjustment()); +} + #endif // LINUX