// 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) }