1 package fs2
2
3 import (
4 "bufio"
5 "bytes"
6 "fmt"
7 "os"
8 "strconv"
9 "strings"
10
11 "github.com/sirupsen/logrus"
12
13 "github.com/opencontainers/runc/libcontainer/cgroups"
14 "github.com/opencontainers/runc/libcontainer/configs"
15 )
16
17 func isIoSet(r *configs.Resources) bool {
18 return r.BlkioWeight != 0 ||
19 len(r.BlkioWeightDevice) > 0 ||
20 len(r.BlkioThrottleReadBpsDevice) > 0 ||
21 len(r.BlkioThrottleWriteBpsDevice) > 0 ||
22 len(r.BlkioThrottleReadIOPSDevice) > 0 ||
23 len(r.BlkioThrottleWriteIOPSDevice) > 0
24 }
25
26
27
28 func bfqDeviceWeightSupported(bfq *os.File) bool {
29 if bfq == nil {
30 return false
31 }
32 _, _ = bfq.Seek(0, 0)
33 buf := make([]byte, 32)
34 _, _ = bfq.Read(buf)
35
36 _, err := strconv.ParseInt(string(bytes.TrimSpace(buf)), 10, 64)
37 return err != nil
38 }
39
40 func setIo(dirPath string, r *configs.Resources) error {
41 if !isIoSet(r) {
42 return nil
43 }
44
45
46 var bfq *os.File
47 if r.BlkioWeight != 0 || len(r.BlkioWeightDevice) > 0 {
48 var err error
49 bfq, err = cgroups.OpenFile(dirPath, "io.bfq.weight", os.O_RDWR)
50 if err == nil {
51 defer bfq.Close()
52 } else if !os.IsNotExist(err) {
53 return err
54 }
55 }
56
57 if r.BlkioWeight != 0 {
58 if bfq != nil {
59 if _, err := bfq.WriteString(strconv.FormatUint(uint64(r.BlkioWeight), 10)); err != nil {
60 return err
61 }
62 } else {
63
64 v := cgroups.ConvertBlkIOToIOWeightValue(r.BlkioWeight)
65 if err := cgroups.WriteFile(dirPath, "io.weight", strconv.FormatUint(v, 10)); err != nil {
66 return err
67 }
68 }
69 }
70 if bfqDeviceWeightSupported(bfq) {
71 for _, wd := range r.BlkioWeightDevice {
72 if _, err := bfq.WriteString(wd.WeightString() + "\n"); err != nil {
73 return fmt.Errorf("setting device weight %q: %w", wd.WeightString(), err)
74 }
75 }
76 }
77 for _, td := range r.BlkioThrottleReadBpsDevice {
78 if err := cgroups.WriteFile(dirPath, "io.max", td.StringName("rbps")); err != nil {
79 return err
80 }
81 }
82 for _, td := range r.BlkioThrottleWriteBpsDevice {
83 if err := cgroups.WriteFile(dirPath, "io.max", td.StringName("wbps")); err != nil {
84 return err
85 }
86 }
87 for _, td := range r.BlkioThrottleReadIOPSDevice {
88 if err := cgroups.WriteFile(dirPath, "io.max", td.StringName("riops")); err != nil {
89 return err
90 }
91 }
92 for _, td := range r.BlkioThrottleWriteIOPSDevice {
93 if err := cgroups.WriteFile(dirPath, "io.max", td.StringName("wiops")); err != nil {
94 return err
95 }
96 }
97
98 return nil
99 }
100
101 func readCgroup2MapFile(dirPath string, name string) (map[string][]string, error) {
102 ret := map[string][]string{}
103 f, err := cgroups.OpenFile(dirPath, name, os.O_RDONLY)
104 if err != nil {
105 return nil, err
106 }
107 defer f.Close()
108 scanner := bufio.NewScanner(f)
109 for scanner.Scan() {
110 line := scanner.Text()
111 parts := strings.Fields(line)
112 if len(parts) < 2 {
113 continue
114 }
115 ret[parts[0]] = parts[1:]
116 }
117 if err := scanner.Err(); err != nil {
118 return nil, &parseError{Path: dirPath, File: name, Err: err}
119 }
120 return ret, nil
121 }
122
123 func statIo(dirPath string, stats *cgroups.Stats) error {
124 const file = "io.stat"
125 values, err := readCgroup2MapFile(dirPath, file)
126 if err != nil {
127 return err
128 }
129
130 var parsedStats cgroups.BlkioStats
131 for k, v := range values {
132 d := strings.Split(k, ":")
133 if len(d) != 2 {
134 continue
135 }
136 major, err := strconv.ParseUint(d[0], 10, 64)
137 if err != nil {
138 return &parseError{Path: dirPath, File: file, Err: err}
139 }
140 minor, err := strconv.ParseUint(d[1], 10, 64)
141 if err != nil {
142 return &parseError{Path: dirPath, File: file, Err: err}
143 }
144
145 for _, item := range v {
146 d := strings.Split(item, "=")
147 if len(d) != 2 {
148 continue
149 }
150 op := d[0]
151
152
153 var targetTable *[]cgroups.BlkioStatEntry
154 switch op {
155
156 case "rbytes":
157 op = "Read"
158 targetTable = &parsedStats.IoServiceBytesRecursive
159 case "wbytes":
160 op = "Write"
161 targetTable = &parsedStats.IoServiceBytesRecursive
162
163 case "rios":
164 op = "Read"
165 targetTable = &parsedStats.IoServicedRecursive
166 case "wios":
167 op = "Write"
168 targetTable = &parsedStats.IoServicedRecursive
169 default:
170
171
172
173 logrus.Debugf("cgroupv2 io stats: skipping over unmappable %s entry", item)
174 continue
175 }
176
177 value, err := strconv.ParseUint(d[1], 10, 64)
178 if err != nil {
179 return &parseError{Path: dirPath, File: file, Err: err}
180 }
181
182 entry := cgroups.BlkioStatEntry{
183 Op: op,
184 Major: major,
185 Minor: minor,
186 Value: value,
187 }
188 *targetTable = append(*targetTable, entry)
189 }
190 }
191 stats.BlkioStats = parsedStats
192 return nil
193 }
194
View as plain text