fs2.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package fs2
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "github.com/opencontainers/runc/libcontainer/cgroups"
  8. "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
  9. "github.com/opencontainers/runc/libcontainer/configs"
  10. )
  11. type parseError = fscommon.ParseError
  12. type manager struct {
  13. config *configs.Cgroup
  14. // dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope"
  15. dirPath string
  16. // controllers is content of "cgroup.controllers" file.
  17. // excludes pseudo-controllers ("devices" and "freezer").
  18. controllers map[string]struct{}
  19. }
  20. // NewManager creates a manager for cgroup v2 unified hierarchy.
  21. // dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope".
  22. // If dirPath is empty, it is automatically set using config.
  23. func NewManager(config *configs.Cgroup, dirPath string) (cgroups.Manager, error) {
  24. if dirPath == "" {
  25. var err error
  26. dirPath, err = defaultDirPath(config)
  27. if err != nil {
  28. return nil, err
  29. }
  30. }
  31. m := &manager{
  32. config: config,
  33. dirPath: dirPath,
  34. }
  35. return m, nil
  36. }
  37. func (m *manager) getControllers() error {
  38. if m.controllers != nil {
  39. return nil
  40. }
  41. data, err := cgroups.ReadFile(m.dirPath, "cgroup.controllers")
  42. if err != nil {
  43. if m.config.Rootless && m.config.Path == "" {
  44. return nil
  45. }
  46. return err
  47. }
  48. fields := strings.Fields(data)
  49. m.controllers = make(map[string]struct{}, len(fields))
  50. for _, c := range fields {
  51. m.controllers[c] = struct{}{}
  52. }
  53. return nil
  54. }
  55. func (m *manager) Apply(pid int) error {
  56. if err := CreateCgroupPath(m.dirPath, m.config); err != nil {
  57. // Related tests:
  58. // - "runc create (no limits + no cgrouppath + no permission) succeeds"
  59. // - "runc create (rootless + no limits + cgrouppath + no permission) fails with permission error"
  60. // - "runc create (rootless + limits + no cgrouppath + no permission) fails with informative error"
  61. if m.config.Rootless {
  62. if m.config.Path == "" {
  63. if blNeed, nErr := needAnyControllers(m.config.Resources); nErr == nil && !blNeed {
  64. return nil
  65. }
  66. return fmt.Errorf("rootless needs no limits + no cgrouppath when no permission is granted for cgroups: %w", err)
  67. }
  68. }
  69. return err
  70. }
  71. if err := cgroups.WriteCgroupProc(m.dirPath, pid); err != nil {
  72. return err
  73. }
  74. return nil
  75. }
  76. func (m *manager) GetPids() ([]int, error) {
  77. return cgroups.GetPids(m.dirPath)
  78. }
  79. func (m *manager) GetAllPids() ([]int, error) {
  80. return cgroups.GetAllPids(m.dirPath)
  81. }
  82. func (m *manager) GetStats() (*cgroups.Stats, error) {
  83. var errs []error
  84. st := cgroups.NewStats()
  85. // pids (since kernel 4.5)
  86. if err := statPids(m.dirPath, st); err != nil {
  87. errs = append(errs, err)
  88. }
  89. // memory (since kernel 4.5)
  90. if err := statMemory(m.dirPath, st); err != nil && !os.IsNotExist(err) {
  91. errs = append(errs, err)
  92. }
  93. // io (since kernel 4.5)
  94. if err := statIo(m.dirPath, st); err != nil && !os.IsNotExist(err) {
  95. errs = append(errs, err)
  96. }
  97. // cpu (since kernel 4.15)
  98. // Note cpu.stat is available even if the controller is not enabled.
  99. if err := statCpu(m.dirPath, st); err != nil && !os.IsNotExist(err) {
  100. errs = append(errs, err)
  101. }
  102. // hugetlb (since kernel 5.6)
  103. if err := statHugeTlb(m.dirPath, st); err != nil && !os.IsNotExist(err) {
  104. errs = append(errs, err)
  105. }
  106. // rdma (since kernel 4.11)
  107. if err := fscommon.RdmaGetStats(m.dirPath, st); err != nil && !os.IsNotExist(err) {
  108. errs = append(errs, err)
  109. }
  110. if len(errs) > 0 && !m.config.Rootless {
  111. return st, fmt.Errorf("error while statting cgroup v2: %+v", errs)
  112. }
  113. return st, nil
  114. }
  115. func (m *manager) Freeze(state configs.FreezerState) error {
  116. if m.config.Resources == nil {
  117. return errors.New("cannot toggle freezer: cgroups not configured for container")
  118. }
  119. if err := setFreezer(m.dirPath, state); err != nil {
  120. return err
  121. }
  122. m.config.Resources.Freezer = state
  123. return nil
  124. }
  125. func (m *manager) Destroy() error {
  126. return cgroups.RemovePath(m.dirPath)
  127. }
  128. func (m *manager) Path(_ string) string {
  129. return m.dirPath
  130. }
  131. func (m *manager) Set(r *configs.Resources) error {
  132. if r == nil {
  133. return nil
  134. }
  135. if err := m.getControllers(); err != nil {
  136. return err
  137. }
  138. // pids (since kernel 4.5)
  139. if err := setPids(m.dirPath, r); err != nil {
  140. return err
  141. }
  142. // memory (since kernel 4.5)
  143. if err := setMemory(m.dirPath, r); err != nil {
  144. return err
  145. }
  146. // io (since kernel 4.5)
  147. if err := setIo(m.dirPath, r); err != nil {
  148. return err
  149. }
  150. // cpu (since kernel 4.15)
  151. if err := setCpu(m.dirPath, r); err != nil {
  152. return err
  153. }
  154. // devices (since kernel 4.15, pseudo-controller)
  155. //
  156. // When rootless is true, errors from the device subsystem are ignored because it is really not expected to work.
  157. // However, errors from other subsystems are not ignored.
  158. // see @test "runc create (rootless + limits + no cgrouppath + no permission) fails with informative error"
  159. if err := setDevices(m.dirPath, r); err != nil && !m.config.Rootless {
  160. return err
  161. }
  162. // cpuset (since kernel 5.0)
  163. if err := setCpuset(m.dirPath, r); err != nil {
  164. return err
  165. }
  166. // hugetlb (since kernel 5.6)
  167. if err := setHugeTlb(m.dirPath, r); err != nil {
  168. return err
  169. }
  170. // rdma (since kernel 4.11)
  171. if err := fscommon.RdmaSet(m.dirPath, r); err != nil {
  172. return err
  173. }
  174. // freezer (since kernel 5.2, pseudo-controller)
  175. if err := setFreezer(m.dirPath, r.Freezer); err != nil {
  176. return err
  177. }
  178. if err := m.setUnified(r.Unified); err != nil {
  179. return err
  180. }
  181. m.config.Resources = r
  182. return nil
  183. }
  184. func (m *manager) setUnified(res map[string]string) error {
  185. for k, v := range res {
  186. if strings.Contains(k, "/") {
  187. return fmt.Errorf("unified resource %q must be a file name (no slashes)", k)
  188. }
  189. if err := cgroups.WriteFile(m.dirPath, k, v); err != nil {
  190. // Check for both EPERM and ENOENT since O_CREAT is used by WriteFile.
  191. if errors.Is(err, os.ErrPermission) || errors.Is(err, os.ErrNotExist) {
  192. // Check if a controller is available,
  193. // to give more specific error if not.
  194. sk := strings.SplitN(k, ".", 2)
  195. if len(sk) != 2 {
  196. return fmt.Errorf("unified resource %q must be in the form CONTROLLER.PARAMETER", k)
  197. }
  198. c := sk[0]
  199. if _, ok := m.controllers[c]; !ok && c != "cgroup" {
  200. return fmt.Errorf("unified resource %q can't be set: controller %q not available", k, c)
  201. }
  202. }
  203. return fmt.Errorf("unable to set unified resource %q: %w", k, err)
  204. }
  205. }
  206. return nil
  207. }
  208. func (m *manager) GetPaths() map[string]string {
  209. paths := make(map[string]string, 1)
  210. paths[""] = m.dirPath
  211. return paths
  212. }
  213. func (m *manager) GetCgroups() (*configs.Cgroup, error) {
  214. return m.config, nil
  215. }
  216. func (m *manager) GetFreezerState() (configs.FreezerState, error) {
  217. return getFreezer(m.dirPath)
  218. }
  219. func (m *manager) Exists() bool {
  220. return cgroups.PathExists(m.dirPath)
  221. }
  222. func OOMKillCount(path string) (uint64, error) {
  223. return fscommon.GetValueByKey(path, "memory.events", "oom_kill")
  224. }
  225. func (m *manager) OOMKillCount() (uint64, error) {
  226. c, err := OOMKillCount(m.dirPath)
  227. if err != nil && m.config.Rootless && os.IsNotExist(err) {
  228. err = nil
  229. }
  230. return c, err
  231. }