From b101884e2ef31aca46b5f36325b7d3c11009e976 Mon Sep 17 00:00:00 2001 From: Markus Lehtonen Date: Tue, 16 Feb 2021 13:14:57 +0200 Subject: [PATCH] rdt: implement GetCPUs() and SetCPUs() Functionality for associating CPUs with classes. Under the hood goresctrl will write the given CPU ids to the "cpus_list" file of the corresponding resctrl group. The hierarchy between task and cpu lists is as follows (imposed by the resctrl filesystem): 1. If a process PID is in the tasks file of a non-root class then that class is in effect 2. Else, if a process is running on a CPU that is associated with a class, then this class is in effect 3. Else the root class is in effect --- pkg/rdt/rdt.go | 27 +++++++++++++++++++++++++++ pkg/rdt/rdt_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/pkg/rdt/rdt.go b/pkg/rdt/rdt.go index d67f8d8..d881fe0 100644 --- a/pkg/rdt/rdt.go +++ b/pkg/rdt/rdt.go @@ -87,6 +87,12 @@ type ResctrlGroup interface { // AddPids assigns the given process ids to the group AddPids(pids ...string) error + // GetCPUs returns the cpu ids associated with the group + GetCPUs() (string, error) + + // SetCPUs associates the given cpu ids with the group + SetCPUs(cpus ...string) error + // GetMonData retrieves the monitoring data of the group GetMonData() MonData } @@ -644,6 +650,27 @@ func (r *resctrlGroup) AddPids(pids ...string) error { return nil } +func (r *resctrlGroup) GetCPUs() (string, error) { + data, err := rdt.readRdtFile(r.relPath("cpus_list")) + if err != nil { + return "", err + } + return strings.TrimSpace(string(data)), nil +} + +func (r *resctrlGroup) SetCPUs(cpus ...string) error { + f, err := os.OpenFile(r.path("cpus_list"), os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + + if _, err := f.WriteString(strings.Join(cpus, ",")); err != nil { + return rdtError("failed to associate cpus %v to class %q: %v", cpus, r.name, rdt.cmdError(err)) + } + return nil +} + func (r *resctrlGroup) GetMonData() MonData { m := MonData{} diff --git a/pkg/rdt/rdt_test.go b/pkg/rdt/rdt_test.go index d10df39..71af784 100644 --- a/pkg/rdt/rdt_test.go +++ b/pkg/rdt/rdt_test.go @@ -222,6 +222,9 @@ partitions: if err := cls.AddPids("99"); err != nil { t.Fatalf("AddPids() failed: %v", err) } + if err := cls.SetCPUs("1"); err != nil { + t.Fatalf("SetCPUs() failed: %v", err) + } // Configuration should fail as "Stale" class has pids assigned to it if err := SetConfig(conf, false); err == nil { @@ -286,6 +289,23 @@ partitions: mockFs.verifyTextFile(rdt.classes["Guaranteed"].relPath("tasks"), "10\n11\n12\n") + // Verify assigning CPUs to classes (ctrl groups) + if p, err := cls.GetCPUs(); err != nil { + t.Errorf("GetCPUs() failed: %v", err) + } else if !cmp.Equal(p, "") { + t.Errorf("GetCPUs() returned %s, expected an empty string", p) + } + cpus := []string{"4", "10-13"} + if err := cls.SetCPUs(cpus...); err != nil { + t.Errorf("SetCPUs() failed: %v", err) + } + if p, err := cls.GetCPUs(); err != nil { + t.Errorf("GetCPUs() failed: %v", err) + } else if !cmp.Equal(p, strings.Join(cpus, ",")) { + t.Errorf("GetCPUs() returned %s, expected %s", p, cpus) + } + mockFs.verifyTextFile(rdt.classes["Guaranteed"].relPath("cpus_list"), "4,10-13") + // Verify MonSupported and GetMonFeatures if !MonSupported() { t.Errorf("MonSupported() returned false, expected true") @@ -376,6 +396,17 @@ partitions: } mockFs.verifyTextFile(rdt.classes["Guaranteed"].monGroups[mgName].relPath("tasks"), "10\n") + // Verify assigning cpus to monitor group + if err := mg.SetCPUs("0-7"); err != nil { + t.Errorf("SetCPUs() failed: %v", err) + } + if p, err := mg.GetCPUs(); err != nil { + t.Errorf("GetCPUs() failed: %v", err) + } else if !cmp.Equal(p, "0-7") { + t.Errorf("GetCPUs() returned %s, expected 0-7", p) + } + mockFs.verifyTextFile(rdt.classes["Guaranteed"].monGroups[mgName].relPath("cpus_list"), "0-7") + // Verify monitoring functionality expected := MonData{ L3: MonL3Data{