Browse code

Implement systemd support for freezer

These PR does a few things. It ensures that the freezer cgroup is
joined in the systemd driver. It also provides a public api for setting
the freezer state via the cgroups package.
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/05/31 07:09:07
Showing 5 changed files
... ...
@@ -10,6 +10,14 @@ var (
10 10
 	ErrNotFound = errors.New("mountpoint not found")
11 11
 )
12 12
 
13
+type FreezerState string
14
+
15
+const (
16
+	Undefined FreezerState = ""
17
+	Frozen    FreezerState = "FROZEN"
18
+	Thawed    FreezerState = "THAWED"
19
+)
20
+
13 21
 type Cgroup struct {
14 22
 	Name   string `json:"name,omitempty"`
15 23
 	Parent string `json:"parent,omitempty"` // name of parent cgroup or slice
... ...
@@ -23,9 +31,8 @@ type Cgroup struct {
23 23
 	CpuQuota          int64             `json:"cpu_quota,omitempty"`          // CPU hardcap limit (in usecs). Allowed cpu time in a given period.
24 24
 	CpuPeriod         int64             `json:"cpu_period,omitempty"`         // CPU period to be used for hardcapping (in usecs). 0 to use system default.
25 25
 	CpusetCpus        string            `json:"cpuset_cpus,omitempty"`        // CPU to use
26
-	Freezer           string            `json:"freezer,omitempty"`            // set the freeze value for the process
27
-
28
-	Slice string `json:"slice,omitempty"` // Parent slice to use for systemd
26
+	Freezer           FreezerState      `json:"freezer,omitempty"`            // set the freeze value for the process
27
+	Slice             string            `json:"slice,omitempty"`              // Parent slice to use for systemd
29 28
 }
30 29
 
31 30
 type ActiveCgroup interface {
... ...
@@ -37,65 +37,28 @@ type data struct {
37 37
 }
38 38
 
39 39
 func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
40
-	// We have two implementation of cgroups support, one is based on
41
-	// systemd and the dbus api, and one is based on raw cgroup fs operations
42
-	// following the pre-single-writer model docs at:
43
-	// http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups/
44
-	//
45
-	// we can pick any subsystem to find the root
46
-
47
-	cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
40
+	d, err := getCgroupData(c, pid)
48 41
 	if err != nil {
49 42
 		return nil, err
50 43
 	}
51
-	cgroupRoot = filepath.Dir(cgroupRoot)
52 44
 
53
-	if _, err := os.Stat(cgroupRoot); err != nil {
54
-		return nil, fmt.Errorf("cgroups fs not found")
55
-	}
56
-
57
-	cgroup := c.Name
58
-	if c.Parent != "" {
59
-		cgroup = filepath.Join(c.Parent, cgroup)
60
-	}
61
-
62
-	d := &data{
63
-		root:   cgroupRoot,
64
-		cgroup: cgroup,
65
-		c:      c,
66
-		pid:    pid,
67
-	}
68 45
 	for _, sys := range subsystems {
69 46
 		if err := sys.Set(d); err != nil {
70 47
 			d.Cleanup()
71 48
 			return nil, err
72 49
 		}
73 50
 	}
51
+
74 52
 	return d, nil
75 53
 }
76 54
 
77 55
 func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
78 56
 	stats := cgroups.NewStats()
79
-	cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
57
+
58
+	d, err := getCgroupData(c, 0)
80 59
 	if err != nil {
81 60
 		return nil, err
82 61
 	}
83
-	cgroupRoot = filepath.Dir(cgroupRoot)
84
-
85
-	if _, err := os.Stat(cgroupRoot); err != nil {
86
-		return nil, fmt.Errorf("cgroups fs not found")
87
-	}
88
-
89
-	cgroup := c.Name
90
-	if c.Parent != "" {
91
-		cgroup = filepath.Join(c.Parent, cgroup)
92
-	}
93
-
94
-	d := &data{
95
-		root:   cgroupRoot,
96
-		cgroup: cgroup,
97
-		c:      c,
98
-	}
99 62
 
100 63
 	for _, sys := range subsystems {
101 64
 		if err := sys.GetStats(d, stats); err != nil {
... ...
@@ -106,7 +69,37 @@ func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
106 106
 	return stats, nil
107 107
 }
108 108
 
109
+// Freeze toggles the container's freezer cgroup depending on the state
110
+// provided
111
+func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
112
+	d, err := getCgroupData(c, 0)
113
+	if err != nil {
114
+		return err
115
+	}
116
+
117
+	c.Freezer = state
118
+
119
+	freezer := subsystems["freezer"]
120
+
121
+	return freezer.Set(d)
122
+}
123
+
109 124
 func GetPids(c *cgroups.Cgroup) ([]int, error) {
125
+	d, err := getCgroupData(c, 0)
126
+	if err != nil {
127
+		return nil, err
128
+	}
129
+
130
+	dir, err := d.path("devices")
131
+	if err != nil {
132
+		return nil, err
133
+	}
134
+
135
+	return cgroups.ReadProcsFile(dir)
136
+}
137
+
138
+func getCgroupData(c *cgroups.Cgroup, pid int) (*data, error) {
139
+	// we can pick any subsystem to find the root
110 140
 	cgroupRoot, err := cgroups.FindCgroupMountpoint("cpu")
111 141
 	if err != nil {
112 142
 		return nil, err
... ...
@@ -114,7 +107,7 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) {
114 114
 	cgroupRoot = filepath.Dir(cgroupRoot)
115 115
 
116 116
 	if _, err := os.Stat(cgroupRoot); err != nil {
117
-		return nil, fmt.Errorf("cgroup root %s not found", cgroupRoot)
117
+		return nil, fmt.Errorf("cgroups fs not found")
118 118
 	}
119 119
 
120 120
 	cgroup := c.Name
... ...
@@ -122,18 +115,12 @@ func GetPids(c *cgroups.Cgroup) ([]int, error) {
122 122
 		cgroup = filepath.Join(c.Parent, cgroup)
123 123
 	}
124 124
 
125
-	d := &data{
125
+	return &data{
126 126
 		root:   cgroupRoot,
127 127
 		cgroup: cgroup,
128 128
 		c:      c,
129
-	}
130
-
131
-	dir, err := d.path("devices")
132
-	if err != nil {
133
-		return nil, err
134
-	}
135
-
136
-	return cgroups.ReadProcsFile(dir)
129
+		pid:    pid,
130
+	}, nil
137 131
 }
138 132
 
139 133
 func (raw *data) parent(subsystem string) (string, error) {
... ...
@@ -20,11 +20,12 @@ func (s *freezerGroup) Set(d *data) error {
20 20
 		return nil
21 21
 	}
22 22
 
23
-	if d.c.Freezer != "" {
24
-		if err := writeFile(dir, "freezer.state", d.c.Freezer); err != nil {
23
+	if d.c.Freezer != cgroups.Undefined {
24
+		if err := writeFile(dir, "freezer.state", string(d.c.Freezer)); err != nil {
25 25
 			return err
26 26
 		}
27 27
 	}
28
+
28 29
 	return nil
29 30
 }
30 31
 
... ...
@@ -19,3 +19,7 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
19 19
 func GetPids(c *cgroups.Cgroup) ([]int, error) {
20 20
 	return nil, fmt.Errorf("Systemd not supported")
21 21
 }
22
+
23
+func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
24
+	return fmt.Errorf("Systemd not supported")
25
+}
... ...
@@ -218,6 +218,14 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
218 218
 		}
219 219
 	}
220 220
 
221
+	// we need to manually join the freezer cgroup in systemd because it does not currently support it
222
+	// via the dbus api
223
+	freezerPath, err := joinFreezer(c, pid)
224
+	if err != nil {
225
+		return nil, err
226
+	}
227
+	res.cleanupDirs = append(res.cleanupDirs, freezerPath)
228
+
221 229
 	if len(cpusetArgs) != 0 {
222 230
 		// systemd does not atm set up the cpuset controller, so we must manually
223 231
 		// join it. Additionally that is a very finicky controller where each
... ...
@@ -227,14 +235,19 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
227 227
 		if err != nil {
228 228
 			return nil, err
229 229
 		}
230
+
230 231
 		initPath, err := cgroups.GetInitCgroupDir("cpuset")
231 232
 		if err != nil {
232 233
 			return nil, err
233 234
 		}
234 235
 
235
-		rootPath := filepath.Join(mountpoint, initPath)
236
+		var (
237
+			foundCpus bool
238
+			foundMems bool
236 239
 
237
-		path := filepath.Join(mountpoint, initPath, c.Parent+"-"+c.Name)
240
+			rootPath = filepath.Join(mountpoint, initPath)
241
+			path     = filepath.Join(mountpoint, initPath, c.Parent+"-"+c.Name)
242
+		)
238 243
 
239 244
 		res.cleanupDirs = append(res.cleanupDirs, path)
240 245
 
... ...
@@ -242,9 +255,6 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
242 242
 			return nil, err
243 243
 		}
244 244
 
245
-		foundCpus := false
246
-		foundMems := false
247
-
248 245
 		for _, arg := range cpusetArgs {
249 246
 			if arg.File == "cpuset.cpus" {
250 247
 				foundCpus = true
... ...
@@ -303,6 +313,47 @@ func (c *systemdCgroup) Cleanup() error {
303 303
 	return nil
304 304
 }
305 305
 
306
+func joinFreezer(c *cgroups.Cgroup, pid int) (string, error) {
307
+	path, err := getFreezerPath(c)
308
+	if err != nil {
309
+		return "", err
310
+	}
311
+
312
+	if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
313
+		return "", err
314
+	}
315
+
316
+	if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil {
317
+		return "", err
318
+	}
319
+
320
+	return path, nil
321
+}
322
+
323
+func getFreezerPath(c *cgroups.Cgroup) (string, error) {
324
+	mountpoint, err := cgroups.FindCgroupMountpoint("freezer")
325
+	if err != nil {
326
+		return "", err
327
+	}
328
+
329
+	initPath, err := cgroups.GetInitCgroupDir("freezer")
330
+	if err != nil {
331
+		return "", err
332
+	}
333
+
334
+	return filepath.Join(mountpoint, initPath, fmt.Sprintf("%s-%s", c.Parent, c.Name)), nil
335
+
336
+}
337
+
338
+func Freeze(c *cgroups.Cgroup, state cgroups.FreezerState) error {
339
+	path, err := getFreezerPath(c)
340
+	if err != nil {
341
+		return err
342
+	}
343
+
344
+	return ioutil.WriteFile(filepath.Join(path, "freezer.state"), []byte(state), 0)
345
+}
346
+
306 347
 func GetPids(c *cgroups.Cgroup) ([]int, error) {
307 348
 	unitName := getUnitName(c)
308 349