cpuset.go 4.4 KB

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