Collapse errors into lost, corrupt, link and internal

This commit is contained in:
flamingcow
2026-07-31 17:10:25 -07:00
parent 3c8637f931
commit de2ff13eb6
2 changed files with 29 additions and 30 deletions
+27 -25
View File
@@ -94,24 +94,25 @@ func (d *direction) capture(t time.Time) counterSet {
return counterSet{t: t, s: d.snapshot(), drops: d.drops, nic: d.nic.Load()} return counterSet{t: t, s: d.snapshot(), drops: d.drops, nic: d.nic.Load()}
} }
// Late is counted but left out of the total, since a reordered frame arrived. // What someone testing a cable is asking, rather than how each failure happened
// to be noticed. Corruption arrives as three different symptoms and the kernel
// drops frames for reasons that are ours rather than the cable's, but none of
// that is a distinction worth reading off a panel.
type errs struct { type errs struct {
lost, late uint64 lost uint64
crc, badMagic uint64 corrupt uint64
badLen uint64 link uint64
kdrop, link uint64 internal uint64
} }
func (e errs) total() uint64 { func (e errs) total() uint64 {
return e.lost + e.crc + e.badMagic + e.badLen + e.kdrop + e.link return e.lost + e.corrupt + e.link + e.internal
} }
func (e errs) add(o errs) errs { func (e errs) add(o errs) errs {
return errs{ return errs{
lost: e.lost + o.lost, late: e.late + o.late, lost: e.lost + o.lost, corrupt: e.corrupt + o.corrupt,
crc: e.crc + o.crc, badMagic: e.badMagic + o.badMagic, link: e.link + o.link, internal: e.internal + o.internal,
badLen: e.badLen + o.badLen,
kdrop: e.kdrop + o.kdrop, link: e.link + o.link,
} }
} }
@@ -309,11 +310,9 @@ var intervalCols = []colSpec{
{title: "RX pps", width: 9, right: true}, {title: "RX pps", width: 9, right: true},
{title: "RX Gb/s", width: 7, right: true}, {title: "RX Gb/s", width: 7, right: true},
{title: "LOST", width: 11, right: true}, {title: "LOST", width: 11, right: true},
{title: "LATE", width: 9, right: true}, {title: "CORRUPT", width: 11, right: true},
{title: "CRC", width: 7, right: true}, {title: "LINK", width: 11, right: true},
{title: "BADMAG", width: 7, right: true}, {title: "INTERNAL", width: 11, right: true},
{title: "KDROP", width: 11, right: true},
{title: "LINK", width: 13, right: true},
{title: "ERRORS", width: 13, right: true}, {title: "ERRORS", width: 13, right: true},
{title: "MIN ns", width: 9, right: true}, {title: "MIN ns", width: 9, right: true},
{title: "LEN m", width: 6, right: true}, {title: "LEN m", width: 6, right: true},
@@ -332,16 +331,21 @@ type view struct {
func errsBetween(b, n counterSet) errs { func errsBetween(b, n counterSet) errs {
return errs{ return errs{
lost: n.s.lost - b.s.lost, lost: n.s.lost - b.s.lost,
late: n.s.late - b.s.late, // Three ways of noticing one thing: a payload that does not match its
crc: n.s.crcErr - b.s.crcErr, // checksum, a header that is not ours, and a length that cannot be.
badMagic: n.s.badMagic - b.s.badMagic, corrupt: (n.s.crcErr - b.s.crcErr) + (n.s.badMagic - b.s.badMagic) +
badLen: n.s.badLen - b.s.badLen, (n.s.badLen - b.s.badLen),
kdrop: n.drops - b.drops,
// A frame the stack refused and a frame the driver dropped are the same // A frame the stack refused and a frame the driver dropped are the same
// failure seen from either side of the ring, and never the same frame // failure seen from either side of the ring, and never the same frame
// twice: a send that fails never reaches the driver to be dropped. // twice: a send that fails never reaches the driver to be dropped.
link: (n.nic - b.nic) + (n.s.txErrs - b.s.txErrs) + (n.s.rxErrs - b.s.rxErrs), link: (n.nic - b.nic) + (n.s.txErrs - b.s.txErrs) + (n.s.rxErrs - b.s.rxErrs),
// Ours rather than the cable's: frames the kernel threw away because we
// did not drain the ring fast enough, and frames so far out of order
// that our own bookkeeping had already written them off. The second is
// unreachable while each stream has a flow rule to its own queue, which
// is exactly why it is worth counting: it fires if that stops holding.
internal: (n.drops - b.drops) + (n.s.late - b.s.late),
} }
} }
@@ -433,11 +437,9 @@ func (d *direction) row(elapsed time.Duration, v view, target float64, length st
commas(uint64(v.rxPPS)), commas(uint64(v.rxPPS)),
rateCell(v.rxGbps, target), rateCell(v.rxGbps, target),
statusCell(v.since.lost), statusCell(v.since.lost),
statusCell(v.since.late), statusCell(v.since.corrupt),
statusCell(v.since.crc),
statusCell(v.since.badMagic),
statusCell(v.since.kdrop),
statusCell(v.since.link), statusCell(v.since.link),
statusCell(v.since.internal),
statusCell(v.since.total()), statusCell(v.since.total()),
paint(v.cable.minText(), cCyan), paint(v.cable.minText(), cCyan),
paint(length, cCyan), paint(length, cCyan),
+2 -5
View File
@@ -195,12 +195,9 @@ var errRows = []struct {
get func(errs) uint64 get func(errs) uint64
}{ }{
{"lost", func(e errs) uint64 { return e.lost }}, {"lost", func(e errs) uint64 { return e.lost }},
{"late", func(e errs) uint64 { return e.late }}, {"corrupt", func(e errs) uint64 { return e.corrupt }},
{"crc", func(e errs) uint64 { return e.crc }},
{"bad magic", func(e errs) uint64 { return e.badMagic }},
{"bad length", func(e errs) uint64 { return e.badLen }},
{"kernel drops", func(e errs) uint64 { return e.kdrop }},
{"link", func(e errs) uint64 { return e.link }}, {"link", func(e errs) uint64 { return e.link }},
{"internal", func(e errs) uint64 { return e.internal }},
} }
func (d *display) errBlock(x, w, y int, e errs) int { func (d *display) errBlock(x, w, y int, e errs) int {