github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/mergeCode/runc/libcontainer/cgroups/fs/apply_raw.go (about) 1 // +build linux 2 3 package fs 4 5 import ( 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "sync" 13 14 "github.com/opencontainers/runc/libcontainer/cgroups" 15 "github.com/opencontainers/runc/libcontainer/configs" 16 libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" 17 ) 18 19 var ( 20 subsystems = subsystemSet{ 21 &CpusetGroup{}, 22 &DevicesGroup{}, 23 &MemoryGroup{}, 24 &CpuGroup{}, 25 &CpuacctGroup{}, 26 &PidsGroup{}, 27 &BlkioGroup{}, 28 &HugetlbGroup{}, 29 &NetClsGroup{}, 30 &NetPrioGroup{}, 31 &PerfEventGroup{}, 32 &FreezerGroup{}, 33 &NameGroup{GroupName: "name=systemd", Join: true}, 34 } 35 HugePageSizes, _ = cgroups.GetHugePageSize() 36 ) 37 38 var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist") 39 40 type subsystemSet []subsystem 41 42 func (s subsystemSet) Get(name string) (subsystem, error) { 43 for _, ss := range s { 44 if ss.Name() == name { 45 return ss, nil 46 } 47 } 48 return nil, errSubsystemDoesNotExist 49 } 50 51 type subsystem interface { 52 // Name returns the name of the subsystem. 53 Name() string 54 // Returns the stats, as 'stats', corresponding to the cgroup under 'path'. 55 GetStats(path string, stats *cgroups.Stats) error 56 // Removes the cgroup represented by 'cgroupData'. 57 Remove(*cgroupData) error 58 // Creates and joins the cgroup represented by 'cgroupData'. 59 Apply(*cgroupData) error 60 // Set the cgroup represented by cgroup. 61 Set(path string, cgroup *configs.Cgroup) error 62 } 63 64 type Manager struct { 65 mu sync.Mutex 66 Cgroups *configs.Cgroup 67 Paths map[string]string 68 } 69 70 // The absolute path to the root of the cgroup hierarchies. 71 var cgroupRootLock sync.Mutex 72 var cgroupRoot string 73 74 // Gets the cgroupRoot. 75 func getCgroupRoot() (string, error) { 76 cgroupRootLock.Lock() 77 defer cgroupRootLock.Unlock() 78 79 if cgroupRoot != "" { 80 return cgroupRoot, nil 81 } 82 83 root, err := cgroups.FindCgroupMountpointDir() 84 if err != nil { 85 return "", err 86 } 87 88 if _, err := os.Stat(root); err != nil { 89 return "", err 90 } 91 92 cgroupRoot = root 93 return cgroupRoot, nil 94 } 95 96 type cgroupData struct { 97 root string 98 innerPath string 99 config *configs.Cgroup 100 pid int 101 } 102 103 func (m *Manager) Apply(pid int) (err error) { 104 if m.Cgroups == nil { 105 return nil 106 } 107 m.mu.Lock() 108 defer m.mu.Unlock() 109 110 var c = m.Cgroups 111 112 d, err := getCgroupData(m.Cgroups, pid) 113 if err != nil { 114 return err 115 } 116 117 if c.Paths != nil { 118 paths := make(map[string]string) 119 for name, path := range c.Paths { 120 _, err := d.path(name) 121 if err != nil { 122 if cgroups.IsNotFound(err) { 123 continue 124 } 125 return err 126 } 127 paths[name] = path 128 } 129 m.Paths = paths 130 return cgroups.EnterPid(m.Paths, pid) 131 } 132 133 paths := make(map[string]string) 134 for _, sys := range subsystems { 135 if err := sys.Apply(d); err != nil { 136 return err 137 } 138 // TODO: Apply should, ideally, be reentrant or be broken up into a separate 139 // create and join phase so that the cgroup hierarchy for a container can be 140 // created then join consists of writing the process pids to cgroup.procs 141 p, err := d.path(sys.Name()) 142 if err != nil { 143 // The non-presence of the devices subsystem is 144 // considered fatal for security reasons. 145 if cgroups.IsNotFound(err) && sys.Name() != "devices" { 146 continue 147 } 148 return err 149 } 150 paths[sys.Name()] = p 151 } 152 m.Paths = paths 153 return nil 154 } 155 156 func (m *Manager) Destroy() error { 157 if m.Cgroups.Paths != nil { 158 return nil 159 } 160 m.mu.Lock() 161 defer m.mu.Unlock() 162 if err := cgroups.RemovePaths(m.Paths); err != nil { 163 return err 164 } 165 m.Paths = make(map[string]string) 166 return nil 167 } 168 169 func (m *Manager) GetPaths() map[string]string { 170 m.mu.Lock() 171 paths := m.Paths 172 m.mu.Unlock() 173 return paths 174 } 175 176 func (m *Manager) GetStats() (*cgroups.Stats, error) { 177 m.mu.Lock() 178 defer m.mu.Unlock() 179 stats := cgroups.NewStats() 180 for name, path := range m.Paths { 181 sys, err := subsystems.Get(name) 182 if err == errSubsystemDoesNotExist || !cgroups.PathExists(path) { 183 continue 184 } 185 if err := sys.GetStats(path, stats); err != nil { 186 return nil, err 187 } 188 } 189 return stats, nil 190 } 191 192 func (m *Manager) Set(container *configs.Config) error { 193 // If Paths are set, then we are just joining cgroups paths 194 // and there is no need to set any values. 195 if m.Cgroups.Paths != nil { 196 return nil 197 } 198 199 paths := m.GetPaths() 200 for _, sys := range subsystems { 201 path := paths[sys.Name()] 202 if err := sys.Set(path, container.Cgroups); err != nil { 203 return err 204 } 205 } 206 207 if m.Paths["cpu"] != "" { 208 if err := CheckCpushares(m.Paths["cpu"], container.Cgroups.Resources.CpuShares); err != nil { 209 return err 210 } 211 } 212 return nil 213 } 214 215 // Freeze toggles the container's freezer cgroup depending on the state 216 // provided 217 func (m *Manager) Freeze(state configs.FreezerState) error { 218 paths := m.GetPaths() 219 dir := paths["freezer"] 220 prevState := m.Cgroups.Resources.Freezer 221 m.Cgroups.Resources.Freezer = state 222 freezer, err := subsystems.Get("freezer") 223 if err != nil { 224 return err 225 } 226 err = freezer.Set(dir, m.Cgroups) 227 if err != nil { 228 m.Cgroups.Resources.Freezer = prevState 229 return err 230 } 231 return nil 232 } 233 234 func (m *Manager) GetPids() ([]int, error) { 235 paths := m.GetPaths() 236 return cgroups.GetPids(paths["devices"]) 237 } 238 239 func (m *Manager) GetAllPids() ([]int, error) { 240 paths := m.GetPaths() 241 return cgroups.GetAllPids(paths["devices"]) 242 } 243 244 func getCgroupData(c *configs.Cgroup, pid int) (*cgroupData, error) { 245 root, err := getCgroupRoot() 246 if err != nil { 247 return nil, err 248 } 249 250 if (c.Name != "" || c.Parent != "") && c.Path != "" { 251 return nil, fmt.Errorf("cgroup: either Path or Name and Parent should be used") 252 } 253 254 // XXX: Do not remove this code. Path safety is important! -- cyphar 255 cgPath := libcontainerUtils.CleanPath(c.Path) 256 cgParent := libcontainerUtils.CleanPath(c.Parent) 257 cgName := libcontainerUtils.CleanPath(c.Name) 258 259 innerPath := cgPath 260 if innerPath == "" { 261 innerPath = filepath.Join(cgParent, cgName) 262 } 263 264 return &cgroupData{ 265 root: root, 266 innerPath: innerPath, 267 config: c, 268 pid: pid, 269 }, nil 270 } 271 272 func (raw *cgroupData) parentPath(subsystem, mountpoint, root string) (string, error) { 273 // Use GetThisCgroupDir instead of GetInitCgroupDir, because the creating 274 // process could in container and shared pid namespace with host, and 275 // /proc/1/cgroup could point to whole other world of cgroups. 276 initPath, err := cgroups.GetThisCgroupDir(subsystem) 277 if err != nil { 278 return "", err 279 } 280 // This is needed for nested containers, because in /proc/self/cgroup we 281 // see pathes from host, which don't exist in container. 282 relDir, err := filepath.Rel(root, initPath) 283 if err != nil { 284 return "", err 285 } 286 return filepath.Join(mountpoint, relDir), nil 287 } 288 289 func (raw *cgroupData) path(subsystem string) (string, error) { 290 mnt, root, err := cgroups.FindCgroupMountpointAndRoot(subsystem) 291 // If we didn't mount the subsystem, there is no point we make the path. 292 if err != nil { 293 return "", err 294 } 295 296 // If the cgroup name/path is absolute do not look relative to the cgroup of the init process. 297 if filepath.IsAbs(raw.innerPath) { 298 // Sometimes subsystems can be mounted together as 'cpu,cpuacct'. 299 return filepath.Join(raw.root, filepath.Base(mnt), raw.innerPath), nil 300 } 301 302 parentPath, err := raw.parentPath(subsystem, mnt, root) 303 if err != nil { 304 return "", err 305 } 306 307 return filepath.Join(parentPath, raw.innerPath), nil 308 } 309 310 func (raw *cgroupData) join(subsystem string) (string, error) { 311 path, err := raw.path(subsystem) 312 if err != nil { 313 return "", err 314 } 315 if err := os.MkdirAll(path, 0755); err != nil { 316 return "", err 317 } 318 if err := cgroups.WriteCgroupProc(path, raw.pid); err != nil { 319 return "", err 320 } 321 return path, nil 322 } 323 324 func writeFile(dir, file, data string) error { 325 // Normally dir should not be empty, one case is that cgroup subsystem 326 // is not mounted, we will get empty dir, and we want it fail here. 327 if dir == "" { 328 return fmt.Errorf("no such directory for %s", file) 329 } 330 if err := ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700); err != nil { 331 return fmt.Errorf("failed to write %v to %v: %v", data, file, err) 332 } 333 return nil 334 } 335 336 func readFile(dir, file string) (string, error) { 337 data, err := ioutil.ReadFile(filepath.Join(dir, file)) 338 return string(data), err 339 } 340 341 func removePath(p string, err error) error { 342 if err != nil { 343 return err 344 } 345 if p != "" { 346 return os.RemoveAll(p) 347 } 348 return nil 349 } 350 351 func CheckCpushares(path string, c int64) error { 352 var cpuShares int64 353 354 if c == 0 { 355 return nil 356 } 357 358 fd, err := os.Open(filepath.Join(path, "cpu.shares")) 359 if err != nil { 360 return err 361 } 362 defer fd.Close() 363 364 _, err = fmt.Fscanf(fd, "%d", &cpuShares) 365 if err != nil && err != io.EOF { 366 return err 367 } 368 369 if c > cpuShares { 370 return fmt.Errorf("The maximum allowed cpu-shares is %d", cpuShares) 371 } else if c < cpuShares { 372 return fmt.Errorf("The minimum allowed cpu-shares is %d", cpuShares) 373 } 374 375 return nil 376 }