Skip to content

Commit

Permalink
cgroupv2: fix setting MemorySwap
Browse files Browse the repository at this point in the history
The resources.MemorySwap field from OCI is memory+swap, while cgroupv2
has a separate swap limit, so subtract memory from the limit (and make
sure values are set and sane).

Make sure to set MemorySwapMax for systemd, too. Since systemd does not
have MemorySwapMax for cgroupv1, it is only needed for v2 driver.

[v2: return -1 on any negative value, add unit test]
[v3: treat any negative value other than -1 as error]

Signed-off-by: Kir Kolyshkin <[email protected]>
  • Loading branch information
kolyshkin committed Apr 8, 2020
1 parent d3fdacb commit c86be8a
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 1 deletion.
7 changes: 6 additions & 1 deletion libcontainer/cgroups/fs2/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,16 @@ func numToStr(value int64) (ret string) {
}

func setMemory(dirPath string, cgroup *configs.Cgroup) error {
if val := numToStr(cgroup.Resources.MemorySwap); val != "" {
swap, err := cgroups.ConvertMemorySwapToCgroupV2Value(cgroup.Resources.MemorySwap, cgroup.Resources.Memory)
if err != nil {
return err
}
if val := numToStr(swap); val != "" {
if err := fscommon.WriteFile(dirPath, "memory.swap.max", val); err != nil {
return err
}
}

if val := numToStr(cgroup.Resources.Memory); val != "" {
if err := fscommon.WriteFile(dirPath, "memory.max", val); err != nil {
return err
Expand Down
9 changes: 9 additions & 0 deletions libcontainer/cgroups/systemd/unified_hierarchy.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ func (m *UnifiedManager) Apply(pid int) error {
newProp("MemoryMax", uint64(c.Resources.Memory)))
}

swap, err := cgroups.ConvertMemorySwapToCgroupV2Value(c.Resources.MemorySwap, c.Resources.Memory)
if err != nil {
return err
}
if swap > 0 {
properties = append(properties,
newProp("MemorySwapMax", uint64(swap)))
}

if c.Resources.CpuWeight != 0 {
properties = append(properties,
newProp("CPUWeight", c.Resources.CpuWeight))
Expand Down
22 changes: 22 additions & 0 deletions libcontainer/cgroups/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,3 +623,25 @@ func ConvertCPUQuotaCPUPeriodToCgroupV2Value(quota int64, period uint64) string
}
return fmt.Sprintf("%d %d", quota, period)
}

// ConvertMemorySwapToCgroupV2Value converts MemorySwap value from OCI spec
// for use by cgroup v2 drivers. A conversion is needed since Resources.MemorySwap
// is defined as memory+swap combined, while in cgroup v2 swap is a separate value.
func ConvertMemorySwapToCgroupV2Value(memorySwap, memory int64) (int64, error) {
if memorySwap == -1 || memorySwap == 0 {
// -1 is "max", 0 is "unset", so treat as is
return memorySwap, nil
}
// sanity checks
if memory == 0 || memory == -1 {
return 0, errors.New("unable to set swap limit without memory limit")
}
if memory < 0 {
return 0, fmt.Errorf("invalid memory value: %d", memory)
}
if memorySwap < memory {
return 0, errors.New("memory+swap limit should be > memory limit")
}

return memorySwap - memory, nil
}
81 changes: 81 additions & 0 deletions libcontainer/cgroups/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,3 +530,84 @@ func TestConvertCPUQuotaCPUPeriodToCgroupV2Value(t *testing.T) {
}
}
}

func TestConvertMemorySwapToCgroupV2Value(t *testing.T) {
cases := []struct {
memswap, memory int64
expected int64
expErr bool
}{
{
memswap: 0,
memory: 0,
expected: 0,
},
{
memswap: -1,
memory: 0,
expected: -1,
},
{
memswap: -1,
memory: -1,
expected: -1,
},
{
memswap: -2,
memory: 0,
expErr: true,
},
{
memswap: -1,
memory: 1000,
expected: -1,
},
{
memswap: 1000,
memory: 1000,
expected: 0,
},
{
memswap: 500,
memory: 200,
expected: 300,
},
{
memswap: 300,
memory: 400,
expErr: true,
},
{
memswap: 300,
memory: 0,
expErr: true,
},
{
memswap: 300,
memory: -300,
expErr: true,
},
{
memswap: 300,
memory: -1,
expErr: true,
},
}

for _, c := range cases {
swap, err := ConvertMemorySwapToCgroupV2Value(c.memswap, c.memory)
if c.expErr {
if err == nil {
t.Errorf("memswap: %d, memory %d, expected error, got %d, nil", c.memswap, c.memory, swap)
}
// no more checks
continue
}
if err != nil {
t.Errorf("memswap: %d, memory %d, expected success, got error %s", c.memswap, c.memory, err)
}
if swap != c.expected {
t.Errorf("memswap: %d, memory %d, expected %d, got %d", c.memswap, c.memory, c.expected, swap)
}
}
}

0 comments on commit c86be8a

Please sign in to comment.