Path: blob/master/src/hotspot/os/linux/cgroupSubsystem_linux.hpp
40951 views
/*1* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#ifndef CGROUP_SUBSYSTEM_LINUX_HPP25#define CGROUP_SUBSYSTEM_LINUX_HPP2627#include "memory/allocation.hpp"28#include "runtime/os.hpp"29#include "logging/log.hpp"30#include "utilities/globalDefinitions.hpp"31#include "utilities/macros.hpp"32#include "osContainer_linux.hpp"3334// Shared cgroups code (used by cgroup version 1 and version 2)3536/*37* PER_CPU_SHARES has been set to 1024 because CPU shares' quota38* is commonly used in cloud frameworks like Kubernetes[1],39* AWS[2] and Mesos[3] in a similar way. They spawn containers with40* --cpu-shares option values scaled by PER_CPU_SHARES. Thus, we do41* the inverse for determining the number of possible available42* CPUs to the JVM inside a container. See JDK-8216366.43*44* [1] https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu45* In particular:46* When using Docker:47* The spec.containers[].resources.requests.cpu is converted to its core value, which is potentially48* fractional, and multiplied by 1024. The greater of this number or 2 is used as the value of the49* --cpu-shares flag in the docker run command.50* [2] https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html51* [3] https://github.com/apache/mesos/blob/3478e344fb77d931f6122980c6e94cd3913c441d/src/docker/docker.cpp#L64852* https://github.com/apache/mesos/blob/3478e344fb77d931f6122980c6e94cd3913c441d/src/slave/containerizer/mesos/isolators/cgroups/constants.hpp#L3053*/54#define PER_CPU_SHARES 10245556#define CGROUPS_V1 157#define CGROUPS_V2 258#define INVALID_CGROUPS_V2 359#define INVALID_CGROUPS_V1 460#define INVALID_CGROUPS_NO_MOUNT 561#define INVALID_CGROUPS_GENERIC 66263// Four controllers: cpu, cpuset, cpuacct, memory64#define CG_INFO_LENGTH 465#define CPUSET_IDX 066#define CPU_IDX 167#define CPUACCT_IDX 268#define MEMORY_IDX 36970typedef char * cptr;7172class CgroupController: public CHeapObj<mtInternal> {73public:74virtual char *subsystem_path() = 0;75};7677PRAGMA_DIAG_PUSH78PRAGMA_FORMAT_NONLITERAL_IGNORED79template <typename T> int subsystem_file_line_contents(CgroupController* c,80const char *filename,81const char *matchline,82const char *scan_fmt,83T returnval) {84FILE *fp = NULL;85char *p;86char file[MAXPATHLEN+1];87char buf[MAXPATHLEN+1];88char discard[MAXPATHLEN+1];89bool found_match = false;9091if (c == NULL) {92log_debug(os, container)("subsystem_file_line_contents: CgroupController* is NULL");93return OSCONTAINER_ERROR;94}95if (c->subsystem_path() == NULL) {96log_debug(os, container)("subsystem_file_line_contents: subsystem path is NULL");97return OSCONTAINER_ERROR;98}99100strncpy(file, c->subsystem_path(), MAXPATHLEN);101file[MAXPATHLEN-1] = '\0';102int filelen = strlen(file);103if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) {104log_debug(os, container)("File path too long %s, %s", file, filename);105return OSCONTAINER_ERROR;106}107strncat(file, filename, MAXPATHLEN-filelen);108log_trace(os, container)("Path to %s is %s", filename, file);109fp = fopen(file, "r");110if (fp != NULL) {111int err = 0;112while ((p = fgets(buf, MAXPATHLEN, fp)) != NULL) {113found_match = false;114if (matchline == NULL) {115// single-line file case116int matched = sscanf(p, scan_fmt, returnval);117found_match = (matched == 1);118} else {119// multi-line file case120if (strstr(p, matchline) != NULL) {121// discard matchline string prefix122int matched = sscanf(p, scan_fmt, discard, returnval);123found_match = (matched == 2);124} else {125continue; // substring not found126}127}128if (found_match) {129fclose(fp);130return 0;131} else {132err = 1;133log_debug(os, container)("Type %s not found in file %s", scan_fmt, file);134}135}136if (err == 0) {137log_debug(os, container)("Empty file %s", file);138}139} else {140log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno));141}142if (fp != NULL)143fclose(fp);144return OSCONTAINER_ERROR;145}146PRAGMA_DIAG_POP147148#define GET_CONTAINER_INFO(return_type, subsystem, filename, \149logstring, scan_fmt, variable) \150return_type variable; \151{ \152int err; \153err = subsystem_file_line_contents(subsystem, \154filename, \155NULL, \156scan_fmt, \157&variable); \158if (err != 0) \159return (return_type) OSCONTAINER_ERROR; \160\161log_trace(os, container)(logstring, variable); \162}163164#define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename, \165logstring, scan_fmt, variable, bufsize) \166char variable[bufsize]; \167{ \168int err; \169err = subsystem_file_line_contents(subsystem, \170filename, \171NULL, \172scan_fmt, \173variable); \174if (err != 0) \175return (return_type) NULL; \176\177log_trace(os, container)(logstring, variable); \178}179180#define GET_CONTAINER_INFO_LINE(return_type, controller, filename, \181matchline, logstring, scan_fmt, variable) \182return_type variable; \183{ \184int err; \185err = subsystem_file_line_contents(controller, \186filename, \187matchline, \188scan_fmt, \189&variable); \190if (err != 0) \191return (return_type) OSCONTAINER_ERROR; \192\193log_trace(os, container)(logstring, variable); \194}195196197class CachedMetric : public CHeapObj<mtInternal>{198private:199volatile jlong _metric;200volatile jlong _next_check_counter;201public:202CachedMetric() {203_metric = -1;204_next_check_counter = min_jlong;205}206bool should_check_metric() {207return os::elapsed_counter() > _next_check_counter;208}209jlong value() { return _metric; }210void set_value(jlong value, jlong timeout) {211_metric = value;212// Metric is unlikely to change, but we want to remain213// responsive to configuration changes. A very short grace time214// between re-read avoids excessive overhead during startup without215// significantly reducing the VMs ability to promptly react to changed216// metric config217_next_check_counter = os::elapsed_counter() + timeout;218}219};220221class CachingCgroupController : public CHeapObj<mtInternal> {222private:223CgroupController* _controller;224CachedMetric* _metrics_cache;225226public:227CachingCgroupController(CgroupController* cont) {228_controller = cont;229_metrics_cache = new CachedMetric();230}231232CachedMetric* metrics_cache() { return _metrics_cache; }233CgroupController* controller() { return _controller; }234};235236class CgroupSubsystem: public CHeapObj<mtInternal> {237public:238jlong memory_limit_in_bytes();239int active_processor_count();240241virtual int cpu_quota() = 0;242virtual int cpu_period() = 0;243virtual int cpu_shares() = 0;244virtual jlong memory_usage_in_bytes() = 0;245virtual jlong memory_and_swap_limit_in_bytes() = 0;246virtual jlong memory_soft_limit_in_bytes() = 0;247virtual jlong memory_max_usage_in_bytes() = 0;248virtual char * cpu_cpuset_cpus() = 0;249virtual char * cpu_cpuset_memory_nodes() = 0;250virtual jlong read_memory_limit_in_bytes() = 0;251virtual const char * container_type() = 0;252virtual CachingCgroupController* memory_controller() = 0;253virtual CachingCgroupController* cpu_controller() = 0;254};255256// Utility class for storing info retrieved from /proc/cgroups,257// /proc/self/cgroup and /proc/self/mountinfo258// For reference see man 7 cgroups and CgroupSubsystemFactory259class CgroupInfo : public StackObj {260friend class CgroupSubsystemFactory;261friend class WhiteBox;262263private:264char* _name;265int _hierarchy_id;266bool _enabled;267bool _data_complete; // indicating cgroup v1 data is complete for this controller268char* _cgroup_path; // cgroup controller path from /proc/self/cgroup269char* _root_mount_path; // root mount path from /proc/self/mountinfo. Unused for cgroup v2270char* _mount_path; // mount path from /proc/self/mountinfo.271272public:273CgroupInfo() {274_name = NULL;275_hierarchy_id = -1;276_enabled = false;277_data_complete = false;278_cgroup_path = NULL;279_root_mount_path = NULL;280_mount_path = NULL;281}282283};284285class CgroupSubsystemFactory: AllStatic {286friend class WhiteBox;287288public:289static CgroupSubsystem* create();290private:291static inline bool is_cgroup_v2(u1* flags) {292return *flags == CGROUPS_V2;293}294295#ifdef ASSERT296static inline bool is_valid_cgroup(u1* flags) {297return *flags == CGROUPS_V1 || *flags == CGROUPS_V2;298}299static inline bool is_cgroup_v1(u1* flags) {300return *flags == CGROUPS_V1;301}302#endif303304// Determine the cgroup type (version 1 or version 2), given305// relevant paths to files. Sets 'flags' accordingly.306static bool determine_type(CgroupInfo* cg_infos,307const char* proc_cgroups,308const char* proc_self_cgroup,309const char* proc_self_mountinfo,310u1* flags);311static void cleanup(CgroupInfo* cg_infos);312};313314#endif // CGROUP_SUBSYSTEM_LINUX_HPP315316317