github.com/intel/goresctrl@v0.5.0/pkg/cgroups/cgroupcontrol.go (about) 1 // Copyright 2020-2021 Intel Corporation. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cgroups 16 17 import ( 18 "bufio" 19 "bytes" 20 "errors" 21 "fmt" 22 "os" 23 "path" 24 "strings" 25 "syscall" 26 ) 27 28 // Controller is our enumerated type for cgroup controllers. 29 type Controller int 30 31 // Group represents a control group. 32 type Group string 33 34 // nolint 35 const ( 36 // UnkownController represents a controller of unknown type. 37 UnknownController Controller = iota 38 // blkio cgroup controller. 39 Blkio 40 // cpu cgroup controller. 41 Cpu 42 // cpuacct cgroup controller. 43 Cpuacct 44 // cpuset cgroup controller. 45 Cpuset 46 // devices cgroup controller. 47 Devices 48 // freezer cgroup controller. 49 Freezer 50 // hugetlb cgroup controller. 51 Hugetlb 52 // memory cgroup controller. 53 Memory 54 // net_cls cgroup controller. 55 NetCls 56 // net_prio cgroup controller. 57 NetPrio 58 // per_event cgroup controller. 59 PerfEvent 60 // pids cgroup controller. 61 Pids 62 ) 63 64 var ( 65 // controllerNames maps controllers to names/relative paths. 66 controllerNames = map[Controller]string{ 67 Blkio: "blkio", 68 Cpu: "cpu", 69 Cpuacct: "cpuacct", 70 Cpuset: "cpuset", 71 Devices: "devices", 72 Freezer: "freezer", 73 Hugetlb: "hugetlb", 74 Memory: "memory", 75 NetCls: "net_cls", 76 NetPrio: "net_prio", 77 PerfEvent: "perf_event", 78 Pids: "pids", 79 } 80 81 // controllerNames maps controllers to names/relative paths. 82 controllerDirs = map[string]Controller{ 83 "blkio": Blkio, 84 "cpu": Cpu, 85 "cpuacct": Cpuacct, 86 "cpuset": Cpuset, 87 "devices": Devices, 88 "freezer": Freezer, 89 "hugetlb": Hugetlb, 90 "memory": Memory, 91 "net_cls": NetCls, 92 "net_prio": NetPrio, 93 "perf_event": PerfEvent, 94 "pids": Pids, 95 } 96 ) 97 98 // String returns the name of the given controller. 99 func (c Controller) String() string { 100 if name, ok := controllerNames[c]; ok { 101 return name 102 } 103 return "unknown" 104 } 105 106 // Path returns the absolute path of the given controller. 107 func (c Controller) Path() string { 108 return cgroupPath(c.String()) 109 } 110 111 // RelPath returns the relative path of the given controller. 112 func (c Controller) RelPath() string { 113 return c.String() 114 } 115 116 // Group returns the given group for the controller. 117 func (c Controller) Group(group string) Group { 118 return Group(cgroupPath(c.String(), group)) 119 } 120 121 // AsGroup returns the group for the given absolute directory path. 122 func AsGroup(absDir string) Group { 123 return Group(absDir) 124 } 125 126 // Controller returns the controller for the group. 127 func (g Group) Controller() Controller { 128 relPath := strings.TrimPrefix(string(g), cgroupPath()+"/") 129 split := strings.SplitN(relPath, "/", 2) 130 if len(split) > 0 { 131 return controllerDirs[split[0]] 132 } 133 return UnknownController 134 } 135 136 // GetTasks reads the pids of threads currently assigned to the group. 137 func (g Group) GetTasks() ([]string, error) { 138 return g.readPids(Tasks) 139 } 140 141 // GetProcesses reads the pids of processes currently assigned to the group. 142 func (g Group) GetProcesses() ([]string, error) { 143 return g.readPids(Procs) 144 } 145 146 // AddTasks writes the given thread pids to the group. 147 func (g Group) AddTasks(pids ...string) error { 148 return g.writePids(Tasks, pids...) 149 } 150 151 // AddProcesses writes the given process pids to the group. 152 func (g Group) AddProcesses(pids ...string) error { 153 return g.writePids(Procs, pids...) 154 } 155 156 // Write writes the formatted data to the groups entry. 157 func (g Group) Write(entry, format string, args ...interface{}) error { 158 entryPath := path.Join(string(g), entry) 159 f, err := fsi.OpenFile(entryPath, os.O_WRONLY, 0644) 160 if err != nil { 161 return g.errorf("%q: failed to open for writing: %v", entry, err) 162 } 163 defer f.Close() 164 165 data := fmt.Sprintf(format, args...) 166 if _, err := f.Write([]byte(data)); err != nil { 167 return g.errorf("%q: failed to write %q: %v", entry, data, err) 168 } 169 170 return nil 171 } 172 173 // Read the groups entry and return contents in a string 174 func (g Group) Read(entry string) (string, error) { 175 var buf bytes.Buffer 176 entryPath := path.Join(string(g), entry) 177 f, err := fsi.OpenFile(entryPath, os.O_RDONLY, 0644) 178 if err != nil { 179 return "", g.errorf("%q: failed to open for reading: %v", entry, err) 180 } 181 defer f.Close() 182 if _, err := buf.ReadFrom(f); err != nil { 183 return "", err 184 } 185 return buf.String(), nil 186 } 187 188 // readPids reads pids from a cgroup's tasks or procs entry. 189 func (g Group) readPids(entry string) ([]string, error) { 190 var pids []string 191 192 pidFile := path.Join(string(g), entry) 193 194 f, err := fsi.OpenFile(pidFile, os.O_RDONLY, 0644) 195 if err != nil { 196 return nil, g.errorf("failed to open %q: %v", entry, err) 197 } 198 defer f.Close() 199 200 s := bufio.NewScanner(f) 201 for s.Scan() { 202 pids = append(pids, s.Text()) 203 } 204 if s.Err() != nil { 205 return nil, g.errorf("failed to read %q: %v", entry, err) 206 } 207 208 return pids, nil 209 } 210 211 // writePids writes pids to a cgroup's tasks or procs entry. 212 func (g Group) writePids(entry string, pids ...string) error { 213 pidFile := path.Join(string(g), entry) 214 215 f, err := fsi.OpenFile(pidFile, os.O_WRONLY, 0644) 216 if err != nil { 217 return g.errorf("failed to write pids to %q: %v", pidFile, err) 218 } 219 defer f.Close() 220 221 for _, pid := range pids { 222 if _, err := f.Write([]byte(pid)); err != nil { 223 if !errors.Is(err, syscall.ESRCH) { 224 return g.errorf("failed to write pid %s to %q: %v", 225 pidFile, pid, err) 226 } 227 } 228 } 229 230 return nil 231 } 232 233 // error returns a formatted group-specific error. 234 func (g Group) errorf(format string, args ...interface{}) error { 235 name := strings.TrimPrefix(string(g), cgroupPath()+"/") 236 return fmt.Errorf("cgroup "+name+": "+format, args...) 237 }