diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index e15054ebbc9..d0bcb2b509b 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ #include #include #include +#include #include "cgroupSubsystem_linux.hpp" #include "cgroupV1Subsystem_linux.hpp" #include "cgroupV2Subsystem_linux.hpp" @@ -34,8 +35,26 @@ #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" +// Inlined from for portability. +#ifndef CGROUP2_SUPER_MAGIC +# define CGROUP2_SUPER_MAGIC 0x63677270 +#endif + // controller names have to match the *_IDX indices static const char* cg_controller_name[] = { "cpuset", "cpu", "cpuacct", "memory", "pids" }; +static inline int cg_v2_controller_index(const char* name) { + if (strcmp(name, "cpuset") == 0) { + return CPUSET_IDX; + } else if (strcmp(name, "cpu") == 0) { + return CPU_IDX; + } else if (strcmp(name, "memory") == 0) { + return MEMORY_IDX; + } else if (strcmp(name, "pids") == 0) { + return PIDS_IDX; + } else { + return -1; + } +} CgroupSubsystem* CgroupSubsystemFactory::create() { CgroupV1MemoryController* memory = NULL; @@ -46,10 +65,25 @@ CgroupSubsystem* CgroupSubsystemFactory::create() { CgroupInfo cg_infos[CG_INFO_LENGTH]; u1 cg_type_flags = INVALID_CGROUPS_GENERIC; const char* proc_cgroups = "/proc/cgroups"; + const char* sys_fs_cgroup_cgroup_controllers = "/sys/fs/cgroup/cgroup.controllers"; + const char* controllers_file = proc_cgroups; const char* proc_self_cgroup = "/proc/self/cgroup"; const char* proc_self_mountinfo = "/proc/self/mountinfo"; + const char* sys_fs_cgroup = "/sys/fs/cgroup"; + struct statfs fsstat = {}; + bool cgroups_v2_enabled = false; + + // Assume cgroups v2 is usable by the JDK iff /sys/fs/cgroup has the cgroup v2 + // file system magic. If it does not then heuristics are required to determine + // if cgroups v1 is usable or not. + if (statfs(sys_fs_cgroup, &fsstat) != -1) { + cgroups_v2_enabled = (fsstat.f_type == CGROUP2_SUPER_MAGIC); + if (cgroups_v2_enabled) { + controllers_file = sys_fs_cgroup_cgroup_controllers; + } + } - bool valid_cgroup = determine_type(cg_infos, proc_cgroups, proc_self_cgroup, proc_self_mountinfo, &cg_type_flags); + bool valid_cgroup = determine_type(cg_infos, cgroups_v2_enabled, controllers_file, proc_self_cgroup, proc_self_mountinfo, &cg_type_flags); if (!valid_cgroup) { // Could not detect cgroup type @@ -149,84 +183,119 @@ void CgroupSubsystemFactory::set_controller_paths(CgroupInfo* cg_infos, } bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, - const char* proc_cgroups, + bool cgroups_v2_enabled, + const char* controllers_file, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* flags) { FILE *mntinfo = NULL; - FILE *cgroups = NULL; + FILE* controllers = NULL; FILE *cgroup = NULL; char buf[MAXPATHLEN+1]; char *p; - bool is_cgroupsV2; // true iff all required controllers, memory, cpu, cpuacct are enabled // at the kernel level. // pids might not be enabled on older Linux distros (SLES 12.1, RHEL 7.1) // cpuset might not be enabled on newer Linux distros (Fedora 41) - bool all_required_controllers_enabled; + bool all_required_controllers_enabled = true; - /* - * Read /proc/cgroups so as to be able to distinguish cgroups v2 vs cgroups v1. - * - * For cgroups v1 hierarchy (hybrid or legacy), cpu, cpuacct, cpuset, memory controllers - * must have non-zero for the hierarchy ID field and relevant controllers mounted. - * Conversely, for cgroups v2 (unified hierarchy), cpu, cpuacct, cpuset, memory - * controllers must have hierarchy ID 0 and the unified controller mounted. - */ - cgroups = fopen(proc_cgroups, "r"); - if (cgroups == NULL) { - log_debug(os, container)("Can't open %s, %s", proc_cgroups, os::strerror(errno)); + // If cgroups v2 is enabled, open /sys/fs/cgroup/cgroup.controllers. If not, open /proc/cgroups. + controllers = os::fopen(controllers_file, "r"); + if (controllers == nullptr) { + log_debug(os, container)("Can't open %s, %s", controllers_file, os::strerror(errno)); *flags = INVALID_CGROUPS_GENERIC; return false; } - while ((p = fgets(buf, MAXPATHLEN, cgroups)) != NULL) { - char name[MAXPATHLEN+1]; - int hierarchy_id; - int enabled; - - // Format of /proc/cgroups documented via man 7 cgroups - if (sscanf(p, "%s %d %*d %d", name, &hierarchy_id, &enabled) != 3) { - continue; + if (cgroups_v2_enabled) { + /* + * cgroups v2 is enabled. For cgroups v2 (unified hierarchy), the cpu and memory + * controllers must be enabled. + */ + if ((p = fgets(buf, MAXPATHLEN, controllers)) != nullptr) { + char* controller = nullptr; + #define ISSPACE_CHARS " \n\t\r\f\v" + while ((controller = strsep(&p, ISSPACE_CHARS)) != nullptr) { + int i; + if ((i = cg_v2_controller_index(controller)) != -1) { + cg_infos[i]._name = os::strdup(controller); + cg_infos[i]._enabled = true; + if (i == PIDS_IDX || i == CPUSET_IDX) { + log_debug(os, container)("Detected optional %s controller entry in %s", + controller, controllers_file); + } + } + } + #undef ISSPACE_CHARS + } else { + log_debug(os, container)("Can't read %s, %s", controllers_file, os::strerror(errno)); + *flags = INVALID_CGROUPS_V2; + fclose(controllers); + return false; } - if (strcmp(name, "memory") == 0) { - cg_infos[MEMORY_IDX]._name = os::strdup(name); - cg_infos[MEMORY_IDX]._hierarchy_id = hierarchy_id; - cg_infos[MEMORY_IDX]._enabled = (enabled == 1); - } else if (strcmp(name, "cpuset") == 0) { - log_debug(os, container)("Detected optional cpuset controller entry in %s", proc_cgroups); - cg_infos[CPUSET_IDX]._name = os::strdup(name); - cg_infos[CPUSET_IDX]._hierarchy_id = hierarchy_id; - cg_infos[CPUSET_IDX]._enabled = (enabled == 1); - } else if (strcmp(name, "cpu") == 0) { - cg_infos[CPU_IDX]._name = os::strdup(name); - cg_infos[CPU_IDX]._hierarchy_id = hierarchy_id; - cg_infos[CPU_IDX]._enabled = (enabled == 1); - } else if (strcmp(name, "cpuacct") == 0) { - cg_infos[CPUACCT_IDX]._name = os::strdup(name); - cg_infos[CPUACCT_IDX]._hierarchy_id = hierarchy_id; - cg_infos[CPUACCT_IDX]._enabled = (enabled == 1); - } else if (strcmp(name, "pids") == 0) { - log_debug(os, container)("Detected optional pids controller entry in %s", proc_cgroups); - cg_infos[PIDS_IDX]._name = os::strdup(name); - cg_infos[PIDS_IDX]._hierarchy_id = hierarchy_id; - cg_infos[PIDS_IDX]._enabled = (enabled == 1); + for (int i = 0; i < CG_INFO_LENGTH; i++) { + // cgroups v2 does not have cpuacct. + if (i == CPUACCT_IDX) { + continue; + } + // For cgroups v2, cpuacct is rolled into cpu, and the pids and cpuset controllers + // are optional; the remaining controllers, cpu and memory, are required. + if (i == CPU_IDX || i == MEMORY_IDX) { + all_required_controllers_enabled = all_required_controllers_enabled && cg_infos[i]._enabled; + } + if (log_is_enabled(Debug, os, container) && !cg_infos[i]._enabled) { + log_debug(os, container)("controller %s is not enabled", cg_controller_name[i]); + } } - } - fclose(cgroups); + } else { + /* + * The /sys/fs/cgroup filesystem magic hint suggests we have cg v1. Read /proc/cgroups; for + * cgroups v1 hierarchy (hybrid or legacy), cpu, cpuacct, cpuset, and memory controllers must + * have non-zero for the hierarchy ID field and relevant controllers mounted. + */ + while ((p = fgets(buf, MAXPATHLEN, controllers)) != NULL) { + char name[MAXPATHLEN+1]; + int hierarchy_id; + int enabled; - is_cgroupsV2 = true; - all_required_controllers_enabled = true; - for (int i = 0; i < CG_INFO_LENGTH; i++) { - // pids and cpuset controllers are optional. All other controllers are required - if (i != PIDS_IDX && i != CPUSET_IDX) { - is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0; - all_required_controllers_enabled = all_required_controllers_enabled && cg_infos[i]._enabled; + // Format of /proc/cgroups documented via man 7 cgroups + if (sscanf(p, "%s %d %*d %d", name, &hierarchy_id, &enabled) != 3) { + continue; + } + if (strcmp(name, "memory") == 0) { + cg_infos[MEMORY_IDX]._name = os::strdup(name); + cg_infos[MEMORY_IDX]._hierarchy_id = hierarchy_id; + cg_infos[MEMORY_IDX]._enabled = (enabled == 1); + } else if (strcmp(name, "cpuset") == 0) { + cg_infos[CPUSET_IDX]._name = os::strdup(name); + cg_infos[CPUSET_IDX]._hierarchy_id = hierarchy_id; + cg_infos[CPUSET_IDX]._enabled = (enabled == 1); + } else if (strcmp(name, "cpu") == 0) { + cg_infos[CPU_IDX]._name = os::strdup(name); + cg_infos[CPU_IDX]._hierarchy_id = hierarchy_id; + cg_infos[CPU_IDX]._enabled = (enabled == 1); + } else if (strcmp(name, "cpuacct") == 0) { + cg_infos[CPUACCT_IDX]._name = os::strdup(name); + cg_infos[CPUACCT_IDX]._hierarchy_id = hierarchy_id; + cg_infos[CPUACCT_IDX]._enabled = (enabled == 1); + } else if (strcmp(name, "pids") == 0) { + log_debug(os, container)("Detected optional pids controller entry in %s", controllers_file); + cg_infos[PIDS_IDX]._name = os::strdup(name); + cg_infos[PIDS_IDX]._hierarchy_id = hierarchy_id; + cg_infos[PIDS_IDX]._enabled = (enabled == 1); + } } - if (log_is_enabled(Debug, os, container) && !cg_infos[i]._enabled) { - log_debug(os, container)("controller %s is not enabled\n", cg_controller_name[i]); + for (int i = 0; i < CG_INFO_LENGTH; i++) { + // pids controller is optional. All other controllers are required + if (i != PIDS_IDX) { + all_required_controllers_enabled = all_required_controllers_enabled && cg_infos[i]._enabled; + } + if (log_is_enabled(Debug, os, container) && !cg_infos[i]._enabled) { + log_debug(os, container)("controller %s is not enabled\n", cg_controller_name[i]); + } } } + fclose(controllers); if (!all_required_controllers_enabled) { // one or more required controllers disabled, disable container support @@ -268,7 +337,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, continue; } - while (!is_cgroupsV2 && (token = strsep(&controllers, ",")) != NULL) { + while (!cgroups_v2_enabled && (token = strsep(&controllers, ",")) != NULL) { if (strcmp(token, "memory") == 0) { assert(hierarchy_id == cg_infos[MEMORY_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for memory"); cg_infos[MEMORY_IDX]._cgroup_path = os::strdup(cgroup_path); @@ -279,7 +348,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, assert(hierarchy_id == cg_infos[CPU_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpu"); cg_infos[CPU_IDX]._cgroup_path = os::strdup(cgroup_path); } else if (strcmp(token, "cpuacct") == 0) { - assert(hierarchy_id == cg_infos[CPUACCT_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpuacc"); + assert(hierarchy_id == cg_infos[CPUACCT_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpuacct"); cg_infos[CPUACCT_IDX]._cgroup_path = os::strdup(cgroup_path); } else if (strcmp(token, "pids") == 0) { assert(hierarchy_id == cg_infos[PIDS_IDX]._hierarchy_id, "/proc/cgroups (%d) and /proc/self/cgroup (%d) hierarchy mismatch for pids", @@ -287,7 +356,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, cg_infos[PIDS_IDX]._cgroup_path = os::strdup(cgroup_path); } } - if (is_cgroupsV2) { + if (cgroups_v2_enabled) { // On some systems we have mixed cgroups v1 and cgroups v2 controllers (e.g. freezer on cg1 and // all relevant controllers on cg2). Only set the cgroup path when we see a hierarchy id of 0. if (hierarchy_id != 0) { @@ -322,10 +391,10 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, char *cptr = tmpcgroups; char *token; - // Cgroup v2 relevant info. We only look for the _mount_path iff is_cgroupsV2 so + // Cgroup v2 relevant info. We only look for the _mount_path iff cgroups_v2_enabled so // as to avoid memory stomping of the _mount_path pointer later on in the cgroup v1 // block in the hybrid case. - if (is_cgroupsV2 && sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- %s %*s %*s", tmproot, tmpmount, tmp_fs_type) == 3) { + if (cgroups_v2_enabled && sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- %s %*s %*s", tmproot, tmpmount, tmp_fs_type) == 3) { // we likely have an early match return (e.g. cgroup fs match), be sure we have cgroup2 as fstype if (strcmp("cgroup2", tmp_fs_type) == 0) { cgroupv2_mount_point_found = true; @@ -389,7 +458,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, return false; } - if (is_cgroupsV2) { + if (cgroups_v2_enabled) { if (!cgroupv2_mount_point_found) { log_trace(os, container)("Mount point for cgroupv2 not found in /proc/self/mountinfo"); cleanup(cg_infos); diff --git a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index c7f34fa65c0..60c294029af 100644 --- a/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -335,7 +335,8 @@ class CgroupSubsystemFactory: AllStatic { // Determine the cgroup type (version 1 or version 2), given // relevant paths to files. Sets 'flags' accordingly. static bool determine_type(CgroupInfo* cg_infos, - const char* proc_cgroups, + bool cgroups_v2_enabled, + const char* controllers_file, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* flags); diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index dd0cfdbbe5a..a56b6ddfea5 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -960,12 +960,13 @@ WB_ENTRY(jboolean, WB_TestSetForceInlineMethod(JNIEnv* env, jobject o, jobject m WB_END #ifdef LINUX -bool WhiteBox::validate_cgroup(const char* proc_cgroups, +bool WhiteBox::validate_cgroup(bool cgroups_v2_enabled, + const char* controllers_file, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags) { CgroupInfo cg_infos[CG_INFO_LENGTH]; - return CgroupSubsystemFactory::determine_type(cg_infos, proc_cgroups, + return CgroupSubsystemFactory::determine_type(cg_infos, cgroups_v2_enabled, controllers_file, proc_self_cgroup, proc_self_mountinfo, cg_flags); } @@ -2209,13 +2210,14 @@ WB_END WB_ENTRY(jint, WB_ValidateCgroup(JNIEnv* env, jobject o, - jstring proc_cgroups, + jboolean cgroups_v2_enabled, + jstring controllers_file, jstring proc_self_cgroup, jstring proc_self_mountinfo)) jint ret = 0; #ifdef LINUX ThreadToNativeFromVM ttnfv(thread); - const char* p_cgroups = env->GetStringUTFChars(proc_cgroups, NULL); + const char* c_file = env->GetStringUTFChars(controllers_file, NULL); CHECK_JNI_EXCEPTION_(env, 0); const char* p_s_cgroup = env->GetStringUTFChars(proc_self_cgroup, NULL); CHECK_JNI_EXCEPTION_(env, 0); @@ -2223,9 +2225,9 @@ WB_ENTRY(jint, WB_ValidateCgroup(JNIEnv* env, CHECK_JNI_EXCEPTION_(env, 0); u1 cg_type_flags = 0; // This sets cg_type_flags - WhiteBox::validate_cgroup(p_cgroups, p_s_cgroup, p_s_mountinfo, &cg_type_flags); + WhiteBox::validate_cgroup(cgroups_v2_enabled, c_file, p_s_cgroup, p_s_mountinfo, &cg_type_flags); ret = (jint)cg_type_flags; - env->ReleaseStringUTFChars(proc_cgroups, p_cgroups); + env->ReleaseStringUTFChars(controllers_file, c_file); env->ReleaseStringUTFChars(proc_self_cgroup, p_s_cgroup); env->ReleaseStringUTFChars(proc_self_mountinfo, p_s_mountinfo); #endif @@ -2606,7 +2608,7 @@ static JNINativeMethod methods[] = { (void*)&WB_CheckLibSpecifiesNoexecstack}, {CC"isContainerized", CC"()Z", (void*)&WB_IsContainerized }, {CC"validateCgroup", - CC"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + CC"(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", (void*)&WB_ValidateCgroup }, {CC"hostPhysicalMemory", CC"()J", (void*)&WB_HostPhysicalMemory }, {CC"hostPhysicalSwap", CC"()J", (void*)&WB_HostPhysicalSwap }, diff --git a/src/hotspot/share/prims/whitebox.hpp b/src/hotspot/share/prims/whitebox.hpp index 79461d5aadb..9117fe45571 100644 --- a/src/hotspot/share/prims/whitebox.hpp +++ b/src/hotspot/share/prims/whitebox.hpp @@ -69,7 +69,7 @@ class WhiteBox : public AllStatic { static void register_extended(JNIEnv* env, jclass wbclass, JavaThread* thread); static bool compile_method(Method* method, int comp_level, int bci, JavaThread* THREAD); #ifdef LINUX - static bool validate_cgroup(const char* proc_cgroups, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags); + static bool validate_cgroup(bool cgroups_v2_enabled, const char* controllers_file, const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags); #endif }; diff --git a/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java b/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java index 7eb78fa447f..c38186b9f7e 100644 --- a/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java +++ b/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java @@ -54,13 +54,13 @@ public class CgroupSubsystemFactory { private static final int INVALID_CGROUPS_V2 = 3; private static final int INVALID_CGROUPS_V1 = 4; private static final int INVALID_CGROUPS_NO_MOUNT = 5; + private static final int INVALID_CGROUPS_GENERIC = 6; private Path existingDirectory; private Path cgroupv1CgroupsJoinControllers; private Path cgroupv1SelfCgroupsJoinControllers; private Path cgroupv1MountInfoJoinControllers; private Path cgroupv1CgInfoZeroHierarchy; private Path cgroupv1MntInfoZeroHierarchy; - private Path cgroupv2CgInfoZeroHierarchy; private Path cgroupv2MntInfoZeroHierarchy; private Path cgroupv2MntInfoDouble; private Path cgroupv2MntInfoDouble2; @@ -81,9 +81,16 @@ public class CgroupSubsystemFactory { private Path cgroupV2SelfCgroup; private Path cgroupV2MntInfoMissingCgroupv2; private Path cgroupv1MntInfoMissingMemoryController; - private Path cgroupv2CgInfoNoZeroHierarchyOnlyFreezer; private Path cgroupv2MntInfoNoZeroHierarchyOnlyFreezer; private Path cgroupv2SelfNoZeroHierarchyOnlyFreezer; + private Path sysFsCgroupCgroupControllersTypicalPath; + private Path sysFsCgroupCgroupControllersEmptyPath; + private Path sysFsCgroupCgroupControllersBlankLinePath; + private Path sysFsCgroupCgroupControllersNoMemoryPath; + private Path sysFsCgroupCgroupControllersNoCpuPath; + private Path sysFsCgroupCgroupControllersNoPidsPath; + private Path sysFsCgroupCgroupControllersCpuMemoryOnlyPath; + private Path sysFsCgroupCgroupControllersExtraWhitespacePath; private String procSelfCgroupHybridContent = "11:hugetlb:/\n" + "10:devices:/user.slice\n" + "9:pids:/user.slice/user-15263.slice/user@15263.service\n" + @@ -108,6 +115,14 @@ public class CgroupSubsystemFactory { "2:perf_event:/\n" + "1:name=systemd:/user.slice/user-1000.slice/session-2.scope\n" + "0::/user.slice/user-1000.slice/session-2.scope\n"; + private String sysFsCgroupCgroupControllersTypicalContent = "cpuset cpu io memory hugetlb pids rdma misc\n"; + private String sysFsCgroupCgroupControllersEmptyContent = ""; + private String sysFsCgroupCgroupControllersBlankLineContent = "\n"; + private String sysFsCgroupCgroupControllersNoMemoryContent = "cpuset cpu io hugetlb pids rdma misc\n"; + private String sysFsCgroupCgroupControllersNoCpuContent = "cpuset io memory hugetlb pids rdma misc\n"; + private String sysFsCgroupCgroupControllersNoPidsContent = "cpuset cpu io memory hugetlb rdma misc\n"; + private String sysFsCgroupCgroupControllersCpuMemoryOnlyContent = "memory cpu\n"; + private String sysFsCgroupCgroupControllersExtraWhitespaceContent = " cpu\t \fmemory\r \n"; private String cgroupsZeroHierarchy = "#subsys_name hierarchy num_cgroups enabled\n" + "cpuset 0 1 1\n" + @@ -224,6 +239,72 @@ public class CgroupSubsystemFactory { private String mntInfoOnlyFreezerInV1 = "32 23 0:27 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:9 - cgroup2 cgroup2 rw,nsdelegate,memory_recursiveprot\n" + "911 32 0:47 / /sys/fs/cgroup/freezer rw,relatime shared:476 - cgroup freezer rw,freezer\n"; + // Test RHEL 8 (cgroups v1) with cpuset controller disabled via the kernel command line. + // # grep cgroup /boot/grub2/grubenv + // kernelopts=[...] cgroup_disable=cpuset + private String procCgroupsCgroupsV1CpusetDisabledContent = + "#subsys_name\thierarchy\tnum_cgroups\tenabled\n" + + "cpuset\t0\t1\t0\n" + + "cpu\t8\t1\t1\n" + + "cpuacct\t8\t1\t1\n" + + "blkio\t7\t1\t1\n" + + "memory\t9\t114\t1\n" + + "devices\t3\t67\t1\n" + + "freezer\t2\t1\t1\n" + + "net_cls\t6\t1\t1\n" + + "perf_event\t4\t1\t1\n" + + "net_prio\t6\t1\t1\n" + + "hugetlb\t11\t1\t1\n" + + "pids\t10\t91\t1\n" + + "rdma\t5\t1\t1\n"; + private String procSelfCgroupCgroupsV1CpusetDisabledContent = + "11:hugetlb:/\n" + + "10:pids:/user.slice/user-0.slice/session-1.scope\n" + + "9:memory:/user.slice/user-0.slice/session-1.scope\n" + + "8:cpu,cpuacct:/\n" + + "7:blkio:/\n" + + "6:net_cls,net_prio:/\n" + + "5:rdma:/\n" + + "4:perf_event:/\n" + + "3:devices:/system.slice/sshd.service\n" + + "2:freezer:/\n" + + "1:name=systemd:/user.slice/user-0.slice/session-1.scope\n"; + private String procSelfMountinfoCgroupsV1CpusetDisabledContent = + "22 93 0:21 / /sys rw,nosuid,nodev,noexec,relatime shared:2 - sysfs sysfs rw,seclabel\n" + + "23 93 0:5 / /proc rw,nosuid,nodev,noexec,relatime shared:25 - proc proc rw\n" + + "24 93 0:6 / /dev rw,nosuid shared:21 - devtmpfs devtmpfs rw,seclabel,size=632252k,nr_inodes=158063,mode=755\n" + + "25 22 0:7 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:3 - securityfs securityfs rw\n" + + "26 24 0:22 / /dev/shm rw,nosuid,nodev shared:22 - tmpfs tmpfs rw,seclabel\n" + + "27 24 0:23 / /dev/pts rw,nosuid,noexec,relatime shared:23 - devpts devpts rw,seclabel,gid=5,mode=620,ptmxmode=000\n" + + "28 93 0:24 / /run rw,nosuid,nodev shared:24 - tmpfs tmpfs rw,seclabel,mode=755\n" + + "29 22 0:25 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:4 - tmpfs tmpfs ro,seclabel,mode=755\n" + + "30 29 0:26 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:5 - cgroup cgroup rw,seclabel,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd\n" + + "31 22 0:27 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:16 - pstore pstore rw,seclabel\n" + + "32 22 0:28 / /sys/fs/bpf rw,nosuid,nodev,noexec,relatime shared:17 - bpf bpf rw,mode=700\n" + + "33 29 0:29 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:6 - cgroup cgroup rw,seclabel,freezer\n" + + "34 29 0:30 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:7 - cgroup cgroup rw,seclabel,devices\n" + + "35 29 0:31 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:8 - cgroup cgroup rw,seclabel,perf_event\n" + + "36 29 0:32 / /sys/fs/cgroup/rdma rw,nosuid,nodev,noexec,relatime shared:9 - cgroup cgroup rw,seclabel,rdma\n" + + "37 29 0:33 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,seclabel,net_cls,net_prio\n" + + "38 29 0:34 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,seclabel,blkio\n" + + "39 29 0:35 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,seclabel,cpu,cpuacct\n" + + "40 29 0:36 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,seclabel,memory\n" + + "41 29 0:37 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,seclabel,pids\n" + + "42 29 0:38 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,seclabel,hugetlb\n" + + "43 22 0:12 / /sys/kernel/tracing rw,relatime shared:18 - tracefs none rw,seclabel\n" + + "90 22 0:39 / /sys/kernel/config rw,relatime shared:19 - configfs configfs rw\n" + + "93 1 253:0 / / rw,relatime shared:1 - xfs /dev/mapper/rhel-root rw,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota\n" + + "44 22 0:20 / /sys/fs/selinux rw,relatime shared:20 - selinuxfs selinuxfs rw\n" + + "45 24 0:19 / /dev/mqueue rw,relatime shared:26 - mqueue mqueue rw,seclabel\n" + + "46 23 0:40 / /proc/sys/fs/binfmt_misc rw,relatime shared:27 - autofs systemd-1 rw,fd=31,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=28718\n" + + "47 24 0:41 / /dev/hugepages rw,relatime shared:28 - hugetlbfs hugetlbfs rw,seclabel,pagesize=2M\n" + + "48 22 0:8 / /sys/kernel/debug rw,relatime shared:29 - debugfs debugfs rw,seclabel\n" + + "49 22 0:42 / /sys/fs/fuse/connections rw,relatime shared:30 - fusectl fusectl rw\n" + + "114 93 252:1 / /boot rw,relatime shared:61 - xfs /dev/vda1 rw,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota\n" + + "466 28 0:46 / /run/user/0 rw,nosuid,nodev,relatime shared:251 - tmpfs tmpfs rw,seclabel,size=130188k,mode=700\n"; + private Path procCgroupsCgroupsV1CpusetDisabledPath; + private Path procSelfCgroupCgroupsV1CpusetDisabledPath; + private Path procSelfMountinfoCgroupsV1CpusetDisabledPath; private void setup() { try { @@ -231,10 +312,33 @@ private void setup() { Path cgroupsZero = Paths.get(existingDirectory.toString(), "cgroups_zero"); Files.writeString(cgroupsZero, cgroupsZeroHierarchy, StandardCharsets.UTF_8); cgroupv1CgInfoZeroHierarchy = cgroupsZero; - cgroupv2CgInfoZeroHierarchy = cgroupsZero; cgroupv1MntInfoZeroHierarchy = Paths.get(existingDirectory.toString(), "mountinfo_empty"); Files.writeString(cgroupv1MntInfoZeroHierarchy, mntInfoEmpty); + sysFsCgroupCgroupControllersTypicalPath = Paths.get(existingDirectory.toString(), "sys_fs_cgroup_cgroup_controllers_typical"); + Files.writeString(sysFsCgroupCgroupControllersTypicalPath, sysFsCgroupCgroupControllersTypicalContent, StandardCharsets.UTF_8); + + sysFsCgroupCgroupControllersEmptyPath = Paths.get(existingDirectory.toString(), "sys_fs_cgroup_cgroup_controllers_empty"); + Files.writeString(sysFsCgroupCgroupControllersEmptyPath, sysFsCgroupCgroupControllersEmptyContent, StandardCharsets.UTF_8); + + sysFsCgroupCgroupControllersBlankLinePath = Paths.get(existingDirectory.toString(), "sys_fs_cgroup_cgroup_controllers_blank_line"); + Files.writeString(sysFsCgroupCgroupControllersBlankLinePath, sysFsCgroupCgroupControllersBlankLineContent, StandardCharsets.UTF_8); + + sysFsCgroupCgroupControllersNoMemoryPath = Paths.get(existingDirectory.toString(), "sys_fs_cgroup_cgroup_controllers_no_memory"); + Files.writeString(sysFsCgroupCgroupControllersNoMemoryPath, sysFsCgroupCgroupControllersNoMemoryContent, StandardCharsets.UTF_8); + + sysFsCgroupCgroupControllersNoCpuPath = Paths.get(existingDirectory.toString(), "sys_fs_cgroup_cgroup_controllers_no_cpu"); + Files.writeString(sysFsCgroupCgroupControllersNoCpuPath, sysFsCgroupCgroupControllersNoCpuContent, StandardCharsets.UTF_8); + + sysFsCgroupCgroupControllersNoPidsPath = Paths.get(existingDirectory.toString(), "sys_fs_cgroup_cgroup_controllers_no_pids"); + Files.writeString(sysFsCgroupCgroupControllersNoPidsPath, sysFsCgroupCgroupControllersNoPidsContent, StandardCharsets.UTF_8); + + sysFsCgroupCgroupControllersCpuMemoryOnlyPath = Paths.get(existingDirectory.toString(), "sys_fs_cgroup_cgroup_controllers_cpu_memory_only"); + Files.writeString(sysFsCgroupCgroupControllersCpuMemoryOnlyPath, sysFsCgroupCgroupControllersCpuMemoryOnlyContent, StandardCharsets.UTF_8); + + sysFsCgroupCgroupControllersExtraWhitespacePath = Paths.get(existingDirectory.toString(), "sys_fs_cgroup_cgroup_controllers_extra_whitespace"); + Files.writeString(sysFsCgroupCgroupControllersExtraWhitespacePath, sysFsCgroupCgroupControllersExtraWhitespaceContent, StandardCharsets.UTF_8); + cgroupv2MntInfoZeroHierarchy = Paths.get(existingDirectory.toString(), "mountinfo_cgroupv2"); Files.writeString(cgroupv2MntInfoZeroHierarchy, mntInfoCgroupsV2Only); @@ -301,14 +405,18 @@ private void setup() { cgroupv1MountInfoJoinControllers = Paths.get(existingDirectory.toString(), "mntinfo_cgv1_join_controllers"); Files.writeString(cgroupv1MountInfoJoinControllers, mntInfoCgroupv1JoinControllers); - cgroupv2CgInfoNoZeroHierarchyOnlyFreezer = Paths.get(existingDirectory.toString(), "cgroups_cgv2_non_zero_only_freezer"); - Files.writeString(cgroupv2CgInfoNoZeroHierarchyOnlyFreezer, cgroupsNonZeroHierarchyOnlyFreezer); - cgroupv2SelfNoZeroHierarchyOnlyFreezer = Paths.get(existingDirectory.toString(), "self_cgroup_non_zero_only_freezer"); Files.writeString(cgroupv2SelfNoZeroHierarchyOnlyFreezer, cgroupv1SelfOnlyFreezerContent); cgroupv2MntInfoNoZeroHierarchyOnlyFreezer = Paths.get(existingDirectory.toString(), "self_mountinfo_cgv2_non_zero_only_freezer"); Files.writeString(cgroupv2MntInfoNoZeroHierarchyOnlyFreezer, mntInfoOnlyFreezerInV1); + + procCgroupsCgroupsV1CpusetDisabledPath = Paths.get(existingDirectory.toString(), "proc_cgroups_cgroups_v1_cpuset_disabled"); + Files.writeString(procCgroupsCgroupsV1CpusetDisabledPath, procCgroupsCgroupsV1CpusetDisabledContent); + procSelfCgroupCgroupsV1CpusetDisabledPath = Paths.get(existingDirectory.toString(), "proc_self_cgroup_cgroups_v1_cpuset_disabled"); + Files.writeString(procSelfCgroupCgroupsV1CpusetDisabledPath, procSelfCgroupCgroupsV1CpusetDisabledContent); + procSelfMountinfoCgroupsV1CpusetDisabledPath = Paths.get(existingDirectory.toString(), "proc_self_mountinfo_cgroups_v1_cpuset_disabled"); + Files.writeString(procSelfMountinfoCgroupsV1CpusetDisabledPath, procSelfMountinfoCgroupsV1CpusetDisabledContent); } catch (IOException e) { throw new RuntimeException(e); } @@ -330,7 +438,7 @@ public void testCgroupv1JoinControllerCombo(WhiteBox wb) { String procCgroups = cgroupv1CgroupsJoinControllers.toString(); String procSelfCgroup = cgroupv1SelfCgroupsJoinControllers.toString(); String procSelfMountinfo = cgroupv1MountInfoJoinControllers.toString(); - int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo); + int retval = wb.validateCgroup(false, procCgroups, procSelfCgroup, procSelfMountinfo); Asserts.assertEQ(CGROUPS_V1, retval, "Join controllers should be properly detected"); Asserts.assertTrue(isValidCgroup(retval)); System.out.println("testCgroupv1JoinControllerMounts PASSED!"); @@ -340,37 +448,37 @@ public void testCgroupv1MultipleControllerMounts(WhiteBox wb, Path mountInfo) { String procCgroups = cgroupv1CgInfoNonZeroHierarchy.toString(); String procSelfCgroup = cgroupV1SelfCgroup.toString(); String procSelfMountinfo = mountInfo.toString(); - int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo); + int retval = wb.validateCgroup(false, procCgroups, procSelfCgroup, procSelfMountinfo); Asserts.assertEQ(CGROUPS_V1, retval, "Multiple controllers, but only one in /sys/fs/cgroup"); Asserts.assertTrue(isValidCgroup(retval)); System.out.println("testCgroupv1MultipleControllerMounts PASSED!"); } public void testCgroupv1SystemdOnly(WhiteBox wb) { - String procCgroups = cgroupv1CgInfoZeroHierarchy.toString(); + String procCgroups = cgroupv1CgInfoNonZeroHierarchy.toString(); String procSelfCgroup = cgroupV1SelfCgroup.toString(); String procSelfMountinfo = cgroupv1MntInfoSystemdOnly.toString(); - int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo); + int retval = wb.validateCgroup(false, procCgroups, procSelfCgroup, procSelfMountinfo); Asserts.assertEQ(INVALID_CGROUPS_NO_MOUNT, retval, "Only systemd mounted. Invalid"); Asserts.assertFalse(isValidCgroup(retval)); System.out.println("testCgroupv1SystemdOnly PASSED!"); } public void testCgroupv1NoMounts(WhiteBox wb) { - String procCgroups = cgroupv1CgInfoZeroHierarchy.toString(); + String procCgroups = cgroupv1CgInfoNonZeroHierarchy.toString(); String procSelfCgroup = cgroupV1SelfCgroup.toString(); String procSelfMountinfo = cgroupv1MntInfoZeroHierarchy.toString(); - int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo); + int retval = wb.validateCgroup(false, procCgroups, procSelfCgroup, procSelfMountinfo); Asserts.assertEQ(INVALID_CGROUPS_NO_MOUNT, retval, "No cgroups mounted in /proc/self/mountinfo. Invalid."); Asserts.assertFalse(isValidCgroup(retval)); System.out.println("testCgroupv1NoMounts PASSED!"); } public void testCgroupv2NoCgroup2Fs(WhiteBox wb) { - String procCgroups = cgroupv2CgInfoZeroHierarchy.toString(); + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersTypicalPath.toString(); String procSelfCgroup = cgroupV2SelfCgroup.toString(); String procSelfMountinfo = cgroupV2MntInfoMissingCgroupv2.toString(); - int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, procSelfCgroup, procSelfMountinfo); Asserts.assertEQ(INVALID_CGROUPS_V2, retval, "No cgroup2 filesystem in /proc/self/mountinfo. Invalid."); Asserts.assertFalse(isValidCgroup(retval)); System.out.println("testCgroupv2NoCgroup2Fs PASSED!"); @@ -380,17 +488,17 @@ public void testCgroupv1MissingMemoryController(WhiteBox wb) { String procCgroups = cgroupv1CgInfoNonZeroHierarchy.toString(); String procSelfCgroup = cgroupV1SelfCgroup.toString(); String procSelfMountinfo = cgroupv1MntInfoMissingMemoryController.toString(); - int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo); + int retval = wb.validateCgroup(false, procCgroups, procSelfCgroup, procSelfMountinfo); Asserts.assertEQ(INVALID_CGROUPS_V1, retval, "Required memory controller path missing in mountinfo. Invalid."); Asserts.assertFalse(isValidCgroup(retval)); System.out.println("testCgroupv1MissingMemoryController PASSED!"); } public void testCgroupv2(WhiteBox wb, Path mountInfo) { - String procCgroups = cgroupv2CgInfoZeroHierarchy.toString(); + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersTypicalPath.toString(); String procSelfCgroup = cgroupV2SelfCgroup.toString(); String procSelfMountinfo = mountInfo.toString(); - int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, procSelfCgroup, procSelfMountinfo); Asserts.assertEQ(CGROUPS_V2, retval, "Expected"); Asserts.assertTrue(isValidCgroup(retval)); System.out.println("testCgroupv2 PASSED!"); @@ -400,7 +508,7 @@ public void testCgroupV1Hybrid(WhiteBox wb) { String procCgroups = cgroupv1CgInfoNonZeroHierarchy.toString(); String procSelfCgroup = cgroupV1SelfCgroup.toString(); String procSelfMountinfo = cgroupv1MntInfoNonZeroHierarchy.toString(); - int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo); + int retval = wb.validateCgroup(false, procCgroups, procSelfCgroup, procSelfMountinfo); Asserts.assertEQ(CGROUPS_V1, retval, "Hybrid cgroups expected as cgroups v1"); Asserts.assertTrue(isValidCgroup(retval)); System.out.println("testCgroupv1Hybrid PASSED!"); @@ -410,22 +518,102 @@ public void testCgroupV1HybridMntInfoOrder(WhiteBox wb) { String procCgroups = cgroupv1CgInfoNonZeroHierarchy.toString(); String procSelfCgroup = cgroupV1SelfCgroup.toString(); String procSelfMountinfo = cgroupv1MntInfoNonZeroHierarchyOtherOrder.toString(); - int retval = wb.validateCgroup(procCgroups, procSelfCgroup, procSelfMountinfo); + int retval = wb.validateCgroup(false, procCgroups, procSelfCgroup, procSelfMountinfo); Asserts.assertEQ(CGROUPS_V1, retval, "Hybrid cgroups expected as cgroups v1"); Asserts.assertTrue(isValidCgroup(retval)); System.out.println("testCgroupv1HybridMntInfoOrder PASSED!"); } public void testNonZeroHierarchyOnlyFreezer(WhiteBox wb) { - String cgroups = cgroupv2CgInfoNoZeroHierarchyOnlyFreezer.toString(); + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersTypicalPath.toString(); String mountInfo = cgroupv2MntInfoNoZeroHierarchyOnlyFreezer.toString(); String selfCgroup = cgroupv2SelfNoZeroHierarchyOnlyFreezer.toString(); - int retval = wb.validateCgroup(cgroups, selfCgroup, mountInfo); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, selfCgroup, mountInfo); Asserts.assertEQ(CGROUPS_V2, retval, "All V1 controllers are ignored"); Asserts.assertTrue(isValidCgroup(retval)); System.out.println("testNonZeroHierarchyOnlyFreezer PASSED!"); } + public void testCgroupv2ControllerFileEmpty(WhiteBox wb, Path mountInfo) { + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersEmptyPath.toString(); + String procSelfCgroup = cgroupV2SelfCgroup.toString(); + String procSelfMountinfo = mountInfo.toString(); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, procSelfCgroup, procSelfMountinfo); + Asserts.assertEQ(INVALID_CGROUPS_V2, retval, "Empty cgroup v2 controllers file. Invalid."); + Asserts.assertFalse(isValidCgroup(retval)); + System.out.println("testCgroupv2ControllerFileEmpty PASSED!"); + } + + public void testCgroupv2ControllerFileBlankLine(WhiteBox wb, Path mountInfo) { + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersBlankLinePath.toString(); + String procSelfCgroup = cgroupV2SelfCgroup.toString(); + String procSelfMountinfo = mountInfo.toString(); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, procSelfCgroup, procSelfMountinfo); + Asserts.assertEQ(INVALID_CGROUPS_GENERIC, retval, "cgroup v2 controllers file contains a single blank line. Invalid."); + Asserts.assertFalse(isValidCgroup(retval)); + System.out.println("testCgroupv2ControllerFileBlankLine PASSED!"); + } + + public void testCgroupv2ControllerFileNoMemory(WhiteBox wb, Path mountInfo) { + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersNoMemoryPath.toString(); + String procSelfCgroup = cgroupV2SelfCgroup.toString(); + String procSelfMountinfo = mountInfo.toString(); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, procSelfCgroup, procSelfMountinfo); + Asserts.assertEQ(INVALID_CGROUPS_GENERIC, retval, "cgroup v2 memory controller disabled. Invalid."); + Asserts.assertFalse(isValidCgroup(retval)); + System.out.println("testCgroupv2ControllerFileNoMemory PASSED!"); + } + + public void testCgroupv2ControllerFileNoCpu(WhiteBox wb, Path mountInfo) { + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersNoCpuPath.toString(); + String procSelfCgroup = cgroupV2SelfCgroup.toString(); + String procSelfMountinfo = mountInfo.toString(); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, procSelfCgroup, procSelfMountinfo); + Asserts.assertEQ(INVALID_CGROUPS_GENERIC, retval, "cgroup v2 cpu controller disabled. Invalid."); + Asserts.assertFalse(isValidCgroup(retval)); + System.out.println("testCgroupv2ControllerFileNoCpu PASSED!"); + } + + public void testCgroupv2ControllerFileNoPids(WhiteBox wb, Path mountInfo) { + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersNoPidsPath.toString(); + String procSelfCgroup = cgroupV2SelfCgroup.toString(); + String procSelfMountinfo = mountInfo.toString(); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, procSelfCgroup, procSelfMountinfo); + Asserts.assertEQ(CGROUPS_V2, retval, "cgroup v2 pids controller disabled. Valid."); + Asserts.assertTrue(isValidCgroup(retval)); + System.out.println("testCgroupv2ControllerFileNoPids PASSED!"); + } + + public void testCgroupv2ControllerFileCpuMemoryOnly(WhiteBox wb, Path mountInfo) { + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersCpuMemoryOnlyPath.toString(); + String procSelfCgroup = cgroupV2SelfCgroup.toString(); + String procSelfMountinfo = mountInfo.toString(); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, procSelfCgroup, procSelfMountinfo); + Asserts.assertEQ(CGROUPS_V2, retval, "only cgroup v2 memory and cpu controllers enabled. Valid."); + Asserts.assertTrue(isValidCgroup(retval)); + System.out.println("testCgroupv2ControllerFileCpuMemoryOnly PASSED!"); + } + + public void testCgroupv2ControllerFileExtraWhitespace(WhiteBox wb, Path mountInfo) { + String sysFsCgroupCgroupControllers = sysFsCgroupCgroupControllersExtraWhitespacePath.toString(); + String procSelfCgroup = cgroupV2SelfCgroup.toString(); + String procSelfMountinfo = mountInfo.toString(); + int retval = wb.validateCgroup(true, sysFsCgroupCgroupControllers, procSelfCgroup, procSelfMountinfo); + Asserts.assertEQ(CGROUPS_V2, retval, "cgroup v2 controllers file contains extra whitespace. Valid."); + Asserts.assertTrue(isValidCgroup(retval)); + System.out.println("testCgroupv2ControllerFileExtraWhitespace PASSED!"); + } + + public void testCgroupv1CpusetDisabled(WhiteBox wb) { + String procCgroups = procCgroupsCgroupsV1CpusetDisabledPath.toString(); + String procSelfCgroup = procSelfCgroupCgroupsV1CpusetDisabledPath.toString(); + String procSelfMountinfo = procSelfMountinfoCgroupsV1CpusetDisabledPath.toString(); + int retval = wb.validateCgroup(false, procCgroups, procSelfCgroup, procSelfMountinfo); + Asserts.assertEQ(INVALID_CGROUPS_GENERIC, retval, "Required cpuset controller disabled in /proc/cgroups. Invalid."); + Asserts.assertFalse(isValidCgroup(retval)); + System.out.println("testCgroupv1CpusetDisabled PASSED!"); + } + public static void main(String[] args) throws Exception { WhiteBox wb = WhiteBox.getWhiteBox(); CgroupSubsystemFactory test = new CgroupSubsystemFactory(); @@ -436,6 +624,13 @@ public static void main(String[] args) throws Exception { test.testCgroupv2(wb, test.cgroupv2MntInfoZeroHierarchy); test.testCgroupv2(wb, test.cgroupv2MntInfoDouble); test.testCgroupv2(wb, test.cgroupv2MntInfoDouble2); + test.testCgroupv2ControllerFileEmpty(wb, test.cgroupv2MntInfoZeroHierarchy); + test.testCgroupv2ControllerFileBlankLine(wb, test.cgroupv2MntInfoZeroHierarchy); + test.testCgroupv2ControllerFileNoMemory(wb, test.cgroupv2MntInfoZeroHierarchy); + test.testCgroupv2ControllerFileNoCpu(wb, test.cgroupv2MntInfoZeroHierarchy); + test.testCgroupv2ControllerFileNoPids(wb, test.cgroupv2MntInfoZeroHierarchy); + test.testCgroupv2ControllerFileCpuMemoryOnly(wb, test.cgroupv2MntInfoZeroHierarchy); + test.testCgroupv2ControllerFileExtraWhitespace(wb, test.cgroupv2MntInfoZeroHierarchy); test.testCgroupV1Hybrid(wb); test.testCgroupV1HybridMntInfoOrder(wb); test.testCgroupv1MissingMemoryController(wb); @@ -450,6 +645,7 @@ public static void main(String[] args) throws Exception { test.testCgroupv1MultipleControllerMounts(wb, test.cgroupv1MntInfoDoublePids2); test.testCgroupv1JoinControllerCombo(wb); test.testNonZeroHierarchyOnlyFreezer(wb); + test.testCgroupv1CpusetDisabled(wb); } finally { test.teardown(); } diff --git a/test/lib/jdk/test/whitebox/WhiteBox.java b/test/lib/jdk/test/whitebox/WhiteBox.java index 546c47a548b..aa050322a55 100644 --- a/test/lib/jdk/test/whitebox/WhiteBox.java +++ b/test/lib/jdk/test/whitebox/WhiteBox.java @@ -678,7 +678,8 @@ public Object getMethodOption(Executable method, String name) { // Container testing public native boolean isContainerized(); - public native int validateCgroup(String procCgroups, + public native int validateCgroup(boolean cgroupsV2Enabled, + String controllersFile, String procSelfCgroup, String procSelfMountinfo); public native void printOsInfo();