Skip to content

Commit 4af20b5

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 1b772ea commit 4af20b5

File tree

6 files changed

+212
-28
lines changed

6 files changed

+212
-28
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
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111
"sync"
1212

13+
securejoin "github.com/cyphar/filepath-securejoin"
1314
"github.com/moby/sys/mountinfo"
1415
"golang.org/x/sys/unix"
1516

@@ -480,6 +481,16 @@ func (m *Manager) Apply(pid int) (err error) {
480481
return newLastCmdError(err)
481482
}
482483

484+
// Create MON group
485+
if monPath := m.GetMonPath(); monPath != "" {
486+
if err := os.Mkdir(monPath, 0o755); err != nil && !os.IsExist(err) {
487+
return newLastCmdError(err)
488+
}
489+
if err := WriteIntelRdtTasks(monPath, pid); err != nil {
490+
return newLastCmdError(err)
491+
}
492+
}
493+
483494
m.path = path
484495
return nil
485496
}
@@ -489,13 +500,21 @@ func (m *Manager) Destroy() error {
489500
// Don't remove resctrl group if closid has been explicitly specified. The
490501
// group is likely externally managed, i.e. by some other entity than us.
491502
// There are probably other containers/tasks sharing the same group.
492-
if m.config.IntelRdt != nil && m.config.IntelRdt.ClosID == "" {
493-
m.mu.Lock()
494-
defer m.mu.Unlock()
495-
if err := os.Remove(m.GetPath()); err != nil && !os.IsNotExist(err) {
496-
return err
503+
if m.config.IntelRdt != nil {
504+
if m.config.IntelRdt.ClosID == "" {
505+
m.mu.Lock()
506+
defer m.mu.Unlock()
507+
if err := os.Remove(m.GetPath()); err != nil && !os.IsNotExist(err) {
508+
return err
509+
}
510+
m.path = ""
511+
} else if monPath := m.GetMonPath(); monPath != "" {
512+
// If ClosID is not specified the possible monintoring group was
513+
// removed with the CLOS above.
514+
if err := os.Remove(monPath); err != nil && !os.IsNotExist(err) {
515+
return err
516+
}
497517
}
498-
m.path = ""
499518
}
500519
return nil
501520
}
@@ -509,6 +528,20 @@ func (m *Manager) GetPath() string {
509528
return m.path
510529
}
511530

531+
// GetMonPath returns path of the monitoring group of the container. Returns an
532+
// empty string if the container does not have a individual dedicated
533+
// monitoring group.
534+
func (m *Manager) GetMonPath() string {
535+
if closPath := m.GetPath(); closPath != "" && m.config.IntelRdt.EnableMonitoring {
536+
path, err := securejoin.SecureJoin(filepath.Join(closPath, "mon_groups"), m.id)
537+
if err != nil {
538+
return ""
539+
}
540+
return path
541+
}
542+
return ""
543+
}
544+
512545
// GetStats returns statistics for Intel RDT.
513546
func (m *Manager) GetStats() (*Stats, error) {
514547
// If intelRdt is not specified in config
@@ -586,7 +619,16 @@ func (m *Manager) GetStats() (*Stats, error) {
586619
}
587620

588621
if IsMBMEnabled() || IsCMTEnabled() {
589-
err = getMonitoringStats(containerPath, stats)
622+
monPath := m.GetMonPath()
623+
if monPath == "" {
624+
// NOTE: If per-container monitoring is not enabled, the monitoring
625+
// data we get here might have little to do with this container as
626+
// there might be anything from this single container to the half
627+
// of the system assigned in the group. Should consider not
628+
// exposing stats in this case(?)
629+
monPath = containerPath
630+
}
631+
err = getMonitoringStats(monPath, stats)
590632
if err != nil {
591633
return nil, err
592634
}

libcontainer/intelrdt/intelrdt_test.go

Lines changed: 146 additions & 17 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,30 +100,157 @@ func TestIntelRdtSetMemBwScSchema(t *testing.T) {
98100
}
99101

100102
func TestApply(t *testing.T) {
101-
helper := NewIntelRdtTestUtil(t)
102-
103103
const closID = "test-clos"
104+
// TC-1: failure because non-pre-existing CLOS
105+
{
106+
helper := NewIntelRdtTestUtil(t)
107+
helper.config.IntelRdt = &configs.IntelRdt{
108+
ClosID: closID,
109+
}
104110

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

114-
// Dir should be created if some schema has been specified
115-
intelrdt.config.IntelRdt.L3CacheSchema = "L3:0=f"
116-
if err := intelrdt.Apply(1235); err != nil {
117-
t.Fatalf("Apply() failed: %v", err)
179+
intelrdt := newManager(helper.config, id, "")
180+
if err := intelrdt.Apply(1234); err != nil {
181+
t.Fatalf("Apply() failed: %v", err)
182+
}
183+
closPath := filepath.Join(intelRdtRoot, id)
184+
if _, err := os.Stat(closPath); err != nil {
185+
t.Fatal("CLOS dir should exist")
186+
}
187+
// Need to delete the tasks file so that the dir is empty
188+
os.Remove(filepath.Join(closPath, "tasks"))
189+
if err := intelrdt.Destroy(); err != nil {
190+
t.Fatalf("Destroy() failed: %v", err)
191+
}
192+
if _, err := os.Stat(closPath); err == nil {
193+
t.Fatal("CLOS dir should not exist")
194+
}
118195
}
196+
// TC-2: pre-existing CLOS should not be removed
197+
{
198+
helper := NewIntelRdtTestUtil(t)
199+
helper.config.IntelRdt = &configs.IntelRdt{
200+
ClosID: closID,
201+
}
119202

120-
pids, err := getIntelRdtParamString(intelrdt.GetPath(), "tasks")
121-
if err != nil {
122-
t.Fatalf("failed to read tasks file: %v", err)
203+
closPath := filepath.Join(intelRdtRoot, closID)
204+
if err := os.MkdirAll(closPath, 0o755); err != nil {
205+
t.Fatal(err)
206+
}
207+
208+
intelrdt := newManager(helper.config, "", "")
209+
if err := intelrdt.Apply(1234); err != nil {
210+
t.Fatalf("Apply() failed: %v", err)
211+
}
212+
if _, err := os.Stat(closPath); err != nil {
213+
t.Fatal("CLOS dir should exist")
214+
}
215+
if err := intelrdt.Destroy(); err != nil {
216+
t.Fatalf("Destroy() failed: %v", err)
217+
}
218+
if _, err := os.Stat(closPath); err != nil {
219+
t.Fatal("CLOS dir should exist")
220+
}
123221
}
124-
if pids != "1235" {
125-
t.Fatalf("unexpected tasks file, expected '1235', got %q", pids)
222+
// TC-3: per-container MON dir in pre-existing CLOS should be removed
223+
{
224+
helper := NewIntelRdtTestUtil(t)
225+
helper.config.IntelRdt = &configs.IntelRdt{
226+
ClosID: closID,
227+
EnableMonitoring: true,
228+
}
229+
id := "abcd-efgh"
230+
231+
closPath := filepath.Join(intelRdtRoot, closID)
232+
if err := os.MkdirAll(filepath.Join(closPath, "mon_groups"), 0o755); err != nil {
233+
t.Fatal(err)
234+
}
235+
236+
intelrdt := newManager(helper.config, id, "")
237+
if err := intelrdt.Apply(1234); err != nil {
238+
t.Fatalf("Apply() failed: %v", err)
239+
}
240+
monPath := filepath.Join(closPath, "mon_groups", id)
241+
if _, err := os.Stat(monPath); err != nil {
242+
t.Fatal("MON dir should exist")
243+
}
244+
// Need to delete the tasks file so that the dir is empty
245+
os.Remove(filepath.Join(monPath, "tasks"))
246+
if err := intelrdt.Destroy(); err != nil {
247+
t.Fatalf("Destroy() failed: %v", err)
248+
}
249+
if _, err := os.Stat(closPath); err != nil {
250+
t.Fatalf("CLOS dir should exist: %f", err)
251+
}
252+
if _, err := os.Stat(monPath); err == nil {
253+
t.Fatal("MON dir should not exist")
254+
}
126255
}
127256
}

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)