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