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 }