summaryrefslogtreecommitdiffhomepage
path: root/runsc/specutils/cpu.go
diff options
context:
space:
mode:
Diffstat (limited to 'runsc/specutils/cpu.go')
-rw-r--r--runsc/specutils/cpu.go90
1 files changed, 90 insertions, 0 deletions
diff --git a/runsc/specutils/cpu.go b/runsc/specutils/cpu.go
new file mode 100644
index 000000000..9abe26b64
--- /dev/null
+++ b/runsc/specutils/cpu.go
@@ -0,0 +1,90 @@
+// Copyright 2018 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package specutils
+
+import (
+ "fmt"
+ "runtime"
+ "strconv"
+ "strings"
+
+ specs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// CalculateCPUNumber calculates the number of CPUs that should be exposed
+// inside the sandbox.
+func CalculateCPUNumber(spec *specs.Spec) (int, error) {
+ // If spec does not contain CPU field, then return the number of host CPUs.
+ if spec == nil || spec.Linux == nil || spec.Linux.Resources == nil || spec.Linux.Resources.CPU == nil {
+ return runtime.NumCPU(), nil
+ }
+ cpuSpec := spec.Linux.Resources.CPU
+
+ // If cpuSpec.Cpus is specified, then parse and return that. They must be in
+ // the list format for cpusets, which is "a comma-separated list of CPU
+ // numbers and ranges of numbers, in ASCII decimal." --man 7 cpuset.
+ cpus := cpuSpec.Cpus
+ if cpus != "" {
+ cpuNum := 0
+ for _, subs := range strings.Split(cpus, ",") {
+ result, err := parseCPUNumber(subs)
+ if err != nil {
+ return 0, err
+ }
+ cpuNum += result
+ }
+ return cpuNum, nil
+ }
+
+ // If CPU.Quota and CPU.Period are specified, we can divide them to get an
+ // approximation of the number of CPUs needed.
+ if cpuSpec.Quota != nil && cpuSpec.Period != nil && *cpuSpec.Period != 0 {
+ cpuQuota := *cpuSpec.Quota
+ cpuPeriod := *cpuSpec.Period
+ return int(cpuQuota)/int(cpuPeriod) + 1, nil
+ }
+
+ // Default to number of host cpus.
+ return runtime.NumCPU(), nil
+}
+
+// parseCPUNumber converts a cpuset string into the number of cpus included in
+// the string , e.g. "3-6" -> 4.
+func parseCPUNumber(cpus string) (int, error) {
+ switch cpusSlice := strings.Split(cpus, "-"); len(cpusSlice) {
+ case 1:
+ // cpus is not a range. We must only check that it is a valid number.
+ if _, err := strconv.Atoi(cpus); err != nil {
+ return 0, fmt.Errorf("invalid individual cpu number %q", cpus)
+ }
+ return 1, nil
+ case 2:
+ // cpus is a range. We must check that start and end are valid numbers,
+ // and calculate their difference (inclusively).
+ first, err := strconv.Atoi(cpusSlice[0])
+ if err != nil || first < 0 {
+ return 0, fmt.Errorf("invalid first cpu number %q in range %q", cpusSlice[0], cpus)
+ }
+ last, err := strconv.Atoi(cpusSlice[1])
+ if err != nil || last < 0 {
+ return 0, fmt.Errorf("invalid last cpu number %q in range %q", cpusSlice[1], cpus)
+ }
+ cpuNum := last - first + 1
+ if cpuNum <= 0 {
+ return 0, fmt.Errorf("cpu range %q does not include positive number of cpus", cpus)
+ }
+ }
+ return 0, fmt.Errorf("invalid cpu string %q", cpus)
+}