kmem.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. // +build linux,!nokmem
  2. package fs
  3. import (
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "strconv"
  10. "syscall" // for Errno type only
  11. "github.com/opencontainers/runc/libcontainer/cgroups"
  12. "golang.org/x/sys/unix"
  13. )
  14. const cgroupKernelMemoryLimit = "memory.kmem.limit_in_bytes"
  15. func EnableKernelMemoryAccounting(path string) error {
  16. // Ensure that kernel memory is available in this kernel build. If it
  17. // isn't, we just ignore it because EnableKernelMemoryAccounting is
  18. // automatically called for all memory limits.
  19. if !cgroups.PathExists(filepath.Join(path, cgroupKernelMemoryLimit)) {
  20. return nil
  21. }
  22. // We have to limit the kernel memory here as it won't be accounted at all
  23. // until a limit is set on the cgroup and limit cannot be set once the
  24. // cgroup has children, or if there are already tasks in the cgroup.
  25. for _, i := range []int64{1, -1} {
  26. if err := setKernelMemory(path, i); err != nil {
  27. return err
  28. }
  29. }
  30. return nil
  31. }
  32. func setKernelMemory(path string, kernelMemoryLimit int64) error {
  33. if path == "" {
  34. return fmt.Errorf("no such directory for %s", cgroupKernelMemoryLimit)
  35. }
  36. if !cgroups.PathExists(filepath.Join(path, cgroupKernelMemoryLimit)) {
  37. // We have specifically been asked to set a kmem limit. If the kernel
  38. // doesn't support it we *must* error out.
  39. return errors.New("kernel memory accounting not supported by this kernel")
  40. }
  41. if err := ioutil.WriteFile(filepath.Join(path, cgroupKernelMemoryLimit), []byte(strconv.FormatInt(kernelMemoryLimit, 10)), 0700); err != nil {
  42. // Check if the error number returned by the syscall is "EBUSY"
  43. // The EBUSY signal is returned on attempts to write to the
  44. // memory.kmem.limit_in_bytes file if the cgroup has children or
  45. // once tasks have been attached to the cgroup
  46. if pathErr, ok := err.(*os.PathError); ok {
  47. if errNo, ok := pathErr.Err.(syscall.Errno); ok {
  48. if errNo == unix.EBUSY {
  49. return fmt.Errorf("failed to set %s, because either tasks have already joined this cgroup or it has children", cgroupKernelMemoryLimit)
  50. }
  51. }
  52. }
  53. return fmt.Errorf("failed to write %v to %v: %v", kernelMemoryLimit, cgroupKernelMemoryLimit, err)
  54. }
  55. return nil
  56. }