|
|
|
@ -2,6 +2,7 @@ package internal
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"math"
|
|
|
|
|
"os"
|
|
|
|
@ -18,6 +19,7 @@ import (
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
cgroupDir = "/sys/fs/cgroup"
|
|
|
|
|
cpuMaxFile = cgroupDir + "/cpu.max"
|
|
|
|
|
cpuStatFile = cgroupDir + "/cpu.stat"
|
|
|
|
|
cpusetFile = cgroupDir + "/cpuset.cpus.effective"
|
|
|
|
|
)
|
|
|
|
@ -30,10 +32,9 @@ var (
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type cgroup interface {
|
|
|
|
|
cpuQuotaUs() (int64, error)
|
|
|
|
|
cpuPeriodUs() (uint64, error)
|
|
|
|
|
cpus() ([]uint64, error)
|
|
|
|
|
usageAllCpus() (uint64, error)
|
|
|
|
|
cpuQuota() (float64, error)
|
|
|
|
|
cpuUsage() (uint64, error)
|
|
|
|
|
effectiveCpus() (int, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func currentCgroup() (cgroup, error) {
|
|
|
|
@ -48,13 +49,22 @@ type cgroupV1 struct {
|
|
|
|
|
cgroups map[string]string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cgroupV1) cpuQuotaUs() (int64, error) {
|
|
|
|
|
data, err := iox.ReadText(path.Join(c.cgroups["cpu"], "cpu.cfs_quota_us"))
|
|
|
|
|
func (c *cgroupV1) cpuQuota() (float64, error) {
|
|
|
|
|
quotaUs, err := c.cpuQuotaUs()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strconv.ParseInt(data, 10, 64)
|
|
|
|
|
if quotaUs == -1 {
|
|
|
|
|
return -1, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
periodUs, err := c.cpuPeriodUs()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return float64(quotaUs) / float64(periodUs), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cgroupV1) cpuPeriodUs() (uint64, error) {
|
|
|
|
@ -66,16 +76,16 @@ func (c *cgroupV1) cpuPeriodUs() (uint64, error) {
|
|
|
|
|
return parseUint(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cgroupV1) cpus() ([]uint64, error) {
|
|
|
|
|
data, err := iox.ReadText(path.Join(c.cgroups["cpuset"], "cpuset.cpus"))
|
|
|
|
|
func (c *cgroupV1) cpuQuotaUs() (int64, error) {
|
|
|
|
|
data, err := iox.ReadText(path.Join(c.cgroups["cpu"], "cpu.cfs_quota_us"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parseUints(data)
|
|
|
|
|
return strconv.ParseInt(data, 10, 64)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cgroupV1) usageAllCpus() (uint64, error) {
|
|
|
|
|
func (c *cgroupV1) cpuUsage() (uint64, error) {
|
|
|
|
|
data, err := iox.ReadText(path.Join(c.cgroups["cpuacct"], "cpuacct.usage"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
@ -84,38 +94,53 @@ func (c *cgroupV1) usageAllCpus() (uint64, error) {
|
|
|
|
|
return parseUint(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type cgroupV2 struct {
|
|
|
|
|
cgroups map[string]string
|
|
|
|
|
func (c *cgroupV1) effectiveCpus() (int, error) {
|
|
|
|
|
data, err := iox.ReadText(path.Join(c.cgroups["cpuset"], "cpuset.cpus"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cgroupV2) cpuQuotaUs() (int64, error) {
|
|
|
|
|
data, err := iox.ReadText(path.Join(cgroupDir, "cpu.cfs_quota_us"))
|
|
|
|
|
cpus, err := parseUints(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strconv.ParseInt(data, 10, 64)
|
|
|
|
|
return len(cpus), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type cgroupV2 struct {
|
|
|
|
|
cgroups map[string]string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cgroupV2) cpuPeriodUs() (uint64, error) {
|
|
|
|
|
data, err := iox.ReadText(path.Join(cgroupDir, "cpu.cfs_period_us"))
|
|
|
|
|
func (c *cgroupV2) cpuQuota() (float64, error) {
|
|
|
|
|
data, err := iox.ReadText(cpuMaxFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parseUint(data)
|
|
|
|
|
fields := strings.Fields(data)
|
|
|
|
|
if len(fields) != 2 {
|
|
|
|
|
return 0, fmt.Errorf("cgroup: bad /sys/fs/cgroup/cpu.max file: %s", data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cgroupV2) cpus() ([]uint64, error) {
|
|
|
|
|
data, err := iox.ReadText(cpusetFile)
|
|
|
|
|
if fields[0] == "max" {
|
|
|
|
|
return -1, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quotaUs, err := strconv.ParseInt(fields[0], 10, 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
periodUs, err := strconv.ParseUint(fields[1], 10, 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parseUints(data)
|
|
|
|
|
return float64(quotaUs) / float64(periodUs), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cgroupV2) usageAllCpus() (uint64, error) {
|
|
|
|
|
func (c *cgroupV2) cpuUsage() (uint64, error) {
|
|
|
|
|
usec, err := parseUint(c.cgroups["usage_usec"])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
@ -124,6 +149,20 @@ func (c *cgroupV2) usageAllCpus() (uint64, error) {
|
|
|
|
|
return usec * uint64(time.Microsecond), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *cgroupV2) effectiveCpus() (int, error) {
|
|
|
|
|
data, err := iox.ReadText(cpusetFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cpus, err := parseUints(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return len(cpus), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func currentCgroupV1() (cgroup, error) {
|
|
|
|
|
cgroupFile := fmt.Sprintf("/proc/%d/cgroup", os.Getpid())
|
|
|
|
|
lines, err := iox.ReadTextLines(cgroupFile, iox.WithoutBlank())
|
|
|
|
@ -200,7 +239,7 @@ func isCgroup2UnifiedMode() bool {
|
|
|
|
|
func parseUint(s string) (uint64, error) {
|
|
|
|
|
v, err := strconv.ParseInt(s, 10, 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err.(*strconv.NumError).Err == strconv.ErrRange {
|
|
|
|
|
if errors.Is(err, strconv.ErrRange) {
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -225,21 +264,21 @@ func parseUints(val string) ([]uint64, error) {
|
|
|
|
|
for _, r := range cols {
|
|
|
|
|
if strings.Contains(r, "-") {
|
|
|
|
|
fields := strings.SplitN(r, "-", 2)
|
|
|
|
|
min, err := parseUint(fields[0])
|
|
|
|
|
minimum, err := parseUint(fields[0])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cgroup: bad int list format: %s", val)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
max, err := parseUint(fields[1])
|
|
|
|
|
maximum, err := parseUint(fields[1])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("cgroup: bad int list format: %s", val)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if max < min {
|
|
|
|
|
if maximum < minimum {
|
|
|
|
|
return nil, fmt.Errorf("cgroup: bad int list format: %s", val)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i := min; i <= max; i++ {
|
|
|
|
|
for i := minimum; i <= maximum; i++ {
|
|
|
|
|
if _, ok := ints[i]; !ok {
|
|
|
|
|
ints[i] = lang.Placeholder
|
|
|
|
|
sets = append(sets, i)
|
|
|
|
|