cpuset: support setting mems for sandbox

CPUSet cgroup allows for pinning the memory associated with a cpuset to
a given numa node. Similar to cpuset.cpus, we should take cpuset.mems
into account for the sandbox-cgroup that Kata creates.

Signed-off-by: Eric Ernst <eric.g.ernst@gmail.com>
This commit is contained in:
Eric Ernst 2020-10-12 21:15:21 -07:00 committed by Eric Ernst
parent 2d690536b8
commit 77a463e57a
3 changed files with 71 additions and 47 deletions

View File

@ -332,7 +332,7 @@ func (m *Manager) RemoveDevice(device string) error {
return fmt.Errorf("device %v not found in the cgroup", device) return fmt.Errorf("device %v not found in the cgroup", device)
} }
func (m *Manager) SetCPUSet(cpuset string) error { func (m *Manager) SetCPUSet(cpuset, memset string) error {
cgroups, err := m.GetCgroups() cgroups, err := m.GetCgroups()
if err != nil { if err != nil {
return err return err
@ -340,6 +340,7 @@ func (m *Manager) SetCPUSet(cpuset string) error {
m.Lock() m.Lock()
cgroups.CpusetCpus = cpuset cgroups.CpusetCpus = cpuset
cgroups.CpusetMems = memset
m.Unlock() m.Unlock()
return m.Apply() return m.Apply()

View File

@ -1995,16 +1995,14 @@ func (s *Sandbox) cgroupsUpdate() error {
// in the Kata sandbox cgroup (inherited). Check to see if sandbox cpuset needs to be // in the Kata sandbox cgroup (inherited). Check to see if sandbox cpuset needs to be
// updated. // updated.
if s.config.SandboxCgroupOnly { if s.config.SandboxCgroupOnly {
cpuset, err := s.getSandboxCPUSet() cpuset, memset, err := s.getSandboxCPUSet()
if err != nil { if err != nil {
return err return err
} }
if cpuset != "" { if err := s.cgroupMgr.SetCPUSet(cpuset, memset); err != nil {
if err := s.cgroupMgr.SetCPUSet(cpuset); err != nil {
return err return err
} }
}
return nil return nil
} }
@ -2304,23 +2302,30 @@ func (s *Sandbox) GetAgentURL() (string, error) {
return s.agent.getAgentURL() return s.agent.getAgentURL()
} }
// getSandboxCPUSet returns the union of each of the sandbox's containers' CPU sets // getSandboxCPUSet returns the union of each of the sandbox's containers' CPU sets'
// as a string in canonical linux CPU list format // cpus and mems as a string in canonical linux CPU/mems list format
func (s *Sandbox) getSandboxCPUSet() (string, error) { func (s *Sandbox) getSandboxCPUSet() (string, string, error) {
if s.config == nil { if s.config == nil {
return "", nil return "", "", nil
} }
result := cpuset.NewCPUSet() cpuResult := cpuset.NewCPUSet()
memResult := cpuset.NewCPUSet()
for _, ctr := range s.config.Containers { for _, ctr := range s.config.Containers {
if ctr.Resources.CPU != nil { if ctr.Resources.CPU != nil {
currSet, err := cpuset.Parse(ctr.Resources.CPU.Cpus) currCpuSet, err := cpuset.Parse(ctr.Resources.CPU.Cpus)
if err != nil { if err != nil {
return "", fmt.Errorf("unable to parse CPUset for container %s", ctr.ID) return "", "", fmt.Errorf("unable to parse CPUset.cpus for container %s: %v", ctr.ID, err)
} }
result = result.Union(currSet) cpuResult = cpuResult.Union(currCpuSet)
currMemSet, err := cpuset.Parse(ctr.Resources.CPU.Mems)
if err != nil {
return "", "", fmt.Errorf("unable to parse CPUset.mems for container %s: %v", ctr.ID, err)
}
memResult = memResult.Union(currMemSet)
} }
} }
return result.String(), nil return cpuResult.String(), memResult.String(), nil
} }

View File

@ -1420,24 +1420,25 @@ func TestSandbox_SetupSandboxCgroup(t *testing.T) {
} }
} }
func getContainerConfigWithCPUSet(cpuset string) ContainerConfig { func getContainerConfigWithCPUSet(cpuset, memset string) ContainerConfig {
return ContainerConfig{ return ContainerConfig{
Resources: specs.LinuxResources{ Resources: specs.LinuxResources{
CPU: &specs.LinuxCPU{ CPU: &specs.LinuxCPU{
Cpus: cpuset, Cpus: cpuset,
Mems: memset,
}, },
}, },
} }
} }
func getSimpleSandbox(cpuset0, cpuset1, cpuset2 string) *Sandbox { func getSimpleSandbox(cpusets, memsets [3]string) *Sandbox {
sandbox := Sandbox{} sandbox := Sandbox{}
sandbox.config = &SandboxConfig{ sandbox.config = &SandboxConfig{
Containers: []ContainerConfig{ Containers: []ContainerConfig{
getContainerConfigWithCPUSet(cpuset0), getContainerConfigWithCPUSet(cpusets[0], memsets[0]),
getContainerConfigWithCPUSet(cpuset1), getContainerConfigWithCPUSet(cpusets[1], memsets[1]),
getContainerConfigWithCPUSet(cpuset2), getContainerConfigWithCPUSet(cpusets[2], memsets[2]),
}, },
} }
@ -1448,79 +1449,96 @@ func TestGetSandboxCpuSet(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
cpuset0 string cpusets [3]string
cpuset1 string memsets [3]string
cpuset2 string cpuResult string
result string memResult string
wantErr bool wantErr bool
}{ }{
{ {
"single, no cpuset", "single, no cpuset",
"", [3]string{"", "", ""},
"", [3]string{"", "", ""},
"", "",
"", "",
false, false,
}, },
{ {
"single cpuset", "single cpuset",
[3]string{"0", "", ""},
[3]string{"", "", ""},
"0", "0",
"", "",
"",
"0",
false, false,
}, },
{ {
"two duplicate cpuset", "two duplicate cpuset",
"0", [3]string{"0", "0", ""},
[3]string{"", "", ""},
"0", "0",
"", "",
"0",
false, false,
}, },
{ {
"3 cpusets", "3 cpusets",
"0-3", [3]string{"0-3", "5-7", "1"},
"5-7", [3]string{"", "", ""},
"1",
"0-3,5-7", "0-3,5-7",
"",
false, false,
}, },
{ {
"weird, but should be okay", "weird, but should be okay",
"0-3", [3]string{"0-3", "99999", ""},
"99999", [3]string{"", "", ""},
"",
"0-3,99999", "0-3,99999",
"",
false, false,
}, },
{ {
"two, overlapping cpuset", "two, overlapping cpuset",
[3]string{"0-3", "1-2", ""},
[3]string{"", "", ""},
"0-3", "0-3",
"1-2",
"", "",
"0-3",
false, false,
}, },
{ {
"garbage, should fail", "garbage, should fail",
"7 beard-seconds", [3]string{"7 beard-seconds", "Audrey + 7", "Elliott - 17"},
"Audrey + 7", [3]string{"", "", ""},
"Elliott - 17", "",
"", "",
true, true,
}, },
{
"cpuset and memset",
[3]string{"0-3", "1-2", ""},
[3]string{"0", "1", "0-1"},
"0-3",
"0-1",
false,
},
{
"memset",
[3]string{"0-3", "1-2", ""},
[3]string{"0", "3", ""},
"0-3",
"0,3",
false,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
s := getSimpleSandbox(tt.cpuset0, tt.cpuset1, tt.cpuset2) s := getSimpleSandbox(tt.cpusets, tt.memsets)
res, err := s.getSandboxCPUSet() res, _, err := s.getSandboxCPUSet()
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("getSandboxCPUSet() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("getSandboxCPUSet() error = %v, wantErr %v", err, tt.wantErr)
} }
if res != tt.result { if res != tt.cpuResult {
t.Errorf("getSandboxCPUSet() result = %s, wanted result %s", res, tt.result) t.Errorf("getSandboxCPUSet() result = %s, wanted result %s", res, tt.cpuResult)
} }
}) })
} }