github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/cgroups/fs/blkio.go (about) 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 /* 77 examples: 78 79 blkio.sectors 80 8:0 6792 81 82 blkio.io_service_bytes 83 8:0 Read 1282048 84 8:0 Write 2195456 85 8:0 Sync 2195456 86 8:0 Async 1282048 87 8:0 Total 3477504 88 Total 3477504 89 90 blkio.io_serviced 91 8:0 Read 124 92 8:0 Write 104 93 8:0 Sync 104 94 8:0 Async 124 95 8:0 Total 228 96 Total 228 97 98 blkio.io_queued 99 8:0 Read 0 100 8:0 Write 0 101 8:0 Sync 0 102 8:0 Async 0 103 8:0 Total 0 104 Total 0 105 */ 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 // format: dev type amount 125 fields := strings.FieldsFunc(sc.Text(), splitBlkioStatLine) 126 if len(fields) < 3 { 127 if len(fields) == 2 && fields[0] == "Total" { 128 // skip total line 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 // if error occurs on first file, move to next group 284 if i == 0 { 285 break 286 } 287 return err 288 } 289 *statInfo.blkioStatEntriesPtr = blkioStats 290 // finish if all stats are gathered 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 // Already detected. 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 }