cpu.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. package fs
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "strconv"
  8. "github.com/opencontainers/runc/libcontainer/cgroups"
  9. "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
  10. "github.com/opencontainers/runc/libcontainer/configs"
  11. "golang.org/x/sys/unix"
  12. )
  13. type CpuGroup struct{}
  14. func (s *CpuGroup) Name() string {
  15. return "cpu"
  16. }
  17. func (s *CpuGroup) Apply(path string, r *configs.Resources, pid int) error {
  18. if err := os.MkdirAll(path, 0o755); err != nil {
  19. return err
  20. }
  21. // We should set the real-Time group scheduling settings before moving
  22. // in the process because if the process is already in SCHED_RR mode
  23. // and no RT bandwidth is set, adding it will fail.
  24. if err := s.SetRtSched(path, r); err != nil {
  25. return err
  26. }
  27. // Since we are not using apply(), we need to place the pid
  28. // into the procs file.
  29. return cgroups.WriteCgroupProc(path, pid)
  30. }
  31. func (s *CpuGroup) SetRtSched(path string, r *configs.Resources) error {
  32. if r.CpuRtPeriod != 0 {
  33. if err := cgroups.WriteFile(path, "cpu.rt_period_us", strconv.FormatUint(r.CpuRtPeriod, 10)); err != nil {
  34. return err
  35. }
  36. }
  37. if r.CpuRtRuntime != 0 {
  38. if err := cgroups.WriteFile(path, "cpu.rt_runtime_us", strconv.FormatInt(r.CpuRtRuntime, 10)); err != nil {
  39. return err
  40. }
  41. }
  42. return nil
  43. }
  44. func (s *CpuGroup) Set(path string, r *configs.Resources) error {
  45. if r.CpuShares != 0 {
  46. shares := r.CpuShares
  47. if err := cgroups.WriteFile(path, "cpu.shares", strconv.FormatUint(shares, 10)); err != nil {
  48. return err
  49. }
  50. // read it back
  51. sharesRead, err := fscommon.GetCgroupParamUint(path, "cpu.shares")
  52. if err != nil {
  53. return err
  54. }
  55. // ... and check
  56. if shares > sharesRead {
  57. return fmt.Errorf("the maximum allowed cpu-shares is %d", sharesRead)
  58. } else if shares < sharesRead {
  59. return fmt.Errorf("the minimum allowed cpu-shares is %d", sharesRead)
  60. }
  61. }
  62. var period string
  63. if r.CpuPeriod != 0 {
  64. period = strconv.FormatUint(r.CpuPeriod, 10)
  65. if err := cgroups.WriteFile(path, "cpu.cfs_period_us", period); err != nil {
  66. // Sometimes when the period to be set is smaller
  67. // than the current one, it is rejected by the kernel
  68. // (EINVAL) as old_quota/new_period exceeds the parent
  69. // cgroup quota limit. If this happens and the quota is
  70. // going to be set, ignore the error for now and retry
  71. // after setting the quota.
  72. if !errors.Is(err, unix.EINVAL) || r.CpuQuota == 0 {
  73. return err
  74. }
  75. } else {
  76. period = ""
  77. }
  78. }
  79. if r.CpuQuota != 0 {
  80. if err := cgroups.WriteFile(path, "cpu.cfs_quota_us", strconv.FormatInt(r.CpuQuota, 10)); err != nil {
  81. return err
  82. }
  83. if period != "" {
  84. if err := cgroups.WriteFile(path, "cpu.cfs_period_us", period); err != nil {
  85. return err
  86. }
  87. }
  88. }
  89. return s.SetRtSched(path, r)
  90. }
  91. func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error {
  92. const file = "cpu.stat"
  93. f, err := cgroups.OpenFile(path, file, os.O_RDONLY)
  94. if err != nil {
  95. if os.IsNotExist(err) {
  96. return nil
  97. }
  98. return err
  99. }
  100. defer f.Close()
  101. sc := bufio.NewScanner(f)
  102. for sc.Scan() {
  103. t, v, err := fscommon.ParseKeyValue(sc.Text())
  104. if err != nil {
  105. return &parseError{Path: path, File: file, Err: err}
  106. }
  107. switch t {
  108. case "nr_periods":
  109. stats.CpuStats.ThrottlingData.Periods = v
  110. case "nr_throttled":
  111. stats.CpuStats.ThrottlingData.ThrottledPeriods = v
  112. case "throttled_time":
  113. stats.CpuStats.ThrottlingData.ThrottledTime = v
  114. }
  115. }
  116. return nil
  117. }