Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions bcache/bcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ type BcacheStats struct { // nolint:golint

// BdevStats contains statistics for one backing device.
type BdevStats struct {
Name string
DirtyData uint64
FiveMin PeriodStats
Total PeriodStats
Name string
DirtyData uint64
FiveMin PeriodStats
Total PeriodStats
WritebackRateDebug WritebackRateDebugStats
}

// CacheStats contains statistics for one cache device.
Expand Down Expand Up @@ -82,3 +83,14 @@ type PeriodStats struct {
CacheMisses uint64
CacheReadaheads uint64
}

// WritebackRateDebugStats contains bcache writeback statistics.
type WritebackRateDebugStats struct {
Rate uint64
Dirty uint64
Target uint64
Proportional int64
Integral int64
Change int64
NextIO int64
}
109 changes: 109 additions & 0 deletions bcache/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ func dehumanize(hbytes []byte) (uint64, error) {
return res, nil
}

func dehumanizeSigned(str string) (int64, error) {
value, err := dehumanize([]byte(strings.TrimPrefix(str, "-")))
if err != nil {
return 0, err
}
if strings.HasPrefix(str, "-") {
return int64(-value), nil
}
return int64(value), nil
}

type parser struct {
uuidPath string
subDir string
Expand Down Expand Up @@ -232,6 +243,72 @@ func parsePriorityStats(line string, ps *PriorityStats) error {
return nil
}

// ParseWritebackRateDebug parses lines from the writeback_rate_debug file.
func parseWritebackRateDebug(line string, wrd *WritebackRateDebugStats) error {
switch {
case strings.HasPrefix(line, "rate:"):
fields := strings.Fields(line)
rawValue := fields[len(fields)-1]
valueStr := strings.TrimSuffix(rawValue, "/sec")
value, err := dehumanize([]byte(valueStr))
if err != nil {
return err
}
wrd.Rate = value
case strings.HasPrefix(line, "dirty:"):
fields := strings.Fields(line)
valueStr := fields[len(fields)-1]
value, err := dehumanize([]byte(valueStr))
if err != nil {
return err
}
wrd.Dirty = value
case strings.HasPrefix(line, "target:"):
fields := strings.Fields(line)
valueStr := fields[len(fields)-1]
value, err := dehumanize([]byte(valueStr))
if err != nil {
return err
}
wrd.Target = value
case strings.HasPrefix(line, "proportional:"):
fields := strings.Fields(line)
valueStr := fields[len(fields)-1]
value, err := dehumanizeSigned(valueStr)
if err != nil {
return err
}
wrd.Proportional = value
case strings.HasPrefix(line, "integral:"):
fields := strings.Fields(line)
valueStr := fields[len(fields)-1]
value, err := dehumanizeSigned(valueStr)
if err != nil {
return err
}
wrd.Integral = value
case strings.HasPrefix(line, "change:"):
fields := strings.Fields(line)
rawValue := fields[len(fields)-1]
valueStr := strings.TrimSuffix(rawValue, "/sec")
value, err := dehumanizeSigned(valueStr)
if err != nil {
return err
}
wrd.Change = value
case strings.HasPrefix(line, "next io:"):
fields := strings.Fields(line)
rawValue := fields[len(fields)-1]
valueStr := strings.TrimSuffix(rawValue, "ms")
value, err := strconv.ParseInt(valueStr, 10, 64)
if err != nil {
return err
}
wrd.NextIO = value
}
return nil
}

func (p *parser) getPriorityStats() PriorityStats {
var res PriorityStats

Expand Down Expand Up @@ -263,6 +340,35 @@ func (p *parser) getPriorityStats() PriorityStats {
return res
}

func (p *parser) getWritebackRateDebug() WritebackRateDebugStats {
var res WritebackRateDebugStats

if p.err != nil {
return res
}
path := path.Join(p.currentDir, "writeback_rate_debug")
file, err := os.Open(path)
if err != nil {
p.err = fmt.Errorf("failed to read: %s", path)
return res
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
err = parseWritebackRateDebug(scanner.Text(), &res)
if err != nil {
p.err = fmt.Errorf("failed to parse: %s (%s)", path, err)
return res
}
}
if err := scanner.Err(); err != nil {
p.err = fmt.Errorf("failed to parse: %s (%s)", path, err)
return res
}
return res
}

// GetStats collects from sysfs files data tied to one bcache ID.
func GetStats(uuidPath string, priorityStats bool) (*Stats, error) {
var bs Stats
Expand Down Expand Up @@ -339,6 +445,9 @@ func GetStats(uuidPath string, priorityStats bool) (*Stats, error) {
par.setSubDir(bds.Name)
bds.DirtyData = par.readValue("dirty_data")

wrd := par.getWritebackRateDebug()
bds.WritebackRateDebug = wrd

// dir <uuidPath>/<bds.Name>/stats_five_minute
par.setSubDir(bds.Name, "stats_five_minute")
bds.FiveMin.Bypassed = par.readValue("bypassed")
Expand Down
58 changes: 58 additions & 0 deletions bcache/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,61 @@ func TestPriorityStats(t *testing.T) {
t.Errorf("parsePriorityStats: '%s', want %d, got %d", in, want.UnusedPercent, got.UnusedPercent)
}
}

func TestWritebackRateDebug(t *testing.T) {
var want = WritebackRateDebugStats{
Rate: 1765376,
Dirty: 21789409280,
Target: 21894266880,
Proportional: -1124,
Integral: -257624,
Change: 2648,
NextIO: -150773,
}
var (
in string
gotErr error
got WritebackRateDebugStats
)
in = "rate: 1.7M/sec"
gotErr = parseWritebackRateDebug(in, &got)
if gotErr != nil || got.Rate != want.Rate {
t.Errorf("parsePriorityStats: '%s', want %d, got %d", in, want.Rate, got.Rate)
}

in = "dirty: 20.3G"
gotErr = parseWritebackRateDebug(in, &got)
if gotErr != nil || got.Dirty != want.Dirty {
t.Errorf("parsePriorityStats: '%s', want %d, got %d", in, want.Dirty, got.Dirty)
}

in = "target: 20.4G"
gotErr = parseWritebackRateDebug(in, &got)
if gotErr != nil || got.Target != want.Target {
t.Errorf("parsePriorityStats: '%s', want %d, got %d", in, want.Target, got.Target)
}

in = "proportional: -1.1k"
gotErr = parseWritebackRateDebug(in, &got)
if gotErr != nil || got.Proportional != want.Proportional {
t.Errorf("parsePriorityStats: '%s', want %d, got %d", in, want.Proportional, got.Proportional)
}

in = "integral: -251.6k"
gotErr = parseWritebackRateDebug(in, &got)
if gotErr != nil || got.Integral != want.Integral {
t.Errorf("parsePriorityStats: '%s', want %d, got %d", in, want.Integral, got.Integral)
}

in = "change: 2.6k/sec"
gotErr = parseWritebackRateDebug(in, &got)
if gotErr != nil || got.Change != want.Change {
t.Errorf("parsePriorityStats: '%s', want %d, got %d", in, want.Change, got.Change)
}

in = "next io: -150773ms"
gotErr = parseWritebackRateDebug(in, &got)
if gotErr != nil || got.NextIO != want.NextIO {
t.Errorf("parsePriorityStats: '%s', want %d, got %d", in, want.NextIO, got.NextIO)
}
}
11 changes: 11 additions & 0 deletions fixtures.ttar
Original file line number Diff line number Diff line change
Expand Up @@ -4115,6 +4115,17 @@ Lines: 1
0
Mode: 644
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/writeback_rate_debug
Lines: 7
rate: 1.1M/sec
dirty: 20.4G
target: 20.4G
proportional: 427.5k
integral: 790.0k
change: 321.5k/sec
next io: 17ms
Mode: 644
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day
Mode: 755
# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand Down