Skip to content

Commit aefd02a

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 8e8e5d9 commit aefd02a

File tree

11 files changed

+122
-88
lines changed

11 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/rootless/rootless.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func (m *Manager) GetAllPids() ([]int, error) {
107107
return cgroups.GetAllPids(dir)
108108
}
109109

110-
func (m *Manager) GetStats() (*cgroups.Stats, error) {
110+
func (m *Manager) GetStats() (interface{}, error) {
111111
// TODO(cyphar): We can make this work if we figure out a way to allow usage
112112
// of cgroups with a rootless container. While this doesn't
113113
// actually require write access to a cgroup directory, the

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
@@ -145,7 +146,7 @@ func (c *linuxContainer) State() (*State, error) {
145146
}
146147

147148
func (c *linuxContainer) Processes() ([]int, error) {
148-
pids, err := c.cgroupManager.GetAllPids()
149+
pids, err := c.resourceManagers["cgroups"].GetAllPids()
149150
if err != nil {
150151
return nil, newSystemErrorWithCause(err, "getting all container pids from cgroups")
151152
}
@@ -157,7 +158,9 @@ func (c *linuxContainer) Stats() (*Stats, error) {
157158
err error
158159
stats = &Stats{}
159160
)
160-
if stats.CgroupStats, err = c.cgroupManager.GetStats(); err != nil {
161+
cgroupStats, err := c.resourceManagers["cgroups"].GetStats()
162+
stats.CgroupStats = cgroupStats.(*cgroups.Stats)
163+
if err != nil {
161164
return stats, newSystemErrorWithCause(err, "getting container stats from cgroups")
162165
}
163166
for _, iface := range c.config.Networks {
@@ -184,7 +187,12 @@ func (c *linuxContainer) Set(config configs.Config) error {
184187
return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning)
185188
}
186189
c.config = &config
187-
return c.cgroupManager.Set(c.config)
190+
for _, resourceManager := range c.resourceManagers {
191+
if err := resourceManager.Set(c.config); err != nil {
192+
return err
193+
}
194+
}
195+
return nil
188196
}
189197

190198
func (c *linuxContainer) Start(process *Process) error {
@@ -298,7 +306,7 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
298306

299307
func (c *linuxContainer) Signal(s os.Signal, all bool) error {
300308
if all {
301-
return signalAllProcesses(c.cgroupManager, s)
309+
return signalAllProcesses(c.resourceManagers["cgroups"], s)
302310
}
303311
if err := c.initProcess.signal(s); err != nil {
304312
return newSystemErrorWithCause(err, "signaling init process")
@@ -410,7 +418,7 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
410418
cmd: cmd,
411419
childPipe: childPipe,
412420
parentPipe: parentPipe,
413-
manager: c.cgroupManager,
421+
managers: c.resourceManagers,
414422
config: c.newInitConfig(p),
415423
container: c,
416424
process: p,
@@ -434,7 +442,7 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
434442
}
435443
return &setnsProcess{
436444
cmd: cmd,
437-
cgroupPaths: c.cgroupManager.GetPaths(),
445+
cgroupPaths: c.resourceManagers["cgroups"].GetPaths(),
438446
childPipe: childPipe,
439447
parentPipe: parentPipe,
440448
config: c.newInitConfig(p),
@@ -491,7 +499,7 @@ func (c *linuxContainer) Pause() error {
491499
}
492500
switch status {
493501
case Running, Created:
494-
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
502+
if err := c.resourceManagers["cgroups"].Freeze(configs.Frozen); err != nil {
495503
return err
496504
}
497505
return c.state.transition(&pausedState{
@@ -511,7 +519,7 @@ func (c *linuxContainer) Resume() error {
511519
if status != Paused {
512520
return newGenericError(fmt.Errorf("container not paused"), ContainerNotPaused)
513521
}
514-
if err := c.cgroupManager.Freeze(configs.Thawed); err != nil {
522+
if err := c.resourceManagers["cgroups"].Freeze(configs.Thawed); err != nil {
515523
return err
516524
}
517525
return c.state.transition(&runningState{
@@ -524,15 +532,15 @@ func (c *linuxContainer) NotifyOOM() (<-chan struct{}, error) {
524532
if c.config.Rootless {
525533
return nil, fmt.Errorf("cannot get OOM notifications from rootless container")
526534
}
527-
return notifyOnOOM(c.cgroupManager.GetPaths())
535+
return notifyOnOOM(c.resourceManagers["cgroups"].GetPaths())
528536
}
529537

530538
func (c *linuxContainer) NotifyMemoryPressure(level PressureLevel) (<-chan struct{}, error) {
531539
// XXX(cyphar): This requires cgroups.
532540
if c.config.Rootless {
533541
return nil, fmt.Errorf("cannot get memory pressure notifications from rootless container")
534542
}
535-
return notifyMemoryPressure(c.cgroupManager.GetPaths(), level)
543+
return notifyMemoryPressure(c.resourceManagers["cgroups"].GetPaths(), level)
536544
}
537545

538546
// checkCriuVersion checks Criu version greater than or equal to minVersion
@@ -945,7 +953,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error {
945953

946954
func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error {
947955
// XXX: Do we need to deal with this case? AFAIK criu still requires root.
948-
if err := c.cgroupManager.Apply(pid); err != nil {
956+
if err := c.resourceManagers["cgroups"].Apply(pid); err != nil {
949957
return err
950958
}
951959

@@ -1311,7 +1319,7 @@ func (c *linuxContainer) runType() (Status, error) {
13111319
}
13121320

13131321
func (c *linuxContainer) isPaused() (bool, error) {
1314-
data, err := ioutil.ReadFile(filepath.Join(c.cgroupManager.GetPaths()["freezer"], "freezer.state"))
1322+
data, err := ioutil.ReadFile(filepath.Join(c.resourceManagers["cgroups"].GetPaths()["freezer"], "freezer.state"))
13151323
if err != nil {
13161324
// If freezer cgroup is not mounted, the container would just be not paused.
13171325
if os.IsNotExist(err) {
@@ -1342,7 +1350,7 @@ func (c *linuxContainer) currentState() (*State, error) {
13421350
Created: c.created,
13431351
},
13441352
Rootless: c.config.Rootless,
1345-
CgroupPaths: c.cgroupManager.GetPaths(),
1353+
CgroupPaths: c.resourceManagers["cgroups"].GetPaths(),
13461354
NamespacePaths: make(map[configs.NamespaceType]string),
13471355
ExternalDescriptors: externalDescriptors,
13481356
}

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
@@ -19,6 +19,7 @@ import (
1919
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
2020
"github.com/opencontainers/runc/libcontainer/configs"
2121
"github.com/opencontainers/runc/libcontainer/configs/validate"
22+
"github.com/opencontainers/runc/libcontainer/resourcemanager"
2223
"github.com/opencontainers/runc/libcontainer/utils"
2324
)
2425

@@ -188,13 +189,15 @@ func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, err
188189
RootlessCgroups(l)
189190
}
190191
c := &linuxContainer{
191-
id: id,
192-
root: containerRoot,
193-
config: config,
194-
initArgs: l.InitArgs,
195-
criuPath: l.CriuPath,
196-
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
197-
}
192+
id: id,
193+
root: containerRoot,
194+
config: config,
195+
initArgs: l.InitArgs,
196+
criuPath: l.CriuPath,
197+
}
198+
resourceManagers := make(map[string]resourcemanager.ResourceManager)
199+
resourceManagers["cgroups"] = l.NewCgroupsManager(config.Cgroups, nil)
200+
c.resourceManagers = resourceManagers
198201
c.state = &stoppedState{c: c}
199202
return c, nil
200203
}
@@ -224,10 +227,12 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
224227
config: &state.Config,
225228
initArgs: l.InitArgs,
226229
criuPath: l.CriuPath,
227-
cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths),
228230
root: containerRoot,
229231
created: state.Created,
230232
}
233+
resourceManagers := make(map[string]resourcemanager.ResourceManager)
234+
resourceManagers["cgroups"] = l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths)
235+
c.resourceManagers = resourceManagers
231236
c.state = &loadedState{c: c}
232237
if err := c.refreshState(); err != nil {
233238
return nil, err

0 commit comments

Comments
 (0)