Skip to content

Commit 28daf53

Browse files
authored
Merge pull request #4832 from marquiz/devel/rdt-enablemonitoring
libcontainer/intelrdt: add support for EnableMonitoring field
2 parents b40777d + 7aa4e1a commit 28daf53

File tree

7 files changed

+234
-41
lines changed

7 files changed

+234
-41
lines changed

features.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ var featuresCommand = cli.Command{
5656
Enabled: &t,
5757
},
5858
IntelRdt: &features.IntelRdt{
59-
Enabled: &t,
60-
Schemata: &t,
59+
Enabled: &t,
60+
Schemata: &t,
61+
Monitoring: &t,
6162
},
6263
MemoryPolicy: &features.MemoryPolicy{
6364
Modes: specconv.KnownMemoryPolicyModes(),

libcontainer/configs/intelrdt.go

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

libcontainer/configs/validate/intelrdt_test.go

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,24 @@ func TestValidateIntelRdt(t *testing.T) {
1919
isErr bool
2020
}{
2121
{
22-
name: "rdt not supported, no config",
23-
rdtEnabled: false,
24-
config: nil,
25-
isErr: false,
22+
name: "rdt not supported, no config",
2623
},
2724
{
28-
name: "rdt not supported, with config",
29-
rdtEnabled: false,
30-
config: &configs.IntelRdt{},
31-
isErr: true,
25+
name: "rdt not supported, with config",
26+
config: &configs.IntelRdt{},
27+
isErr: true,
3228
},
3329
{
3430
name: "empty config",
3531
rdtEnabled: true,
3632
config: &configs.IntelRdt{},
37-
isErr: false,
3833
},
3934
{
4035
name: "root clos",
4136
rdtEnabled: true,
4237
config: &configs.IntelRdt{
4338
ClosID: "/",
4439
},
45-
isErr: false,
4640
},
4741
{
4842
name: "invalid ClosID (.)",
@@ -71,7 +65,6 @@ func TestValidateIntelRdt(t *testing.T) {
7165
{
7266
name: "cat not supported",
7367
rdtEnabled: true,
74-
catEnabled: false,
7568
config: &configs.IntelRdt{
7669
L3CacheSchema: "0=ff",
7770
},
@@ -80,7 +73,6 @@ func TestValidateIntelRdt(t *testing.T) {
8073
{
8174
name: "mba not supported",
8275
rdtEnabled: true,
83-
mbaEnabled: false,
8476
config: &configs.IntelRdt{
8577
MemBwSchema: "0=100",
8678
},
@@ -96,7 +88,6 @@ func TestValidateIntelRdt(t *testing.T) {
9688
L3CacheSchema: "0=ff",
9789
MemBwSchema: "0=100",
9890
},
99-
isErr: false,
10091
},
10192
}
10293
for _, tc := range testCases {

libcontainer/container_linux.go

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

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

7782
// ID returns the container's unique ID
@@ -911,8 +916,10 @@ func (c *Container) currentState() *State {
911916
}
912917

913918
intelRdtPath := ""
919+
intelRdtMonPath := ""
914920
if c.intelRdtManager != nil {
915921
intelRdtPath = c.intelRdtManager.GetPath()
922+
intelRdtMonPath = c.intelRdtManager.GetMonPath()
916923
}
917924
state := &State{
918925
BaseState: BaseState{
@@ -925,6 +932,7 @@ func (c *Container) currentState() *State {
925932
Rootless: c.config.RootlessEUID && c.config.RootlessCgroups,
926933
CgroupPaths: c.cgroupManager.GetPaths(),
927934
IntelRdtPath: intelRdtPath,
935+
IntelRdtMonPath: intelRdtMonPath,
928936
NamespacePaths: make(map[configs.NamespaceType]string),
929937
ExternalDescriptors: externalDescriptors,
930938
}

libcontainer/intelrdt/intelrdt.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,22 +468,41 @@ func (m *Manager) Apply(pid int) (err error) {
468468
return newLastCmdError(err)
469469
}
470470

471+
// Create MON group
472+
if monPath := m.GetMonPath(); monPath != "" {
473+
if err := os.Mkdir(monPath, 0o755); err != nil && !os.IsExist(err) {
474+
return newLastCmdError(err)
475+
}
476+
if err := WriteIntelRdtTasks(monPath, pid); err != nil {
477+
return newLastCmdError(err)
478+
}
479+
}
480+
471481
m.path = path
472482
return nil
473483
}
474484

475485
// Destroy destroys the Intel RDT container-specific container_id group.
476486
func (m *Manager) Destroy() error {
487+
if m.config.IntelRdt == nil {
488+
return nil
489+
}
477490
// Don't remove resctrl group if closid has been explicitly specified. The
478491
// group is likely externally managed, i.e. by some other entity than us.
479492
// There are probably other containers/tasks sharing the same group.
480-
if m.config.IntelRdt != nil && m.config.IntelRdt.ClosID == "" {
493+
if m.config.IntelRdt.ClosID == "" {
481494
m.mu.Lock()
482495
defer m.mu.Unlock()
483496
if err := os.Remove(m.GetPath()); err != nil && !os.IsNotExist(err) {
484497
return err
485498
}
486499
m.path = ""
500+
} else if monPath := m.GetMonPath(); monPath != "" {
501+
// If ClosID is not specified the possible monintoring group was
502+
// removed with the CLOS above.
503+
if err := os.Remove(monPath); err != nil && !os.IsNotExist(err) {
504+
return err
505+
}
487506
}
488507
return nil
489508
}
@@ -494,6 +513,21 @@ func (m *Manager) GetPath() string {
494513
return m.path
495514
}
496515

516+
// GetMonPath returns path of the monitoring group of the container. Returns an
517+
// empty string if the container does not have a individual dedicated
518+
// monitoring group.
519+
func (m *Manager) GetMonPath() string {
520+
if !m.config.IntelRdt.EnableMonitoring {
521+
return ""
522+
}
523+
closPath := m.GetPath()
524+
if closPath == "" {
525+
return ""
526+
}
527+
528+
return filepath.Join(closPath, "mon_groups", m.id)
529+
}
530+
497531
// GetStats returns statistics for Intel RDT.
498532
func (m *Manager) GetStats() (*Stats, error) {
499533
// If intelRdt is not specified in config
@@ -573,7 +607,16 @@ func (m *Manager) GetStats() (*Stats, error) {
573607
}
574608

575609
if IsMBMEnabled() || IsCMTEnabled() {
576-
err = getMonitoringStats(containerPath, stats)
610+
monPath := m.GetMonPath()
611+
if monPath == "" {
612+
// NOTE: If per-container monitoring is not enabled, the monitoring
613+
// data we get here might have little to do with this container as
614+
// there might be anything from this single container to the half
615+
// of the system assigned in the group. Should consider not
616+
// exposing stats in this case(?)
617+
monPath = containerPath
618+
}
619+
err = getMonitoringStats(monPath, stats)
577620
if err != nil {
578621
return nil, err
579622
}

libcontainer/intelrdt/intelrdt_test.go

Lines changed: 166 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"os"
55
"path/filepath"
66
"slices"
7+
"strconv"
78
"strings"
89
"testing"
910

@@ -126,31 +127,176 @@ func TestIntelRdtSet(t *testing.T) {
126127
}
127128

128129
func TestApply(t *testing.T) {
129-
helper := NewIntelRdtTestUtil(t)
130+
const pid = 1234
131+
tests := []struct {
132+
name string
133+
config configs.IntelRdt
134+
precreateClos bool
135+
isError bool
136+
postApplyAssert func(*Manager)
137+
}{
138+
{
139+
name: "failure because non-pre-existing CLOS",
140+
config: configs.IntelRdt{
141+
ClosID: "non-existing-clos",
142+
},
143+
isError: true,
144+
postApplyAssert: func(m *Manager) {
145+
if _, err := os.Stat(m.path); err == nil {
146+
t.Fatal("closid dir should not exist")
147+
}
148+
},
149+
},
150+
{
151+
name: "CLOS dir should be created if some schema has been specified",
152+
config: configs.IntelRdt{
153+
ClosID: "clos-to-be-created",
154+
L3CacheSchema: "L3:0=f",
155+
},
156+
postApplyAssert: func(m *Manager) {
157+
pids, err := getIntelRdtParamString(m.path, "tasks")
158+
if err != nil {
159+
t.Fatalf("failed to read tasks file: %v", err)
160+
}
161+
if pids != strconv.Itoa(pid) {
162+
t.Fatalf("unexpected tasks file, expected '%d', got %q", pid, pids)
163+
}
164+
},
165+
},
166+
{
167+
name: "clos and monitoring group should be created if EnableMonitoring is true",
168+
config: configs.IntelRdt{
169+
EnableMonitoring: true,
170+
},
171+
precreateClos: true,
172+
postApplyAssert: func(m *Manager) {
173+
pids, err := getIntelRdtParamString(m.path, "tasks")
174+
if err != nil {
175+
t.Fatalf("failed to read tasks file: %v", err)
176+
}
177+
if pids != strconv.Itoa(pid) {
178+
t.Fatalf("unexpected tasks file, expected '%d', got %q", pid, pids)
179+
}
180+
},
181+
},
182+
}
130183

131-
const closID = "test-clos"
132-
closPath := filepath.Join(helper.IntelRdtPath, closID)
184+
for _, tt := range tests {
185+
t.Run(tt.name, func(t *testing.T) {
186+
NewIntelRdtTestUtil(t)
187+
id := "abcd-1234"
188+
closPath := filepath.Join(intelRdtRoot, id)
189+
if tt.config.ClosID != "" {
190+
closPath = filepath.Join(intelRdtRoot, tt.config.ClosID)
191+
}
133192

134-
helper.config.IntelRdt.ClosID = closID
135-
intelrdt := newManager(helper.config, "container-1", closPath)
136-
if err := intelrdt.Apply(1234); err == nil {
137-
t.Fatal("unexpected success when applying pid")
138-
}
139-
if _, err := os.Stat(closPath); err == nil {
140-
t.Fatal("closid dir should not exist")
193+
if tt.precreateClos {
194+
if err := os.MkdirAll(filepath.Join(closPath, "mon_groups"), 0o755); err != nil {
195+
t.Fatal(err)
196+
}
197+
}
198+
m := newManager(&configs.Config{IntelRdt: &tt.config}, id, closPath)
199+
err := m.Apply(pid)
200+
if tt.isError && err == nil {
201+
t.Fatal("expected error, got nil")
202+
} else if !tt.isError && err != nil {
203+
t.Fatalf("unexpected error: %v", err)
204+
}
205+
tt.postApplyAssert(m)
206+
})
141207
}
208+
}
142209

143-
// Dir should be created if some schema has been specified
144-
intelrdt.config.IntelRdt.L3CacheSchema = "L3:0=f"
145-
if err := intelrdt.Apply(1235); err != nil {
146-
t.Fatalf("Apply() failed: %v", err)
147-
}
210+
func TestDestroy(t *testing.T) {
211+
tests := []struct {
212+
name string
213+
config configs.IntelRdt
214+
testFunc func(*Manager)
215+
}{
216+
{
217+
name: "per-container CLOS dir should be removed",
218+
testFunc: func(m *Manager) {
219+
closPath := m.path
220+
if _, err := os.Stat(closPath); err != nil {
221+
t.Fatal("CLOS dir should exist")
222+
}
223+
// Need to delete the tasks file so that the dir is empty
224+
if err := os.Remove(filepath.Join(closPath, "tasks")); err != nil {
225+
t.Fatalf("failed to remove tasks file: %v", err)
226+
}
227+
if err := m.Destroy(); err != nil {
228+
t.Fatalf("Destroy() failed: %v", err)
229+
}
230+
if _, err := os.Stat(closPath); err == nil {
231+
t.Fatal("CLOS dir should not exist")
232+
}
233+
},
234+
},
235+
{
236+
name: "pre-existing CLOS should not be removed",
237+
config: configs.IntelRdt{
238+
ClosID: "pre-existing-clos",
239+
},
240+
testFunc: func(m *Manager) {
241+
closPath := m.path
148242

149-
pids, err := getIntelRdtParamString(intelrdt.GetPath(), "tasks")
150-
if err != nil {
151-
t.Fatalf("failed to read tasks file: %v", err)
243+
if _, err := os.Stat(closPath); err != nil {
244+
t.Fatal("CLOS dir should exist")
245+
}
246+
if err := m.Destroy(); err != nil {
247+
t.Fatalf("Destroy() failed: %v", err)
248+
}
249+
if _, err := os.Stat(closPath); err != nil {
250+
t.Fatal("CLOS dir should exist")
251+
}
252+
},
253+
},
254+
{
255+
name: "per-container MON dir in pre-existing CLOS should be removed",
256+
config: configs.IntelRdt{
257+
ClosID: "pre-existing-clos",
258+
EnableMonitoring: true,
259+
},
260+
testFunc: func(m *Manager) {
261+
closPath := m.path
262+
263+
monPath := filepath.Join(closPath, "mon_groups", m.id)
264+
if _, err := os.Stat(monPath); err != nil {
265+
t.Fatal("MON dir should exist")
266+
}
267+
// Need to delete the tasks file so that the dir is empty
268+
os.Remove(filepath.Join(monPath, "tasks"))
269+
if err := m.Destroy(); err != nil {
270+
t.Fatalf("Destroy() failed: %v", err)
271+
}
272+
if _, err := os.Stat(closPath); err != nil {
273+
t.Fatalf("CLOS dir should exist: %f", err)
274+
}
275+
if _, err := os.Stat(monPath); err == nil {
276+
t.Fatal("MON dir should not exist")
277+
}
278+
},
279+
},
152280
}
153-
if pids != "1235" {
154-
t.Fatalf("unexpected tasks file, expected '1235', got %q", pids)
281+
282+
for _, tt := range tests {
283+
t.Run(tt.name, func(t *testing.T) {
284+
NewIntelRdtTestUtil(t)
285+
286+
id := "abcd-1234"
287+
closPath := filepath.Join(intelRdtRoot, id)
288+
if tt.config.ClosID != "" {
289+
closPath = filepath.Join(intelRdtRoot, tt.config.ClosID)
290+
// Pre-create the CLOS directory
291+
if err := os.MkdirAll(filepath.Join(closPath, "mon_groups"), 0o755); err != nil {
292+
t.Fatal(err)
293+
}
294+
}
295+
m := newManager(&configs.Config{IntelRdt: &tt.config}, id, closPath)
296+
if err := m.Apply(1234); err != nil {
297+
t.Fatalf("Apply() failed: %v", err)
298+
}
299+
tt.testFunc(m)
300+
})
155301
}
156302
}

0 commit comments

Comments
 (0)