Skip to content

Commit eeecd28

Browse files
committed
libcontainer: refactor resource manager interface
Currently, cgroups manager is the single resource manager in libcontainer. Linux kernel 4.10 will introduce Intel RDT/CAT feature, the kernel interface is exposed via "resource control" filesystem, which is a cgroup-like interface. In order to support Intel RDT/CAT in libcontainer, we need a new resource manager outside cgroups. This patch adds a new "ResourceManager" structure as the base interface for all resource managers, such as cgroups manager and incoming IntelRdt manager. All registered resource managers are consolidated in linuxContainer structure. We can apply to unified operations (e.g., Apply(), Set(), Destroy()) using all of the registered resource managers. Signed-off-by: Xiaochen Shen <[email protected]>
1 parent 6694dd9 commit eeecd28

File tree

10 files changed

+121
-87
lines changed

10 files changed

+121
-87
lines changed

libcontainer/cgroups/cgroups.go

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,11 @@ package cgroups
55
import (
66
"fmt"
77

8-
"github.com/opencontainers/runc/libcontainer/configs"
8+
"github.com/opencontainers/runc/libcontainer/resourcemanager"
99
)
1010

1111
type Manager interface {
12-
// Applies cgroup configuration to the process with the specified pid
13-
Apply(pid int) error
14-
15-
// Returns the PIDs inside the cgroup set
16-
GetPids() ([]int, error)
17-
18-
// Returns the PIDs inside the cgroup set & all sub-cgroups
19-
GetAllPids() ([]int, error)
20-
21-
// Returns statistics for the cgroup set
22-
GetStats() (*Stats, error)
23-
24-
// Toggles the freezer cgroup according with specified state
25-
Freeze(state configs.FreezerState) error
26-
27-
// Destroys the cgroup set
28-
Destroy() error
29-
30-
// The option func SystemdCgroups() and Cgroupfs() require following attributes:
31-
// Paths map[string]string
32-
// Cgroups *configs.Cgroup
33-
// Paths maps cgroup subsystem to path at which it is mounted.
34-
// Cgroups specifies specific cgroup settings for the various subsystems
35-
36-
// Returns cgroup paths to save in a state file and to be able to
37-
// restore the object later.
38-
GetPaths() map[string]string
39-
40-
// Sets the cgroup as configured.
41-
Set(container *configs.Config) error
12+
resourcemanager.ResourceManager
4213
}
4314

4415
type NotFoundError struct {

libcontainer/cgroups/fs/apply_raw.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func (m *Manager) GetPaths() map[string]string {
171171
return paths
172172
}
173173

174-
func (m *Manager) GetStats() (*cgroups.Stats, error) {
174+
func (m *Manager) GetStats() (interface{}, error) {
175175
m.mu.Lock()
176176
defer m.mu.Unlock()
177177
stats := cgroups.NewStats()

libcontainer/cgroups/systemd/apply_nosystemd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func (m *Manager) GetPaths() map[string]string {
3838
return nil
3939
}
4040

41-
func (m *Manager) GetStats() (*cgroups.Stats, error) {
41+
func (m *Manager) GetStats() (interface{}, error) {
4242
return nil, fmt.Errorf("Systemd not supported")
4343
}
4444

libcontainer/cgroups/systemd/apply_systemd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ func (m *Manager) GetAllPids() ([]int, error) {
481481
return cgroups.GetAllPids(path)
482482
}
483483

484-
func (m *Manager) GetStats() (*cgroups.Stats, error) {
484+
func (m *Manager) GetStats() (interface{}, error) {
485485
m.mu.Lock()
486486
defer m.mu.Unlock()
487487
stats := cgroups.NewStats()

libcontainer/container_linux.go

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/opencontainers/runc/libcontainer/cgroups"
2323
"github.com/opencontainers/runc/libcontainer/configs"
2424
"github.com/opencontainers/runc/libcontainer/criurpc"
25+
"github.com/opencontainers/runc/libcontainer/resourcemanager"
2526
"github.com/opencontainers/runc/libcontainer/system"
2627
"github.com/opencontainers/runc/libcontainer/utils"
2728
"github.com/syndtr/gocapability/capability"
@@ -34,7 +35,7 @@ type linuxContainer struct {
3435
id string
3536
root string
3637
config *configs.Config
37-
cgroupManager cgroups.Manager
38+
resourceManagers map[string]resourcemanager.ResourceManager
3839
initArgs []string
3940
initProcess parentProcess
4041
initProcessStartTime string
@@ -142,7 +143,7 @@ func (c *linuxContainer) State() (*State, error) {
142143
}
143144

144145
func (c *linuxContainer) Processes() ([]int, error) {
145-
pids, err := c.cgroupManager.GetAllPids()
146+
pids, err := c.resourceManagers["cgroups"].GetAllPids()
146147
if err != nil {
147148
return nil, newSystemErrorWithCause(err, "getting all container pids from cgroups")
148149
}
@@ -154,7 +155,9 @@ func (c *linuxContainer) Stats() (*Stats, error) {
154155
err error
155156
stats = &Stats{}
156157
)
157-
if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil {
158+
cgroupStats, err := c.resourceManagers["cgroups"].GetStats()
159+
stats.CgroupStats = cgroupStats.(*cgroups.Stats)
160+
if err != nil {
158161
return stats, newSystemErrorWithCause(err, "getting container stats from cgroups")
159162
}
160163
for _, iface := range c.config.Networks {
@@ -181,7 +184,12 @@ func (c *linuxContainer) Set(config configs.Config) error {
181184
return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning)
182185
}
183186
c.config = &config
184-
return c.cgroupManager.Set(c.config)
187+
for _, resourceManager := range c.resourceManagers {
188+
if err := resourceManager.Set(c.config); err != nil {
189+
return err
190+
}
191+
}
192+
return nil
185193
}
186194

187195
func (c *linuxContainer) Start(process *Process) error {
@@ -295,7 +303,7 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
295303

296304
func (c *linuxContainer) Signal(s os.Signal, all bool) error {
297305
if all {
298-
return signalAllProcesses(c.cgroupManager, s)
306+
return signalAllProcesses(c.resourceManagers["cgroups"], s)
299307
}
300308
if err := c.initProcess.signal(s); err != nil {
301309
return newSystemErrorWithCause(err, "signaling init process")
@@ -400,7 +408,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
400408
cmd: cmd,
401409
childPipe: childPipe,
402410
parentPipe: parentPipe,
403-
manager: c.cgroupManager,
411+
managers: c.resourceManagers,
404412
config: c.newInitConfig(p),
405413
container: c,
406414
process: p,
@@ -426,7 +434,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
426434
p.consoleChan = make(chan *os.File, 1)
427435
return &setnsProcess{
428436
cmd: cmd,
429-
cgroupPaths: c.cgroupManager.GetPaths(),
437+
cgroupPaths: c.resourceManagers["cgroups"].GetPaths(),
430438
childPipe: childPipe,
431439
parentPipe: parentPipe,
432440
config: c.newInitConfig(p),
@@ -500,7 +508,7 @@ func (c *linuxContainer) Pause() error {
500508
}
501509
switch status {
502510
case Running, Created:
503-
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
511+
if err := c.resourceManagers["cgroups"].Freeze(configs.Frozen); err != nil {
504512
return err
505513
}
506514
return c.state.transition(&pausedState{
@@ -520,7 +528,7 @@ func (c *linuxContainer) Resume() error {
520528
if status != Paused {
521529
return newGenericError(fmt.Errorf("container not paused"), ContainerNotPaused)
522530
}
523-
if err := c.cgroupManager.Freeze(configs.Thawed); err != nil {
531+
if err := c.resourceManagers["cgroups"].Freeze(configs.Thawed); err != nil {
524532
return err
525533
}
526534
return c.state.transition(&runningState{
@@ -529,11 +537,11 @@ func (c *linuxContainer) Resume() error {
529537
}
530538

531539
func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) {
532-
return notifyOnOOM(c.cgroupManager.GetPaths())
540+
return notifyOnOOM(c.resourceManagers["cgroups"].GetPaths())
533541
}
534542

535543
func (c *linuxContainer) NotifyMemoryPressure(level PressureLevel) (<-chan struct{}, error) {
536-
return notifyMemoryPressure(c.cgroupManager.GetPaths(), level)
544+
return notifyMemoryPressure(c.resourceManagers["cgroups"].GetPaths(), level)
537545
}
538546

539547
// checkCriuVersion checks Criu version greater than or equal to minVersion
@@ -931,7 +939,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
931939
}
932940

933941
func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
934-
if err := c.cgroupManager.Apply(pid); err != nil {
942+
if err := c.resourceManagers["cgroups"].Apply(pid); err != nil {
935943
return err
936944
}
937945

@@ -1297,7 +1305,7 @@ func (c *linuxContainer) runType() (Status, error) {
12971305
}
12981306

12991307
func (c *linuxContainer) isPaused() (bool, error) {
1300-
data, err := ioutil.ReadFile(filepath.Join(c.cgroupManager.GetPaths()["freezer"], "freezer.state"))
1308+
data, err := ioutil.ReadFile(filepath.Join(c.resourceManagers["cgroups"].GetPaths()["freezer"], "freezer.state"))
13011309
if err != nil {
13021310
// If freezer cgroup is not mounted, the container would just be not paused.
13031311
if os.IsNotExist(err) {
@@ -1327,7 +1335,7 @@ func (c *linuxContainer) currentState() (*State, error) {
13271335
InitProcessStartTime: startTime,
13281336
Created: c.created,
13291337
},
1330-
CgroupPaths: c.cgroupManager.GetPaths(),
1338+
CgroupPaths: c.resourceManagers["cgroups"].GetPaths(),
13311339
NamespacePaths: make(map[configs.NamespaceType]string),
13321340
ExternalDescriptors: externalDescriptors,
13331341
}

libcontainer/container_linux_test.go

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/opencontainers/runc/libcontainer/cgroups"
1111
"github.com/opencontainers/runc/libcontainer/configs"
12+
"github.com/opencontainers/runc/libcontainer/resourcemanager"
1213
)
1314

1415
type mockCgroupManager struct {
@@ -26,7 +27,7 @@ func (m *mockCgroupManager) GetAllPids() ([]int, error) {
2627
return m.allPids, nil
2728
}
2829

29-
func (m *mockCgroupManager) GetStats() (*cgroups.Stats, error) {
30+
func (m *mockCgroupManager) GetStats() (interface{}, error) {
3031
return m.stats, nil
3132
}
3233

@@ -88,10 +89,11 @@ func (m *mockProcess) setExternalDescriptors(newFds []string) {
8889

8990
func TestGetContainerPids(t *testing.T) {
9091
container := &linuxContainer{
91-
id: "myid",
92-
config: &configs.Config{},
93-
cgroupManager: &mockCgroupManager{allPids: []int{1, 2, 3}},
92+
id: "myid",
93+
config: &configs.Config{},
9494
}
95+
container.resourceManagers = make(map[string]resourcemanager.ResourceManager)
96+
container.resourceManagers["cgroups"] = &mockCgroupManager{allPids: []int{1, 2, 3}}
9597
pids, err := container.Processes()
9698
if err != nil {
9799
t.Fatal(err)
@@ -107,13 +109,14 @@ func TestGetContainerStats(t *testing.T) {
107109
container := &linuxContainer{
108110
id: "myid",
109111
config: &configs.Config{},
110-
cgroupManager: &mockCgroupManager{
111-
pids: []int{1, 2, 3},
112-
stats: &cgroups.Stats{
113-
MemoryStats: cgroups.MemoryStats{
114-
Usage: cgroups.MemoryData{
115-
Usage: 1024,
116-
},
112+
}
113+
container.resourceManagers = make(map[string]resourcemanager.ResourceManager)
114+
container.resourceManagers["cgroups"] = &mockCgroupManager{
115+
pids: []int{1, 2, 3},
116+
stats: &cgroups.Stats{
117+
MemoryStats: cgroups.MemoryStats{
118+
Usage: cgroups.MemoryData{
119+
Usage: 1024,
117120
},
118121
},
119122
},
@@ -152,18 +155,19 @@ func TestGetContainerState(t *testing.T) {
152155
_pid: pid,
153156
started: "010",
154157
},
155-
cgroupManager: &mockCgroupManager{
156-
pids: []int{1, 2, 3},
157-
stats: &cgroups.Stats{
158-
MemoryStats: cgroups.MemoryStats{
159-
Usage: cgroups.MemoryData{
160-
Usage: 1024,
161-
},
158+
}
159+
container.resourceManagers = make(map[string]resourcemanager.ResourceManager)
160+
container.resourceManagers["cgroups"] = &mockCgroupManager{
161+
pids: []int{1, 2, 3},
162+
stats: &cgroups.Stats{
163+
MemoryStats: cgroups.MemoryStats{
164+
Usage: cgroups.MemoryData{
165+
Usage: 1024,
162166
},
163167
},
164-
paths: map[string]string{
165-
"memory": expectedMemoryPath,
166-
},
168+
},
169+
paths: map[string]string{
170+
"memory": expectedMemoryPath,
167171
},
168172
}
169173
container.state = &createdState{c: container}

libcontainer/factory_linux.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
1919
"github.com/opencontainers/runc/libcontainer/configs"
2020
"github.com/opencontainers/runc/libcontainer/configs/validate"
21+
"github.com/opencontainers/runc/libcontainer/resourcemanager"
2122
"github.com/opencontainers/runc/libcontainer/utils"
2223
)
2324

@@ -170,13 +171,15 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
170171
return nil, newGenericError(err, SystemError)
171172
}
172173
c := &linuxContainer{
173-
id: id,
174-
root: containerRoot,
175-
config: config,
176-
initArgs: l.InitArgs,
177-
criuPath: l.CriuPath,
178-
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
174+
id: id,
175+
root: containerRoot,
176+
config: config,
177+
initArgs: l.InitArgs,
178+
criuPath: l.CriuPath,
179179
}
180+
resourceManagers := make(map[string]resourcemanager.ResourceManager)
181+
resourceManagers["cgroups"] = l.NewCgroupsManager(config.Cgroups, nil)
182+
c.resourceManagers = resourceManagers
180183
c.state = &stoppedState{c: c}
181184
return c, nil
182185
}
@@ -202,10 +205,12 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
202205
config: &state.Config,
203206
initArgs: l.InitArgs,
204207
criuPath: l.CriuPath,
205-
cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths),
206208
root: containerRoot,
207209
created: state.Created,
208210
}
211+
resourceManagers := make(map[string]resourcemanager.ResourceManager)
212+
resourceManagers["cgroups"] = l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths)
213+
c.resourceManagers = resourceManagers
209214
c.state = &loadedState{c: c}
210215
if err := c.refreshState(); err != nil {
211216
return nil, err

libcontainer/process_linux.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/opencontainers/runc/libcontainer/cgroups"
1717
"github.com/opencontainers/runc/libcontainer/configs"
18+
"github.com/opencontainers/runc/libcontainer/resourcemanager"
1819
"github.com/opencontainers/runc/libcontainer/system"
1920
"github.com/opencontainers/runc/libcontainer/utils"
2021
)
@@ -207,7 +208,7 @@ type initProcess struct {
207208
parentPipe *os.File
208209
childPipe *os.File
209210
config *initConfig
210-
manager cgroups.Manager
211+
managers map[string]resourcemanager.ResourceManager
211212
container *linuxContainer
212213
fds []string
213214
process *Process
@@ -278,14 +279,18 @@ func (p *initProcess) start() error {
278279
}
279280
p.setExternalDescriptors(fds)
280281
// Do this before syncing with child so that no children
281-
// can escape the cgroup
282-
if err := p.manager.Apply(p.pid()); err != nil {
283-
return newSystemErrorWithCause(err, "applying cgroup configuration for process")
282+
// can escape
283+
for name, manager := range p.managers {
284+
if err := manager.Apply(p.pid()); err != nil {
285+
return newSystemErrorWithCause(err, name+": applying configuration for process")
286+
}
284287
}
285288
defer func() {
286289
if err != nil {
287290
// TODO: should not be the responsibility to call here
288-
p.manager.Destroy()
291+
for _, manager := range p.managers {
292+
manager.Destroy()
293+
}
289294
}
290295
}()
291296
if err := p.createNetworkInterfaces(); err != nil {
@@ -321,8 +326,10 @@ func (p *initProcess) start() error {
321326
return newSystemErrorWithCause(err, "writing syncT 'ack fd'")
322327
}
323328
case procReady:
324-
if err := p.manager.Set(p.config.Config); err != nil {
325-
return newSystemErrorWithCause(err, "setting cgroup config for ready process")
329+
for name, manager := range p.managers {
330+
if err := manager.Set(p.config.Config); err != nil {
331+
return newSystemErrorWithCause(err, name+": setting config for ready process")
332+
}
326333
}
327334
// set oom_score_adj
328335
if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil {
@@ -405,7 +412,7 @@ func (p *initProcess) wait() (*os.ProcessState, error) {
405412
}
406413
// we should kill all processes in cgroup when init is died if we use host PID namespace
407414
if p.sharePidns {
408-
signalAllProcesses(p.manager, syscall.SIGKILL)
415+
signalAllProcesses(p.managers["cgroups"], syscall.SIGKILL)
409416
}
410417
return p.cmd.ProcessState, nil
411418
}

0 commit comments

Comments
 (0)