1 package fs2
2
3 import (
4 "errors"
5 "fmt"
6 "os"
7 "strings"
8
9 "github.com/opencontainers/runc/libcontainer/cgroups"
10 "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
11 "github.com/opencontainers/runc/libcontainer/configs"
12 )
13
14 type parseError = fscommon.ParseError
15
16 type manager struct {
17 config *configs.Cgroup
18
19 dirPath string
20
21
22 controllers map[string]struct{}
23 }
24
25
26
27
28 func NewManager(config *configs.Cgroup, dirPath string) (cgroups.Manager, error) {
29 if dirPath == "" {
30 var err error
31 dirPath, err = defaultDirPath(config)
32 if err != nil {
33 return nil, err
34 }
35 }
36
37 m := &manager{
38 config: config,
39 dirPath: dirPath,
40 }
41 return m, nil
42 }
43
44 func (m *manager) getControllers() error {
45 if m.controllers != nil {
46 return nil
47 }
48
49 data, err := cgroups.ReadFile(m.dirPath, "cgroup.controllers")
50 if err != nil {
51 if m.config.Rootless && m.config.Path == "" {
52 return nil
53 }
54 return err
55 }
56 fields := strings.Fields(data)
57 m.controllers = make(map[string]struct{}, len(fields))
58 for _, c := range fields {
59 m.controllers[c] = struct{}{}
60 }
61
62 return nil
63 }
64
65 func (m *manager) Apply(pid int) error {
66 if err := CreateCgroupPath(m.dirPath, m.config); err != nil {
67
68
69
70
71 if m.config.Rootless {
72 if m.config.Path == "" {
73 if blNeed, nErr := needAnyControllers(m.config.Resources); nErr == nil && !blNeed {
74 return nil
75 }
76 return fmt.Errorf("rootless needs no limits + no cgrouppath when no permission is granted for cgroups: %w", err)
77 }
78 }
79 return err
80 }
81 if err := cgroups.WriteCgroupProc(m.dirPath, pid); err != nil {
82 return err
83 }
84 return nil
85 }
86
87 func (m *manager) GetPids() ([]int, error) {
88 return cgroups.GetPids(m.dirPath)
89 }
90
91 func (m *manager) GetAllPids() ([]int, error) {
92 return cgroups.GetAllPids(m.dirPath)
93 }
94
95 func (m *manager) GetStats() (*cgroups.Stats, error) {
96 var errs []error
97
98 st := cgroups.NewStats()
99
100
101 if err := statPids(m.dirPath, st); err != nil {
102 errs = append(errs, err)
103 }
104
105 if err := statMemory(m.dirPath, st); err != nil && !os.IsNotExist(err) {
106 errs = append(errs, err)
107 }
108
109 if err := statIo(m.dirPath, st); err != nil && !os.IsNotExist(err) {
110 errs = append(errs, err)
111 }
112
113
114 if err := statCpu(m.dirPath, st); err != nil && !os.IsNotExist(err) {
115 errs = append(errs, err)
116 }
117
118 if err := statHugeTlb(m.dirPath, st); err != nil && !os.IsNotExist(err) {
119 errs = append(errs, err)
120 }
121
122 if err := fscommon.RdmaGetStats(m.dirPath, st); err != nil && !os.IsNotExist(err) {
123 errs = append(errs, err)
124 }
125 if len(errs) > 0 && !m.config.Rootless {
126 return st, fmt.Errorf("error while statting cgroup v2: %+v", errs)
127 }
128 return st, nil
129 }
130
131 func (m *manager) Freeze(state configs.FreezerState) error {
132 if m.config.Resources == nil {
133 return errors.New("cannot toggle freezer: cgroups not configured for container")
134 }
135 if err := setFreezer(m.dirPath, state); err != nil {
136 return err
137 }
138 m.config.Resources.Freezer = state
139 return nil
140 }
141
142 func (m *manager) Destroy() error {
143 return cgroups.RemovePath(m.dirPath)
144 }
145
146 func (m *manager) Path(_ string) string {
147 return m.dirPath
148 }
149
150 func (m *manager) Set(r *configs.Resources) error {
151 if r == nil {
152 return nil
153 }
154 if err := m.getControllers(); err != nil {
155 return err
156 }
157
158 if err := setPids(m.dirPath, r); err != nil {
159 return err
160 }
161
162 if err := setMemory(m.dirPath, r); err != nil {
163 return err
164 }
165
166 if err := setIo(m.dirPath, r); err != nil {
167 return err
168 }
169
170 if err := setCpu(m.dirPath, r); err != nil {
171 return err
172 }
173
174
175
176
177
178 if err := setDevices(m.dirPath, r); err != nil && !m.config.Rootless {
179 return err
180 }
181
182 if err := setCpuset(m.dirPath, r); err != nil {
183 return err
184 }
185
186 if err := setHugeTlb(m.dirPath, r); err != nil {
187 return err
188 }
189
190 if err := fscommon.RdmaSet(m.dirPath, r); err != nil {
191 return err
192 }
193
194 if err := setFreezer(m.dirPath, r.Freezer); err != nil {
195 return err
196 }
197 if err := m.setUnified(r.Unified); err != nil {
198 return err
199 }
200 m.config.Resources = r
201 return nil
202 }
203
204 func (m *manager) setUnified(res map[string]string) error {
205 for k, v := range res {
206 if strings.Contains(k, "/") {
207 return fmt.Errorf("unified resource %q must be a file name (no slashes)", k)
208 }
209 if err := cgroups.WriteFile(m.dirPath, k, v); err != nil {
210
211 if errors.Is(err, os.ErrPermission) || errors.Is(err, os.ErrNotExist) {
212
213
214 sk := strings.SplitN(k, ".", 2)
215 if len(sk) != 2 {
216 return fmt.Errorf("unified resource %q must be in the form CONTROLLER.PARAMETER", k)
217 }
218 c := sk[0]
219 if _, ok := m.controllers[c]; !ok && c != "cgroup" {
220 return fmt.Errorf("unified resource %q can't be set: controller %q not available", k, c)
221 }
222 }
223 return fmt.Errorf("unable to set unified resource %q: %w", k, err)
224 }
225 }
226
227 return nil
228 }
229
230 func (m *manager) GetPaths() map[string]string {
231 paths := make(map[string]string, 1)
232 paths[""] = m.dirPath
233 return paths
234 }
235
236 func (m *manager) GetCgroups() (*configs.Cgroup, error) {
237 return m.config, nil
238 }
239
240 func (m *manager) GetFreezerState() (configs.FreezerState, error) {
241 return getFreezer(m.dirPath)
242 }
243
244 func (m *manager) Exists() bool {
245 return cgroups.PathExists(m.dirPath)
246 }
247
248 func OOMKillCount(path string) (uint64, error) {
249 return fscommon.GetValueByKey(path, "memory.events", "oom_kill")
250 }
251
252 func (m *manager) OOMKillCount() (uint64, error) {
253 c, err := OOMKillCount(m.dirPath)
254 if err != nil && m.config.Rootless && os.IsNotExist(err) {
255 err = nil
256 }
257
258 return c, err
259 }
260
View as plain text