diff --git a/cgroups/fs/blkio.go b/cgroups/fs/blkio.go index f784d011..261a97ff 100644 --- a/cgroups/fs/blkio.go +++ b/cgroups/fs/blkio.go @@ -115,6 +115,14 @@ func getBlkioStat(path string) ([]cgroups.BlkioStatEntry, error) { } func (s *BlkioGroup) GetStats(path string, stats *cgroups.Stats) error { + // Try to read CFQ stats available on all CFQ enabled kernels first + if blkioStats, err := getBlkioStat(filepath.Join(path, "blkio.io_serviced_recursive")); err == nil && blkioStats != nil { + return getCFQStats(path, stats) + } + return getStats(path, stats) // Use generic stats as fallback +} + +func getCFQStats(path string, stats *cgroups.Stats) error { var blkioStats []cgroups.BlkioStatEntry var err error @@ -140,3 +148,20 @@ func (s *BlkioGroup) GetStats(path string, stats *cgroups.Stats) error { return nil } + +func getStats(path string, stats *cgroups.Stats) error { + var blkioStats []cgroups.BlkioStatEntry + var err error + + if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.throttle.io_service_bytes")); err != nil { + return err + } + stats.BlkioStats.IoServiceBytesRecursive = blkioStats + + if blkioStats, err = getBlkioStat(filepath.Join(path, "blkio.throttle.io_serviced")); err != nil { + return err + } + stats.BlkioStats.IoServicedRecursive = blkioStats + + return nil +} diff --git a/cgroups/fs/blkio_test.go b/cgroups/fs/blkio_test.go index db6f8d52..2a79d260 100644 --- a/cgroups/fs/blkio_test.go +++ b/cgroups/fs/blkio_test.go @@ -26,10 +26,30 @@ Total 50` 8:0 Async 3 8:0 Total 5 Total 5` + throttleServiceBytes = `8:0 Read 11030528 +8:0 Write 23 +8:0 Sync 42 +8:0 Async 11030528 +8:0 Total 11030528 +252:0 Read 11030528 +252:0 Write 23 +252:0 Sync 42 +252:0 Async 11030528 +252:0 Total 11030528 +Total 22061056` + throttleServiced = `8:0 Read 164 +8:0 Write 23 +8:0 Sync 42 +8:0 Async 164 +8:0 Total 164 +252:0 Read 164 +252:0 Write 23 +252:0 Sync 42 +252:0 Async 164 +252:0 Total 164 +Total 328` ) -var actualStats = *cgroups.NewStats() - func appendBlkioStatEntry(blkioStatEntries *[]cgroups.BlkioStatEntry, major, minor, value uint64, op string) { *blkioStatEntries = append(*blkioStatEntries, cgroups.BlkioStatEntry{Major: major, Minor: minor, Value: value, Op: op}) } @@ -45,6 +65,7 @@ func TestBlkioStats(t *testing.T) { }) blkio := &BlkioGroup{} + actualStats := *cgroups.NewStats() err := blkio.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatal(err) @@ -85,6 +106,7 @@ func TestBlkioStatsNoSectorsFile(t *testing.T) { }) blkio := &BlkioGroup{} + actualStats := *cgroups.NewStats() err := blkio.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatalf("Failed unexpectedly: %s", err) @@ -101,6 +123,7 @@ func TestBlkioStatsNoServiceBytesFile(t *testing.T) { }) blkio := &BlkioGroup{} + actualStats := *cgroups.NewStats() err := blkio.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatalf("Failed unexpectedly: %s", err) @@ -117,6 +140,7 @@ func TestBlkioStatsNoServicedFile(t *testing.T) { }) blkio := &BlkioGroup{} + actualStats := *cgroups.NewStats() err := blkio.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatalf("Failed unexpectedly: %s", err) @@ -133,6 +157,7 @@ func TestBlkioStatsNoQueuedFile(t *testing.T) { }) blkio := &BlkioGroup{} + actualStats := *cgroups.NewStats() err := blkio.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatalf("Failed unexpectedly: %s", err) @@ -150,6 +175,7 @@ func TestBlkioStatsUnexpectedNumberOfFields(t *testing.T) { }) blkio := &BlkioGroup{} + actualStats := *cgroups.NewStats() err := blkio.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected to fail, but did not") @@ -167,8 +193,56 @@ func TestBlkioStatsUnexpectedFieldType(t *testing.T) { }) blkio := &BlkioGroup{} + actualStats := *cgroups.NewStats() err := blkio.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected to fail, but did not") } } + +func TestNonCFQBlkioStats(t *testing.T) { + helper := NewCgroupTestUtil("blkio", t) + defer helper.cleanup() + helper.writeFileContents(map[string]string{ + "blkio.io_service_bytes_recursive": "", + "blkio.io_serviced_recursive": "", + "blkio.io_queued_recursive": "", + "blkio.sectors_recursive": "", + "blkio.throttle.io_service_bytes": throttleServiceBytes, + "blkio.throttle.io_serviced": throttleServiced, + }) + + blkio := &BlkioGroup{} + actualStats := *cgroups.NewStats() + err := blkio.GetStats(helper.CgroupPath, &actualStats) + if err != nil { + t.Fatal(err) + } + + // Verify expected stats. + expectedStats := cgroups.BlkioStats{} + + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 11030528, "Read") + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 23, "Write") + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 42, "Sync") + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 11030528, "Async") + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 8, 0, 11030528, "Total") + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 11030528, "Read") + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 23, "Write") + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 42, "Sync") + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 11030528, "Async") + appendBlkioStatEntry(&expectedStats.IoServiceBytesRecursive, 252, 0, 11030528, "Total") + + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 164, "Read") + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 23, "Write") + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 42, "Sync") + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 164, "Async") + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 8, 0, 164, "Total") + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 164, "Read") + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 23, "Write") + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 42, "Sync") + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 164, "Async") + appendBlkioStatEntry(&expectedStats.IoServicedRecursive, 252, 0, 164, "Total") + + expectBlkioStatsEquals(t, expectedStats, actualStats.BlkioStats) +} diff --git a/cgroups/fs/cpu_test.go b/cgroups/fs/cpu_test.go index 017a1f4b..2470e689 100644 --- a/cgroups/fs/cpu_test.go +++ b/cgroups/fs/cpu_test.go @@ -24,6 +24,7 @@ func TestCpuStats(t *testing.T) { }) cpu := &CpuGroup{} + actualStats := *cgroups.NewStats() err := cpu.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatal(err) @@ -42,6 +43,7 @@ func TestNoCpuStatFile(t *testing.T) { defer helper.cleanup() cpu := &CpuGroup{} + actualStats := *cgroups.NewStats() err := cpu.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatal("Expected not to fail, but did") @@ -59,6 +61,7 @@ func TestInvalidCpuStat(t *testing.T) { }) cpu := &CpuGroup{} + actualStats := *cgroups.NewStats() err := cpu.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failed stat parsing.") diff --git a/cgroups/fs/memory_test.go b/cgroups/fs/memory_test.go index e92f1daf..a21cec75 100644 --- a/cgroups/fs/memory_test.go +++ b/cgroups/fs/memory_test.go @@ -25,6 +25,7 @@ func TestMemoryStats(t *testing.T) { }) memory := &MemoryGroup{} + actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatal(err) @@ -42,6 +43,7 @@ func TestMemoryStatsNoStatFile(t *testing.T) { }) memory := &MemoryGroup{} + actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err != nil { t.Fatal(err) @@ -57,6 +59,7 @@ func TestMemoryStatsNoUsageFile(t *testing.T) { }) memory := &MemoryGroup{} + actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") @@ -72,6 +75,7 @@ func TestMemoryStatsNoMaxUsageFile(t *testing.T) { }) memory := &MemoryGroup{} + actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") @@ -88,6 +92,7 @@ func TestMemoryStatsBadStatFile(t *testing.T) { }) memory := &MemoryGroup{} + actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") @@ -104,6 +109,7 @@ func TestMemoryStatsBadUsageFile(t *testing.T) { }) memory := &MemoryGroup{} + actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure") @@ -120,6 +126,7 @@ func TestMemoryStatsBadMaxUsageFile(t *testing.T) { }) memory := &MemoryGroup{} + actualStats := *cgroups.NewStats() err := memory.GetStats(helper.CgroupPath, &actualStats) if err == nil { t.Fatal("Expected failure")