Skip to content

Commit b5cb564

Browse files
authored
Merge pull request #4830 from marquiz/devel/rdt-schemata-field
libcontainer/intelrdt: add support for Schemata field
2 parents f3ea522 + 7be025f commit b5cb564

File tree

14 files changed

+208
-139
lines changed

14 files changed

+208
-139
lines changed

events.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ func convertLibcontainerStats(ls *libcontainer.Stats) *types.Stats {
173173
if intelrdt.IsCMTEnabled() {
174174
s.IntelRdt.CMTStats = is.CMTStats
175175
}
176+
177+
s.IntelRdt.Schemata = is.Schemata
176178
}
177179

178180
s.NetworkInterfaces = ls.Interfaces

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+
Schemata: &t,
6061
},
6162
MountExtensions: &features.MountExtensions{
6263
IDMap: &features.IDMap{

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ require (
1515
github.com/moby/sys/userns v0.1.0
1616
github.com/mrunalp/fileutils v0.5.1
1717
github.com/opencontainers/cgroups v0.0.4
18-
github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67
18+
github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0
1919
github.com/opencontainers/selinux v1.12.0
2020
github.com/seccomp/libseccomp-golang v0.11.1
2121
github.com/sirupsen/logrus v1.9.3

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm
4646
github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
4747
github.com/opencontainers/cgroups v0.0.4 h1:XVj8P/IHVms/j+7eh8ggdkTLAxjz84ZzuFyGoE28DR4=
4848
github.com/opencontainers/cgroups v0.0.4/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs=
49-
github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67 h1:Q+KewUGTMamIe6Q39xCD/T1NC1POmaTlWnhjikCrZHA=
50-
github.com/opencontainers/runtime-spec v1.2.2-0.20250401095657-e935f995dd67/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
49+
github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0 h1:RLn0YfUWkiqPGtgUANvJrcjIkCHGRl3jcz/c557M28M=
50+
github.com/opencontainers/runtime-spec v1.2.2-0.20250818071321-383cadbf08c0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
5151
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
5252
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
5353
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

libcontainer/configs/intelrdt.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ type IntelRdt struct {
44
// The identity for RDT Class of Service
55
ClosID string `json:"closID,omitempty"`
66

7+
// Schemata is a generic field to specify schemata file in the resctrl
8+
// filesystem. Each element represents one line written to the schemata file.
9+
Schemata []string `json:"schemata,omitempty"`
10+
711
// The schema for L3 cache id and capacity bitmask (CBM)
812
// Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..."
913
L3CacheSchema string `json:"l3_cache_schema,omitempty"`

libcontainer/intelrdt/intelrdt.go

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -326,16 +326,6 @@ func getIntelRdtParamString(path, file string) (string, error) {
326326
return string(bytes.TrimSpace(contents)), nil
327327
}
328328

329-
func writeFile(dir, file, data string) error {
330-
if dir == "" {
331-
return fmt.Errorf("no such directory for %s", file)
332-
}
333-
if err := os.WriteFile(filepath.Join(dir, file), []byte(data+"\n"), 0o600); err != nil {
334-
return newLastCmdError(fmt.Errorf("intelrdt: unable to write %v: %w", data, err))
335-
}
336-
return nil
337-
}
338-
339329
// Get the read-only L3 cache information
340330
func getL3CacheInfo() (*L3CacheInfo, error) {
341331
l3CacheInfo := &L3CacheInfo{}
@@ -462,11 +452,11 @@ func (m *Manager) Apply(pid int) (err error) {
462452
m.mu.Lock()
463453
defer m.mu.Unlock()
464454

465-
if m.config.IntelRdt.ClosID != "" && m.config.IntelRdt.L3CacheSchema == "" && m.config.IntelRdt.MemBwSchema == "" {
455+
if m.config.IntelRdt.ClosID != "" && m.config.IntelRdt.L3CacheSchema == "" && m.config.IntelRdt.MemBwSchema == "" && len(m.config.IntelRdt.Schemata) == 0 {
466456
// Check that the CLOS exists, i.e. it has been pre-configured to
467457
// conform with the runtime spec
468458
if _, err := os.Stat(path); err != nil {
469-
return fmt.Errorf("clos dir not accessible (must be pre-created when l3CacheSchema and memBwSchema are empty): %w", err)
459+
return fmt.Errorf("clos dir not accessible (must be pre-created when schemata, l3CacheSchema and memBwSchema are empty): %w", err)
470460
}
471461
}
472462

@@ -534,6 +524,8 @@ func (m *Manager) GetStats() (*Stats, error) {
534524
}
535525
schemaStrings := strings.Split(tmpStrings, "\n")
536526

527+
stats.Schemata = schemaStrings
528+
537529
if IsCATEnabled() {
538530
// The read-only L3 cache information
539531
l3CacheInfo, err := getL3CacheInfo()
@@ -637,35 +629,24 @@ func (m *Manager) Set(container *configs.Config) error {
637629
// For example, on a two-socket machine, the schema line could be
638630
// "MB:0=5000;1=7000" which means 5000 MBps memory bandwidth limit on
639631
// socket 0 and 7000 MBps memory bandwidth limit on socket 1.
640-
if container.IntelRdt != nil {
641-
path := m.GetPath()
642-
l3CacheSchema := container.IntelRdt.L3CacheSchema
643-
memBwSchema := container.IntelRdt.MemBwSchema
644-
632+
if r := container.IntelRdt; r != nil {
645633
// TODO: verify that l3CacheSchema and/or memBwSchema match the
646634
// existing schemata if ClosID has been specified. This is a more
647635
// involved than reading the file and doing plain string comparison as
648636
// the value written in does not necessarily match what gets read out
649637
// (leading zeros, cache id ordering etc).
650-
651-
// Write a single joint schema string to schemata file
652-
if l3CacheSchema != "" && memBwSchema != "" {
653-
if err := writeFile(path, "schemata", l3CacheSchema+"\n"+memBwSchema); err != nil {
654-
return err
655-
}
656-
}
657-
658-
// Write only L3 cache schema string to schemata file
659-
if l3CacheSchema != "" && memBwSchema == "" {
660-
if err := writeFile(path, "schemata", l3CacheSchema); err != nil {
661-
return err
638+
var schemata strings.Builder
639+
for _, s := range append([]string{r.L3CacheSchema, r.MemBwSchema}, r.Schemata...) {
640+
if s != "" {
641+
schemata.WriteString(s)
642+
schemata.WriteString("\n")
662643
}
663644
}
664645

665-
// Write only memory bandwidth schema string to schemata file
666-
if l3CacheSchema == "" && memBwSchema != "" {
667-
if err := writeFile(path, "schemata", memBwSchema); err != nil {
668-
return err
646+
if schemata.Len() > 0 {
647+
path := filepath.Join(m.GetPath(), "schemata")
648+
if err := os.WriteFile(path, []byte(schemata.String()), 0o600); err != nil {
649+
return newLastCmdError(fmt.Errorf("intelrdt: unable to write %q: %w", schemata.String(), err))
669650
}
670651
}
671652
}

libcontainer/intelrdt/intelrdt_test.go

Lines changed: 113 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,97 +3,125 @@ package intelrdt
33
import (
44
"os"
55
"path/filepath"
6+
"slices"
67
"strings"
78
"testing"
8-
)
9-
10-
func TestIntelRdtSetL3CacheSchema(t *testing.T) {
11-
helper := NewIntelRdtTestUtil(t)
12-
13-
const (
14-
l3CacheSchemaBefore = "L3:0=f;1=f0"
15-
l3CacheSchemeAfter = "L3:0=f0;1=f"
16-
)
17-
18-
helper.writeFileContents(map[string]string{
19-
"schemata": l3CacheSchemaBefore + "\n",
20-
})
21-
22-
helper.config.IntelRdt.L3CacheSchema = l3CacheSchemeAfter
23-
intelrdt := newManager(helper.config, "", helper.IntelRdtPath)
24-
if err := intelrdt.Set(helper.config); err != nil {
25-
t.Fatal(err)
26-
}
27-
28-
tmpStrings, err := getIntelRdtParamString(helper.IntelRdtPath, "schemata")
29-
if err != nil {
30-
t.Fatalf("Failed to parse file 'schemata' - %s", err)
31-
}
32-
values := strings.Split(tmpStrings, "\n")
33-
value := values[0]
34-
35-
if value != l3CacheSchemeAfter {
36-
t.Fatal("Got the wrong value, set 'schemata' failed.")
37-
}
38-
}
39-
40-
func TestIntelRdtSetMemBwSchema(t *testing.T) {
41-
helper := NewIntelRdtTestUtil(t)
42-
43-
const (
44-
memBwSchemaBefore = "MB:0=20;1=70"
45-
memBwSchemeAfter = "MB:0=70;1=20"
46-
)
479

48-
helper.writeFileContents(map[string]string{
49-
"schemata": memBwSchemaBefore + "\n",
50-
})
51-
52-
helper.config.IntelRdt.MemBwSchema = memBwSchemeAfter
53-
intelrdt := newManager(helper.config, "", helper.IntelRdtPath)
54-
if err := intelrdt.Set(helper.config); err != nil {
55-
t.Fatal(err)
56-
}
57-
58-
tmpStrings, err := getIntelRdtParamString(helper.IntelRdtPath, "schemata")
59-
if err != nil {
60-
t.Fatalf("Failed to parse file 'schemata' - %s", err)
61-
}
62-
values := strings.Split(tmpStrings, "\n")
63-
value := values[0]
64-
65-
if value != memBwSchemeAfter {
66-
t.Fatal("Got the wrong value, set 'schemata' failed.")
67-
}
68-
}
69-
70-
func TestIntelRdtSetMemBwScSchema(t *testing.T) {
71-
helper := NewIntelRdtTestUtil(t)
72-
73-
const (
74-
memBwScSchemaBefore = "MB:0=5000;1=7000"
75-
memBwScSchemeAfter = "MB:0=9000;1=4000"
76-
)
77-
78-
helper.writeFileContents(map[string]string{
79-
"schemata": memBwScSchemaBefore + "\n",
80-
})
81-
82-
helper.config.IntelRdt.MemBwSchema = memBwScSchemeAfter
83-
intelrdt := newManager(helper.config, "", helper.IntelRdtPath)
84-
if err := intelrdt.Set(helper.config); err != nil {
85-
t.Fatal(err)
86-
}
10+
"github.com/opencontainers/runc/libcontainer/configs"
11+
)
8712

88-
tmpStrings, err := getIntelRdtParamString(helper.IntelRdtPath, "schemata")
89-
if err != nil {
90-
t.Fatalf("Failed to parse file 'schemata' - %s", err)
13+
func TestIntelRdtSet(t *testing.T) {
14+
tcs := []struct {
15+
name string
16+
config *configs.IntelRdt
17+
schemataAfter []string
18+
}{
19+
{
20+
name: "L3",
21+
config: &configs.IntelRdt{
22+
L3CacheSchema: "L3:0=f0;1=f",
23+
},
24+
schemataAfter: []string{"L3:0=f0;1=f"},
25+
},
26+
{
27+
name: "MemBw",
28+
config: &configs.IntelRdt{
29+
MemBwSchema: "MB:0=70;1=20",
30+
},
31+
schemataAfter: []string{"MB:0=70;1=20"},
32+
},
33+
{
34+
name: "MemBwSc",
35+
config: &configs.IntelRdt{
36+
MemBwSchema: "MB:0=9000;1=4000",
37+
},
38+
schemataAfter: []string{"MB:0=9000;1=4000"},
39+
},
40+
{
41+
name: "L3 and MemBw",
42+
config: &configs.IntelRdt{
43+
L3CacheSchema: "L3:0=f0;1=f",
44+
MemBwSchema: "MB:0=9000;1=4000",
45+
},
46+
schemataAfter: []string{
47+
"L3:0=f0;1=f",
48+
"MB:0=9000;1=4000",
49+
},
50+
},
51+
{
52+
name: "Schemata",
53+
config: &configs.IntelRdt{
54+
Schemata: []string{
55+
"L3CODE:0=ff;1=ff",
56+
"L3DATA:0=f;1=f0",
57+
},
58+
},
59+
schemataAfter: []string{
60+
"L3CODE:0=ff;1=ff",
61+
"L3DATA:0=f;1=f0",
62+
},
63+
},
64+
{
65+
name: "Schemata and L3",
66+
config: &configs.IntelRdt{
67+
L3CacheSchema: "L3:0=f0;1=f",
68+
Schemata: []string{"L2:0=ff00;1=ff"},
69+
},
70+
schemataAfter: []string{
71+
"L3:0=f0;1=f",
72+
"L2:0=ff00;1=ff",
73+
},
74+
},
75+
{
76+
name: "Schemata and MemBw",
77+
config: &configs.IntelRdt{
78+
MemBwSchema: "MB:0=2000;1=4000",
79+
Schemata: []string{"L3:0=ff;1=ff"},
80+
},
81+
schemataAfter: []string{
82+
"MB:0=2000;1=4000",
83+
"L3:0=ff;1=ff",
84+
},
85+
},
86+
{
87+
name: "Schemata, L3 and MemBw",
88+
config: &configs.IntelRdt{
89+
L3CacheSchema: "L3:0=80;1=7f",
90+
MemBwSchema: "MB:0=2000;1=4000",
91+
Schemata: []string{
92+
"L2:0=ff00;1=ff",
93+
"L3:0=c0;1=3f",
94+
},
95+
},
96+
schemataAfter: []string{
97+
"L3:0=80;1=7f",
98+
"MB:0=2000;1=4000",
99+
"L2:0=ff00;1=ff",
100+
"L3:0=c0;1=3f",
101+
},
102+
},
91103
}
92-
values := strings.Split(tmpStrings, "\n")
93-
value := values[0]
94104

95-
if value != memBwScSchemeAfter {
96-
t.Fatal("Got the wrong value, set 'schemata' failed.")
105+
for _, tc := range tcs {
106+
t.Run(tc.name, func(t *testing.T) {
107+
helper := NewIntelRdtTestUtil(t)
108+
helper.config.IntelRdt = tc.config
109+
110+
intelrdt := newManager(helper.config, "", helper.IntelRdtPath)
111+
if err := intelrdt.Set(helper.config); err != nil {
112+
t.Fatal(err)
113+
}
114+
115+
tmpStrings, err := getIntelRdtParamString(helper.IntelRdtPath, "schemata")
116+
if err != nil {
117+
t.Fatalf("Failed to parse file 'schemata' - %s", err)
118+
}
119+
values := strings.Split(tmpStrings, "\n")
120+
121+
if slices.Compare(values, tc.schemataAfter) != 0 {
122+
t.Fatalf("Got the wrong value, expected %v, got %v", tc.schemataAfter, values)
123+
}
124+
})
97125
}
98126
}
99127

libcontainer/intelrdt/stats.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ type Stats struct {
4545
// The memory bandwidth schema in 'container_id' group
4646
MemBwSchema string `json:"mem_bw_schema,omitempty"`
4747

48+
// Schemata contains the full schemata of the ClosID (resctrl group) that the container is assigned to.
49+
Schemata []string `json:"schemata,omitempty"`
50+
4851
// The memory bandwidth monitoring statistics from NUMA nodes in 'container_id' group
4952
MBMStats *[]MBMNumaNodeStats `json:"mbm_stats,omitempty"`
5053

libcontainer/intelrdt/util_test.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,3 @@ func NewIntelRdtTestUtil(t *testing.T) *intelRdtTestUtil {
4040
}
4141
return &intelRdtTestUtil{config: config, IntelRdtPath: testIntelRdtPath, t: t}
4242
}
43-
44-
// Write the specified contents on the mock of the specified Intel RDT "resource control" files
45-
func (c *intelRdtTestUtil) writeFileContents(fileContents map[string]string) {
46-
for file, contents := range fileContents {
47-
err := writeFile(c.IntelRdtPath, file, contents)
48-
if err != nil {
49-
c.t.Fatal(err)
50-
}
51-
}
52-
}

libcontainer/specconv/spec_linux.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
463463
if spec.Linux.IntelRdt != nil {
464464
config.IntelRdt = &configs.IntelRdt{
465465
ClosID: spec.Linux.IntelRdt.ClosID,
466+
Schemata: spec.Linux.IntelRdt.Schemata,
466467
L3CacheSchema: spec.Linux.IntelRdt.L3CacheSchema,
467468
MemBwSchema: spec.Linux.IntelRdt.MemBwSchema,
468469
}

0 commit comments

Comments
 (0)