Skip to content

Commit 1a75f81

Browse files
systemd cgroup driver supports slice management
Signed-off-by: derekwaynecarr <[email protected]>
1 parent 1359131 commit 1a75f81

File tree

1 file changed

+60
-11
lines changed

1 file changed

+60
-11
lines changed

libcontainer/cgroups/systemd/apply_systemd.go

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"io/ioutil"
99
"os"
1010
"path/filepath"
11-
"strconv"
1211
"strings"
1312
"sync"
1413
"time"
@@ -67,12 +66,14 @@ var subsystems = subsystemSet{
6766

6867
const (
6968
testScopeWait = 4
69+
testSliceWait = 4
7070
)
7171

7272
var (
7373
connLock sync.Mutex
7474
theConn *systemdDbus.Conn
7575
hasStartTransientUnit bool
76+
hasStartTransientSliceUnit bool
7677
hasTransientDefaultDependencies bool
7778
hasDelegate bool
7879
)
@@ -159,8 +160,36 @@ func UseSystemd() bool {
159160
}
160161
}
161162

163+
// Assume we have the ability to start a transient unit as a slice
164+
// This was broken until systemd v229, but has been back-ported on RHEL environments >= 219
165+
// For details, see: https://bugzilla.redhat.com/show_bug.cgi?id=1370299
166+
hasStartTransientSliceUnit = true
167+
168+
// To ensure simple clean-up, we create a slice off the root with no hierarchy
169+
slice := fmt.Sprintf("libcontainer_%d_systemd_test_default.slice", os.Getpid())
170+
if _, err := theConn.StartTransientUnit(slice, "replace", nil, nil); err != nil {
171+
if _, ok := err.(dbus.Error); ok {
172+
hasStartTransientSliceUnit = false
173+
}
174+
}
175+
176+
for i := 0; i <= testSliceWait; i++ {
177+
if _, err := theConn.StopUnit(slice, "replace", nil); err != nil {
178+
if dbusError, ok := err.(dbus.Error); ok {
179+
if strings.Contains(dbusError.Name, "org.freedesktop.systemd1.NoSuchUnit") {
180+
hasStartTransientSliceUnit = false
181+
break
182+
}
183+
}
184+
} else {
185+
break
186+
}
187+
time.Sleep(time.Millisecond)
188+
}
189+
162190
// Not critical because of the stop unit logic above.
163191
theConn.StopUnit(scope, "replace", nil)
192+
theConn.StopUnit(slice, "replace", nil)
164193
}
165194
return hasStartTransientUnit
166195
}
@@ -194,11 +223,24 @@ func (m *Manager) Apply(pid int) error {
194223
slice = c.Parent
195224
}
196225

197-
properties = append(properties,
198-
systemdDbus.PropSlice(slice),
199-
systemdDbus.PropDescription("docker container "+c.Name),
200-
newProp("PIDs", []uint32{uint32(pid)}),
201-
)
226+
properties = append(properties, systemdDbus.PropDescription("libcontainer container "+c.Name))
227+
228+
// if we create a slice, the parent is defined via a Wants=
229+
if strings.HasSuffix(unitName, ".slice") {
230+
// This was broken until systemd v229, but has been back-ported on RHEL environments >= 219
231+
if !hasStartTransientSliceUnit {
232+
return fmt.Errorf("systemd version does not support ability to start a slice as transient unit")
233+
}
234+
properties = append(properties, systemdDbus.PropWants(slice))
235+
} else {
236+
// otherwise, we use Slice=
237+
properties = append(properties, systemdDbus.PropSlice(slice))
238+
}
239+
240+
// only add pid if its valid, -1 is used w/ general slice creation.
241+
if pid != -1 {
242+
properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
243+
}
202244

203245
if hasDelegate {
204246
// This is only supported on systemd versions 218 and above.
@@ -302,10 +344,9 @@ func join(c *configs.Cgroup, subsystem string, pid int) (string, error) {
302344
if err := os.MkdirAll(path, 0755); err != nil {
303345
return "", err
304346
}
305-
if err := writeFile(path, "cgroup.procs", strconv.Itoa(pid)); err != nil {
347+
if err := cgroups.WriteCgroupProc(path, pid); err != nil {
306348
return "", err
307349
}
308-
309350
return path, nil
310351
}
311352

@@ -350,7 +391,7 @@ func joinCgroups(c *configs.Cgroup, pid int) error {
350391
// systemd represents slice heirarchy using `-`, so we need to follow suit when
351392
// generating the path of slice. Essentially, test-a-b.slice becomes
352393
// test.slice/test-a.slice/test-a-b.slice.
353-
func expandSlice(slice string) (string, error) {
394+
func ExpandSlice(slice string) (string, error) {
354395
suffix := ".slice"
355396
// Name has to end with ".slice", but can't be just ".slice".
356397
if len(slice) < len(suffix) || !strings.HasSuffix(slice, suffix) {
@@ -364,6 +405,10 @@ func expandSlice(slice string) (string, error) {
364405

365406
var path, prefix string
366407
sliceName := strings.TrimSuffix(slice, suffix)
408+
// if input was -.slice, we should just return root now
409+
if sliceName == "-" {
410+
return "/", nil
411+
}
367412
for _, component := range strings.Split(sliceName, "-") {
368413
// test--a.slice isn't permitted, nor is -test.slice.
369414
if component == "" {
@@ -396,7 +441,7 @@ func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
396441
slice = c.Parent
397442
}
398443

399-
slice, err = expandSlice(slice)
444+
slice, err = ExpandSlice(slice)
400445
if err != nil {
401446
return "", err
402447
}
@@ -483,7 +528,11 @@ func (m *Manager) Set(container *configs.Config) error {
483528
}
484529

485530
func getUnitName(c *configs.Cgroup) string {
486-
return fmt.Sprintf("%s-%s.scope", c.ScopePrefix, c.Name)
531+
// by default, we create a scope unless the user explicitly asks for a slice.
532+
if !strings.HasSuffix(c.Name, ".slice") {
533+
return fmt.Sprintf("%s-%s.scope", c.ScopePrefix, c.Name)
534+
}
535+
return c.Name
487536
}
488537

489538
func setKernelMemory(c *configs.Cgroup) error {

0 commit comments

Comments
 (0)