gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

cgroups2.go (4529B)


      1 // Copyright (c) 2022 Uber Technologies, Inc.
      2 //
      3 // Permission is hereby granted, free of charge, to any person obtaining a copy
      4 // of this software and associated documentation files (the "Software"), to deal
      5 // in the Software without restriction, including without limitation the rights
      6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      7 // copies of the Software, and to permit persons to whom the Software is
      8 // furnished to do so, subject to the following conditions:
      9 //
     10 // The above copyright notice and this permission notice shall be included in
     11 // all copies or substantial portions of the Software.
     12 //
     13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     19 // THE SOFTWARE.
     20 
     21 //go:build linux
     22 // +build linux
     23 
     24 package cgroups
     25 
     26 import (
     27 	"bufio"
     28 	"errors"
     29 	"fmt"
     30 	"io"
     31 	"os"
     32 	"path"
     33 	"strconv"
     34 	"strings"
     35 )
     36 
     37 const (
     38 	// _cgroupv2CPUMax is the file name for the CGroup-V2 CPU max and period
     39 	// parameter.
     40 	_cgroupv2CPUMax = "cpu.max"
     41 	// _cgroupFSType is the Linux CGroup-V2 file system type used in
     42 	// `/proc/$PID/mountinfo`.
     43 	_cgroupv2FSType = "cgroup2"
     44 
     45 	_cgroupv2MountPoint = "/sys/fs/cgroup"
     46 
     47 	_cgroupV2CPUMaxDefaultPeriod = 100000
     48 	_cgroupV2CPUMaxQuotaMax      = "max"
     49 )
     50 
     51 const (
     52 	_cgroupv2CPUMaxQuotaIndex = iota
     53 	_cgroupv2CPUMaxPeriodIndex
     54 )
     55 
     56 // ErrNotV2 indicates that the system is not using cgroups2.
     57 var ErrNotV2 = errors.New("not using cgroups2")
     58 
     59 // CGroups2 provides access to cgroups data for systems using cgroups2.
     60 type CGroups2 struct {
     61 	mountPoint string
     62 	groupPath  string
     63 	cpuMaxFile string
     64 }
     65 
     66 // NewCGroups2ForCurrentProcess builds a CGroups2 for the current process.
     67 //
     68 // This returns ErrNotV2 if the system is not using cgroups2.
     69 func NewCGroups2ForCurrentProcess() (*CGroups2, error) {
     70 	return newCGroups2From(_procPathMountInfo, _procPathCGroup)
     71 }
     72 
     73 func newCGroups2From(mountInfoPath, procPathCGroup string) (*CGroups2, error) {
     74 	isV2, err := isCGroupV2(mountInfoPath)
     75 	if err != nil {
     76 		return nil, err
     77 	}
     78 
     79 	if !isV2 {
     80 		return nil, ErrNotV2
     81 	}
     82 
     83 	subsystems, err := parseCGroupSubsystems(procPathCGroup)
     84 	if err != nil {
     85 		return nil, err
     86 	}
     87 
     88 	// Find v2 subsystem by looking for the `0` id
     89 	var v2subsys *CGroupSubsys
     90 	for _, subsys := range subsystems {
     91 		if subsys.ID == 0 {
     92 			v2subsys = subsys
     93 			break
     94 		}
     95 	}
     96 
     97 	if v2subsys == nil {
     98 		return nil, ErrNotV2
     99 	}
    100 
    101 	return &CGroups2{
    102 		mountPoint: _cgroupv2MountPoint,
    103 		groupPath:  v2subsys.Name,
    104 		cpuMaxFile: _cgroupv2CPUMax,
    105 	}, nil
    106 }
    107 
    108 func isCGroupV2(procPathMountInfo string) (bool, error) {
    109 	var (
    110 		isV2          bool
    111 		newMountPoint = func(mp *MountPoint) error {
    112 			isV2 = isV2 || (mp.FSType == _cgroupv2FSType && mp.MountPoint == _cgroupv2MountPoint)
    113 			return nil
    114 		}
    115 	)
    116 
    117 	if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil {
    118 		return false, err
    119 	}
    120 
    121 	return isV2, nil
    122 }
    123 
    124 // CPUQuota returns the CPU quota applied with the CPU cgroup2 controller.
    125 // It is a result of reading cpu quota and period from cpu.max file.
    126 // It will return `cpu.max / cpu.period`. If cpu.max is set to max, it returns
    127 // (-1, false, nil)
    128 func (cg *CGroups2) CPUQuota() (float64, bool, error) {
    129 	cpuMaxParams, err := os.Open(path.Join(cg.mountPoint, cg.groupPath, cg.cpuMaxFile))
    130 	if err != nil {
    131 		if os.IsNotExist(err) {
    132 			return -1, false, nil
    133 		}
    134 		return -1, false, err
    135 	}
    136 	defer cpuMaxParams.Close()
    137 
    138 	scanner := bufio.NewScanner(cpuMaxParams)
    139 	if scanner.Scan() {
    140 		fields := strings.Fields(scanner.Text())
    141 		if len(fields) == 0 || len(fields) > 2 {
    142 			return -1, false, fmt.Errorf("invalid format")
    143 		}
    144 
    145 		if fields[_cgroupv2CPUMaxQuotaIndex] == _cgroupV2CPUMaxQuotaMax {
    146 			return -1, false, nil
    147 		}
    148 
    149 		max, err := strconv.Atoi(fields[_cgroupv2CPUMaxQuotaIndex])
    150 		if err != nil {
    151 			return -1, false, err
    152 		}
    153 
    154 		var period int
    155 		if len(fields) == 1 {
    156 			period = _cgroupV2CPUMaxDefaultPeriod
    157 		} else {
    158 			period, err = strconv.Atoi(fields[_cgroupv2CPUMaxPeriodIndex])
    159 			if err != nil {
    160 				return -1, false, err
    161 			}
    162 		}
    163 
    164 		return float64(max) / float64(period), true, nil
    165 	}
    166 
    167 	if err := scanner.Err(); err != nil {
    168 		return -1, false, err
    169 	}
    170 
    171 	return 0, false, io.ErrUnexpectedEOF
    172 }