Skip to content

Commit d4b1a3e

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 9fc42a7 commit d4b1a3e

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
@@ -484,7 +484,7 @@ func (m *Manager) GetAllPids() ([]int, error) {
484484
return cgroups.GetAllPids(path)
485485
}
486486

487-
func (m *Manager) GetStats() (*cgroups.Stats, error) {
487+
func (m *Manager) GetStats() (interface{}, error) {
488488
m.mu.Lock()
489489
defer m.mu.Unlock()
490490
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 {
@@ -283,7 +291,7 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
283291

284292
func (c *linuxContainer) Signal(s os.Signal, all bool) error {
285293
if all {
286-
return signalAllProcesses(c.cgroupManager, s)
294+
return signalAllProcesses(c.resourceManagers["cgroups"], s)
287295
}
288296
if err := c.initProcess.signal(s); err != nil {
289297
return newSystemErrorWithCause(err, "signaling init process")
@@ -350,7 +358,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
350358
cmd: cmd,
351359
childPipe: childPipe,
352360
parentPipe: parentPipe,
353-
manager: c.cgroupManager,
361+
managers: c.resourceManagers,
354362
config: c.newInitConfig(p),
355363
container: c,
356364
process: p,
@@ -376,7 +384,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
376384
p.consoleChan = make(chan *os.File, 1)
377385
return &setnsProcess{
378386
cmd: cmd,
379-
cgroupPaths: c.cgroupManager.GetPaths(),
387+
cgroupPaths: c.resourceManagers["cgroups"].GetPaths(),
380388
childPipe: childPipe,
381389
parentPipe: parentPipe,
382390
config: c.newInitConfig(p),
@@ -452,7 +460,7 @@ func (c *linuxContainer) Pause() error {
452460
}
453461
switch status {
454462
case Running, Created:
455-
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
463+
if err := c.resourceManagers["cgroups"].Freeze(configs.Frozen); err != nil {
456464
return err
457465
}
458466
return c.state.transition(&pausedState{
@@ -472,7 +480,7 @@ func (c *linuxContainer) Resume() error {
472480
if status != Paused {
473481
return newGenericError(fmt.Errorf("container not paused"), ContainerNotPaused)
474482
}
475-
if err := c.cgroupManager.Freeze(configs.Thawed); err != nil {
483+
if err := c.resourceManagers["cgroups"].Freeze(configs.Thawed); err != nil {
476484
return err
477485
}
478486
return c.state.transition(&runningState{
@@ -481,11 +489,11 @@ func (c *linuxContainer) Resume() error {
481489
}
482490

483491
func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) {
484-
return notifyOnOOM(c.cgroupManager.GetPaths())
492+
return notifyOnOOM(c.resourceManagers["cgroups"].GetPaths())
485493
}
486494

487495
func (c *linuxContainer) NotifyMemoryPressure(level PressureLevel) (<-chan struct{}, error) {
488-
return notifyMemoryPressure(c.cgroupManager.GetPaths(), level)
496+
return notifyMemoryPressure(c.resourceManagers["cgroups"].GetPaths(), level)
489497
}
490498

491499
// checkCriuVersion checks Criu version greater than or equal to minVersion
@@ -870,7 +878,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
870878
}
871879

872880
func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
873-
if err := c.cgroupManager.Apply(pid); err != nil {
881+
if err := c.resourceManagers["cgroups"].Apply(pid); err != nil {
874882
return err
875883
}
876884

@@ -1224,7 +1232,7 @@ func (c *linuxContainer) runType() (Status, error) {
12241232
}
12251233

12261234
func (c *linuxContainer) isPaused() (bool, error) {
1227-
data, err := ioutil.ReadFile(filepath.Join(c.cgroupManager.GetPaths()["freezer"], "freezer.state"))
1235+
data, err := ioutil.ReadFile(filepath.Join(c.resourceManagers["cgroups"].GetPaths()["freezer"], "freezer.state"))
12281236
if err != nil {
12291237
// If freezer cgroup is not mounted, the container would just be not paused.
12301238
if os.IsNotExist(err) {
@@ -1254,7 +1262,7 @@ func (c *linuxContainer) currentState() (*State, error) {
12541262
InitProcessStartTime: startTime,
12551263
Created: c.created,
12561264
},
1257-
CgroupPaths: c.cgroupManager.GetPaths(),
1265+
CgroupPaths: c.resourceManagers["cgroups"].GetPaths(),
12581266
NamespacePaths: make(map[configs.NamespaceType]string),
12591267
ExternalDescriptors: externalDescriptors,
12601268
}

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

@@ -172,13 +173,15 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
172173
return nil, newGenericError(err, SystemError)
173174
}
174175
c := &linuxContainer{
175-
id: id,
176-
root: containerRoot,
177-
config: config,
178-
initArgs: l.InitArgs,
179-
criuPath: l.CriuPath,
180-
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
176+
id: id,
177+
root: containerRoot,
178+
config: config,
179+
initArgs: l.InitArgs,
180+
criuPath: l.CriuPath,
181181
}
182+
resourceManagers := make(map[string]resourcemanager.ResourceManager)
183+
resourceManagers["cgroups"] = l.NewCgroupsManager(config.Cgroups, nil)
184+
c.resourceManagers = resourceManagers
182185
c.state = &stoppedState{c: c}
183186
return c, nil
184187
}
@@ -204,10 +207,12 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
204207
config: &state.Config,
205208
initArgs: l.InitArgs,
206209
criuPath: l.CriuPath,
207-
cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths),
208210
root: containerRoot,
209211
created: state.Created,
210212
}
213+
resourceManagers := make(map[string]resourcemanager.ResourceManager)
214+
resourceManagers["cgroups"] = l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths)
215+
c.resourceManagers = resourceManagers
211216
c.state = &loadedState{c: c}
212217
if err := c.refreshState(); err != nil {
213218
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
)
@@ -209,7 +210,7 @@ type initProcess struct {
209210
parentPipe *os.File
210211
childPipe *os.File
211212
config *initConfig
212-
manager cgroups.Manager
213+
managers map[string]resourcemanager.ResourceManager
213214
container *linuxContainer
214215
fds []string
215216
process *Process
@@ -280,14 +281,18 @@ func (p *initProcess) start() error {
280281
}
281282
p.setExternalDescriptors(fds)
282283
// Do this before syncing with child so that no children
283-
// can escape the cgroup
284-
if err := p.manager.Apply(p.pid()); err != nil {
285-
return newSystemErrorWithCause(err, "applying cgroup configuration for process")
284+
// can escape
285+
for name, manager := range p.managers {
286+
if err := manager.Apply(p.pid()); err != nil {
287+
return newSystemErrorWithCause(err, name+": applying configuration for process")
288+
}
286289
}
287290
defer func() {
288291
if err != nil {
289292
// TODO: should not be the responsibility to call here
290-
p.manager.Destroy()
293+
for _, manager := range p.managers {
294+
manager.Destroy()
295+
}
291296
}
292297
}()
293298
if err := p.createNetworkInterfaces(); err != nil {
@@ -323,8 +328,10 @@ func (p *initProcess) start() error {
323328
return newSystemErrorWithCause(err, "writing syncT 'ack fd'")
324329
}
325330
case procReady:
326-
if err := p.manager.Set(p.config.Config); err != nil {
327-
return newSystemErrorWithCause(err, "setting cgroup config for ready process")
331+
for name, manager := range p.managers {
332+
if err := manager.Set(p.config.Config); err != nil {
333+
return newSystemErrorWithCause(err, name+": setting config for ready process")
334+
}
328335
}
329336
// set oom_score_adj
330337
if err := setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid()); err != nil {
@@ -407,7 +414,7 @@ func (p *initProcess) wait() (*os.ProcessState, error) {
407414
}
408415
// we should kill all processes in cgroup when init is died if we use host PID namespace
409416
if p.sharePidns {
410-
signalAllProcesses(p.manager, syscall.SIGKILL)
417+
signalAllProcesses(p.managers["cgroups"], syscall.SIGKILL)
411418
}
412419
return p.cmd.ProcessState, nil
413420
}

0 commit comments

Comments
 (0)