Drive a noise cable on the i40e pair, cycling its link down and up since the module PHYs ignore everything softer, and flag its absence beside the error columns
This commit is contained in:
@@ -334,8 +334,9 @@ func getCoalesce(fd int, ifname string) (ethtoolCoalesce, error) {
|
||||
}
|
||||
|
||||
const (
|
||||
ethSSStats = 1
|
||||
ethGstringLen = 32
|
||||
ethSSStats = 1
|
||||
ethSSPrivFlags = 2
|
||||
ethGstringLen = 32
|
||||
)
|
||||
|
||||
// Each of these has a __u32 or __u8 tail the kernel fills past the end of the
|
||||
@@ -364,33 +365,33 @@ type ethtoolStatsHdr struct {
|
||||
// boundary. A []byte carries no such guarantee.
|
||||
func statsBuf(n int) []uint64 { return make([]uint64, n) }
|
||||
|
||||
func statCount(fd int, ifname string) (uint32, error) {
|
||||
req := ethtoolSsetInfo{cmd: unix.ETHTOOL_GSSET_INFO, ssetMask: 1 << ethSSStats}
|
||||
func stringSetCount(fd int, ifname string, set uint32) (uint32, error) {
|
||||
req := ethtoolSsetInfo{cmd: unix.ETHTOOL_GSSET_INFO, ssetMask: 1 << set}
|
||||
if err := ethtoolCall(fd, ifname, unsafe.Pointer(&req)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// A cleared mask bit means the driver does not have that string set at all,
|
||||
// in which case no count was written into the tail.
|
||||
if req.ssetMask == 0 {
|
||||
return 0, fmt.Errorf("driver has no ETH_SS_STATS string set")
|
||||
return 0, fmt.Errorf("driver has no string set %d", set)
|
||||
}
|
||||
return req.data[0], nil
|
||||
}
|
||||
|
||||
func statNames(fd int, ifname string) ([]string, error) {
|
||||
n, err := statCount(fd, ifname)
|
||||
func stringSetNames(fd int, ifname string, set uint32) ([]string, error) {
|
||||
n, err := stringSetCount(fd, ifname, set)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n == 0 {
|
||||
return nil, fmt.Errorf("driver reports zero statistics")
|
||||
return nil, fmt.Errorf("driver reports an empty string set %d", set)
|
||||
}
|
||||
|
||||
hdrLen := int(unsafe.Sizeof(ethtoolGstrings{}))
|
||||
buf := statsBuf((hdrLen + int(n)*ethGstringLen + 7) / 8)
|
||||
hdr := (*ethtoolGstrings)(unsafe.Pointer(&buf[0]))
|
||||
hdr.cmd = unix.ETHTOOL_GSTRINGS
|
||||
hdr.stringSet = ethSSStats
|
||||
hdr.stringSet = set
|
||||
hdr.len = n
|
||||
if err := ethtoolCall(fd, ifname, unsafe.Pointer(&buf[0])); err != nil {
|
||||
return nil, err
|
||||
@@ -426,7 +427,7 @@ type statReader struct {
|
||||
}
|
||||
|
||||
func newStatReader(fd int, ifname string, want []string) (*statReader, error) {
|
||||
names, err := statNames(fd, ifname)
|
||||
names, err := stringSetNames(fd, ifname, ethSSStats)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s statistics: %w", ifname, err)
|
||||
}
|
||||
@@ -623,6 +624,67 @@ func withIoctlSocket(fn func(fd int) []checkResult) []checkResult {
|
||||
return fn(fd)
|
||||
}
|
||||
|
||||
type ethtoolValue struct {
|
||||
cmd uint32
|
||||
data uint32
|
||||
}
|
||||
|
||||
// i40e leaves the module transmitting when a port is closed, so the peer never
|
||||
// sees anything happen and the copper stays trained. This flag is what makes
|
||||
// an administrative down reach the wire, and the noise cycle is switched
|
||||
// entirely through it.
|
||||
func checkPrivFlag(fd int, ifname, flag string) checkResult {
|
||||
res := checkResult{item: ifname + " " + flag}
|
||||
names, err := stringSetNames(fd, ifname, ethSSPrivFlags)
|
||||
if err != nil {
|
||||
res.err = err
|
||||
return res
|
||||
}
|
||||
bit := -1
|
||||
for i, s := range names {
|
||||
if s == flag {
|
||||
bit = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if bit < 0 {
|
||||
res.err = fmt.Errorf("driver has no private flag %q", flag)
|
||||
return res
|
||||
}
|
||||
v := ethtoolValue{cmd: unix.ETHTOOL_GPFLAGS}
|
||||
if err := ethtoolCall(fd, ifname, unsafe.Pointer(&v)); err != nil {
|
||||
res.err = err
|
||||
return res
|
||||
}
|
||||
if v.data&(1<<bit) != 0 {
|
||||
res.state = "on"
|
||||
return res
|
||||
}
|
||||
v.cmd = unix.ETHTOOL_SPFLAGS
|
||||
v.data |= 1 << bit
|
||||
if err := ethtoolCall(fd, ifname, unsafe.Pointer(&v)); err != nil {
|
||||
res.err = err
|
||||
res.state = "could not set"
|
||||
return res
|
||||
}
|
||||
res.fixed = true
|
||||
res.state = "was off, now on"
|
||||
return res
|
||||
}
|
||||
|
||||
// The noise ports get none of the test pair's tuning: nothing is measured on
|
||||
// them, so rings, coalescing and steering are all beside the point. All they
|
||||
// need is for a closed port to really drop the link.
|
||||
func configureNoise(ifnames []string) []checkResult {
|
||||
return withIoctlSocket(func(fd int) []checkResult {
|
||||
var out []checkResult
|
||||
for _, ifname := range ifnames {
|
||||
out = append(out, checkPrivFlag(fd, ifname, "link-down-on-close"))
|
||||
}
|
||||
return out
|
||||
})
|
||||
}
|
||||
|
||||
// Nothing here waits for a carrier: the two ports are the two ends of the cable
|
||||
// under test, so with no cable there is never going to be one.
|
||||
func configureSystem(ifnames []string, ethertypes []uint16) []checkResult {
|
||||
|
||||
Reference in New Issue
Block a user