aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/os_freebsd.go
diff options
context:
space:
mode:
authorDavid NewHamlet <david@newhamlet.com>2017-03-11 09:13:20 +1300
committerIan Lance Taylor <iant@golang.org>2017-03-10 22:06:24 +0000
commite19f184b8f61529980c24973d5522dc67e3d8525 (patch)
tree253572abfe37550316bd052889ffca6644705e07 /src/runtime/os_freebsd.go
parent135ce43c8731506d541329a1dfea2c737c6dd0b1 (diff)
downloadgo-e19f184b8f61529980c24973d5522dc67e3d8525.tar.gz
go-e19f184b8f61529980c24973d5522dc67e3d8525.zip
runtime: use cpuset_getaffinity for runtime.NumCPU() on FreeBSD
In FreeBSD when run Go proc under a given sub-list of processors(e.g. 'cpuset -l 0 ./a.out' in multi-core system), runtime.NumCPU() still return all physical CPUs from sysctl hw.ncpu instead of account from sub-list. Fix by use syscall cpuset_getaffinity to account the number of sub-list. Fixes #15206 Change-Id: If87c4b620e870486efa100685db5debbf1210a5b Reviewed-on: https://go-review.googlesource.com/29341 Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/runtime/os_freebsd.go')
-rw-r--r--src/runtime/os_freebsd.go82
1 files changed, 74 insertions, 8 deletions
diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go
index 35ed02646c..f736019faa 100644
--- a/src/runtime/os_freebsd.go
+++ b/src/runtime/os_freebsd.go
@@ -42,21 +42,87 @@ func osyield()
// From FreeBSD's <sys/sysctl.h>
const (
_CTL_HW = 6
- _HW_NCPU = 3
_HW_PAGESIZE = 7
)
var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
+// Undocumented numbers from FreeBSD's lib/libc/gen/sysctlnametomib.c.
+const (
+ _CTL_QUERY = 0
+ _CTL_QUERY_MIB = 3
+)
+
+// sysctlnametomib fill mib with dynamically assigned sysctl entries of name,
+// return count of effected mib slots, return 0 on error.
+func sysctlnametomib(name []byte, mib *[_CTL_MAXNAME]uint32) uint32 {
+ oid := [2]uint32{_CTL_QUERY, _CTL_QUERY_MIB}
+ miblen := uintptr(_CTL_MAXNAME)
+ if sysctl(&oid[0], 2, (*byte)(unsafe.Pointer(mib)), &miblen, (*byte)(unsafe.Pointer(&name[0])), (uintptr)(len(name))) < 0 {
+ return 0
+ }
+ miblen /= unsafe.Sizeof(uint32(0))
+ if miblen <= 0 {
+ return 0
+ }
+ return uint32(miblen)
+}
+
+const (
+ _CPU_SETSIZE_MAX = 32 // Limited by _MaxGomaxprocs(256) in runtime2.go.
+ _CPU_CURRENT_PID = -1 // Current process ID.
+)
+
+//go:noescape
+func cpuset_getaffinity(level int, which int, id int64, size int, mask *byte) int32
+
func getncpu() int32 {
- mib := [2]uint32{_CTL_HW, _HW_NCPU}
- out := uint32(0)
- nout := unsafe.Sizeof(out)
- ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
- if ret >= 0 {
- return int32(out)
+ var mask [_CPU_SETSIZE_MAX]byte
+ var mib [_CTL_MAXNAME]uint32
+
+ // According to FreeBSD's /usr/src/sys/kern/kern_cpuset.c,
+ // cpuset_getaffinity return ERANGE when provided buffer size exceed the limits in kernel.
+ // Querying kern.smp.maxcpus to calculate maximum buffer size.
+ // See https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=200802
+
+ // Variable kern.smp.maxcpus introduced at Dec 23 2003, revision 123766,
+ // with dynamically assigned sysctl entries.
+ miblen := sysctlnametomib([]byte("kern.smp.maxcpus"), &mib)
+ if miblen == 0 {
+ return 1
+ }
+
+ // Query kern.smp.maxcpus.
+ dstsize := uintptr(4)
+ maxcpus := uint32(0)
+ if sysctl(&mib[0], miblen, (*byte)(unsafe.Pointer(&maxcpus)), &dstsize, nil, 0) != 0 {
+ return 1
+ }
+
+ size := maxcpus / _NBBY
+ ptrsize := uint32(unsafe.Sizeof(uintptr(0)))
+ if size < ptrsize {
+ size = ptrsize
+ }
+ if size > _CPU_SETSIZE_MAX {
+ return 1
+ }
+
+ if cpuset_getaffinity(_CPU_LEVEL_WHICH, _CPU_WHICH_PID, _CPU_CURRENT_PID,
+ int(size), (*byte)(unsafe.Pointer(&mask[0]))) != 0 {
+ return 1
+ }
+ n := int32(0)
+ for _, v := range mask[:size] {
+ for v != 0 {
+ n += int32(v & 1)
+ v >>= 1
+ }
+ }
+ if n == 0 {
+ return 1
}
- return 1
+ return n
}
func getPageSize() uintptr {