Skip to content

Commit ad228cf

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 9c5707c commit ad228cf

File tree

10 files changed

+122
-88
lines changed

10 files changed

+122
-88
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")
@@ -357,7 +365,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
357365
cmd: cmd,
358366
childPipe: childPipe,
359367
parentPipe: parentPipe,
360-
manager: c.cgroupManager,
368+
managers: c.resourceManagers,
361369
config: c.newInitConfig(p),
362370
container: c,
363371
process: p,
@@ -383,7 +391,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
383391
p.consoleChan = make(chan *os.File, 1)
384392
return &setnsProcess{
385393
cmd: cmd,
386-
cgroupPaths: c.cgroupManager.GetPaths(),
394+
cgroupPaths: c.resourceManagers["cgroups"].GetPaths(),
387395
childPipe: childPipe,
388396
parentPipe: parentPipe,
389397
config: c.newInitConfig(p),
@@ -458,7 +466,7 @@ func (c *linuxContainer) Pause() error {
458466
}
459467
switch status {
460468
case Running, Created:
461-
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
469+
if err := c.resourceManagers["cgroups"].Freeze(configs.Frozen); err != nil {
462470
return err
463471
}
464472
return c.state.transition(&pausedState{
@@ -478,7 +486,7 @@ func (c *linuxContainer) Resume() error {
478486
if status != Paused {
479487
return newGenericError(fmt.Errorf("container not paused"), ContainerNotPaused)
480488
}
481-
if err := c.cgroupManager.Freeze(configs.Thawed); err != nil {
489+
if err := c.resourceManagers["cgroups"].Freeze(configs.Thawed); err != nil {
482490
return err
483491
}
484492
return c.state.transition(&runningState{
@@ -487,11 +495,11 @@ func (c *linuxContainer) Resume() error {
487495
}
488496

489497
func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) {
490-
return notifyOnOOM(c.cgroupManager.GetPaths())
498+
return notifyOnOOM(c.resourceManagers["cgroups"].GetPaths())
491499
}
492500

493501
func (c *linuxContainer) NotifyMemoryPressure(level PressureLevel) (<-chan struct{}, error) {
494-
return notifyMemoryPressure(c.cgroupManager.GetPaths(), level)
502+
return notifyMemoryPressure(c.resourceManagers["cgroups"].GetPaths(), level)
495503
}
496504

497505
// checkCriuVersion checks Criu version greater than or equal to minVersion
@@ -876,7 +884,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
876884
}
877885

878886
func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
879-
if err := c.cgroupManager.Apply(pid); err != nil {
887+
if err := c.resourceManagers["cgroups"].Apply(pid); err != nil {
880888
return err
881889
}
882890

@@ -1230,7 +1238,7 @@ func (c *linuxContainer) runType() (Status, error) {
12301238
}
12311239

12321240
func (c *linuxContainer) isPaused() (bool, error) {
1233-
data, err := ioutil.ReadFile(filepath.Join(c.cgroupManager.GetPaths()["freezer"], "freezer.state"))
1241+
data, err := ioutil.ReadFile(filepath.Join(c.resourceManagers["cgroups"].GetPaths()["freezer"], "freezer.state"))
12341242
if err != nil {
12351243
// If freezer cgroup is not mounted, the container would just be not paused.
12361244
if os.IsNotExist(err) {
@@ -1260,7 +1268,7 @@ func (c *linuxContainer) currentState() (*State, error) {
12601268
InitProcessStartTime: startTime,
12611269
Created: c.created,
12621270
},
1263-
CgroupPaths: c.cgroupManager.GetPaths(),
1271+
CgroupPaths: c.resourceManagers["cgroups"].GetPaths(),
12641272
NamespacePaths: make(map[configs.NamespaceType]string),
12651273
ExternalDescriptors: externalDescriptors,
12661274
}

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: 13 additions & 8 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

@@ -180,13 +181,15 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
180181
return nil, newGenericError(err, SystemError)
181182
}
182183
c := &linuxContainer{
183-
id: id,
184-
root: containerRoot,
185-
config: config,
186-
initArgs: l.InitArgs,
187-
criuPath: l.CriuPath,
188-
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
189-
}
184+
id: id,
185+
root: containerRoot,
186+
config: config,
187+
initArgs: l.InitArgs,
188+
criuPath: l.CriuPath,
189+
}
190+
resourceManagers := make(map[string]resourcemanager.ResourceManager)
191+
resourceManagers["cgroups"] = l.NewCgroupsManager(config.Cgroups, nil)
192+
c.resourceManagers = resourceManagers
190193
c.state = &stoppedState{c: c}
191194
return c, nil
192195
}
@@ -212,10 +215,12 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
212215
config: &state.Config,
213216
initArgs: l.InitArgs,
214217
criuPath: l.CriuPath,
215-
cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths),
216218
root: containerRoot,
217219
created: state.Created,
218220
}
221+
resourceManagers := make(map[string]resourcemanager.ResourceManager)
222+
resourceManagers["cgroups"] = l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths)
223+
c.resourceManagers = resourceManagers
219224
c.state = &loadedState{c: c}
220225
if err := c.refreshState(); err != nil {
221226
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)