Windowed module protocol: every op admitted inside a 3.4s window after the firmware's observed temp poll (cadence 3.5-4.2s measured, stuck-at-last-fetch stale mechanism proven and avoided), acquire/window with panic on dead heartbeat; noise column shows green on/off cycle phase, cable-missing state unchanged
This commit is contained in:
@@ -38,9 +38,16 @@ const (
|
||||
bcmPHYIDHi = 0x3590
|
||||
bcmPHYIDLo = 0x5081
|
||||
|
||||
bcmCmdResidentTemp uint16 = 0x0031
|
||||
|
||||
bcmReadDelayUs = 3000
|
||||
bcmRetryDelayUs = 10000
|
||||
|
||||
bcmWindow = 3400 * time.Millisecond
|
||||
bcmWindowFit = 100 * time.Millisecond
|
||||
bcmFlipPoll = 10 * time.Millisecond
|
||||
bcmFlipWait = 5 * time.Second
|
||||
|
||||
bcmStatusPoll = 100 * time.Millisecond
|
||||
// Covers the handler's documented 2 s freeze during 10GBASE-T training.
|
||||
bcmStatusTimeout = 3 * time.Second
|
||||
@@ -68,7 +75,35 @@ type bcm struct {
|
||||
|
||||
// Every method holds it for its whole logical operation: exactly one
|
||||
// host-side conversation with the module at a time, by construction.
|
||||
mu sync.Mutex
|
||||
mu sync.Mutex
|
||||
windowEnd time.Time
|
||||
windowed atomic.Bool
|
||||
}
|
||||
|
||||
// The firmware's internal temp poll (every 3.5–4.2 s, never under 3.49) serves
|
||||
// stale bridge reads for ~50 ms around it; work stays inside 3.4 s post-poll.
|
||||
func (b *bcm) window() {
|
||||
if !b.windowed.Load() || time.Now().Add(bcmWindowFit).Before(b.windowEnd) {
|
||||
return
|
||||
}
|
||||
deadline := time.Now().Add(bcmFlipWait)
|
||||
for {
|
||||
v, err := b.mdioRead(bcmMMDVendor, bcmRegCmd)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("%s: heartbeat poll: %v", b.ifname, err))
|
||||
}
|
||||
if v == bcmCmdResidentTemp || time.Now().After(deadline) {
|
||||
b.windowEnd = time.Now().Add(bcmWindow)
|
||||
return
|
||||
}
|
||||
time.Sleep(bcmFlipPoll)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bcm) acquire() func() {
|
||||
b.mu.Lock()
|
||||
b.window()
|
||||
return b.mu.Unlock
|
||||
}
|
||||
|
||||
func openBCM(ifname string) (*bcm, error) {
|
||||
@@ -197,8 +232,7 @@ func (b *bcm) waitStatus(want func(uint16) bool) (uint16, error) {
|
||||
// executing as a no-op); SETs must pass their full parameter set (the handler
|
||||
// executes stale DATA).
|
||||
func (b *bcm) command(code uint16, params ...uint16) ([5]uint16, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
defer b.acquire()()
|
||||
|
||||
var data [5]uint16
|
||||
if _, err := b.waitStatus(func(st uint16) bool {
|
||||
@@ -235,8 +269,7 @@ func (b *bcm) command(code uint16, params ...uint16) ([5]uint16, error) {
|
||||
}
|
||||
|
||||
func (b *bcm) identify() (string, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
defer b.acquire()()
|
||||
hi, err := b.mdioRead(1, 2)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -259,8 +292,7 @@ func (b *bcm) identify() (string, error) {
|
||||
// PMA 1.1 latches low, so the first read reports any drop since it was last
|
||||
// read and the second reports the wire as it is now.
|
||||
func (b *bcm) linkUp() (bool, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
defer b.acquire()()
|
||||
if _, err := b.mdioRead(1, 1); err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -282,8 +314,7 @@ func (b *bcm) forceJumbo() error {
|
||||
}
|
||||
|
||||
func (b *bcm) restartAN() error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
defer b.acquire()()
|
||||
v, err := b.mdioRead(7, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -292,8 +323,7 @@ func (b *bcm) restartAN() error {
|
||||
}
|
||||
|
||||
func (b *bcm) eeeAdvert() (uint16, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
defer b.acquire()()
|
||||
return b.mdioRead(7, 60)
|
||||
}
|
||||
|
||||
@@ -318,8 +348,7 @@ func (b *bcm) snr() ([4]float64, error) {
|
||||
}
|
||||
|
||||
func (b *bcm) pcsLatch() (blocks, ber uint64, err error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
defer b.acquire()()
|
||||
v, err := b.mdioRead(3, 33)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
@@ -328,8 +357,7 @@ func (b *bcm) pcsLatch() (blocks, ber uint64, err error) {
|
||||
}
|
||||
|
||||
func (b *bcm) fastRetrainCount() (uint16, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
defer b.acquire()()
|
||||
v, err := b.mdioRead(1, 147)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -343,8 +371,7 @@ type ecdResult struct {
|
||||
}
|
||||
|
||||
func (b *bcm) cableDiag() (ecdResult, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
defer b.acquire()()
|
||||
|
||||
var res ecdResult
|
||||
ctrl, err := b.mdioRead(bcmMMDVendor, bcmRegECDCtrl)
|
||||
@@ -368,6 +395,7 @@ func (b *bcm) cableDiag() (ecdResult, error) {
|
||||
}
|
||||
time.Sleep(ecdPoll)
|
||||
}
|
||||
b.window()
|
||||
v, err := b.mdioRead(1, bcmRegECDResult)
|
||||
if err != nil {
|
||||
return res, err
|
||||
@@ -812,6 +840,9 @@ func moduleChecks(mods []*phyModule, names [2]string) []checkResult {
|
||||
}
|
||||
}
|
||||
res.state = adv[0] + "/" + adv[1]
|
||||
for _, m := range mods {
|
||||
m.bcm.windowed.Store(true)
|
||||
}
|
||||
return append(out, res)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user