Skip to content

Commit 4eb8c2f

Browse files
authored
Merge pull request #935 from vishh/cgo
Use update time to detect if kmem limits have been set
2 parents 9d7831e + c501cc0 commit 4eb8c2f

File tree

3 files changed

+42
-45
lines changed

3 files changed

+42
-45
lines changed

libcontainer/cgroups/fs/memory.go

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ package fs
55
import (
66
"bufio"
77
"fmt"
8-
"math"
98
"os"
109
"path/filepath"
1110
"strconv"
1211
"strings"
12+
"time"
1313

1414
"github.com/opencontainers/runc/libcontainer/cgroups"
1515
"github.com/opencontainers/runc/libcontainer/configs"
16-
"github.com/opencontainers/runc/libcontainer/system"
1716
)
1817

1918
type MemoryGroup struct {
@@ -28,53 +27,65 @@ func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
2827
if err != nil && !cgroups.IsNotFound(err) {
2928
return err
3029
}
31-
if memoryAssigned(d.config) {
32-
if path != "" {
33-
if err := os.MkdirAll(path, 0755); err != nil {
34-
return err
35-
}
36-
}
37-
// We have to set kernel memory here, as we can't change it once
38-
// processes have been attached to the cgroup.
39-
if err := s.SetKernelMemory(path, d.config); err != nil {
40-
return err
41-
}
30+
// reset error.
31+
err = nil
32+
if path == "" {
33+
// Invalid input.
34+
return fmt.Errorf("invalid path for memory cgroups: %+v", d)
4235
}
43-
4436
defer func() {
4537
if err != nil {
4638
os.RemoveAll(path)
4739
}
4840
}()
49-
41+
if !cgroups.PathExists(path) {
42+
if err = os.MkdirAll(path, 0755); err != nil {
43+
return err
44+
}
45+
}
46+
if memoryAssigned(d.config) {
47+
// We have to set kernel memory here, as we can't change it once
48+
// processes have been attached to the cgroup.
49+
if err = s.SetKernelMemory(path, d.config); err != nil {
50+
return err
51+
}
52+
}
5053
// We need to join memory cgroup after set memory limits, because
5154
// kmem.limit_in_bytes can only be set when the cgroup is empty.
52-
_, err = d.join("memory")
53-
if err != nil && !cgroups.IsNotFound(err) {
55+
if _, jerr := d.join("memory"); jerr != nil && !cgroups.IsNotFound(jerr) {
56+
err = jerr
5457
return err
5558
}
5659
return nil
5760
}
5861

62+
func getModifyTime(path string) (time.Time, error) {
63+
stat, err := os.Stat(path)
64+
if err != nil {
65+
return time.Time{}, fmt.Errorf("failed to get memory cgroups creation time: %v", err)
66+
}
67+
return stat.ModTime(), nil
68+
}
69+
5970
func (s *MemoryGroup) SetKernelMemory(path string, cgroup *configs.Cgroup) error {
6071
// This has to be done separately because it has special
6172
// constraints (it can only be initialized before setting up a
6273
// hierarchy or adding a task to the cgroups. However, if
6374
// sucessfully initialized, it can be updated anytime afterwards)
6475
if cgroup.Resources.KernelMemory != 0 {
65-
kmemInitialized := false
6676
// Is kmem.limit_in_bytes already set?
67-
kmemValue, err := getCgroupParamUint(path, "memory.kmem.limit_in_bytes")
77+
// memory.kmem.max_usage_in_bytes is a read-only file. Use it to get cgroups creation time.
78+
kmemCreationTime, err := getModifyTime(filepath.Join(path, "memory.kmem.max_usage_in_bytes"))
6879
if err != nil {
6980
return err
7081
}
71-
switch system.GetLongBit() {
72-
case 32:
73-
kmemInitialized = uint32(kmemValue) != uint32(math.MaxUint32)
74-
case 64:
75-
kmemInitialized = kmemValue != uint64(math.MaxUint64)
82+
kmemLimitsUpdateTime, err := getModifyTime(filepath.Join(path, "memory.kmem.limit_in_bytes"))
83+
if err != nil {
84+
return err
7685
}
77-
86+
// kmem.limit_in_bytes has already been set if its update time is after that of creation time.
87+
// We use `!=` op instead of `>` because updates are losing precision compared to creation.
88+
kmemInitialized := !kmemLimitsUpdateTime.Equal(kmemCreationTime)
7889
if !kmemInitialized {
7990
// If there's already tasks in the cgroup, we can't change the limit either
8091
tasks, err := getCgroupParamString(path, "tasks")
@@ -85,7 +96,6 @@ func (s *MemoryGroup) SetKernelMemory(path string, cgroup *configs.Cgroup) error
8596
return fmt.Errorf("cannot set kmem.limit_in_bytes after task have joined this cgroup")
8697
}
8798
}
88-
8999
if err := writeFile(path, "memory.kmem.limit_in_bytes", strconv.FormatInt(cgroup.Resources.KernelMemory, 10)); err != nil {
90100
return err
91101
}

libcontainer/cgroups/fs/memory_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ func TestMemorySetKernelMemory(t *testing.T) {
227227
helper.writeFileContents(map[string]string{
228228
"memory.kmem.limit_in_bytes": strconv.Itoa(kernelMemoryBefore),
229229
})
230+
helper.writeFileContents(map[string]string{
231+
"memory.kmem.max_usage_in_bytes": strconv.Itoa(kernelMemoryBefore),
232+
})
233+
helper.writeFileContents(map[string]string{
234+
"tasks": "",
235+
})
230236

231237
helper.CgroupData.config.Resources.KernelMemory = kernelMemoryAfter
232238
memory := &MemoryGroup{}

libcontainer/system/sysconfig.go

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,9 @@ package system
44

55
/*
66
#include <unistd.h>
7-
#include <limits.h>
8-
9-
int GetLongBit() {
10-
#ifdef _SC_LONG_BIT
11-
int longbits;
12-
13-
longbits = sysconf(_SC_LONG_BIT);
14-
if (longbits < 0) {
15-
longbits = (CHAR_BIT * sizeof(long));
16-
}
17-
return longbits;
18-
#else
19-
return (CHAR_BIT * sizeof(long));
20-
#endif
21-
}
227
*/
238
import "C"
249

2510
func GetClockTicks() int {
2611
return int(C.sysconf(C._SC_CLK_TCK))
2712
}
28-
29-
func GetLongBit() int {
30-
return int(C.GetLongBit())
31-
}

0 commit comments

Comments
 (0)