--- old/src/hotspot/os/linux/osContainer_linux.cpp 2019-03-21 17:15:45.807885713 +0100 +++ new/src/hotspot/os/linux/osContainer_linux.cpp 2019-03-21 17:15:45.269885297 +0100 @@ -122,7 +122,25 @@ char *subsystem_path() { return _path; } }; -CgroupSubsystem* memory = NULL; +class CgroupMemorySubsystem: CgroupSubsystem { + friend class OSContainer; + + 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; + + public: + CgroupMemorySubsystem(char *root, char *mountpoint) : CgroupSubsystem::CgroupSubsystem(root, mountpoint) { + _uses_mem_hierarchy = false; + } + + bool is_hierarchical() { return _uses_mem_hierarchy; } + void set_hierarchical(bool value) { _uses_mem_hierarchy = value; } +}; + +CgroupMemorySubsystem* memory = NULL; CgroupSubsystem* cpuset = NULL; CgroupSubsystem* cpu = NULL; CgroupSubsystem* cpuacct = NULL; @@ -181,6 +199,65 @@ } PRAGMA_DIAG_POP +PRAGMA_DIAG_PUSH +PRAGMA_FORMAT_NONLITERAL_IGNORED +template int subsystem_file_line_contents(CgroupSubsystem* c, + const char *filename, + const char *matchline, + const char *scan_fmt, + T returnval) { + FILE *fp = NULL; + char *p; + char file[MAXPATHLEN+1]; + char buf[MAXPATHLEN+1]; + char discard[MAXPATHLEN+1]; + + if (c == NULL) { + log_debug(os, container)("subsystem_file_contents: CgroupSubsytem* is NULL"); + return OSCONTAINER_ERROR; + } + if (c->subsystem_path() == NULL) { + log_debug(os, container)("subsystem_file_contents: subsystem path is NULL"); + return OSCONTAINER_ERROR; + } + + strncpy(file, c->subsystem_path(), MAXPATHLEN); + file[MAXPATHLEN-1] = '\0'; + int filelen = strlen(file); + if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) { + log_debug(os, container)("File path too long %s, %s", file, filename); + return OSCONTAINER_ERROR; + } + strncat(file, filename, MAXPATHLEN-filelen); + log_trace(os, container)("Path to %s is %s", filename, file); + fp = fopen(file, "r"); + if (fp != NULL) { + int err = 0; + while ((p = fgets(buf, MAXPATHLEN, fp)) != NULL) { + if (strstr(p, matchline) != NULL) { + // discard matchline string prefix + int matched = sscanf(p, scan_fmt, discard, returnval); + if (matched == 2) { + fclose(fp); + return 0; + } else { + err = 1; + log_debug(os, container)("Type %s not found in file %s", scan_fmt, file); + } + } + } + if (err == 0) { + log_debug(os, container)("Empty file %s, or no match found for %s", file, matchline); + } + } else { + log_debug(os, container)("Open of file %s failed, %s", file, os::strerror(errno)); + } + if (fp != NULL) + fclose(fp); + return OSCONTAINER_ERROR; +} +PRAGMA_DIAG_POP + #define GET_CONTAINER_INFO(return_type, subsystem, filename, \ logstring, scan_fmt, variable) \ return_type variable; \ @@ -211,6 +288,22 @@ log_trace(os, container)(logstring, variable); \ } +#define GET_CONTAINER_INFO_LINE(return_type, subsystem, filename, \ + matchline, logstring, scan_fmt, variable) \ + return_type variable; \ +{ \ + int err; \ + err = subsystem_file_line_contents(subsystem, \ + filename, \ + matchline, \ + scan_fmt, \ + &variable); \ + if (err != 0) \ + return (return_type) OSCONTAINER_ERROR; \ + \ + log_trace(os, container)(logstring, variable); \ +} + /* init * * Initialize the container support and determine if @@ -266,7 +359,7 @@ } while ((token = strsep(&cptr, ",")) != NULL) { if (strcmp(token, "memory") == 0) { - memory = new CgroupSubsystem(tmproot, tmpmount); + memory = new CgroupMemorySubsystem(tmproot, tmpmount); } else if (strcmp(token, "cpuset") == 0) { cpuset = new CgroupSubsystem(tmproot, tmpmount); } else if (strcmp(token, "cpu") == 0) { @@ -344,6 +437,10 @@ while ((token = strsep(&controllers, ",")) != NULL) { if (strcmp(token, "memory") == 0) { memory->set_subsystem_path(base); + jlong hierarchy = uses_mem_hierarchy(); + if (hierarchy > 0) { + memory->set_hierarchical(true); + } } else if (strcmp(token, "cpuset") == 0) { cpuset->set_subsystem_path(base); } else if (strcmp(token, "cpu") == 0) { @@ -360,6 +457,7 @@ // command line arguments have been processed. if ((mem_limit = memory_limit_in_bytes()) > 0) { os::Linux::set_physical_memory(mem_limit); + log_info(os, container)("Memory Limit is: " JLONG_FORMAT, mem_limit); } _is_containerized = true; @@ -374,6 +472,21 @@ } } +/* 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 OSContainer::uses_mem_hierarchy() { + GET_CONTAINER_INFO(jlong, memory, "/memory.use_hierarchy", + "Use Hierarchy is: " JLONG_FORMAT, JLONG_FORMAT, use_hierarchy); + return use_hierarchy; +} + /* memory_limit_in_bytes * @@ -389,7 +502,18 @@ "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit); if (memlimit >= _unlimited_memory) { - log_trace(os, container)("Memory Limit is: Unlimited"); + log_trace(os, container)("Non-Hierarchical Memory Limit is: Unlimited"); + if (memory->is_hierarchical()) { + const char* matchline = "hierarchical_memory_limit"; + char* format = "%s " JULONG_FORMAT; + GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline, + "Hierarchical Memory Limit is: " JULONG_FORMAT, format, hier_memlimit) + if (hier_memlimit >= _unlimited_memory) { + log_trace(os, container)("Hierarchical Memory Limit is: Unlimited"); + } else { + return (jlong)hier_memlimit; + } + } return (jlong)-1; } else { @@ -401,7 +525,18 @@ GET_CONTAINER_INFO(julong, memory, "/memory.memsw.limit_in_bytes", "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit); if (memswlimit >= _unlimited_memory) { - log_trace(os, container)("Memory and Swap Limit is: Unlimited"); + log_trace(os, container)("Non-Hierarchical Memory and Swap Limit is: Unlimited"); + if (memory->is_hierarchical()) { + const char* matchline = "hierarchical_memsw_limit"; + char* format = "%s " JULONG_FORMAT; + GET_CONTAINER_INFO_LINE(julong, memory, "/memory.stat", matchline, + "Hierarchical Memory and Swap Limit is : " JULONG_FORMAT, format, hier_memlimit) + if (hier_memlimit >= _unlimited_memory) { + log_trace(os, container)("Hierarchical Memory and Swap Limit is: Unlimited"); + } else { + return (jlong)hier_memlimit; + } + } return (jlong)-1; } else { return (jlong)memswlimit; --- old/src/hotspot/os/linux/osContainer_linux.hpp 2019-03-21 17:15:47.456886988 +0100 +++ new/src/hotspot/os/linux/osContainer_linux.hpp 2019-03-21 17:15:46.921886574 +0100 @@ -42,6 +42,7 @@ static inline bool is_containerized(); static const char * container_type(); + static jlong uses_mem_hierarchy(); static jlong memory_limit_in_bytes(); static jlong memory_and_swap_limit_in_bytes(); static jlong memory_soft_limit_in_bytes(); --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java 2019-03-21 17:15:49.046888217 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java 2019-03-21 17:15:48.527887816 +0100 @@ -25,15 +25,16 @@ package jdk.internal.platform.cgroupv1; -import java.io.BufferedReader; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.stream.Stream; +import jdk.internal.platform.cgroupv1.SubSystem.MemorySubSystem; + public class Metrics implements jdk.internal.platform.Metrics { - private SubSystem memory; + private MemorySubSystem memory; private SubSystem cpu; private SubSystem cpuacct; private SubSystem cpuset; @@ -133,7 +134,7 @@ for (String subsystemName: subsystemNames) { switch (subsystemName) { case "memory": - metric.setMemorySubSystem(new SubSystem(mountentry[3], mountentry[4])); + metric.setMemorySubSystem(new MemorySubSystem(mountentry[3], mountentry[4])); break; case "cpuset": metric.setCpuSetSubSystem(new SubSystem(mountentry[3], mountentry[4])); @@ -195,6 +196,11 @@ if (subsystem != null) { subsystem.setPath(base); + if (subsystem instanceof MemorySubSystem) { + MemorySubSystem memorySubSystem = (MemorySubSystem)subsystem; + boolean isHierarchial = getHierarchical(memorySubSystem); + memorySubSystem.setHierarchical(isHierarchial); + } metric.setActiveSubSystems(); } if (subsystem2 != null) { @@ -203,6 +209,11 @@ } + private static boolean getHierarchical(MemorySubSystem subsystem) { + long hierarchical = SubSystem.getLongValue(subsystem, "memory.use_hierarchy"); + return hierarchical > 0; + } + private void setActiveSubSystems() { activeSubSystems = true; } @@ -211,7 +222,7 @@ return activeSubSystems; } - private void setMemorySubSystem(SubSystem memory) { + private void setMemorySubSystem(MemorySubSystem memory) { this.memory = memory; } @@ -366,9 +377,29 @@ public long getMemoryLimit() { long retval = SubSystem.getLongValue(memory, "memory.limit_in_bytes"); + if (retval > unlimited_minimum) { + if (memory.isHierarchical()) { + // memory.limit_in_bytes returned unlimited, attempt + // hierarchical memory limit + String match = "hierarchical_memory_limit"; + retval = SubSystem.getLongValueMatchingLine(memory, + "memory.stat", + match, + Metrics::convertHierachicalLimitLine); + } + } return retval > unlimited_minimum ? -1L : retval; } + public static long convertHierachicalLimitLine(String line) { + String[] tokens = line.split("\\s"); + if (tokens.length == 2) { + String strVal = tokens[1]; + return SubSystem.convertStringToLong(strVal); + } + return unlimited_minimum + 1; // unlimited + } + public long getMemoryMaxUsage() { return SubSystem.getLongValue(memory, "memory.max_usage_in_bytes"); } @@ -417,6 +448,17 @@ public long getMemoryAndSwapLimit() { long retval = SubSystem.getLongValue(memory, "memory.memsw.limit_in_bytes"); + if (retval > unlimited_minimum) { + if (memory.isHierarchical()) { + // memory.memsw.limit_in_bytes returned unlimited, attempt + // hierarchical memory limit + String match = "hierarchical_memsw_limit"; + retval = SubSystem.getLongValueMatchingLine(memory, + "memory.stat", + match, + Metrics::convertHierachicalLimitLine); + } + } return retval > unlimited_minimum ? -1L : retval; } --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java 2019-03-21 17:15:50.820889588 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java 2019-03-21 17:15:50.193889103 +0100 @@ -29,10 +29,11 @@ import java.io.IOException; import java.math.BigInteger; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Stream; public class SubSystem { @@ -99,10 +100,32 @@ } + public static long getLongValueMatchingLine(SubSystem subsystem, + String param, + String match, + Function conversion) { + long retval = Metrics.unlimited_minimum + 1; // default unlimited + try { + List lines = Files.readAllLines(Paths.get(subsystem.path(), param)); + for (String line: lines) { + if (line.contains(match)) { + retval = conversion.apply(line); + break; + } + } + } catch (IOException e) { + // Ignore. Default is unlimited. + } + return retval; + } + public static long getLongValue(SubSystem subsystem, String parm) { String strval = getStringValue(subsystem, parm); - long retval = 0; + return convertStringToLong(strval); + } + public static long convertStringToLong(String strval) { + long retval = 0; if (strval == null) return 0L; try { @@ -215,4 +238,22 @@ return ints; } + + public static class MemorySubSystem extends SubSystem { + + private boolean hierarchical; + + public MemorySubSystem(String root, String mountPoint) { + super(root, mountPoint); + } + + boolean isHierarchical() { + return hierarchical; + } + + void setHierarchical(boolean hierarchical) { + this.hierarchical = hierarchical; + } + + } }