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