github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/cgroups/cpu.go (about)

     1  package cgroups
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  	"strings"
    10  
    11  	spec "github.com/opencontainers/runtime-spec/specs-go"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  type cpuHandler struct {
    16  }
    17  
    18  func getCPUHandler() *cpuHandler {
    19  	return &cpuHandler{}
    20  }
    21  
    22  func cleanString(s string) string {
    23  	return strings.Trim(s, "\n")
    24  }
    25  
    26  func readAcct(ctr *CgroupControl, name string) (uint64, error) {
    27  	p := filepath.Join(ctr.getCgroupv1Path(CPUAcct), name)
    28  	return readFileAsUint64(p)
    29  }
    30  
    31  func readAcctList(ctr *CgroupControl, name string) ([]uint64, error) {
    32  	var r []uint64
    33  
    34  	p := filepath.Join(ctr.getCgroupv1Path(CPUAcct), name)
    35  	data, err := ioutil.ReadFile(p)
    36  	if err != nil {
    37  		return nil, errors.Wrapf(err, "reading %s", p)
    38  	}
    39  	for _, s := range strings.Split(string(data), " ") {
    40  		s = cleanString(s)
    41  		if s == "" {
    42  			break
    43  		}
    44  		v, err := strconv.ParseUint(s, 10, 0)
    45  		if err != nil {
    46  			return nil, errors.Wrapf(err, "parsing %s", s)
    47  		}
    48  		r = append(r, v)
    49  	}
    50  	return r, nil
    51  }
    52  
    53  // Apply set the specified constraints
    54  func (c *cpuHandler) Apply(ctr *CgroupControl, res *spec.LinuxResources) error {
    55  	if res.CPU == nil {
    56  		return nil
    57  	}
    58  	return fmt.Errorf("cpu apply not implemented yet")
    59  }
    60  
    61  // Create the cgroup
    62  func (c *cpuHandler) Create(ctr *CgroupControl) (bool, error) {
    63  	if ctr.cgroup2 {
    64  		return false, nil
    65  	}
    66  	return ctr.createCgroupDirectory(CPU)
    67  }
    68  
    69  // Destroy the cgroup
    70  func (c *cpuHandler) Destroy(ctr *CgroupControl) error {
    71  	return rmDirRecursively(ctr.getCgroupv1Path(CPU))
    72  }
    73  
    74  // Stat fills a metrics structure with usage stats for the controller
    75  func (c *cpuHandler) Stat(ctr *CgroupControl, m *Metrics) error {
    76  	var err error
    77  	usage := CPUUsage{}
    78  	if ctr.cgroup2 {
    79  		values, err := readCgroup2MapFile(ctr, "cpu.stat")
    80  		if err != nil {
    81  			return err
    82  		}
    83  		if val, found := values["usage_usec"]; found {
    84  			usage.Total, err = strconv.ParseUint(cleanString(val[0]), 10, 0)
    85  			if err != nil {
    86  				return err
    87  			}
    88  			usage.Kernel *= 1000
    89  		}
    90  		if val, found := values["system_usec"]; found {
    91  			usage.Kernel, err = strconv.ParseUint(cleanString(val[0]), 10, 0)
    92  			if err != nil {
    93  				return err
    94  			}
    95  			usage.Total *= 1000
    96  		}
    97  		// FIXME: How to read usage.PerCPU?
    98  	} else {
    99  		usage.Total, err = readAcct(ctr, "cpuacct.usage")
   100  		if err != nil {
   101  			if !os.IsNotExist(errors.Cause(err)) {
   102  				return err
   103  			}
   104  			usage.Total = 0
   105  		}
   106  		usage.Kernel, err = readAcct(ctr, "cpuacct.usage_sys")
   107  		if err != nil {
   108  			if !os.IsNotExist(errors.Cause(err)) {
   109  				return err
   110  			}
   111  			usage.Kernel = 0
   112  		}
   113  		usage.PerCPU, err = readAcctList(ctr, "cpuacct.usage_percpu")
   114  		if err != nil {
   115  			if !os.IsNotExist(errors.Cause(err)) {
   116  				return err
   117  			}
   118  			usage.PerCPU = nil
   119  		}
   120  	}
   121  	m.CPU = CPUMetrics{Usage: usage}
   122  	return nil
   123  }
   124  
   125  // GetSystemCPUUsage returns the system usage for all the cgroups
   126  func GetSystemCPUUsage() (uint64, error) {
   127  	cgroupv2, err := IsCgroup2UnifiedMode()
   128  	if err != nil {
   129  		return 0, err
   130  	}
   131  	if !cgroupv2 {
   132  		p := filepath.Join(cgroupRoot, CPUAcct, "cpuacct.usage")
   133  		return readFileAsUint64(p)
   134  	}
   135  
   136  	files, err := ioutil.ReadDir(cgroupRoot)
   137  	if err != nil {
   138  		return 0, errors.Wrapf(err, "read directory %q", cgroupRoot)
   139  	}
   140  	var total uint64
   141  	for _, file := range files {
   142  		if !file.IsDir() {
   143  			continue
   144  		}
   145  		p := filepath.Join(cgroupRoot, file.Name(), "cpu.stat")
   146  
   147  		values, err := readCgroup2MapPath(p)
   148  		if err != nil {
   149  			return 0, err
   150  		}
   151  
   152  		if val, found := values["usage_usec"]; found {
   153  			v, err := strconv.ParseUint(cleanString(val[0]), 10, 0)
   154  			if err != nil {
   155  				return 0, err
   156  			}
   157  			total += v * 1000
   158  		}
   159  
   160  	}
   161  	return total, nil
   162  }