1 package fs
2
3 import (
4 "bufio"
5 "os"
6 "path/filepath"
7 "strconv"
8 "strings"
9
10 "github.com/opencontainers/runc/libcontainer/cgroups"
11 "github.com/opencontainers/runc/libcontainer/configs"
12 )
13
14 type BlkioGroup struct {
15 weightFilename string
16 weightDeviceFilename string
17 }
18
19 func (s *BlkioGroup) Name() string {
20 return "blkio"
21 }
22
23 func (s *BlkioGroup) Apply(path string, _ *configs.Resources, pid int) error {
24 return apply(path, pid)
25 }
26
27 func (s *BlkioGroup) Set(path string, r *configs.Resources) error {
28 s.detectWeightFilenames(path)
29 if r.BlkioWeight != 0 {
30 if err := cgroups.WriteFile(path, s.weightFilename, strconv.FormatUint(uint64(r.BlkioWeight), 10)); err != nil {
31 return err
32 }
33 }
34
35 if r.BlkioLeafWeight != 0 {
36 if err := cgroups.WriteFile(path, "blkio.leaf_weight", strconv.FormatUint(uint64(r.BlkioLeafWeight), 10)); err != nil {
37 return err
38 }
39 }
40 for _, wd := range r.BlkioWeightDevice {
41 if wd.Weight != 0 {
42 if err := cgroups.WriteFile(path, s.weightDeviceFilename, wd.WeightString()); err != nil {
43 return err
44 }
45 }
46 if wd.LeafWeight != 0 {
47 if err := cgroups.WriteFile(path, "blkio.leaf_weight_device", wd.LeafWeightString()); err != nil {
48 return err
49 }
50 }
51 }
52 for _, td := range r.BlkioThrottleReadBpsDevice {
53 if err := cgroups.WriteFile(path, "blkio.throttle.read_bps_device", td.String()); err != nil {
54 return err
55 }
56 }
57 for _, td := range r.BlkioThrottleWriteBpsDevice {
58 if err := cgroups.WriteFile(path, "blkio.throttle.write_bps_device", td.String()); err != nil {
59 return err
60 }
61 }
62 for _, td := range r.BlkioThrottleReadIOPSDevice {
63 if err := cgroups.WriteFile(path, "blkio.throttle.read_iops_device", td.String()); err != nil {
64 return err
65 }
66 }
67 for _, td := range r.BlkioThrottleWriteIOPSDevice {
68 if err := cgroups.WriteFile(path, "blkio.throttle.write_iops_device", td.String()); err != nil {
69 return err
70 }
71 }
72
73 return nil
74 }
75
76
106
107 func splitBlkioStatLine(r rune) bool {
108 return r == ' ' || r == ':'
109 }
110
111 func getBlkioStat(dir, file string) ([]cgroups.BlkioStatEntry, error) {
112 var blkioStats []cgroups.BlkioStatEntry
113 f, err := cgroups.OpenFile(dir, file, os.O_RDONLY)
114 if err != nil {
115 if os.IsNotExist(err) {
116 return blkioStats, nil
117 }
118 return nil, err
119 }
120 defer f.Close()
121
122 sc := bufio.NewScanner(f)
123 for sc.Scan() {
124
125 fields := strings.FieldsFunc(sc.Text(), splitBlkioStatLine)
126 if len(fields) < 3 {
127 if len(fields) == 2 && fields[0] == "Total" {
128
129 continue
130 } else {
131 return nil, malformedLine(dir, file, sc.Text())
132 }
133 }
134
135 v, err := strconv.ParseUint(fields[0], 10, 64)
136 if err != nil {
137 return nil, &parseError{Path: dir, File: file, Err: err}
138 }
139 major := v
140
141 v, err = strconv.ParseUint(fields[1], 10, 64)
142 if err != nil {
143 return nil, &parseError{Path: dir, File: file, Err: err}
144 }
145 minor := v
146
147 op := ""
148 valueField := 2
149 if len(fields) == 4 {
150 op = fields[2]
151 valueField = 3
152 }
153 v, err = strconv.ParseUint(fields[valueField], 10, 64)
154 if err != nil {
155 return nil, &parseError{Path: dir, File: file, Err: err}
156 }
157 blkioStats = append(blkioStats, cgroups.BlkioStatEntry{Major: major, Minor: minor, Op: op, Value: v})
158 }
159 if err := sc.Err(); err != nil {
160 return nil, &parseError{Path: dir, File: file, Err: err}
161 }
162
163 return blkioStats, nil
164 }
165
166 func (s *BlkioGroup) GetStats(path string, stats *cgroups.Stats) error {
167 type blkioStatInfo struct {
168 filename string
169 blkioStatEntriesPtr *[]cgroups.BlkioStatEntry
170 }
171 bfqDebugStats := []blkioStatInfo{
172 {
173 filename: "blkio.bfq.sectors_recursive",
174 blkioStatEntriesPtr: &stats.BlkioStats.SectorsRecursive,
175 },
176 {
177 filename: "blkio.bfq.io_service_time_recursive",
178 blkioStatEntriesPtr: &stats.BlkioStats.IoServiceTimeRecursive,
179 },
180 {
181 filename: "blkio.bfq.io_wait_time_recursive",
182 blkioStatEntriesPtr: &stats.BlkioStats.IoWaitTimeRecursive,
183 },
184 {
185 filename: "blkio.bfq.io_merged_recursive",
186 blkioStatEntriesPtr: &stats.BlkioStats.IoMergedRecursive,
187 },
188 {
189 filename: "blkio.bfq.io_queued_recursive",
190 blkioStatEntriesPtr: &stats.BlkioStats.IoQueuedRecursive,
191 },
192 {
193 filename: "blkio.bfq.time_recursive",
194 blkioStatEntriesPtr: &stats.BlkioStats.IoTimeRecursive,
195 },
196 {
197 filename: "blkio.bfq.io_serviced_recursive",
198 blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
199 },
200 {
201 filename: "blkio.bfq.io_service_bytes_recursive",
202 blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
203 },
204 }
205 bfqStats := []blkioStatInfo{
206 {
207 filename: "blkio.bfq.io_serviced_recursive",
208 blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
209 },
210 {
211 filename: "blkio.bfq.io_service_bytes_recursive",
212 blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
213 },
214 }
215 cfqStats := []blkioStatInfo{
216 {
217 filename: "blkio.sectors_recursive",
218 blkioStatEntriesPtr: &stats.BlkioStats.SectorsRecursive,
219 },
220 {
221 filename: "blkio.io_service_time_recursive",
222 blkioStatEntriesPtr: &stats.BlkioStats.IoServiceTimeRecursive,
223 },
224 {
225 filename: "blkio.io_wait_time_recursive",
226 blkioStatEntriesPtr: &stats.BlkioStats.IoWaitTimeRecursive,
227 },
228 {
229 filename: "blkio.io_merged_recursive",
230 blkioStatEntriesPtr: &stats.BlkioStats.IoMergedRecursive,
231 },
232 {
233 filename: "blkio.io_queued_recursive",
234 blkioStatEntriesPtr: &stats.BlkioStats.IoQueuedRecursive,
235 },
236 {
237 filename: "blkio.time_recursive",
238 blkioStatEntriesPtr: &stats.BlkioStats.IoTimeRecursive,
239 },
240 {
241 filename: "blkio.io_serviced_recursive",
242 blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
243 },
244 {
245 filename: "blkio.io_service_bytes_recursive",
246 blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
247 },
248 }
249 throttleRecursiveStats := []blkioStatInfo{
250 {
251 filename: "blkio.throttle.io_serviced_recursive",
252 blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
253 },
254 {
255 filename: "blkio.throttle.io_service_bytes_recursive",
256 blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
257 },
258 }
259 baseStats := []blkioStatInfo{
260 {
261 filename: "blkio.throttle.io_serviced",
262 blkioStatEntriesPtr: &stats.BlkioStats.IoServicedRecursive,
263 },
264 {
265 filename: "blkio.throttle.io_service_bytes",
266 blkioStatEntriesPtr: &stats.BlkioStats.IoServiceBytesRecursive,
267 },
268 }
269 orderedStats := [][]blkioStatInfo{
270 bfqDebugStats,
271 bfqStats,
272 cfqStats,
273 throttleRecursiveStats,
274 baseStats,
275 }
276
277 var blkioStats []cgroups.BlkioStatEntry
278 var err error
279
280 for _, statGroup := range orderedStats {
281 for i, statInfo := range statGroup {
282 if blkioStats, err = getBlkioStat(path, statInfo.filename); err != nil || blkioStats == nil {
283
284 if i == 0 {
285 break
286 }
287 return err
288 }
289 *statInfo.blkioStatEntriesPtr = blkioStats
290
291 if i == len(statGroup)-1 {
292 return nil
293 }
294 }
295 }
296 return nil
297 }
298
299 func (s *BlkioGroup) detectWeightFilenames(path string) {
300 if s.weightFilename != "" {
301
302 return
303 }
304 if cgroups.PathExists(filepath.Join(path, "blkio.weight")) {
305 s.weightFilename = "blkio.weight"
306 s.weightDeviceFilename = "blkio.weight_device"
307 } else {
308 s.weightFilename = "blkio.bfq.weight"
309 s.weightDeviceFilename = "blkio.bfq.weight_device"
310 }
311 }
312
View as plain text