@@ -5,15 +5,14 @@ package fs
55import (
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
1918type 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+
5970func (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 }
0 commit comments