8197589: Update CPU count algorithm when both cpu shares and quotas are used
authorbobv
Fri, 23 Feb 2018 10:17:35 -0500
changeset 49058 15765495db12
parent 49056 91ada5977172
child 49059 c548301de07e
8197589: Update CPU count algorithm when both cpu shares and quotas are used Reviewed-by: dholmes, mseledtsov
src/hotspot/os/linux/globals_linux.hpp
src/hotspot/os/linux/osContainer_linux.cpp
test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java
--- a/src/hotspot/os/linux/globals_linux.hpp	Fri Feb 23 07:47:29 2018 -0500
+++ b/src/hotspot/os/linux/globals_linux.hpp	Fri Feb 23 10:17:35 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2018, 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
@@ -62,6 +62,11 @@
   product(bool, UseContainerSupport, true,                              \
           "Enable detection and runtime container configuration support") \
                                                                         \
+  product(bool, PreferContainerQuotaForCPUCount, true,                  \
+          "Calculate the container CPU availability based on the value" \
+          " of quotas (if set), when true. Otherwise, use the CPU"    \
+          " shares value, provided it is less than quota.")             \
+                                                                        \
   diagnostic(bool, UseCpuAllocPath, false,                              \
              "Use CPU_ALLOC code path in os::active_processor_count ")
 
--- a/src/hotspot/os/linux/osContainer_linux.cpp	Fri Feb 23 07:47:29 2018 -0500
+++ b/src/hotspot/os/linux/osContainer_linux.cpp	Fri Feb 23 10:17:35 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -499,11 +499,11 @@
 /* active_processor_count
  *
  * Calculate an appropriate number of active processors for the
- * VM to use based on these three cgroup options.
+ * VM to use based on these three inputs.
  *
  * cpu affinity
- * cpu quota & cpu period
- * cpu shares
+ * cgroup cpu quota & cpu period
+ * cgroup cpu shares
  *
  * Algorithm:
  *
@@ -513,42 +513,61 @@
  * required CPUs by dividing quota by period.
  *
  * If shares are in effect (shares != -1), calculate the number
- * of cpus required for the shares by dividing the share value
+ * of CPUs required for the shares by dividing the share value
  * by PER_CPU_SHARES.
  *
  * All results of division are rounded up to the next whole number.
  *
- * Return the smaller number from the three different settings.
+ * If neither shares or quotas have been specified, return the
+ * number of active processors in the system.
+ *
+ * If both shares and quotas have been specified, the results are
+ * based on the flag PreferContainerQuotaForCPUCount.  If true,
+ * return the quota value.  If false return the smallest value
+ * between shares or quotas.
+ *
+ * If shares and/or quotas have been specified, the resulting number
+ * returned will never exceed the number of active processors.
  *
  * return:
- *    number of cpus
- *    OSCONTAINER_ERROR if failure occured during extract of cpuset info
+ *    number of CPUs
  */
 int OSContainer::active_processor_count() {
-  int cpu_count, share_count, quota_count;
-  int share, quota, period;
+  int quota_count = 0, share_count = 0;
+  int cpu_count, limit_count;
   int result;
 
-  cpu_count = os::Linux::active_processor_count();
+  cpu_count = limit_count = os::Linux::active_processor_count();
+  int quota  = cpu_quota();
+  int period = cpu_period();
+  int share  = cpu_shares();
 
-  share = cpu_shares();
+  if (quota > -1 && period > 0) {
+    quota_count = ceilf((float)quota / (float)period);
+    log_trace(os, container)("CPU Quota count based on quota/period: %d", quota_count);
+  }
   if (share > -1) {
     share_count = ceilf((float)share / (float)PER_CPU_SHARES);
-    log_trace(os, container)("cpu_share count: %d", share_count);
-  } else {
-    share_count = cpu_count;
+    log_trace(os, container)("CPU Share count based on shares: %d", share_count);
   }
 
-  quota = cpu_quota();
-  period = cpu_period();
-  if (quota > -1 && period > 0) {
-    quota_count = ceilf((float)quota / (float)period);
-    log_trace(os, container)("quota_count: %d", quota_count);
-  } else {
-    quota_count = cpu_count;
+  // If both shares and quotas are setup results depend
+  // on flag PreferContainerQuotaForCPUCount.
+  // If true, limit CPU count to quota
+  // If false, use minimum of shares and quotas
+  if (quota_count !=0 && share_count != 0) {
+    if (PreferContainerQuotaForCPUCount) {
+      limit_count = quota_count;
+    } else {
+      limit_count = MIN2(quota_count, share_count);
+    }
+  } else if (quota_count != 0) {
+    limit_count = quota_count;
+  } else if (share_count != 0) {
+    limit_count = share_count;
   }
 
-  result = MIN2(cpu_count, MIN2(share_count, quota_count));
+  result = MIN2(cpu_count, limit_count);
   log_trace(os, container)("OSContainer::active_processor_count: %d", result);
   return result;
 }
--- a/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java	Fri Feb 23 07:47:29 2018 -0500
+++ b/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java	Fri Feb 23 10:17:35 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -94,14 +94,23 @@
             // Test subset of cpuset with one element
             if (cpuSet.size() >= 1) {
                 String testCpuSet = CPUSetsReader.listToString(cpuSet, 1);
-                testAPCCombo(testCpuSet, 200*1000, 100*1000,   4*1024, 1);
+                testAPCCombo(testCpuSet, 200*1000, 100*1000,   4*1024, true, 1);
             }
 
             // Test subset of cpuset with two elements
             if (cpuSet.size() >= 2) {
                 String testCpuSet = CPUSetsReader.listToString(cpuSet, 2);
-                testAPCCombo(testCpuSet, 200*1000, 100*1000, 4*1024, 2);
-                testAPCCombo(testCpuSet, 200*1000, 100*1000, 1*1024, 2);
+                testAPCCombo(testCpuSet, 200*1000, 100*1000, 4*1024, true, 2);
+                testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023,   true, 2);
+                testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023,   false,  1);
+            }
+
+            // Test subset of cpuset with three elements
+            if (cpuSet.size() >= 3) {
+                String testCpuSet = CPUSetsReader.listToString(cpuSet, 3);
+                testAPCCombo(testCpuSet, 100*1000, 100*1000, 2*1024, true, 1);
+                testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023,   true, 2);
+                testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023,   false,  1);
             }
         }
     }
@@ -159,12 +168,14 @@
 
     // Test correctess of automatically selected active processor cound
     private static void testAPCCombo(String cpuset, int quota, int period, int shares,
-                                         int expectedAPC) throws Exception {
+                                     boolean usePreferContainerQuotaForCPUCount,
+                                     int expectedAPC) throws Exception {
         Common.logNewTestCase("test APC Combo");
         System.out.println("cpuset = " + cpuset);
         System.out.println("quota = " + quota);
         System.out.println("period = " + period);
         System.out.println("shares = " + period);
+        System.out.println("usePreferContainerQuotaForCPUCount = " + usePreferContainerQuotaForCPUCount);
         System.out.println("expectedAPC = " + expectedAPC);
 
         expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC);
@@ -174,6 +185,9 @@
             .addDockerOpts("--cpu-period=" + period)
             .addDockerOpts("--cpu-quota=" + quota)
             .addDockerOpts("--cpu-shares=" + shares);
+
+        if (!usePreferContainerQuotaForCPUCount) opts.addJavaOpts("-XX:-PreferContainerQuotaForCPUCount");
+
         Common.run(opts)
             .shouldMatch("active_processor_count.*" + expectedAPC);
     }