Commit 91f3997e authored by Joshua M. Clulow's avatar Joshua M. Clulow Committed by Brad Fitzpatrick

runtime: make NumCPU respect zone CPU cap on illumos

On illumos systems, check for the "zone.cpu-cap" resource control when
determining how many usable CPUs are available.  If the resource control
is not set, or we are unable to read it, ignore the failure and return
the value we used to return; i.e., the CPU count from
sysconf(_SC_NPROCESSORS_ONLN).

Fixes golang/go#35199

Change-Id: Ic8a408f84cd140d544d128f1281baad527fb5e35
Reviewed-on: https://go-review.googlesource.com/c/go/+/203758
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 503bccb5
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
const (
_RCTL_LOCAL_DENY = 0x2
_RCTL_LOCAL_MAXIMAL = 0x80000000
_RCTL_FIRST = 0x0
_RCTL_NEXT = 0x1
)
......@@ -119,14 +119,6 @@ var (
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
func getncpu() int32 {
n := int32(sysconf(__SC_NPROCESSORS_ONLN))
if n < 1 {
return 1
}
return n
}
func getPageSize() uintptr {
n := int32(sysconf(__SC_PAGESIZE))
if n <= 0 {
......
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import (
"unsafe"
)
//go:cgo_import_dynamic libc_getrctl getrctl "libc.so"
//go:cgo_import_dynamic libc_rctlblk_get_local_action rctlblk_get_local_action "libc.so"
//go:cgo_import_dynamic libc_rctlblk_get_local_flags rctlblk_get_local_flags "libc.so"
//go:cgo_import_dynamic libc_rctlblk_get_value rctlblk_get_value "libc.so"
//go:cgo_import_dynamic libc_rctlblk_size rctlblk_size "libc.so"
//go:linkname libc_getrctl libc_getrctl
//go:linkname libc_rctlblk_get_local_action libc_rctlblk_get_local_action
//go:linkname libc_rctlblk_get_local_flags libc_rctlblk_get_local_flags
//go:linkname libc_rctlblk_get_value libc_rctlblk_get_value
//go:linkname libc_rctlblk_size libc_rctlblk_size
var (
libc_getrctl,
libc_rctlblk_get_local_action,
libc_rctlblk_get_local_flags,
libc_rctlblk_get_value,
libc_rctlblk_size libcFunc
)
// Return the minimum value seen for the zone CPU cap, or 0 if no cap is
// detected.
func getcpucap() uint64 {
// The resource control block is an opaque object whose size is only
// known to libc. In practice, given the contents, it is unlikely to
// grow beyond 8KB so we'll use a static buffer of that size here.
const rblkmaxsize = 8 * 1024
if rctlblk_size() > rblkmaxsize {
return 0
}
// The "zone.cpu-cap" resource control, as described in
// resource_controls(5), "sets a limit on the amount of CPU time that
// can be used by a zone. The unit used is the percentage of a single
// CPU that can be used by all user threads in a zone, expressed as an
// integer." A C string of the name must be passed to getrctl(2).
name := []byte("zone.cpu-cap\x00")
// To iterate over the list of values for a particular resource
// control, we need two blocks: one for the previously read value and
// one for the next value.
var rblk0 [rblkmaxsize]byte
var rblk1 [rblkmaxsize]byte
rblk := &rblk0[0]
rblkprev := &rblk1[0]
var flag uint32 = _RCTL_FIRST
var capval uint64 = 0
for {
if getrctl(unsafe.Pointer(&name[0]), unsafe.Pointer(rblkprev), unsafe.Pointer(rblk), flag) != 0 {
// The end of the sequence is reported as an ENOENT
// failure, but determining the CPU cap is not critical
// here. We'll treat any failure as if it were the end
// of sequence.
break
}
lflags := rctlblk_get_local_flags(unsafe.Pointer(rblk))
action := rctlblk_get_local_action(unsafe.Pointer(rblk))
if (lflags&_RCTL_LOCAL_MAXIMAL) == 0 && action == _RCTL_LOCAL_DENY {
// This is a finite (not maximal) value representing a
// cap (deny) action.
v := rctlblk_get_value(unsafe.Pointer(rblk))
if capval == 0 || capval > v {
capval = v
}
}
// Swap the blocks around so that we can fetch the next value
t := rblk
rblk = rblkprev
rblkprev = t
flag = _RCTL_NEXT
}
return capval
}
func getncpu() int32 {
n := int32(sysconf(__SC_NPROCESSORS_ONLN))
if n < 1 {
return 1
}
if cents := int32(getcpucap()); cents > 0 {
// Convert from a percentage of CPUs to a number of CPUs,
// rounding up to make use of a fractional CPU
// e.g., 336% becomes 4 CPUs
ncap := (cents + 99) / 100
if ncap < n {
return ncap
}
}
return n
}
//go:nosplit
func getrctl(controlname, oldbuf, newbuf unsafe.Pointer, flags uint32) uintptr {
return sysvicall4(&libc_getrctl, uintptr(controlname), uintptr(oldbuf), uintptr(newbuf), uintptr(flags))
}
//go:nosplit
func rctlblk_get_local_action(buf unsafe.Pointer) uintptr {
return sysvicall2(&libc_rctlblk_get_local_action, uintptr(buf), uintptr(0))
}
//go:nosplit
func rctlblk_get_local_flags(buf unsafe.Pointer) uintptr {
return sysvicall1(&libc_rctlblk_get_local_flags, uintptr(buf))
}
//go:nosplit
func rctlblk_get_value(buf unsafe.Pointer) uint64 {
return uint64(sysvicall1(&libc_rctlblk_get_value, uintptr(buf)))
}
//go:nosplit
func rctlblk_size() uintptr {
return sysvicall0(&libc_rctlblk_size)
}
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Solaris code that doesn't also apply to illumos.
// +build !illumos
package runtime
func getncpu() int32 {
n := int32(sysconf(__SC_NPROCESSORS_ONLN))
if n < 1 {
return 1
}
return n
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment