github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/os_illumos.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime 6 7 import ( 8 "unsafe" 9 ) 10 11 //go:cgo_import_dynamic libc_getrctl getrctl "libc.so" 12 //go:cgo_import_dynamic libc_rctlblk_get_local_action rctlblk_get_local_action "libc.so" 13 //go:cgo_import_dynamic libc_rctlblk_get_local_flags rctlblk_get_local_flags "libc.so" 14 //go:cgo_import_dynamic libc_rctlblk_get_value rctlblk_get_value "libc.so" 15 //go:cgo_import_dynamic libc_rctlblk_size rctlblk_size "libc.so" 16 17 //go:linkname libc_getrctl libc_getrctl 18 //go:linkname libc_rctlblk_get_local_action libc_rctlblk_get_local_action 19 //go:linkname libc_rctlblk_get_local_flags libc_rctlblk_get_local_flags 20 //go:linkname libc_rctlblk_get_value libc_rctlblk_get_value 21 //go:linkname libc_rctlblk_size libc_rctlblk_size 22 23 var ( 24 libc_getrctl, 25 libc_rctlblk_get_local_action, 26 libc_rctlblk_get_local_flags, 27 libc_rctlblk_get_value, 28 libc_rctlblk_size libcFunc 29 ) 30 31 // Return the minimum value seen for the zone CPU cap, or 0 if no cap is 32 // detected. 33 func getcpucap() uint64 { 34 // The resource control block is an opaque object whose size is only 35 // known to libc. In practice, given the contents, it is unlikely to 36 // grow beyond 8KB so we'll use a static buffer of that size here. 37 const rblkmaxsize = 8 * 1024 38 if rctlblk_size() > rblkmaxsize { 39 return 0 40 } 41 42 // The "zone.cpu-cap" resource control, as described in 43 // resource_controls(5), "sets a limit on the amount of CPU time that 44 // can be used by a zone. The unit used is the percentage of a single 45 // CPU that can be used by all user threads in a zone, expressed as an 46 // integer." A C string of the name must be passed to getrctl(2). 47 name := []byte("zone.cpu-cap\x00") 48 49 // To iterate over the list of values for a particular resource 50 // control, we need two blocks: one for the previously read value and 51 // one for the next value. 52 var rblk0 [rblkmaxsize]byte 53 var rblk1 [rblkmaxsize]byte 54 rblk := &rblk0[0] 55 rblkprev := &rblk1[0] 56 57 var flag uint32 = _RCTL_FIRST 58 var capval uint64 = 0 59 60 for { 61 if getrctl(unsafe.Pointer(&name[0]), unsafe.Pointer(rblkprev), unsafe.Pointer(rblk), flag) != 0 { 62 // The end of the sequence is reported as an ENOENT 63 // failure, but determining the CPU cap is not critical 64 // here. We'll treat any failure as if it were the end 65 // of sequence. 66 break 67 } 68 69 lflags := rctlblk_get_local_flags(unsafe.Pointer(rblk)) 70 action := rctlblk_get_local_action(unsafe.Pointer(rblk)) 71 if (lflags&_RCTL_LOCAL_MAXIMAL) == 0 && action == _RCTL_LOCAL_DENY { 72 // This is a finite (not maximal) value representing a 73 // cap (deny) action. 74 v := rctlblk_get_value(unsafe.Pointer(rblk)) 75 if capval == 0 || capval > v { 76 capval = v 77 } 78 } 79 80 // Swap the blocks around so that we can fetch the next value 81 t := rblk 82 rblk = rblkprev 83 rblkprev = t 84 flag = _RCTL_NEXT 85 } 86 87 return capval 88 } 89 90 func getncpu() int32 { 91 n := int32(sysconf(__SC_NPROCESSORS_ONLN)) 92 if n < 1 { 93 return 1 94 } 95 96 if cents := int32(getcpucap()); cents > 0 { 97 // Convert from a percentage of CPUs to a number of CPUs, 98 // rounding up to make use of a fractional CPU 99 // e.g., 336% becomes 4 CPUs 100 ncap := (cents + 99) / 100 101 if ncap < n { 102 return ncap 103 } 104 } 105 106 return n 107 } 108 109 //go:nosplit 110 func getrctl(controlname, oldbuf, newbuf unsafe.Pointer, flags uint32) uintptr { 111 return sysvicall4(&libc_getrctl, uintptr(controlname), uintptr(oldbuf), uintptr(newbuf), uintptr(flags)) 112 } 113 114 //go:nosplit 115 func rctlblk_get_local_action(buf unsafe.Pointer) uintptr { 116 return sysvicall2(&libc_rctlblk_get_local_action, uintptr(buf), uintptr(0)) 117 } 118 119 //go:nosplit 120 func rctlblk_get_local_flags(buf unsafe.Pointer) uintptr { 121 return sysvicall1(&libc_rctlblk_get_local_flags, uintptr(buf)) 122 } 123 124 //go:nosplit 125 func rctlblk_get_value(buf unsafe.Pointer) uint64 { 126 return uint64(sysvicall1(&libc_rctlblk_get_value, uintptr(buf))) 127 } 128 129 //go:nosplit 130 func rctlblk_size() uintptr { 131 return sysvicall0(&libc_rctlblk_size) 132 }