diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp index fdb138c864f..4927aa989ca 100644 --- a/src/hotspot/os/linux/osContainer_linux.cpp +++ b/src/hotspot/os/linux/osContainer_linux.cpp @@ -58,8 +58,37 @@ void OSContainer::init() { if (cgroup_subsystem == nullptr) { return; // Required subsystem files not found or other error } - - _is_containerized = true; + /* + * In order to avoid a false positive on is_containerized() on + * Linux systems outside a container *and* to ensure compatibility + * with in-container usage, we detemine is_containerized() by two + * steps: + * 1.) Determine if all the cgroup controllers are mounted read only. + * If yes, is_containerized() == true. Otherwise, do the fallback + * in 2.) + * 2.) Query for memory and cpu limits. If any limit is set, we set + * is_containerized() == true. + * + * Step 1.) covers the basic in container use-cases. Step 2.) ensures + * that limits enforced by other means (e.g. systemd slice) are properly + * detected. + */ + // We can be in one of two cases: + // 1.) On a physical Linux system without any limit + // 2.) On a physical Linux system with a limit enforced by other means (like systemd slice) + bool any_mem_cpu_limit_present = cgroup_subsystem->memory_limit_in_bytes() > 0 || + os::Linux::active_processor_count() != cgroup_subsystem->active_processor_count() || + cgroup_subsystem->pids_max() > 0; + const char *reason; + if (any_mem_cpu_limit_present) { + reason = " because either a cpu or a memory limit is present"; + } else { + reason = " because no cpu or memory limit is present"; + } + _is_containerized = any_mem_cpu_limit_present; + log_debug(os, container)("OSContainer::init: is_containerized() = %s%s", + _is_containerized ? "true" : "false", + reason); } const char * OSContainer::container_type() { diff --git a/test/hotspot/jtreg/containers/cgroup/PlainRead.java b/test/hotspot/jtreg/containers/cgroup/PlainRead.java deleted file mode 100644 index 21eccd79835..00000000000 --- a/test/hotspot/jtreg/containers/cgroup/PlainRead.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2017, 2023, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* - * @test PlainRead - * @key cgroups - * @requires os.family == "linux" - * @requires vm.flagless - * @library /testlibrary /test/lib - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI PlainRead - */ - -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.Platform; -import jdk.test.whitebox.WhiteBox; - -public class PlainRead { - - static public void match(OutputAnalyzer oa, String what, String value) { - oa.shouldMatch("^.*" + what + " *" + value + ".*$"); - } - - static public void noMatch(OutputAnalyzer oa, String what, String value) { - oa.shouldNotMatch("^.*" + what + " *" + value + ".*$"); - } - - static final String good_value = "(\\d+|-1|-2|Unlimited)"; - static final String bad_value = "(failed)"; - - static final String[] variables = {"Memory Limit is:", "CPU Quota is:", "CPU Period is:", "active_processor_count:"}; - - static public void isContainer(OutputAnalyzer oa) { - for (String v: variables) { - match(oa, v, good_value); - } - for (String v: variables) { - noMatch(oa, v, bad_value); - } - } - - static public void isNotContainer(OutputAnalyzer oa) { - oa.shouldMatch("^.*Can't open /proc/self/mountinfo.*$"); - } - - public static void main(String[] args) throws Exception { - WhiteBox wb = WhiteBox.getWhiteBox(); - ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:os+container=trace", "-version"); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - - if (wb.isContainerized()) { - System.out.println("Inside a cgroup, testing..."); - isContainer(output); - } - } -} diff --git a/test/hotspot/jtreg/containers/docker/TestCPUSets.java b/test/hotspot/jtreg/containers/docker/TestCPUSets.java index de35388f5a8..2a8d4c7827f 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUSets.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUSets.java @@ -153,6 +153,9 @@ private static void testCpuSet(String value) throws Exception { DockerRunOptions opts = commonOpts(); opts.addDockerOpts("--cpuset-cpus=" + value); + // is_containerized() would return false as it does not test the cpu limit. + opts.addDockerOpts("--memory=10G"); + List lines = Common.run(opts).asLines(); checkResult(lines, "cpuset.cpus is:", value); } diff --git a/test/hotspot/jtreg/containers/docker/TestMisc.java b/test/hotspot/jtreg/containers/docker/TestMisc.java index 5b7f96112b9..5673ff89ab0 100644 --- a/test/hotspot/jtreg/containers/docker/TestMisc.java +++ b/test/hotspot/jtreg/containers/docker/TestMisc.java @@ -79,6 +79,9 @@ private static void testIsContainerized() throws Exception { DockerRunOptions opts = Common.newOpts(imageName, "CheckContainerized"); Common.addWhiteBoxOpts(opts); + // is_containerized() would return false as it did not find any limit. + opts.addDockerOpts("--memory=10G"); + Common.run(opts) .shouldContain(CheckContainerized.INSIDE_A_CONTAINER); } @@ -90,6 +93,9 @@ private static void testPrintContainerInfo() throws Exception { DockerRunOptions opts = Common.newOpts(imageName, "PrintContainerInfo"); Common.addWhiteBoxOpts(opts); + // is_containerized() would return false as it did not find any limit. + opts.addDockerOpts("--memory=10G"); + checkContainerInfo(Common.run(opts)); } @@ -99,6 +105,9 @@ private static void testPrintContainerInfoActiveProcessorCount() throws Exceptio DockerRunOptions opts = Common.newOpts(imageName, "PrintContainerInfo").addJavaOpts("-XX:ActiveProcessorCount=2"); Common.addWhiteBoxOpts(opts); + // is_containerized() would return false as it did not find any limit. + opts.addDockerOpts("--memory=10G"); + OutputAnalyzer out = Common.run(opts); out.shouldContain("but overridden by -XX:ActiveProcessorCount 2"); }