blkio.go 7.8 KB

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