github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/cgroups.go (about) 1 // Copyright (c) 2018 Huawei Corporation 2 // Copyright (c) 2019 Intel Corporation 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 package virtcontainers 8 9 import ( 10 "bufio" 11 "fmt" 12 "os" 13 "path/filepath" 14 "strings" 15 16 "github.com/containerd/cgroups" 17 specs "github.com/opencontainers/runtime-spec/specs-go" 18 ) 19 20 type cgroupPather interface { 21 cgroups.Subsystem 22 Path(path string) string 23 } 24 25 // unconstrained cgroups are placed here. 26 // for example /sys/fs/cgroup/memory/kata/$CGPATH 27 // where path is defined by the containers manager 28 const cgroupKataPath = "/kata/" 29 30 var cgroupsLoadFunc = cgroups.Load 31 var cgroupsNewFunc = cgroups.New 32 33 // V1Constraints returns the cgroups that are compatible with the VC architecture 34 // and hypervisor, constraints can be applied to these cgroups. 35 func V1Constraints() ([]cgroups.Subsystem, error) { 36 root, err := cgroupV1MountPoint() 37 if err != nil { 38 return nil, err 39 } 40 subsystems := []cgroups.Subsystem{ 41 cgroups.NewCputset(root), 42 cgroups.NewCpu(root), 43 cgroups.NewCpuacct(root), 44 } 45 return cgroupsSubsystems(subsystems) 46 } 47 48 // V1NoConstraints returns the cgroups that are *not* compatible with the VC 49 // architecture and hypervisor, constraints MUST NOT be applied to these cgroups. 50 func V1NoConstraints() ([]cgroups.Subsystem, error) { 51 root, err := cgroupV1MountPoint() 52 if err != nil { 53 return nil, err 54 } 55 subsystems := []cgroups.Subsystem{ 56 // Some constainers managers, like k8s, take the control of cgroups. 57 // k8s: the memory cgroup for the dns containers is small to place 58 // a hypervisor there. 59 cgroups.NewMemory(root), 60 } 61 return cgroupsSubsystems(subsystems) 62 } 63 64 func cgroupsSubsystems(subsystems []cgroups.Subsystem) ([]cgroups.Subsystem, error) { 65 var enabled []cgroups.Subsystem 66 for _, s := range cgroupPathers(subsystems) { 67 // check and remove the default groups that do not exist 68 if _, err := os.Lstat(s.Path("/")); err == nil { 69 enabled = append(enabled, s) 70 } 71 } 72 return enabled, nil 73 } 74 75 func cgroupPathers(subystems []cgroups.Subsystem) []cgroupPather { 76 var out []cgroupPather 77 for _, s := range subystems { 78 if p, ok := s.(cgroupPather); ok { 79 out = append(out, p) 80 } 81 } 82 return out 83 } 84 85 // v1MountPoint returns the mount point where the cgroup 86 // mountpoints are mounted in a single hiearchy 87 func cgroupV1MountPoint() (string, error) { 88 f, err := os.Open("/proc/self/mountinfo") 89 if err != nil { 90 return "", err 91 } 92 defer f.Close() 93 scanner := bufio.NewScanner(f) 94 for scanner.Scan() { 95 if err := scanner.Err(); err != nil { 96 return "", err 97 } 98 var ( 99 text = scanner.Text() 100 fields = strings.Split(text, " ") 101 // safe as mountinfo encodes mountpoints with spaces as \040. 102 index = strings.Index(text, " - ") 103 postSeparatorFields = strings.Fields(text[index+3:]) 104 numPostFields = len(postSeparatorFields) 105 ) 106 // this is an error as we can't detect if the mount is for "cgroup" 107 if numPostFields == 0 { 108 return "", fmt.Errorf("Found no fields post '-' in %q", text) 109 } 110 if postSeparatorFields[0] == "cgroup" { 111 // check that the mount is properly formated. 112 if numPostFields < 3 { 113 return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text) 114 } 115 return filepath.Dir(fields[4]), nil 116 } 117 } 118 return "", cgroups.ErrMountPointNotExist 119 } 120 121 func cgroupNoConstraintsPath(path string) string { 122 return filepath.Join(cgroupKataPath, path) 123 } 124 125 // return the parent cgroup for the given path 126 func parentCgroup(hierarchy cgroups.Hierarchy, path string) (cgroups.Cgroup, error) { 127 // append '/' just in case CgroupsPath doesn't start with it 128 parent := filepath.Dir("/" + path) 129 130 parentCgroup, err := cgroupsLoadFunc(hierarchy, 131 cgroups.StaticPath(parent)) 132 if err != nil { 133 return nil, fmt.Errorf("Could not load parent cgroup %v: %v", parent, err) 134 } 135 136 return parentCgroup, nil 137 } 138 139 // validCPUResources checks CPU resources coherency 140 func validCPUResources(cpuSpec *specs.LinuxCPU) *specs.LinuxCPU { 141 if cpuSpec == nil { 142 return nil 143 } 144 145 cpu := *cpuSpec 146 if cpu.Period != nil && *cpu.Period < 1 { 147 cpu.Period = nil 148 } 149 150 if cpu.Quota != nil && *cpu.Quota < 1 { 151 cpu.Quota = nil 152 } 153 154 if cpu.Shares != nil && *cpu.Shares < 1 { 155 cpu.Shares = nil 156 } 157 158 if cpu.RealtimePeriod != nil && *cpu.RealtimePeriod < 1 { 159 cpu.RealtimePeriod = nil 160 } 161 162 if cpu.RealtimeRuntime != nil && *cpu.RealtimeRuntime < 1 { 163 cpu.RealtimeRuntime = nil 164 } 165 166 return &cpu 167 }