--- old/src/os/linux/vm/os_linux.cpp 2016-01-27 02:08:22.236482314 -0500 +++ new/src/os/linux/vm/os_linux.cpp 2016-01-27 02:08:20.184366480 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2016, 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 @@ -32,6 +32,7 @@ #include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" #include "jvm_linux.h" +#include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "memory/filemap.hpp" #include "mutex_linux.inline.hpp" @@ -106,6 +107,14 @@ # include # include +#ifndef _GNU_SOURCE + #define _GNU_SOURCE + #include + #undef _GNU_SOURCE +#else + #include +#endif + // if RUSAGE_THREAD for getrusage() has not been defined, do it here. The code calling // getrusage() is prepared to handle the associated failure. #ifndef RUSAGE_THREAD @@ -4762,12 +4771,72 @@ } } +// Get the current number of available processors for this process. +// This value can change at any time during a process's lifetime. +// sched_getaffinity gives an accurate answer as it accounts for cpusets. +// If it appears there may be more than 1024 processors then we do a +// dynamic check - see 6515172 for details. +// If anything goes wrong we fallback to returning the number of online +// processors - which can be greater than the number available to the process. int os::active_processor_count() { - // Linux doesn't yet have a (official) notion of processor sets, - // so just return the number of online processors. - int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); - assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); - return online_cpus; + cpu_set_t cpus; // can represent at most 1024 (CPU_SETSIZE) processors + cpu_set_t* cpus_p = &cpus; + int cpus_size = sizeof(cpu_set_t); + + int configured_cpus = processor_count(); // upper bound on available cpus + int cpu_count = 0; + + // To enable easy testing of the dynamic path on different platforms we + // introduce an experimental flag: UseCpuAllocPath + if (configured_cpus >= CPU_SETSIZE || UseCpuAllocPath) { + // kernel may use a mask bigger than cpu_set_t + log_trace(os)("active_processor_count: using dynamic path %s" + "- configured processors: %d", + UseCpuAllocPath ? "(forced) " : "", + configured_cpus); + cpus_p = CPU_ALLOC(configured_cpus); + if (cpus_p != NULL) { + cpus_size = CPU_ALLOC_SIZE(configured_cpus); + // zero it just to be safe + CPU_ZERO_S(cpus_size, cpus_p); + } + else { + // failed to allocate so fallback to online cpus + int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); + log_trace(os)("active_processor_count: " + "CPU_ALLOC failed (%s) - using " + "online processor count: %d", + strerror(errno), online_cpus); + return online_cpus; + } + } + else { + log_trace(os)("active_processor_count: using static path - configured processors: %d", + configured_cpus); + } + + // pid 0 means the current thread - which we have to assume represents the process + if (sched_getaffinity(0, cpus_size, cpus_p) == 0) { + if (cpus_p != &cpus) { + cpu_count = CPU_COUNT_S(cpus_size, cpus_p); + } + else { + cpu_count = CPU_COUNT(cpus_p); + } + log_trace(os)("active_processor_count: sched_getaffinity processor count: %d", cpu_count); + } + else { + cpu_count = ::sysconf(_SC_NPROCESSORS_ONLN); + warning("sched_getaffinity failed (%s)- using online processor count (%d) " + "which may exceed available processors", strerror(errno), cpu_count); + } + + if (cpus_p != &cpus) { + CPU_FREE(cpus_p); + } + + assert(cpu_count > 0 && cpu_count <= processor_count(), "sanity check"); + return cpu_count; } void os::set_native_thread_name(const char *name) { --- old/src/share/vm/logging/logTag.hpp 2016-01-27 02:08:28.648844267 -0500 +++ new/src/share/vm/logging/logTag.hpp 2016-01-27 02:08:26.600728659 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2016, 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 @@ -59,6 +59,7 @@ LOG_TAG(marking) \ LOG_TAG(metaspace) \ LOG_TAG(monitorinflation) \ + LOG_TAG(os) \ LOG_TAG(phases) \ LOG_TAG(plab) \ LOG_TAG(promotion) \ --- old/src/share/vm/runtime/globals.hpp 2016-01-27 02:08:34.869195380 -0500 +++ new/src/share/vm/runtime/globals.hpp 2016-01-27 02:08:32.781077514 -0500 @@ -1303,6 +1303,10 @@ product(bool, ReduceSignalUsage, false, \ "Reduce the use of OS signals in Java and/or the VM") \ \ + experimental(bool, UseCpuAllocPath, false, \ + "Use CPU_ALLOC code path in os::active_processor_count " \ + "(Linux only)") \ + \ develop_pd(bool, ShareVtableStubs, \ "Share vtable stubs (smaller code but worse branch prediction") \ \ --- /dev/null 2015-11-22 10:00:13.236500999 -0500 +++ new/test/runtime/os/AvailableProcessors.java 2016-01-27 02:08:39.065432239 -0500 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016, 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. + */ +import java.io.File; +import jdk.test.lib.ProcessTools; +import jdk.test.lib.OutputAnalyzer; +import java.util.ArrayList; + +/* + * @test + * @bug 6515172 + * @summary Check that availableProcessors reports the correct value when running in a cpuset on linux + * @requires os.family == "linux" + * @library /testlibrary + * @build jdk.test.lib.* + * @run driver AvailableProcessors + */ +public class AvailableProcessors { + + static final String SUCCESS_STRING = "Found expected processors: "; + + public static void main(String[] args) throws Exception { + if (args.length > 0) + checkProcessors(Integer.parseInt(args[0])); + else { + // run ourselves under different cpu configurations + // using the taskset command + String taskset; + final String taskset1 = "/bin/taskset"; + final String taskset2 = "/usr/bin/taskset"; + if (new File(taskset1).exists()) + taskset = taskset1; + else if (new File(taskset2).exists()) + taskset = taskset2; + else { + System.out.println("Skipping test: could not find taskset command"); + return; + } + + int available = Runtime.getRuntime().availableProcessors(); + + if (available == 1) { + System.out.println("Skipping test: only one processor available"); + return; + } + + // Get the java command we want to execute + // Enable logging for easier failure diagnosis + ProcessBuilder master = + ProcessTools.createJavaProcessBuilder(false, + "-Xlog:os=trace", + "AvailableProcessors"); + + int[] expected = new int[] { 1, available/2, available-1, available }; + + for (int i : expected) { + System.out.println("Testing for " + i + " processors ..."); + int max = i - 1; + ArrayList cmdline = new ArrayList<>(master.command()); + // prepend taskset command + cmdline.add(0, "0-" + max); + cmdline.add(0, "-c"); + cmdline.add(0, taskset); + // append expected processor count + cmdline.add(String.valueOf(i)); + ProcessBuilder pb = new ProcessBuilder(cmdline); + System.out.println("Final command line: " + + ProcessTools.getCommandLine(pb)); + OutputAnalyzer output = ProcessTools.executeProcess(pb); + output.shouldContain(SUCCESS_STRING); + } + } + } + + static void checkProcessors(int expected) { + int available = Runtime.getRuntime().availableProcessors(); + if (available != expected) + throw new Error("Expected " + expected + " processors, but found " + + available); + else + System.out.println(SUCCESS_STRING + available); + } +}