2026-07-25 22:38:52 -07:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
2026-08-13 07:10:39 -07:00
|
|
|
hwtstampTxOff = 0
|
2026-07-25 22:38:52 -07:00
|
|
|
hwtstampFilterAll = 1
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type hwtstampConfig struct {
|
|
|
|
|
flags int32
|
|
|
|
|
txType int32
|
|
|
|
|
rxFilter int32
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-13 08:04:26 -07:00
|
|
|
// Temporarily bypassed so BCM work can run on the X520, which cannot stamp;
|
|
|
|
|
// restore to fatal for the product NIC (docs/open-questions.md).
|
2026-08-13 07:10:39 -07:00
|
|
|
func checkTimestamping(fd int, ifname string) checkResult {
|
|
|
|
|
res := configureTimestamping(fd, ifname)
|
|
|
|
|
if res.err != nil {
|
|
|
|
|
res.state = "bypassed: " + res.detail()
|
|
|
|
|
res.err = nil
|
|
|
|
|
res.fixed = true
|
|
|
|
|
}
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-25 23:03:31 -07:00
|
|
|
// Receive stamping is filtered by protocol and ours is not PTP, so nothing
|
|
|
|
|
// narrower than "all" will see our frames.
|
2026-08-13 07:10:39 -07:00
|
|
|
func configureTimestamping(fd int, ifname string) checkResult {
|
2026-07-25 22:38:52 -07:00
|
|
|
res := checkResult{item: ifname + " hw timestamps"}
|
|
|
|
|
desc := func(c hwtstampConfig) string {
|
|
|
|
|
return fmt.Sprintf("tx_type=%d rx_filter=%d", c.txType, c.rxFilter)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hwtstampCall := func(req uintptr, cfg *hwtstampConfig) error {
|
|
|
|
|
var ifr dataIfreq
|
|
|
|
|
copy(ifr.name[:], ifname)
|
|
|
|
|
ifr.data = unsafe.Pointer(cfg)
|
|
|
|
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), req,
|
|
|
|
|
uintptr(unsafe.Pointer(&ifr))); errno != 0 {
|
|
|
|
|
return errno
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var have hwtstampConfig
|
|
|
|
|
if err := hwtstampCall(unix.SIOCGHWTSTAMP, &have); err != nil {
|
|
|
|
|
res.err = err
|
|
|
|
|
return res
|
|
|
|
|
}
|
2026-08-13 07:10:39 -07:00
|
|
|
if have.txType == hwtstampTxOff && have.rxFilter == hwtstampFilterAll {
|
2026-07-25 22:38:52 -07:00
|
|
|
res.state = desc(have)
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The ioctl reports back what the driver actually applied, which can be
|
|
|
|
|
// narrower than what was asked for.
|
2026-08-13 07:10:39 -07:00
|
|
|
want := hwtstampConfig{txType: hwtstampTxOff, rxFilter: hwtstampFilterAll}
|
2026-07-25 22:38:52 -07:00
|
|
|
if err := hwtstampCall(unix.SIOCSHWTSTAMP, &want); err != nil {
|
|
|
|
|
res.err = err
|
|
|
|
|
res.state = "could not set"
|
|
|
|
|
return res
|
|
|
|
|
}
|
2026-08-13 07:10:39 -07:00
|
|
|
if want.txType != hwtstampTxOff || want.rxFilter != hwtstampFilterAll {
|
2026-07-25 22:38:52 -07:00
|
|
|
res.err = fmt.Errorf("driver applied %s instead", desc(want))
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
res.fixed = true
|
|
|
|
|
res.state = fmt.Sprintf("was %s, now %s", desc(have), desc(want))
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func enableRxTimestamps(fd int) error {
|
|
|
|
|
return unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_TIMESTAMPING,
|
|
|
|
|
unix.SOF_TIMESTAMPING_RX_HARDWARE|unix.SOF_TIMESTAMPING_RAW_HARDWARE)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-25 23:03:31 -07:00
|
|
|
// Three timespecs, of which the third is the raw hardware clock. A zero there
|
|
|
|
|
// means the mac did not produce a stamp.
|
2026-07-25 22:38:52 -07:00
|
|
|
const scmTimestampingLen = 3 * int(unsafe.Sizeof(unix.Timespec{}))
|
|
|
|
|
|
2026-08-04 20:48:17 -07:00
|
|
|
// A control message's payload starts at its header rounded up to a pointer, and
|
|
|
|
|
// the next starts at its length rounded up the same way.
|
|
|
|
|
const (
|
|
|
|
|
cmsgAlignMask = unix.SizeofPtr - 1
|
|
|
|
|
cmsgDataOff = (unix.SizeofCmsghdr + cmsgAlignMask) &^ cmsgAlignMask
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Walked by hand rather than through ParseSocketControlMessage, which allocates
|
|
|
|
|
// per call on a path that runs for every frame received.
|
2026-07-25 22:38:52 -07:00
|
|
|
func hwTimestamp(oob []byte) (int64, bool) {
|
2026-08-04 20:48:17 -07:00
|
|
|
for off := 0; off+unix.SizeofCmsghdr <= len(oob); {
|
|
|
|
|
h := (*unix.Cmsghdr)(unsafe.Pointer(&oob[off]))
|
|
|
|
|
n := int(h.Len)
|
|
|
|
|
if n < unix.SizeofCmsghdr || off+n > len(oob) {
|
2026-07-25 22:38:52 -07:00
|
|
|
return 0, false
|
|
|
|
|
}
|
2026-08-04 20:48:17 -07:00
|
|
|
if h.Level == unix.SOL_SOCKET && h.Type == unix.SCM_TIMESTAMPING {
|
|
|
|
|
if cmsgDataOff+scmTimestampingLen > n {
|
|
|
|
|
return 0, false
|
|
|
|
|
}
|
|
|
|
|
ts := (*[3]unix.Timespec)(unsafe.Pointer(&oob[off+cmsgDataOff]))
|
|
|
|
|
ns := ts[2].Nano()
|
|
|
|
|
return ns, ns != 0
|
|
|
|
|
}
|
|
|
|
|
off += (n + cmsgAlignMask) &^ cmsgAlignMask
|
2026-07-25 22:38:52 -07:00
|
|
|
}
|
|
|
|
|
return 0, false
|
|
|
|
|
}
|