...
1 package fs
2
3 import (
4 "bufio"
5 "errors"
6 "fmt"
7 "os"
8 "strconv"
9
10 "github.com/opencontainers/runc/libcontainer/cgroups"
11 "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
12 "github.com/opencontainers/runc/libcontainer/configs"
13 "golang.org/x/sys/unix"
14 )
15
16 type CpuGroup struct{}
17
18 func (s *CpuGroup) Name() string {
19 return "cpu"
20 }
21
22 func (s *CpuGroup) Apply(path string, r *configs.Resources, pid int) error {
23 if err := os.MkdirAll(path, 0o755); err != nil {
24 return err
25 }
26
27
28
29 if err := s.SetRtSched(path, r); err != nil {
30 return err
31 }
32
33
34 return cgroups.WriteCgroupProc(path, pid)
35 }
36
37 func (s *CpuGroup) SetRtSched(path string, r *configs.Resources) error {
38 if r.CpuRtPeriod != 0 {
39 if err := cgroups.WriteFile(path, "cpu.rt_period_us", strconv.FormatUint(r.CpuRtPeriod, 10)); err != nil {
40 return err
41 }
42 }
43 if r.CpuRtRuntime != 0 {
44 if err := cgroups.WriteFile(path, "cpu.rt_runtime_us", strconv.FormatInt(r.CpuRtRuntime, 10)); err != nil {
45 return err
46 }
47 }
48 return nil
49 }
50
51 func (s *CpuGroup) Set(path string, r *configs.Resources) error {
52 if r.CpuShares != 0 {
53 shares := r.CpuShares
54 if err := cgroups.WriteFile(path, "cpu.shares", strconv.FormatUint(shares, 10)); err != nil {
55 return err
56 }
57
58 sharesRead, err := fscommon.GetCgroupParamUint(path, "cpu.shares")
59 if err != nil {
60 return err
61 }
62
63 if shares > sharesRead {
64 return fmt.Errorf("the maximum allowed cpu-shares is %d", sharesRead)
65 } else if shares < sharesRead {
66 return fmt.Errorf("the minimum allowed cpu-shares is %d", sharesRead)
67 }
68 }
69
70 var period string
71 if r.CpuPeriod != 0 {
72 period = strconv.FormatUint(r.CpuPeriod, 10)
73 if err := cgroups.WriteFile(path, "cpu.cfs_period_us", period); err != nil {
74
75
76
77
78
79
80 if !errors.Is(err, unix.EINVAL) || r.CpuQuota == 0 {
81 return err
82 }
83 } else {
84 period = ""
85 }
86 }
87 if r.CpuQuota != 0 {
88 if err := cgroups.WriteFile(path, "cpu.cfs_quota_us", strconv.FormatInt(r.CpuQuota, 10)); err != nil {
89 return err
90 }
91 if period != "" {
92 if err := cgroups.WriteFile(path, "cpu.cfs_period_us", period); err != nil {
93 return err
94 }
95 }
96 }
97 return s.SetRtSched(path, r)
98 }
99
100 func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error {
101 const file = "cpu.stat"
102 f, err := cgroups.OpenFile(path, file, os.O_RDONLY)
103 if err != nil {
104 if os.IsNotExist(err) {
105 return nil
106 }
107 return err
108 }
109 defer f.Close()
110
111 sc := bufio.NewScanner(f)
112 for sc.Scan() {
113 t, v, err := fscommon.ParseKeyValue(sc.Text())
114 if err != nil {
115 return &parseError{Path: path, File: file, Err: err}
116 }
117 switch t {
118 case "nr_periods":
119 stats.CpuStats.ThrottlingData.Periods = v
120
121 case "nr_throttled":
122 stats.CpuStats.ThrottlingData.ThrottledPeriods = v
123
124 case "throttled_time":
125 stats.CpuStats.ThrottlingData.ThrottledTime = v
126 }
127 }
128 return nil
129 }
130
View as plain text