@@ -5,15 +5,19 @@ package fs
55import (
66 "bufio"
77 "fmt"
8- "math "
8+ "io/ioutil "
99 "os"
1010 "path/filepath"
1111 "strconv"
1212 "strings"
13+ "syscall"
1314
1415 "github.com/opencontainers/runc/libcontainer/cgroups"
1516 "github.com/opencontainers/runc/libcontainer/configs"
16- "github.com/opencontainers/runc/libcontainer/system"
17+ )
18+
19+ const (
20+ cgroupKernelMemoryLimit = "memory.kmem.limit_in_bytes"
1721)
1822
1923type MemoryGroup struct {
@@ -34,9 +38,7 @@ func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
3438 return err
3539 }
3640 }
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 {
41+ if err := EnableKernelMemoryAccounting (path ); err != nil {
4042 return err
4143 }
4244 }
@@ -55,38 +57,43 @@ func (s *MemoryGroup) Apply(d *cgroupData) (err error) {
5557 return nil
5658}
5759
58- func (s * MemoryGroup ) SetKernelMemory (path string , cgroup * configs.Cgroup ) error {
59- // This has to be done separately because it has special
60- // constraints (it can only be initialized before setting up a
61- // hierarchy or adding a task to the cgroups. However, if
62- // sucessfully initialized, it can be updated anytime afterwards)
63- if cgroup .Resources .KernelMemory != 0 {
64- kmemInitialized := false
65- // Is kmem.limit_in_bytes already set?
66- kmemValue , err := getCgroupParamUint (path , "memory.kmem.limit_in_bytes" )
67- if err != nil {
68- return err
69- }
70- switch system .GetLongBit () {
71- case 32 :
72- kmemInitialized = uint32 (kmemValue ) != uint32 (math .MaxUint32 )
73- case 64 :
74- kmemInitialized = kmemValue != uint64 (math .MaxUint64 )
75- }
76- if ! kmemInitialized {
77- // If there's already tasks in the cgroup, we can't change the limit either
78- tasks , err := getCgroupParamString (path , "tasks" )
79- if err != nil {
80- return err
81- }
82- if tasks != "" {
83- return fmt .Errorf ("cannot set kmem.limit_in_bytes after task have joined this cgroup" )
84- }
85- }
60+ func EnableKernelMemoryAccounting (path string ) error {
61+ // Check if kernel memory is enabled
62+ // We have to limit the kernel memory here as it won't be accounted at all
63+ // until a limit is set on the cgroup and limit cannot be set once the
64+ // cgroup has children, or if there are already tasks in the cgroup.
65+ kernelMemoryLimit := int64 (1 )
66+ if err := setKernelMemory (path , kernelMemoryLimit ); err != nil {
67+ return err
68+ }
69+ kernelMemoryLimit = int64 (- 1 )
70+ if err := setKernelMemory (path , kernelMemoryLimit ); err != nil {
71+ return err
72+ }
73+ return nil
74+ }
8675
87- if err := writeFile (path , "memory.kmem.limit_in_bytes" , strconv .FormatInt (cgroup .Resources .KernelMemory , 10 )); err != nil {
88- return err
76+ func setKernelMemory (path string , kernelMemoryLimit int64 ) error {
77+ if path == "" {
78+ return fmt .Errorf ("no such directory for %s" , cgroupKernelMemoryLimit )
79+ }
80+ if ! cgroups .PathExists (filepath .Join (path , cgroupKernelMemoryLimit )) {
81+ // kernel memory is not enabled on the system so we should do nothing
82+ return nil
83+ }
84+ if err := ioutil .WriteFile (filepath .Join (path , cgroupKernelMemoryLimit ), []byte (strconv .FormatInt (kernelMemoryLimit , 10 )), 0700 ); err != nil {
85+ // Check if the error number returned by the syscall is "EBUSY"
86+ // The EBUSY signal is returned on attempts to write to the
87+ // memory.kmem.limit_in_bytes file if the cgroup has children or
88+ // once tasks have been attached to the cgroup
89+ if pathErr , ok := err .(* os.PathError ); ok {
90+ if errNo , ok := pathErr .Err .(syscall.Errno ); ok {
91+ if errNo == syscall .EBUSY {
92+ return fmt .Errorf ("failed to set %s, because either tasks have already joined this cgroup or it has children" , cgroupKernelMemoryLimit )
93+ }
94+ }
8995 }
96+ return fmt .Errorf ("failed to write %v to %v: %v" , kernelMemoryLimit , cgroupKernelMemoryLimit , err )
9097 }
9198 return nil
9299}
@@ -139,15 +146,18 @@ func (s *MemoryGroup) Set(path string, cgroup *configs.Cgroup) error {
139146 return err
140147 }
141148
142- if err := s .SetKernelMemory (path , cgroup ); err != nil {
143- return err
149+ if cgroup .Resources .KernelMemory != 0 {
150+ if err := setKernelMemory (path , cgroup .Resources .KernelMemory ); err != nil {
151+ return err
152+ }
144153 }
145154
146155 if cgroup .Resources .MemoryReservation != 0 {
147156 if err := writeFile (path , "memory.soft_limit_in_bytes" , strconv .FormatInt (cgroup .Resources .MemoryReservation , 10 )); err != nil {
148157 return err
149158 }
150159 }
160+
151161 if cgroup .Resources .KernelMemoryTCP != 0 {
152162 if err := writeFile (path , "memory.kmem.tcp.limit_in_bytes" , strconv .FormatInt (cgroup .Resources .KernelMemoryTCP , 10 )); err != nil {
153163 return err
0 commit comments