Restructure stats into delta, mask, and aggregate layers

This commit is contained in:
flamingcow
2026-08-31 09:35:32 -07:00
parent ea272e489c
commit 06fa759b1a
11 changed files with 316 additions and 364 deletions
+24 -22
View File
@@ -112,9 +112,11 @@ const (
snrWarnMargin = 1.0
)
// While masked a measure owns the module: every register it would poll is the
// measure's own disturbance, so nothing is read at all.
type phyModule struct {
dev phyDev
busy atomic.Bool
dev phyDev
masked atomic.Bool
mu sync.Mutex
sampled bool
@@ -131,9 +133,8 @@ type phyModule struct {
notes []string
}
// Silent while a measure owns the module.
func (m *phyModule) poll() error {
if m.busy.Load() {
func (m *phyModule) poll(hist *history) error {
if m.masked.Load() {
return nil
}
link, linkRaw, err := devLinkUp(m.dev)
@@ -156,11 +157,11 @@ func (m *phyModule) poll() error {
return err
}
// Setting busy is a measure's first act, so busy still clear here proves
// every read above finished before the measure disturbed anything; a
// measure that began mid-read makes the samples ambiguous, and forgive()
// Setting masked is a measure's first act, so masked still clear here
// proves every read above finished before the measure disturbed anything;
// a measure that began mid-read makes the samples ambiguous, and forgive()
// re-baselines after it anyway.
if m.busy.Load() {
if m.masked.Load() {
return nil
}
@@ -188,6 +189,7 @@ func (m *phyModule) poll() error {
m.ber += ber
m.retrains += rt
m.recentDelta = delta
hist.addCorrected(time.Now(), delta)
} else {
m.recentDelta = 0
m.primed = true
@@ -205,7 +207,7 @@ func (m *phyModule) takeNotes() []string {
return n
}
func (m *phyModule) run(done *atomic.Bool) {
func (m *phyModule) run(done *atomic.Bool, hist *history) {
tick := time.NewTicker(phyInterval)
defer tick.Stop()
@@ -213,7 +215,7 @@ func (m *phyModule) run(done *atomic.Bool) {
var lastErr error
for !done.Load() {
<-tick.C
if err := m.poll(); err != nil {
if err := m.poll(hist); err != nil {
dark++
lastErr = err
if dark >= phyMaxDark {
@@ -437,12 +439,12 @@ func bcmEnd(mods []*phyModule) *bcm {
// relink, so the MDI resolution is the fresh one.
func measureCable(mods []*phyModule, done *atomic.Bool) (cableInfo, error) {
for _, m := range mods {
m.busy.Store(true)
m.masked.Store(true)
}
defer func() {
for _, m := range mods {
m.forgive()
m.busy.Store(false)
m.masked.Store(false)
}
}()
@@ -486,10 +488,10 @@ func measureCable(mods []*phyModule, done *atomic.Bool) (cableInfo, error) {
type cableDiag struct {
mods []*phyModule
// While set, every failure counter is suppressed at its source: the
// measure's own link blip is never counted anywhere, rather than counted,
// hidden and reverted.
measuring atomic.Bool
// The mask itself. While set, every fault delta is dropped at the mask
// gate before it can reach an aggregate: the measure's own link blip is
// never counted anywhere, rather than counted, hidden and reverted.
masked atomic.Bool
mu sync.Mutex
info cableInfo
@@ -503,7 +505,7 @@ func newCableDiag(mods []*phyModule, info cableInfo) *cableDiag {
func (c *cableDiag) snapshot() (cableInfo, cableLengths, bool) {
c.mu.Lock()
defer c.mu.Unlock()
return c.info, c.lengths, c.measuring.Load()
return c.info, c.lengths, c.masked.Load()
}
func (c *cableDiag) resetLengths() {
@@ -516,7 +518,7 @@ func (c *cableDiag) resetLengths() {
// tester that cannot run its diagnostics must not keep testing: the failure is
// fatal, and the reboot's driver reload is also the wedged-µC recovery.
func (c *cableDiag) kick(done *atomic.Bool) bool {
if !c.measuring.CompareAndSwap(false, true) {
if !c.masked.CompareAndSwap(false, true) {
return false
}
go func() {
@@ -533,7 +535,7 @@ func (c *cableDiag) kick(done *atomic.Bool) bool {
}
}
c.mu.Unlock()
c.measuring.Store(false)
c.masked.Store(false)
}()
return true
}
@@ -575,9 +577,9 @@ func openModules(names [2]string) ([]*phyModule, [2]string, error) {
time.Sleep(moduleUpPoll)
}
m := &phyModule{dev: dev}
// Born busy: the pollers stay silent through bringup's SETs and
// Born masked: the pollers stay silent through bringup's SETs and
// retrains until the first measure completes and lifts the gate.
m.busy.Store(true)
m.masked.Store(true)
mods = append(mods, m)
}
return mods, idents, nil