cpuset.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // +build linux
  2. package fs
  3. import (
  4. "bytes"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "github.com/opencontainers/runc/libcontainer/cgroups"
  10. "github.com/opencontainers/runc/libcontainer/configs"
  11. libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
  12. )
  13. type CpusetGroup struct {
  14. }
  15. func (s *CpusetGroup) Name() string {
  16. return "cpuset"
  17. }
  18. func (s *CpusetGroup) Apply(d *cgroupData) error {
  19. dir, err := d.path("cpuset")
  20. if err != nil && !cgroups.IsNotFound(err) {
  21. return err
  22. }
  23. return s.ApplyDir(dir, d.config, d.pid)
  24. }
  25. func (s *CpusetGroup) Set(path string, cgroup *configs.Cgroup) error {
  26. if cgroup.Resources.CpusetCpus != "" {
  27. if err := writeFile(path, "cpuset.cpus", cgroup.Resources.CpusetCpus); err != nil {
  28. return err
  29. }
  30. }
  31. if cgroup.Resources.CpusetMems != "" {
  32. if err := writeFile(path, "cpuset.mems", cgroup.Resources.CpusetMems); err != nil {
  33. return err
  34. }
  35. }
  36. return nil
  37. }
  38. func (s *CpusetGroup) Remove(d *cgroupData) error {
  39. return removePath(d.path("cpuset"))
  40. }
  41. func (s *CpusetGroup) GetStats(path string, stats *cgroups.Stats) error {
  42. return nil
  43. }
  44. func (s *CpusetGroup) ApplyDir(dir string, cgroup *configs.Cgroup, pid int) error {
  45. // This might happen if we have no cpuset cgroup mounted.
  46. // Just do nothing and don't fail.
  47. if dir == "" {
  48. return nil
  49. }
  50. mountInfo, err := ioutil.ReadFile("/proc/self/mountinfo")
  51. if err != nil {
  52. return err
  53. }
  54. root := filepath.Dir(cgroups.GetClosestMountpointAncestor(dir, string(mountInfo)))
  55. // 'ensureParent' start with parent because we don't want to
  56. // explicitly inherit from parent, it could conflict with
  57. // 'cpuset.cpu_exclusive'.
  58. if err := s.ensureParent(filepath.Dir(dir), root); err != nil {
  59. return err
  60. }
  61. if err := os.MkdirAll(dir, 0755); err != nil {
  62. return err
  63. }
  64. // We didn't inherit cpuset configs from parent, but we have
  65. // to ensure cpuset configs are set before moving task into the
  66. // cgroup.
  67. // The logic is, if user specified cpuset configs, use these
  68. // specified configs, otherwise, inherit from parent. This makes
  69. // cpuset configs work correctly with 'cpuset.cpu_exclusive', and
  70. // keep backward compatibility.
  71. if err := s.ensureCpusAndMems(dir, cgroup); err != nil {
  72. return err
  73. }
  74. // because we are not using d.join we need to place the pid into the procs file
  75. // unlike the other subsystems
  76. return cgroups.WriteCgroupProc(dir, pid)
  77. }
  78. func (s *CpusetGroup) getSubsystemSettings(parent string) (cpus []byte, mems []byte, err error) {
  79. if cpus, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.cpus")); err != nil {
  80. return
  81. }
  82. if mems, err = ioutil.ReadFile(filepath.Join(parent, "cpuset.mems")); err != nil {
  83. return
  84. }
  85. return cpus, mems, nil
  86. }
  87. // ensureParent makes sure that the parent directory of current is created
  88. // and populated with the proper cpus and mems files copied from
  89. // it's parent.
  90. func (s *CpusetGroup) ensureParent(current, root string) error {
  91. parent := filepath.Dir(current)
  92. if libcontainerUtils.CleanPath(parent) == root {
  93. return nil
  94. }
  95. // Avoid infinite recursion.
  96. if parent == current {
  97. return fmt.Errorf("cpuset: cgroup parent path outside cgroup root")
  98. }
  99. if err := s.ensureParent(parent, root); err != nil {
  100. return err
  101. }
  102. if err := os.MkdirAll(current, 0755); err != nil {
  103. return err
  104. }
  105. return s.copyIfNeeded(current, parent)
  106. }
  107. // copyIfNeeded copies the cpuset.cpus and cpuset.mems from the parent
  108. // directory to the current directory if the file's contents are 0
  109. func (s *CpusetGroup) copyIfNeeded(current, parent string) error {
  110. var (
  111. err error
  112. currentCpus, currentMems []byte
  113. parentCpus, parentMems []byte
  114. )
  115. if currentCpus, currentMems, err = s.getSubsystemSettings(current); err != nil {
  116. return err
  117. }
  118. if parentCpus, parentMems, err = s.getSubsystemSettings(parent); err != nil {
  119. return err
  120. }
  121. if s.isEmpty(currentCpus) {
  122. if err := writeFile(current, "cpuset.cpus", string(parentCpus)); err != nil {
  123. return err
  124. }
  125. }
  126. if s.isEmpty(currentMems) {
  127. if err := writeFile(current, "cpuset.mems", string(parentMems)); err != nil {
  128. return err
  129. }
  130. }
  131. return nil
  132. }
  133. func (s *CpusetGroup) isEmpty(b []byte) bool {
  134. return len(bytes.Trim(b, "\n")) == 0
  135. }
  136. func (s *CpusetGroup) ensureCpusAndMems(path string, cgroup *configs.Cgroup) error {
  137. if err := s.Set(path, cgroup); err != nil {
  138. return err
  139. }
  140. return s.copyIfNeeded(path, filepath.Dir(path))
  141. }