summaryrefslogtreecommitdiffstats
path: root/src/runtime/os_illumos.go
blob: c3c3e4e6d56df56d29462553efbf24c57d6c3285 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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)
}