Browse code

cgroups: Splity out Apply/Cleanup to separate file/interface

This leaves only the generic cgroup helper functions in cgroups.go and
will allow easy implementations of other cgroup managers.

This also wires up the call to Cleanup the cgroup which was missing
before.

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)

Alexander Larsson authored on 2014/03/14 18:47:49
Showing 3 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,189 @@
0
+package cgroups
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"path/filepath"
6
+	"strconv"
7
+)
8
+
9
+type rawCgroup struct {
10
+	root   string
11
+	cgroup string
12
+}
13
+
14
+func rawApply(c *Cgroup, pid int) (ActiveCgroup, error) {
15
+	// We have two implementation of cgroups support, one is based on
16
+	// systemd and the dbus api, and one is based on raw cgroup fs operations
17
+	// following the pre-single-writer model docs at:
18
+	// http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups/
19
+	//
20
+	// we can pick any subsystem to find the root
21
+
22
+	cgroupRoot, err := FindCgroupMountpoint("cpu")
23
+	if err != nil {
24
+		return nil, err
25
+	}
26
+	cgroupRoot = filepath.Dir(cgroupRoot)
27
+
28
+	if _, err := os.Stat(cgroupRoot); err != nil {
29
+		return nil, fmt.Errorf("cgroups fs not found")
30
+	}
31
+
32
+	cgroup := c.Name
33
+	if c.Parent != "" {
34
+		cgroup = filepath.Join(c.Parent, cgroup)
35
+	}
36
+
37
+	raw := &rawCgroup{
38
+		root:   cgroupRoot,
39
+		cgroup: cgroup,
40
+	}
41
+
42
+	if err := raw.setupDevices(c, pid); err != nil {
43
+		return nil, err
44
+	}
45
+	if err := raw.setupMemory(c, pid); err != nil {
46
+		return nil, err
47
+	}
48
+	if err := raw.setupCpu(c, pid); err != nil {
49
+		return nil, err
50
+	}
51
+	return raw, nil
52
+}
53
+
54
+func (raw *rawCgroup) path(subsystem string) (string, error) {
55
+	initPath, err := GetInitCgroupDir(subsystem)
56
+	if err != nil {
57
+		return "", err
58
+	}
59
+	return filepath.Join(raw.root, subsystem, initPath, raw.cgroup), nil
60
+}
61
+
62
+func (raw *rawCgroup) join(subsystem string, pid int) (string, error) {
63
+	path, err := raw.path(subsystem)
64
+	if err != nil {
65
+		return "", err
66
+	}
67
+	if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
68
+		return "", err
69
+	}
70
+	if err := writeFile(path, "tasks", strconv.Itoa(pid)); err != nil {
71
+		return "", err
72
+	}
73
+	return path, nil
74
+}
75
+
76
+func (raw *rawCgroup) setupDevices(c *Cgroup, pid int) (err error) {
77
+	if !c.DeviceAccess {
78
+		dir, err := raw.join("devices", pid)
79
+		if err != nil {
80
+			return err
81
+		}
82
+
83
+		defer func() {
84
+			if err != nil {
85
+				os.RemoveAll(dir)
86
+			}
87
+		}()
88
+
89
+		if err := writeFile(dir, "devices.deny", "a"); err != nil {
90
+			return err
91
+		}
92
+
93
+		allow := []string{
94
+			// /dev/null, zero, full
95
+			"c 1:3 rwm",
96
+			"c 1:5 rwm",
97
+			"c 1:7 rwm",
98
+
99
+			// consoles
100
+			"c 5:1 rwm",
101
+			"c 5:0 rwm",
102
+			"c 4:0 rwm",
103
+			"c 4:1 rwm",
104
+
105
+			// /dev/urandom,/dev/random
106
+			"c 1:9 rwm",
107
+			"c 1:8 rwm",
108
+
109
+			// /dev/pts/ - pts namespaces are "coming soon"
110
+			"c 136:* rwm",
111
+			"c 5:2 rwm",
112
+
113
+			// tuntap
114
+			"c 10:200 rwm",
115
+		}
116
+
117
+		for _, val := range allow {
118
+			if err := writeFile(dir, "devices.allow", val); err != nil {
119
+				return err
120
+			}
121
+		}
122
+	}
123
+	return nil
124
+}
125
+
126
+func (raw *rawCgroup) setupMemory(c *Cgroup, pid int) (err error) {
127
+	if c.Memory != 0 || c.MemorySwap != 0 {
128
+		dir, err := raw.join("memory", pid)
129
+		if err != nil {
130
+			return err
131
+		}
132
+		defer func() {
133
+			if err != nil {
134
+				os.RemoveAll(dir)
135
+			}
136
+		}()
137
+
138
+		if c.Memory != 0 {
139
+			if err := writeFile(dir, "memory.limit_in_bytes", strconv.FormatInt(c.Memory, 10)); err != nil {
140
+				return err
141
+			}
142
+			if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(c.Memory, 10)); err != nil {
143
+				return err
144
+			}
145
+		}
146
+		// By default, MemorySwap is set to twice the size of RAM.
147
+		// If you want to omit MemorySwap, set it to `-1'.
148
+		if c.MemorySwap != -1 {
149
+			if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(c.Memory*2, 10)); err != nil {
150
+				return err
151
+			}
152
+		}
153
+	}
154
+	return nil
155
+}
156
+
157
+func (raw *rawCgroup) setupCpu(c *Cgroup, pid int) (err error) {
158
+	// We always want to join the cpu group, to allow fair cpu scheduling
159
+	// on a container basis
160
+	dir, err := raw.join("cpu", pid)
161
+	if err != nil {
162
+		return err
163
+	}
164
+	if c.CpuShares != 0 {
165
+		if err := writeFile(dir, "cpu.shares", strconv.FormatInt(c.CpuShares, 10)); err != nil {
166
+			return err
167
+		}
168
+	}
169
+	return nil
170
+}
171
+
172
+func (raw *rawCgroup) Cleanup() error {
173
+	get := func(subsystem string) string {
174
+		path, _ := raw.path(subsystem)
175
+		return path
176
+	}
177
+
178
+	for _, path := range []string{
179
+		get("memory"),
180
+		get("devices"),
181
+		get("cpu"),
182
+	} {
183
+		if path != "" {
184
+			os.RemoveAll(path)
185
+		}
186
+	}
187
+	return nil
188
+}
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"io/ioutil"
9 9
 	"os"
10 10
 	"path/filepath"
11
-	"strconv"
12 11
 	"strings"
13 12
 )
14 13
 
... ...
@@ -22,6 +21,10 @@ type Cgroup struct {
22 22
 	CpuShares    int64 `json:"cpu_shares,omitempty"`    // CPU shares (relative weight vs. other containers)
23 23
 }
24 24
 
25
+type ActiveCgroup interface {
26
+	Cleanup() error
27
+}
28
+
25 29
 // https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
26 30
 func FindCgroupMountpoint(subsystem string) (string, error) {
27 31
 	mounts, err := mount.GetMounts()
... ...
@@ -62,48 +65,6 @@ func GetInitCgroupDir(subsystem string) (string, error) {
62 62
 	return parseCgroupFile(subsystem, f)
63 63
 }
64 64
 
65
-func (c *Cgroup) Path(root, subsystem string) (string, error) {
66
-	cgroup := c.Name
67
-	if c.Parent != "" {
68
-		cgroup = filepath.Join(c.Parent, cgroup)
69
-	}
70
-	initPath, err := GetInitCgroupDir(subsystem)
71
-	if err != nil {
72
-		return "", err
73
-	}
74
-	return filepath.Join(root, subsystem, initPath, cgroup), nil
75
-}
76
-
77
-func (c *Cgroup) Join(root, subsystem string, pid int) (string, error) {
78
-	path, err := c.Path(root, subsystem)
79
-	if err != nil {
80
-		return "", err
81
-	}
82
-	if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
83
-		return "", err
84
-	}
85
-	if err := writeFile(path, "tasks", strconv.Itoa(pid)); err != nil {
86
-		return "", err
87
-	}
88
-	return path, nil
89
-}
90
-
91
-func (c *Cgroup) Cleanup(root string) error {
92
-	get := func(subsystem string) string {
93
-		path, _ := c.Path(root, subsystem)
94
-		return path
95
-	}
96
-
97
-	for _, path := range []string{
98
-		get("memory"),
99
-		get("devices"),
100
-		get("cpu"),
101
-	} {
102
-		os.RemoveAll(path)
103
-	}
104
-	return nil
105
-}
106
-
107 65
 func parseCgroupFile(subsystem string, r io.Reader) (string, error) {
108 66
 	s := bufio.NewScanner(r)
109 67
 	for s.Scan() {
... ...
@@ -125,126 +86,6 @@ func writeFile(dir, file, data string) error {
125 125
 	return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
126 126
 }
127 127
 
128
-func (c *Cgroup) Apply(pid int) error {
129
-	// We have two implementation of cgroups support, one is based on
130
-	// systemd and the dbus api, and one is based on raw cgroup fs operations
131
-	// following the pre-single-writer model docs at:
132
-	// http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups/
133
-	//
134
-	// we can pick any subsystem to find the root
135
-	cgroupRoot, err := FindCgroupMountpoint("cpu")
136
-	if err != nil {
137
-		return err
138
-	}
139
-	cgroupRoot = filepath.Dir(cgroupRoot)
140
-
141
-	if _, err := os.Stat(cgroupRoot); err != nil {
142
-		return fmt.Errorf("cgroups fs not found")
143
-	}
144
-	if err := c.setupDevices(cgroupRoot, pid); err != nil {
145
-		return err
146
-	}
147
-	if err := c.setupMemory(cgroupRoot, pid); err != nil {
148
-		return err
149
-	}
150
-	if err := c.setupCpu(cgroupRoot, pid); err != nil {
151
-		return err
152
-	}
153
-	return nil
154
-}
155
-
156
-func (c *Cgroup) setupDevices(cgroupRoot string, pid int) (err error) {
157
-	if !c.DeviceAccess {
158
-		dir, err := c.Join(cgroupRoot, "devices", pid)
159
-		if err != nil {
160
-			return err
161
-		}
162
-
163
-		defer func() {
164
-			if err != nil {
165
-				os.RemoveAll(dir)
166
-			}
167
-		}()
168
-
169
-		if err := writeFile(dir, "devices.deny", "a"); err != nil {
170
-			return err
171
-		}
172
-
173
-		allow := []string{
174
-			// /dev/null, zero, full
175
-			"c 1:3 rwm",
176
-			"c 1:5 rwm",
177
-			"c 1:7 rwm",
178
-
179
-			// consoles
180
-			"c 5:1 rwm",
181
-			"c 5:0 rwm",
182
-			"c 4:0 rwm",
183
-			"c 4:1 rwm",
184
-
185
-			// /dev/urandom,/dev/random
186
-			"c 1:9 rwm",
187
-			"c 1:8 rwm",
188
-
189
-			// /dev/pts/ - pts namespaces are "coming soon"
190
-			"c 136:* rwm",
191
-			"c 5:2 rwm",
192
-
193
-			// tuntap
194
-			"c 10:200 rwm",
195
-		}
196
-
197
-		for _, val := range allow {
198
-			if err := writeFile(dir, "devices.allow", val); err != nil {
199
-				return err
200
-			}
201
-		}
202
-	}
203
-	return nil
204
-}
205
-
206
-func (c *Cgroup) setupMemory(cgroupRoot string, pid int) (err error) {
207
-	if c.Memory != 0 || c.MemorySwap != 0 {
208
-		dir, err := c.Join(cgroupRoot, "memory", pid)
209
-		if err != nil {
210
-			return err
211
-		}
212
-		defer func() {
213
-			if err != nil {
214
-				os.RemoveAll(dir)
215
-			}
216
-		}()
217
-
218
-		if c.Memory != 0 {
219
-			if err := writeFile(dir, "memory.limit_in_bytes", strconv.FormatInt(c.Memory, 10)); err != nil {
220
-				return err
221
-			}
222
-			if err := writeFile(dir, "memory.soft_limit_in_bytes", strconv.FormatInt(c.Memory, 10)); err != nil {
223
-				return err
224
-			}
225
-		}
226
-		// By default, MemorySwap is set to twice the size of RAM.
227
-		// If you want to omit MemorySwap, set it to `-1'.
228
-		if c.MemorySwap != -1 {
229
-			if err := writeFile(dir, "memory.memsw.limit_in_bytes", strconv.FormatInt(c.Memory*2, 10)); err != nil {
230
-				return err
231
-			}
232
-		}
233
-	}
234
-	return nil
235
-}
236
-
237
-func (c *Cgroup) setupCpu(cgroupRoot string, pid int) (err error) {
238
-	// We always want to join the cpu group, to allow fair cpu scheduling
239
-	// on a container basis
240
-	dir, err := c.Join(cgroupRoot, "cpu", pid)
241
-	if err != nil {
242
-		return err
243
-	}
244
-	if c.CpuShares != 0 {
245
-		if err := writeFile(dir, "cpu.shares", strconv.FormatInt(c.CpuShares, 10)); err != nil {
246
-			return err
247
-		}
248
-	}
249
-	return nil
128
+func (c *Cgroup) Apply(pid int) (ActiveCgroup, error) {
129
+	return rawApply(c, pid)
250 130
 }
... ...
@@ -3,6 +3,7 @@
3 3
 package nsinit
4 4
 
5 5
 import (
6
+	"github.com/dotcloud/docker/pkg/cgroups"
6 7
 	"github.com/dotcloud/docker/pkg/libcontainer"
7 8
 	"github.com/dotcloud/docker/pkg/libcontainer/network"
8 9
 	"github.com/dotcloud/docker/pkg/system"
... ...
@@ -61,10 +62,15 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
61 61
 	// Do this before syncing with child so that no children
62 62
 	// can escape the cgroup
63 63
 	ns.logger.Println("setting cgroups")
64
-	if err := ns.SetupCgroups(container, command.Process.Pid); err != nil {
64
+	activeCgroup, err := ns.SetupCgroups(container, command.Process.Pid)
65
+	if err != nil {
65 66
 		command.Process.Kill()
66 67
 		return -1, err
67 68
 	}
69
+	if activeCgroup != nil {
70
+		defer activeCgroup.Cleanup()
71
+	}
72
+
68 73
 	ns.logger.Println("setting up network")
69 74
 	if err := ns.InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil {
70 75
 		command.Process.Kill()
... ...
@@ -85,13 +91,11 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
85 85
 	return status, err
86 86
 }
87 87
 
88
-func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) error {
88
+func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) {
89 89
 	if container.Cgroups != nil {
90
-		if err := container.Cgroups.Apply(nspid); err != nil {
91
-			return err
92
-		}
90
+		return container.Cgroups.Apply(nspid)
93 91
 	}
94
-	return nil
92
+	return nil, nil
95 93
 }
96 94
 
97 95
 func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {