From 41553216ee2ea6c0b37cf756c19254ad7b2a3833 Mon Sep 17 00:00:00 2001 From: Markus Lehtonen Date: Wed, 30 Jul 2025 16:02:23 +0300 Subject: [PATCH] libcontainer/intelrdt: add support for Schemata field Implement support for the linux.intelRdt.schemata field of the spec. This allows management of the "schemata" file in the resctrl group in a generic way. Signed-off-by: Markus Lehtonen --- features.go | 3 +- libcontainer/configs/intelrdt.go | 4 ++ libcontainer/intelrdt/intelrdt.go | 45 +++++------------ libcontainer/intelrdt/intelrdt_test.go | 68 ++++++++++++++++++++++++-- libcontainer/intelrdt/util_test.go | 10 ---- libcontainer/specconv/spec_linux.go | 1 + 6 files changed, 82 insertions(+), 49 deletions(-) diff --git a/features.go b/features.go index c5dff4a2d..72aef5c3c 100644 --- a/features.go +++ b/features.go @@ -56,7 +56,8 @@ var featuresCommand = cli.Command{ Enabled: &t, }, IntelRdt: &features.IntelRdt{ - Enabled: &t, + Enabled: &t, + Schemata: &t, }, MountExtensions: &features.MountExtensions{ IDMap: &features.IDMap{ diff --git a/libcontainer/configs/intelrdt.go b/libcontainer/configs/intelrdt.go index f8d951ab8..ddf1bb3a8 100644 --- a/libcontainer/configs/intelrdt.go +++ b/libcontainer/configs/intelrdt.go @@ -4,6 +4,10 @@ type IntelRdt struct { // The identity for RDT Class of Service ClosID string `json:"closID,omitempty"` + // Schemata is a generic field to specify schemata file in the resctrl + // filesystem. Each element represents one line written to the schemata file. + Schemata []string `json:"schemata,omitempty"` + // The schema for L3 cache id and capacity bitmask (CBM) // Format: "L3:=;=;..." L3CacheSchema string `json:"l3_cache_schema,omitempty"` diff --git a/libcontainer/intelrdt/intelrdt.go b/libcontainer/intelrdt/intelrdt.go index ac05f2252..f5fde0f83 100644 --- a/libcontainer/intelrdt/intelrdt.go +++ b/libcontainer/intelrdt/intelrdt.go @@ -326,16 +326,6 @@ func getIntelRdtParamString(path, file string) (string, error) { return string(bytes.TrimSpace(contents)), nil } -func writeFile(dir, file, data string) error { - if dir == "" { - return fmt.Errorf("no such directory for %s", file) - } - if err := os.WriteFile(filepath.Join(dir, file), []byte(data+"\n"), 0o600); err != nil { - return newLastCmdError(fmt.Errorf("intelrdt: unable to write %v: %w", data, err)) - } - return nil -} - // Get the read-only L3 cache information func getL3CacheInfo() (*L3CacheInfo, error) { l3CacheInfo := &L3CacheInfo{} @@ -462,11 +452,11 @@ func (m *Manager) Apply(pid int) (err error) { m.mu.Lock() defer m.mu.Unlock() - if m.config.IntelRdt.ClosID != "" && m.config.IntelRdt.L3CacheSchema == "" && m.config.IntelRdt.MemBwSchema == "" { + if m.config.IntelRdt.ClosID != "" && m.config.IntelRdt.L3CacheSchema == "" && m.config.IntelRdt.MemBwSchema == "" && len(m.config.IntelRdt.Schemata) == 0 { // Check that the CLOS exists, i.e. it has been pre-configured to // conform with the runtime spec if _, err := os.Stat(path); err != nil { - return fmt.Errorf("clos dir not accessible (must be pre-created when l3CacheSchema and memBwSchema are empty): %w", err) + return fmt.Errorf("clos dir not accessible (must be pre-created when schemata, l3CacheSchema and memBwSchema are empty): %w", err) } } @@ -637,35 +627,24 @@ func (m *Manager) Set(container *configs.Config) error { // For example, on a two-socket machine, the schema line could be // "MB:0=5000;1=7000" which means 5000 MBps memory bandwidth limit on // socket 0 and 7000 MBps memory bandwidth limit on socket 1. - if container.IntelRdt != nil { - path := m.GetPath() - l3CacheSchema := container.IntelRdt.L3CacheSchema - memBwSchema := container.IntelRdt.MemBwSchema - + if r := container.IntelRdt; r != nil { // TODO: verify that l3CacheSchema and/or memBwSchema match the // existing schemata if ClosID has been specified. This is a more // involved than reading the file and doing plain string comparison as // the value written in does not necessarily match what gets read out // (leading zeros, cache id ordering etc). - - // Write a single joint schema string to schemata file - if l3CacheSchema != "" && memBwSchema != "" { - if err := writeFile(path, "schemata", l3CacheSchema+"\n"+memBwSchema); err != nil { - return err + var schemata strings.Builder + for _, s := range append([]string{r.L3CacheSchema, r.MemBwSchema}, r.Schemata...) { + if s != "" { + schemata.WriteString(s) + schemata.WriteString("\n") } } - // Write only L3 cache schema string to schemata file - if l3CacheSchema != "" && memBwSchema == "" { - if err := writeFile(path, "schemata", l3CacheSchema); err != nil { - return err - } - } - - // Write only memory bandwidth schema string to schemata file - if l3CacheSchema == "" && memBwSchema != "" { - if err := writeFile(path, "schemata", memBwSchema); err != nil { - return err + if schemata.Len() > 0 { + path := filepath.Join(m.GetPath(), "schemata") + if err := os.WriteFile(path, []byte(schemata.String()), 0o600); err != nil { + return newLastCmdError(fmt.Errorf("intelrdt: unable to write %q: %w", schemata.String(), err)) } } } diff --git a/libcontainer/intelrdt/intelrdt_test.go b/libcontainer/intelrdt/intelrdt_test.go index 06c734c54..209424d55 100644 --- a/libcontainer/intelrdt/intelrdt_test.go +++ b/libcontainer/intelrdt/intelrdt_test.go @@ -37,6 +37,69 @@ func TestIntelRdtSet(t *testing.T) { }, schemataAfter: []string{"MB:0=9000;1=4000"}, }, + { + name: "L3 and MemBw", + config: &configs.IntelRdt{ + L3CacheSchema: "L3:0=f0;1=f", + MemBwSchema: "MB:0=9000;1=4000", + }, + schemataAfter: []string{ + "L3:0=f0;1=f", + "MB:0=9000;1=4000", + }, + }, + { + name: "Schemata", + config: &configs.IntelRdt{ + Schemata: []string{ + "L3CODE:0=ff;1=ff", + "L3DATA:0=f;1=f0", + }, + }, + schemataAfter: []string{ + "L3CODE:0=ff;1=ff", + "L3DATA:0=f;1=f0", + }, + }, + { + name: "Schemata and L3", + config: &configs.IntelRdt{ + L3CacheSchema: "L3:0=f0;1=f", + Schemata: []string{"L2:0=ff00;1=ff"}, + }, + schemataAfter: []string{ + "L3:0=f0;1=f", + "L2:0=ff00;1=ff", + }, + }, + { + name: "Schemata and MemBw", + config: &configs.IntelRdt{ + MemBwSchema: "MB:0=2000;1=4000", + Schemata: []string{"L3:0=ff;1=ff"}, + }, + schemataAfter: []string{ + "MB:0=2000;1=4000", + "L3:0=ff;1=ff", + }, + }, + { + name: "Schemata, L3 and MemBw", + config: &configs.IntelRdt{ + L3CacheSchema: "L3:0=80;1=7f", + MemBwSchema: "MB:0=2000;1=4000", + Schemata: []string{ + "L2:0=ff00;1=ff", + "L3:0=c0;1=3f", + }, + }, + schemataAfter: []string{ + "L3:0=80;1=7f", + "MB:0=2000;1=4000", + "L2:0=ff00;1=ff", + "L3:0=c0;1=3f", + }, + }, } for _, tc := range tcs { @@ -44,11 +107,6 @@ func TestIntelRdtSet(t *testing.T) { helper := NewIntelRdtTestUtil(t) helper.config.IntelRdt = tc.config - helper.writeFileContents(map[string]string{ - /* Common initial value for all test cases */ - "schemata": "MB:0=100\nL3:0=ffff\nL2:0=ffffffff\n", - }) - intelrdt := newManager(helper.config, "", helper.IntelRdtPath) if err := intelrdt.Set(helper.config); err != nil { t.Fatal(err) diff --git a/libcontainer/intelrdt/util_test.go b/libcontainer/intelrdt/util_test.go index b29d685e9..1771a231b 100644 --- a/libcontainer/intelrdt/util_test.go +++ b/libcontainer/intelrdt/util_test.go @@ -40,13 +40,3 @@ func NewIntelRdtTestUtil(t *testing.T) *intelRdtTestUtil { } return &intelRdtTestUtil{config: config, IntelRdtPath: testIntelRdtPath, t: t} } - -// Write the specified contents on the mock of the specified Intel RDT "resource control" files -func (c *intelRdtTestUtil) writeFileContents(fileContents map[string]string) { - for file, contents := range fileContents { - err := writeFile(c.IntelRdtPath, file, contents) - if err != nil { - c.t.Fatal(err) - } - } -} diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go index ec417ed37..b19ba358b 100644 --- a/libcontainer/specconv/spec_linux.go +++ b/libcontainer/specconv/spec_linux.go @@ -463,6 +463,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) { if spec.Linux.IntelRdt != nil { config.IntelRdt = &configs.IntelRdt{ ClosID: spec.Linux.IntelRdt.ClosID, + Schemata: spec.Linux.IntelRdt.Schemata, L3CacheSchema: spec.Linux.IntelRdt.L3CacheSchema, MemBwSchema: spec.Linux.IntelRdt.MemBwSchema, }