diff options
Diffstat (limited to 'src/runtime/os_illumos.go')
-rw-r--r-- | src/runtime/os_illumos.go | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/runtime/os_illumos.go b/src/runtime/os_illumos.go new file mode 100644 index 0000000..c3c3e4e --- /dev/null +++ b/src/runtime/os_illumos.go @@ -0,0 +1,132 @@ +// 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) +} |