Skip to content

Commit dac3655

Browse files
committed
libcontainer/intelrdt: add support for EnableMonitoring field
The linux.intelRdt.enableMonitoring field enables the creation of a per-container monitoring group. The monitoring group is removed when the container is destroyed. Signed-off-by: Markus Lehtonen <[email protected]>
1 parent 703e2d4 commit dac3655

File tree

6 files changed

+228
-29
lines changed

6 files changed

+228
-29
lines changed

features.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ var featuresCommand = cli.Command{
5656
Enabled: &t,
5757
},
5858
IntelRdt: &features.IntelRdt{
59-
Enabled: &t,
59+
Enabled: &t,
60+
Monitoring: &t,
6061
},
6162
MountExtensions: &features.MountExtensions{
6263
IDMap: &features.IDMap{

libcontainer/configs/intelrdt.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ type IntelRdt struct {
1313
// The unit of memory bandwidth is specified in "percentages" by
1414
// default, and in "MBps" if MBA Software Controller is enabled.
1515
MemBwSchema string `json:"memBwSchema,omitempty"`
16+
17+
// Create a monitoring group for the container.
18+
EnableMonitoring bool `json:"enableMonitoring,omitempty"`
1619
}

libcontainer/container_linux.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ type State struct {
7373

7474
// Intel RDT "resource control" filesystem path.
7575
IntelRdtPath string `json:"intel_rdt_path,omitempty"`
76+
77+
// Path of the container specific monitoring group in resctrl filesystem.
78+
// Empty if the container does not have aindividual dedicated monitoring
79+
// group.
80+
IntelRdtMonPath string `json:"intel_rdt_mon_path,omitempty"`
7681
}
7782

7883
// ID returns the container's unique ID
@@ -938,8 +943,10 @@ func (c *Container) currentState() *State {
938943
}
939944

940945
intelRdtPath := ""
946+
intelRdtMonPath := ""
941947
if c.intelRdtManager != nil {
942948
intelRdtPath = c.intelRdtManager.GetPath()
949+
intelRdtMonPath = c.intelRdtManager.GetMonPath()
943950
}
944951
state := &State{
945952
BaseState: BaseState{
@@ -952,6 +959,7 @@ func (c *Container) currentState() *State {
952959
Rootless: c.config.RootlessEUID && c.config.RootlessCgroups,
953960
CgroupPaths: c.cgroupManager.GetPaths(),
954961
IntelRdtPath: intelRdtPath,
962+
IntelRdtMonPath: intelRdtMonPath,
955963
NamespacePaths: make(map[configs.NamespaceType]string),
956964
ExternalDescriptors: externalDescriptors,
957965
}

libcontainer/intelrdt/intelrdt.go

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,16 @@ func (m *Manager) Apply(pid int) (err error) {
478478
return newLastCmdError(err)
479479
}
480480

481+
// Create MON group
482+
if monPath := m.GetMonPath(); monPath != "" {
483+
if err := os.Mkdir(monPath, 0o755); err != nil && !os.IsExist(err) {
484+
return newLastCmdError(err)
485+
}
486+
if err := WriteIntelRdtTasks(monPath, pid); err != nil {
487+
return newLastCmdError(err)
488+
}
489+
}
490+
481491
m.path = path
482492
return nil
483493
}
@@ -487,13 +497,21 @@ func (m *Manager) Destroy() error {
487497
// Don't remove resctrl group if closid has been explicitly specified. The
488498
// group is likely externally managed, i.e. by some other entity than us.
489499
// There are probably other containers/tasks sharing the same group.
490-
if m.config.IntelRdt != nil && m.config.IntelRdt.ClosID == "" {
491-
m.mu.Lock()
492-
defer m.mu.Unlock()
493-
if err := os.Remove(m.GetPath()); err != nil && !os.IsNotExist(err) {
494-
return err
500+
if m.config.IntelRdt != nil {
501+
if m.config.IntelRdt.ClosID == "" {
502+
m.mu.Lock()
503+
defer m.mu.Unlock()
504+
if err := os.Remove(m.GetPath()); err != nil && !os.IsNotExist(err) {
505+
return err
506+
}
507+
m.path = ""
508+
} else if monPath := m.GetMonPath(); monPath != "" {
509+
// If ClosID is not specified the possible monintoring group was
510+
// removed with the CLOS above.
511+
if err := os.Remove(monPath); err != nil && !os.IsNotExist(err) {
512+
return err
513+
}
495514
}
496-
m.path = ""
497515
}
498516
return nil
499517
}
@@ -504,6 +522,21 @@ func (m *Manager) GetPath() string {
504522
return m.path
505523
}
506524

525+
// GetMonPath returns path of the monitoring group of the container. Returns an
526+
// empty string if the container does not have a individual dedicated
527+
// monitoring group.
528+
func (m *Manager) GetMonPath() string {
529+
if !m.config.IntelRdt.EnableMonitoring {
530+
return ""
531+
}
532+
closPath := m.GetPath()
533+
if closPath == "" {
534+
return ""
535+
}
536+
537+
return filepath.Join(closPath, "mon_groups", m.id)
538+
}
539+
507540
// GetStats returns statistics for Intel RDT.
508541
func (m *Manager) GetStats() (*Stats, error) {
509542
// If intelRdt is not specified in config
@@ -581,7 +614,16 @@ func (m *Manager) GetStats() (*Stats, error) {
581614
}
582615

583616
if IsMBMEnabled() || IsCMTEnabled() {
584-
err = getMonitoringStats(containerPath, stats)
617+
monPath := m.GetMonPath()
618+
if monPath == "" {
619+
// NOTE: If per-container monitoring is not enabled, the monitoring
620+
// data we get here might have little to do with this container as
621+
// there might be anything from this single container to the half
622+
// of the system assigned in the group. Should consider not
623+
// exposing stats in this case(?)
624+
monPath = containerPath
625+
}
626+
err = getMonitoringStats(monPath, stats)
585627
if err != nil {
586628
return nil, err
587629
}

libcontainer/intelrdt/intelrdt_test.go

Lines changed: 162 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"path/filepath"
66
"strings"
77
"testing"
8+
9+
"github.com/opencontainers/runc/libcontainer/configs"
810
)
911

1012
func TestIntelRdtSetL3CacheSchema(t *testing.T) {
@@ -98,31 +100,173 @@ func TestIntelRdtSetMemBwScSchema(t *testing.T) {
98100
}
99101

100102
func TestApply(t *testing.T) {
101-
helper := NewIntelRdtTestUtil(t)
102-
103103
const closID = "test-clos"
104-
closPath := filepath.Join(helper.IntelRdtPath, closID)
105104

106-
helper.config.IntelRdt.ClosID = closID
107-
intelrdt := newManager(helper.config, "container-1", closPath)
108-
if err := intelrdt.Apply(1234); err == nil {
109-
t.Fatal("unexpected success when applying pid")
105+
tests := []struct {
106+
name string
107+
testFunc func(*intelRdtTestUtil)
108+
}{
109+
{
110+
name: "failure because non-pre-existing CLOS",
111+
testFunc: func(helper *intelRdtTestUtil) {
112+
helper.config.IntelRdt = &configs.IntelRdt{
113+
ClosID: closID,
114+
}
115+
116+
closPath := filepath.Join(intelRdtRoot, closID)
117+
intelrdt := newManager(helper.config, "", closPath)
118+
if err := intelrdt.Apply(1234); err == nil {
119+
t.Fatal("unexpected success when applying pid")
120+
}
121+
if _, err := os.Stat(closPath); err == nil {
122+
t.Fatal("closid dir should not exist")
123+
}
124+
},
125+
},
126+
{
127+
name: "CLOS dir should be created if some schema has been specified",
128+
testFunc: func(helper *intelRdtTestUtil) {
129+
helper.config.IntelRdt = &configs.IntelRdt{
130+
ClosID: closID,
131+
L3CacheSchema: "L3:0=f",
132+
}
133+
134+
closPath := filepath.Join(intelRdtRoot, closID)
135+
intelrdt := newManager(helper.config, "", closPath)
136+
if err := intelrdt.Apply(1235); err != nil {
137+
t.Fatalf("Apply() failed: %v", err)
138+
}
139+
140+
pids, err := getIntelRdtParamString(closPath, "tasks")
141+
if err != nil {
142+
t.Fatalf("failed to read tasks file: %v", err)
143+
}
144+
if pids != "1235" {
145+
t.Fatalf("unexpected tasks file, expected '1235', got %q", pids)
146+
}
147+
},
148+
},
149+
{
150+
name: "clos and monitoring group should be created if EnableMonitoring is true",
151+
testFunc: func(helper *intelRdtTestUtil) {
152+
helper.config.IntelRdt = &configs.IntelRdt{
153+
EnableMonitoring: true,
154+
}
155+
id := "aaaa-bbbb"
156+
157+
closPath := filepath.Join(intelRdtRoot, id)
158+
intelrdt := newManager(helper.config, id, closPath)
159+
// We need to pre-create the CLOS/mon_groups directory
160+
if err := os.MkdirAll(filepath.Join(closPath, "mon_groups"), 0o755); err != nil {
161+
t.Fatal(err)
162+
}
163+
164+
if err := intelrdt.Apply(1236); err != nil {
165+
t.Fatalf("Apply() failed: %v", err)
166+
}
167+
168+
pids, err := getIntelRdtParamString(closPath, "tasks")
169+
if err != nil {
170+
t.Fatalf("failed to read tasks file: %v", err)
171+
}
172+
if pids != "1236" {
173+
t.Fatalf("unexpected tasks file, expected '1236', got %q", pids)
174+
}
175+
},
176+
},
110177
}
111-
if _, err := os.Stat(closPath); err == nil {
112-
t.Fatal("closid dir should not exist")
178+
179+
for _, tt := range tests {
180+
t.Run(tt.name, func(t *testing.T) {
181+
helper := NewIntelRdtTestUtil(t)
182+
tt.testFunc(helper)
183+
})
113184
}
185+
}
186+
187+
func TestDestroy(t *testing.T) {
188+
const closID = "test-clos"
189+
190+
// TC-1: per-container CLOS dir should be removed
191+
{
192+
helper := NewIntelRdtTestUtil(t)
193+
id := "abcd-efgh"
114194

115-
// Dir should be created if some schema has been specified
116-
intelrdt.config.IntelRdt.L3CacheSchema = "L3:0=f"
117-
if err := intelrdt.Apply(1235); err != nil {
118-
t.Fatalf("Apply() failed: %v", err)
195+
closPath := filepath.Join(intelRdtRoot, id)
196+
intelrdt := newManager(helper.config, id, closPath)
197+
if err := intelrdt.Apply(1234); err != nil {
198+
t.Fatalf("Apply() failed: %v", err)
199+
}
200+
if _, err := os.Stat(closPath); err != nil {
201+
t.Fatal("CLOS dir should exist")
202+
}
203+
// Need to delete the tasks file so that the dir is empty
204+
os.Remove(filepath.Join(closPath, "tasks"))
205+
if err := intelrdt.Destroy(); err != nil {
206+
t.Fatalf("Destroy() failed: %v", err)
207+
}
208+
if _, err := os.Stat(closPath); err == nil {
209+
t.Fatal("CLOS dir should not exist")
210+
}
119211
}
212+
// TC-2: pre-existing CLOS should not be removed
213+
{
214+
helper := NewIntelRdtTestUtil(t)
215+
helper.config.IntelRdt = &configs.IntelRdt{
216+
ClosID: closID,
217+
}
120218

121-
pids, err := getIntelRdtParamString(intelrdt.GetPath(), "tasks")
122-
if err != nil {
123-
t.Fatalf("failed to read tasks file: %v", err)
219+
closPath := filepath.Join(intelRdtRoot, closID)
220+
if err := os.MkdirAll(closPath, 0o755); err != nil {
221+
t.Fatal(err)
222+
}
223+
224+
intelrdt := newManager(helper.config, "container-1", closPath)
225+
if err := intelrdt.Apply(1234); err != nil {
226+
t.Fatalf("Apply() failed: %v", err)
227+
}
228+
if _, err := os.Stat(closPath); err != nil {
229+
t.Fatal("CLOS dir should exist")
230+
}
231+
if err := intelrdt.Destroy(); err != nil {
232+
t.Fatalf("Destroy() failed: %v", err)
233+
}
234+
if _, err := os.Stat(closPath); err != nil {
235+
t.Fatal("CLOS dir should exist")
236+
}
124237
}
125-
if pids != "1235" {
126-
t.Fatalf("unexpected tasks file, expected '1235', got %q", pids)
238+
// TC-3: per-container MON dir in pre-existing CLOS should be removed
239+
{
240+
helper := NewIntelRdtTestUtil(t)
241+
helper.config.IntelRdt = &configs.IntelRdt{
242+
ClosID: closID,
243+
EnableMonitoring: true,
244+
}
245+
id := "abcd-efgh"
246+
247+
closPath := filepath.Join(intelRdtRoot, closID)
248+
if err := os.MkdirAll(filepath.Join(closPath, "mon_groups"), 0o755); err != nil {
249+
t.Fatal(err)
250+
}
251+
252+
intelrdt := newManager(helper.config, id, closPath)
253+
if err := intelrdt.Apply(1234); err != nil {
254+
t.Fatalf("Apply() failed: %v", err)
255+
}
256+
monPath := filepath.Join(closPath, "mon_groups", id)
257+
if _, err := os.Stat(monPath); err != nil {
258+
t.Fatal("MON dir should exist")
259+
}
260+
// Need to delete the tasks file so that the dir is empty
261+
os.Remove(filepath.Join(monPath, "tasks"))
262+
if err := intelrdt.Destroy(); err != nil {
263+
t.Fatalf("Destroy() failed: %v", err)
264+
}
265+
if _, err := os.Stat(closPath); err != nil {
266+
t.Fatalf("CLOS dir should exist: %f", err)
267+
}
268+
if _, err := os.Stat(monPath); err == nil {
269+
t.Fatal("MON dir should not exist")
270+
}
127271
}
128272
}

libcontainer/specconv/spec_linux.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -462,9 +462,10 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
462462
}
463463
if spec.Linux.IntelRdt != nil {
464464
config.IntelRdt = &configs.IntelRdt{
465-
ClosID: spec.Linux.IntelRdt.ClosID,
466-
L3CacheSchema: spec.Linux.IntelRdt.L3CacheSchema,
467-
MemBwSchema: spec.Linux.IntelRdt.MemBwSchema,
465+
ClosID: spec.Linux.IntelRdt.ClosID,
466+
L3CacheSchema: spec.Linux.IntelRdt.L3CacheSchema,
467+
MemBwSchema: spec.Linux.IntelRdt.MemBwSchema,
468+
EnableMonitoring: spec.Linux.IntelRdt.EnableMonitoring,
468469
}
469470
}
470471
if spec.Linux.Personality != nil {

0 commit comments

Comments
 (0)