mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-10 21:53:57 +08:00
Merge pull request #2968 from cyphar/cgroup2-io-stats
cgroup2: map io.stats to v1 blkio.stats correctly
This commit is contained in:
@@ -8,6 +8,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
|
"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
@@ -88,22 +90,22 @@ func readCgroup2MapFile(dirPath string, name string) (map[string][]string, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func statIo(dirPath string, stats *cgroups.Stats) error {
|
func statIo(dirPath string, stats *cgroups.Stats) error {
|
||||||
// more details on the io.stat file format: https://www.kernel.org/doc/Documentation/cgroup-v2.txt
|
|
||||||
var ioServiceBytesRecursive []cgroups.BlkioStatEntry
|
|
||||||
values, err := readCgroup2MapFile(dirPath, "io.stat")
|
values, err := readCgroup2MapFile(dirPath, "io.stat")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// more details on the io.stat file format: https://www.kernel.org/doc/Documentation/cgroup-v2.txt
|
||||||
|
var parsedStats cgroups.BlkioStats
|
||||||
for k, v := range values {
|
for k, v := range values {
|
||||||
d := strings.Split(k, ":")
|
d := strings.Split(k, ":")
|
||||||
if len(d) != 2 {
|
if len(d) != 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
major, err := strconv.ParseUint(d[0], 10, 0)
|
major, err := strconv.ParseUint(d[0], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
minor, err := strconv.ParseUint(d[1], 10, 0)
|
minor, err := strconv.ParseUint(d[1], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -115,15 +117,32 @@ func statIo(dirPath string, stats *cgroups.Stats) error {
|
|||||||
}
|
}
|
||||||
op := d[0]
|
op := d[0]
|
||||||
|
|
||||||
// Accommodate the cgroup v1 naming
|
// Map to the cgroupv1 naming and layout (in separate tables).
|
||||||
|
var targetTable *[]cgroups.BlkioStatEntry
|
||||||
switch op {
|
switch op {
|
||||||
|
// Equivalent to cgroupv1's blkio.io_service_bytes.
|
||||||
case "rbytes":
|
case "rbytes":
|
||||||
op = "Read"
|
op = "Read"
|
||||||
|
targetTable = &parsedStats.IoServiceBytesRecursive
|
||||||
case "wbytes":
|
case "wbytes":
|
||||||
op = "Write"
|
op = "Write"
|
||||||
|
targetTable = &parsedStats.IoServiceBytesRecursive
|
||||||
|
// Equivalent to cgroupv1's blkio.io_serviced.
|
||||||
|
case "rios":
|
||||||
|
op = "Read"
|
||||||
|
targetTable = &parsedStats.IoServicedRecursive
|
||||||
|
case "wios":
|
||||||
|
op = "Write"
|
||||||
|
targetTable = &parsedStats.IoServicedRecursive
|
||||||
|
default:
|
||||||
|
// Skip over entries we cannot map to cgroupv1 stats for now.
|
||||||
|
// In the future we should expand the stats struct to include
|
||||||
|
// them.
|
||||||
|
logrus.Debugf("cgroupv2 io stats: skipping over unmappable %s entry", item)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := strconv.ParseUint(d[1], 10, 0)
|
value, err := strconv.ParseUint(d[1], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -134,9 +153,9 @@ func statIo(dirPath string, stats *cgroups.Stats) error {
|
|||||||
Minor: minor,
|
Minor: minor,
|
||||||
Value: value,
|
Value: value,
|
||||||
}
|
}
|
||||||
ioServiceBytesRecursive = append(ioServiceBytesRecursive, entry)
|
*targetTable = append(*targetTable, entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stats.BlkioStats = cgroups.BlkioStats{IoServiceBytesRecursive: ioServiceBytesRecursive}
|
stats.BlkioStats = parsedStats
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package fs2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
|
||||||
|
)
|
||||||
|
|
||||||
|
const exampleIoStatData = `254:1 rbytes=6901432320 wbytes=14245535744 rios=263278 wios=248603 dbytes=0 dios=0
|
||||||
|
254:0 rbytes=2702336 wbytes=0 rios=97 wios=0 dbytes=0 dios=0
|
||||||
|
259:0 rbytes=6911345664 wbytes=14245536256 rios=264538 wios=244914 dbytes=530485248 dios=2`
|
||||||
|
|
||||||
|
var exampleIoStatsParsed = cgroups.BlkioStats{
|
||||||
|
IoServiceBytesRecursive: []cgroups.BlkioStatEntry{
|
||||||
|
{Major: 254, Minor: 1, Value: 6901432320, Op: "Read"},
|
||||||
|
{Major: 254, Minor: 1, Value: 14245535744, Op: "Write"},
|
||||||
|
{Major: 254, Minor: 0, Value: 2702336, Op: "Read"},
|
||||||
|
{Major: 254, Minor: 0, Value: 0, Op: "Write"},
|
||||||
|
{Major: 259, Minor: 0, Value: 6911345664, Op: "Read"},
|
||||||
|
{Major: 259, Minor: 0, Value: 14245536256, Op: "Write"},
|
||||||
|
},
|
||||||
|
IoServicedRecursive: []cgroups.BlkioStatEntry{
|
||||||
|
{Major: 254, Minor: 1, Value: 263278, Op: "Read"},
|
||||||
|
{Major: 254, Minor: 1, Value: 248603, Op: "Write"},
|
||||||
|
{Major: 254, Minor: 0, Value: 97, Op: "Read"},
|
||||||
|
{Major: 254, Minor: 0, Value: 0, Op: "Write"},
|
||||||
|
{Major: 259, Minor: 0, Value: 264538, Op: "Read"},
|
||||||
|
{Major: 259, Minor: 0, Value: 244914, Op: "Write"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func lessBlkioStatEntry(a, b cgroups.BlkioStatEntry) bool {
|
||||||
|
if a.Major != b.Major {
|
||||||
|
return a.Major < b.Major
|
||||||
|
}
|
||||||
|
if a.Minor != b.Minor {
|
||||||
|
return a.Minor < b.Minor
|
||||||
|
}
|
||||||
|
if a.Op != b.Op {
|
||||||
|
return a.Op < b.Op
|
||||||
|
}
|
||||||
|
return a.Value < b.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortBlkioStats(stats *cgroups.BlkioStats) {
|
||||||
|
for _, table := range []*[]cgroups.BlkioStatEntry{
|
||||||
|
&stats.IoServicedRecursive,
|
||||||
|
&stats.IoServiceBytesRecursive,
|
||||||
|
} {
|
||||||
|
sort.SliceStable(*table, func(i, j int) bool { return lessBlkioStatEntry((*table)[i], (*table)[j]) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatIo(t *testing.T) {
|
||||||
|
// We're using a fake cgroupfs.
|
||||||
|
fscommon.TestMode = true
|
||||||
|
|
||||||
|
fakeCgroupDir, err := ioutil.TempDir("", "runc-stat-io-test.*")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(fakeCgroupDir)
|
||||||
|
statPath := filepath.Join(fakeCgroupDir, "io.stat")
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(statPath, []byte(exampleIoStatData), 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var gotStats cgroups.Stats
|
||||||
|
if err := statIo(fakeCgroupDir, &gotStats); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the output since statIo uses a map internally.
|
||||||
|
sortBlkioStats(&gotStats.BlkioStats)
|
||||||
|
sortBlkioStats(&exampleIoStatsParsed)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(gotStats.BlkioStats, exampleIoStatsParsed) {
|
||||||
|
t.Errorf("parsed cgroupv2 io.stat doesn't match expected result: \ngot %#v\nexpected %#v\n", gotStats.BlkioStats, exampleIoStatsParsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user