-
Notifications
You must be signed in to change notification settings - Fork 172
8349988: Change cgroup version detection logic to not depend on /proc/cgroups #779
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jmtd
wants to merge
8
commits into
openjdk:master
Choose a base branch
from
jmtd:8349988-8u
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
e6e2d34
Backport dd842c440e3f50028fc991b29ea096f0e64e967b
jerboaa 2ce81f0
Manually apply diff to whitebox.cpp from 59c7c8ed4b3392d3c8ee26128aaa…
jmtd dee8acd
Adjustments for 8u
jmtd f04d436
Merge remote-tracking branch 'jdk8u-dev/master' into 8349988-8u
jmtd f4d6ad8
restore double-space in comment (reduce delta to original patch)
jmtd d8a5cf6
Merge remote-tracking branch 'jdk8u-dev/master' into 8349988-8u
jmtd 3757a21
Update hotspot/src/os/linux/vm/cgroupSubsystem_linux.cpp
jmtd 09cda0c
remove comment ref to pid controller (not in 8u)
jmtd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| /* | ||
| * Copyright (c) 2019, 2020, 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 <string.h> | ||
| #include <math.h> | ||
| #include <errno.h> | ||
| #include <sys/vfs.h> | ||
| #include "cgroupSubsystem_linux.hpp" | ||
| #include "cgroupV1Subsystem_linux.hpp" | ||
| #include "cgroupV2Subsystem_linux.hpp" | ||
|
|
@@ -33,6 +34,25 @@ | |
| #include "runtime/os.hpp" | ||
| #include "utilities/globalDefinitions.hpp" | ||
|
|
||
| // Inlined from <linux/magic.h> 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" }; | ||
| 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 { | ||
| return -1; | ||
| } | ||
| } | ||
|
|
||
| CgroupSubsystem* CgroupSubsystemFactory::create() { | ||
| CgroupV1MemoryController* memory = NULL; | ||
| CgroupV1Controller* cpuset = NULL; | ||
|
|
@@ -41,10 +61,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 | ||
|
|
@@ -142,75 +177,116 @@ 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 controllers, memory, cpu, cpuset, cpuacct are enabled | ||
| // true iff all required controllers, memory, cpu, cpuacct are enabled | ||
| // at the kernel level. | ||
| bool all_controllers_enabled; | ||
| // cpuset might not be enabled on newer Linux distros (Fedora 41) | ||
| 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) { | ||
| if (PrintContainerInfo) { | ||
| tty->print_cr("Can't open %s, %s", | ||
| proc_cgroups, strerror(errno)); | ||
| } | ||
| *flags = INVALID_CGROUPS_GENERIC; | ||
| return false; | ||
| // If cgroups v2 is enabled, open /sys/fs/cgroup/cgroup.controllers. If not, open /proc/cgroups. | ||
| controllers = fopen(controllers_file, "r"); | ||
| if (controllers == NULL) { | ||
| if (PrintContainerInfo) { | ||
| tty->print_cr("Can't open %s, %s", controllers_file, 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)) != NULL) { | ||
| char* controller = NULL; | ||
| #define ISSPACE_CHARS " \n\t\r\f\v" | ||
| while ((controller = strsep(&p, ISSPACE_CHARS)) != NULL) { | ||
| int i; | ||
| if ((i = cg_v2_controller_index(controller)) != -1) { | ||
| cg_infos[i]._name = os::strdup(controller); | ||
| cg_infos[i]._enabled = true; | ||
| if (i == CPUSET_IDX && PrintContainerInfo) { | ||
| tty->print_cr("Detected optional %s controller entry in %s", | ||
| controller, controllers_file); | ||
|
Comment on lines
+218
to
+220
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment regarding JDK-8347129 |
||
| } | ||
| } | ||
| } | ||
| #undef ISSPACE_CHARS | ||
| } else { | ||
| if(PrintContainerInfo) { | ||
| tty->print_cr("Can't read %s, %s", controllers_file, strerror(errno)); | ||
| } | ||
| *flags = INVALID_CGROUPS_V2; | ||
| fclose(controllers); | ||
|
jmtd marked this conversation as resolved.
|
||
| 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) { | ||
| 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); | ||
| 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, the cpuset controller | ||
| // is 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 (PrintContainerInfo && !cg_infos[i]._enabled) { | ||
| tty->print_cr("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_controllers_enabled = true; | ||
| for (int i = 0; i < CG_INFO_LENGTH; i++) { | ||
| is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0; | ||
| all_controllers_enabled = all_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); | ||
| } | ||
| } | ||
| for (int i = 0; i < CG_INFO_LENGTH; i++) { | ||
| all_required_controllers_enabled = all_required_controllers_enabled && cg_infos[i]._enabled; | ||
| if (PrintContainerInfo && !cg_infos[i]._enabled) { | ||
| tty->print_cr("controller %s is not enabled\n", cg_controller_name[i]); | ||
| } | ||
| } | ||
| } | ||
| fclose(controllers); | ||
|
|
||
| if (!all_controllers_enabled) { | ||
| if (!all_required_controllers_enabled) { | ||
| // one or more controllers disabled, disable container support | ||
| if (PrintContainerInfo) { | ||
| tty->print_cr("One or more required controllers disabled at kernel level."); | ||
|
|
@@ -254,7 +330,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"); | ||
| cg_infos[MEMORY_IDX]._cgroup_path = os::strdup(cgroup_path); | ||
|
|
@@ -265,11 +341,11 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, | |
| assert(hierarchy_id == cg_infos[CPU_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch"); | ||
| 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"); | ||
| 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); | ||
| } | ||
| } | ||
| 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) { | ||
|
|
@@ -306,10 +382,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; | ||
|
|
@@ -369,7 +445,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, | |
| return false; | ||
| } | ||
|
|
||
| if (is_cgroupsV2) { | ||
| if (cgroups_v2_enabled) { | ||
| if (!cgroupv2_mount_point_found) { | ||
| if (PrintContainerInfo) { | ||
| tty->print_cr("Mount point for cgroupv2 not found in /proc/self/mountinfo"); | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment about
cpusetis about JDK-8347129 which isn't in 8u. Perhaps this should get added - with/issue add- as after this patch it's essentially optional.