Skip to content

Commit

Permalink
ptpcheck map: query tx-type/rx-filter value (#408)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #408

Update `ptpcheck map` to display active tx-type and rx-filter values instead of the capabilities.

Reviewed By: leoleovich

Differential Revision: D64471617

fbshipit-source-id: 548f134f2c3ab909f5f705e7a6f7a97ea52dd279
  • Loading branch information
yarikk authored and facebook-github-bot committed Oct 16, 2024
1 parent 2f86bef commit ced8adb
Show file tree
Hide file tree
Showing 19 changed files with 606 additions and 16 deletions.
50 changes: 34 additions & 16 deletions cmd/ptpcheck/cmd/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ import (
"strings"
"unicode"

"github.com/facebook/time/phc/unix" // a temporary shim for "golang.org/x/sys/unix" until v0.27.0 is cut
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/facebook/time/phc"
)

func ptpDeviceNum(ptpPath string) (int, error) {
Expand All @@ -46,25 +45,32 @@ func ptpDeviceNum(ptpPath string) (int, error) {
}))
}

func printIfaceData(ifname string, tsinfo *phc.EthtoolTSinfo, reverse bool) {
if tsinfo.PHCIndex < 0 {
func printIfaceData(ifname string, tsinfo *unix.EthtoolTsInfo, reverse bool) {
if tsinfo.Phc_index < 0 {
fmt.Printf("No PHC support for %s\n", ifname)
return
}
attrs := fmt.Sprintf("cap-tx-types %x cap-rx-filters %x", tsinfo.TXTypes, tsinfo.RXFilters)

var attrs string
if tscfg, err := unix.IoctlGetHwTstamp(mapIofd, ifname); err == nil {
attrs = fmt.Sprintf("tx-type %d rx-filter %d", tscfg.Tx_type, tscfg.Rx_filter)
} else {
log.Warningf("%s: IoctlGetHwTstamp: %v", ifname, err)
}

if reverse {
fmt.Printf("/dev/ptp%d -> %s\t%s\n", tsinfo.PHCIndex, ifname, attrs)
return
fmt.Printf("/dev/ptp%d -> %s\t%s\n", tsinfo.Phc_index, ifname, attrs)
} else {
fmt.Printf("%s -> /dev/ptp%d\t%s\n", ifname, tsinfo.Phc_index, attrs)
}
fmt.Printf("%s -> /dev/ptp%d\t%s\n", ifname, tsinfo.PHCIndex, attrs)
}

func getDevice(iface string) error {
tsinfo, err := phc.IfaceInfo(iface)
func getDevice(ifname string) error {
tsinfo, err := unix.IoctlGetEthtoolTsInfo(mapIofd, ifname)
if err != nil {
return err
return fmt.Errorf("%v: IoctlGetEthtoolTsInfo: %w", ifname, err)
}
printIfaceData(iface, tsinfo, false)
printIfaceData(ifname, tsinfo, false)
return nil
}

Expand All @@ -75,22 +81,26 @@ func getIface(ptpDevice int) error {
}
n := 0
for _, iface := range ifaces {
tsinfo, err := phc.IfaceInfo(iface.Name)
tsinfo, err := unix.IoctlGetEthtoolTsInfo(mapIofd, iface.Name)
if err != nil {
log.Errorf("%v: IoctlGetEthtoolTsInfo: %v", iface.Name, err)
continue
}
if int(tsinfo.PHCIndex) == ptpDevice || ptpDevice < 0 {
if int(tsinfo.Phc_index) == ptpDevice || ptpDevice < 0 {
printIfaceData(iface.Name, tsinfo, true)
n++
}
}
if n == 0 {
if ptpDevice >= 0 && n == 0 {
return fmt.Errorf("no nic found for /dev/ptp%d", ptpDevice)
}
return nil
}

var mapIfaceFlag bool
var (
mapIofd int
mapIfaceFlag bool
)

func init() {
RootCmd.AddCommand(mapCmd)
Expand All @@ -101,7 +111,15 @@ var mapCmd = &cobra.Command{
Use: "map [ptp device/network interface]...",
Short: "Find network interfaces for ptp devices and vice versa",
Run: func(_ *cobra.Command, args []string) {
var err error
ConfigureVerbosity()

mapIofd, err = unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
if err != nil {
log.Fatal(err)
}
defer unix.Close(mapIofd)

// no args - just print map of all ptp devices to all interfaces
if len(args) == 0 {
if err := getIface(-1); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions phc/unix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
A temporary shim for "golang.org/x/sys/unix" until v0.27.0 is cut.

We can't simply update go.mod with pre-release version of the package
because of the dependency management approach Fedora has for building
RPMs from Go sources.
67 changes: 67 additions & 0 deletions phc/unix/ifreq_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//go:build linux

/*
Copyright (c) Facebook, Inc. and its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package unix

import (
"unsafe"
)

// An Ifreq is a type-safe wrapper around the raw ifreq struct. An Ifreq
// contains an interface name and a union of arbitrary data which can be
// accessed using the Ifreq's methods. To create an Ifreq, use the NewIfreq
// function.
type Ifreq struct{ raw ifreq }

// NewIfreq creates an Ifreq with the input network interface name
func NewIfreq(name string) (*Ifreq, error) {
if len(name) >= IFNAMSIZ {
return nil, EINVAL
}

var ifr ifreq
copy(ifr.Ifrn[:], name)

return &Ifreq{raw: ifr}, nil
}

// Name returns the interface name associated with the Ifreq.
func (ifr *Ifreq) Name() string {
return ByteSliceToString(ifr.raw.Ifrn[:])
}

// An ifreqData is an Ifreq which carries pointer data.
// To produce an ifreqData, use the Ifreq.withData method.
type ifreqData struct {
name [IFNAMSIZ]byte
// A type separate from ifreq is required in order to comply with the
// unsafe.Pointer rules since the "pointer-ness" of data would not be
// preserved if it were cast into the byte array of a raw ifreq.
data unsafe.Pointer
// Pad to the same size as ifreq.
_ [len(ifreq{}.Ifru) - SizeofPtr]byte
}

// withData produces an ifreqData with the pointer p set for ioctls which require
// arbitrary pointer data.
func (ifr Ifreq) withData(p unsafe.Pointer) ifreqData {
return ifreqData{
name: ifr.raw.Ifrn,
data: p,
}
}
140 changes: 140 additions & 0 deletions phc/unix/linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//go:build linux

// @generated
/*
Copyright (c) Facebook, Inc. and its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package unix

import (
"syscall"
"unsafe"

"golang.org/x/sys/unix"
)

// https://go-review.googlesource.com/c/sys/+/620376

type HwTstampConfig struct {
Flags int32
Tx_type int32

Check warning on line 33 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use underscores in Go names; struct field Tx_type should be TxType (revive)
Rx_filter int32

Check warning on line 34 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use underscores in Go names; struct field Rx_filter should be RxFilter (revive)
}

// IoctlGetHwTstamp retrieves the hardware timestamping configuration
// for the network device specified by ifname.
func IoctlGetHwTstamp(fd int, ifname string) (*HwTstampConfig, error) {
ifr, err := NewIfreq(ifname)
if err != nil {
return nil, err
}

value := HwTstampConfig{}
ifrd := ifr.withData(unsafe.Pointer(&value))

err = ioctlIfreqData(fd, SIOCGHWTSTAMP, &ifrd)
return &value, err
}

// https://go-review.googlesource.com/c/sys/+/619335

type EthtoolTsInfo struct {
Cmd uint32
So_timestamping uint32

Check warning on line 56 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use underscores in Go names; struct field So_timestamping should be SoTimestamping (revive)
Phc_index int32

Check warning on line 57 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use underscores in Go names; struct field Phc_index should be PhcIndex (revive)
Tx_types uint32

Check warning on line 58 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use underscores in Go names; struct field Tx_types should be TxTypes (revive)
Tx_reserved [3]uint32

Check warning on line 59 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use underscores in Go names; struct field Tx_reserved should be TxReserved (revive)
Rx_filters uint32

Check warning on line 60 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use underscores in Go names; struct field Rx_filters should be RxFilters (revive)
Rx_reserved [3]uint32

Check warning on line 61 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use underscores in Go names; struct field Rx_reserved should be RxReserved (revive)
}

// IoctlGetEthtoolTsInfo fetches ethtool timestamping and PHC
// association for the network device specified by ifname.
func IoctlGetEthtoolTsInfo(fd int, ifname string) (*EthtoolTsInfo, error) {
ifr, err := NewIfreq(ifname)
if err != nil {
return nil, err
}

value := EthtoolTsInfo{Cmd: ETHTOOL_GET_TS_INFO}
ifrd := ifr.withData(unsafe.Pointer(&value))

err = ioctlIfreqData(fd, SIOCETHTOOL, &ifrd)
return &value, err
}

// bridging to upstream

type Errno = unix.Errno
type RawSockaddrInet4 = unix.RawSockaddrInet4

func Socket(domain, typ, proto int) (fd int, err error) { return unix.Socket(domain, typ, proto) }
func Close(fd int) (err error) { return unix.Close(fd) }
func Syscall(a, b, c, d uintptr) (uintptr, uintptr, Errno) { return unix.Syscall(a, b, c, d) }
func ByteSliceToString(b []byte) string { return unix.ByteSliceToString(b) }

const (
AF_INET = unix.AF_INET //nolint:staticcheck

Check warning on line 90 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use ALL_CAPS in Go names; use CamelCase (revive)
ETHTOOL_GET_TS_INFO = unix.ETHTOOL_GET_TS_INFO

Check warning on line 91 in phc/unix/linux.go

View workflow job for this annotation

GitHub Actions / lint

var-naming: don't use ALL_CAPS in Go names; use CamelCase (revive)
SIOCETHTOOL = unix.SIOCETHTOOL
SIOCGHWTSTAMP = unix.SIOCGHWTSTAMP
SOCK_DGRAM = unix.SOCK_DGRAM
SYS_IOCTL = unix.SYS_IOCTL
EAGAIN = unix.EAGAIN
EINVAL = unix.EINVAL
ENOENT = unix.ENOENT
IFNAMSIZ = unix.IFNAMSIZ
SizeofSockaddrInet4 = unix.SizeofSockaddrInet4
SizeofPtr = unix.SizeofPtr
)

var (
errEAGAIN error = syscall.EAGAIN
errEINVAL error = syscall.EINVAL
errENOENT error = syscall.ENOENT
)

// ioctlIfreqData performs an ioctl using an ifreqData structure for input
// and/or output. See the netdevice(7) man page for details.
func ioctlIfreqData(fd int, req uint, value *ifreqData) error {
// The memory layout of IfreqData (type-safe) and ifreq (not type-safe) are
// identical so pass *IfreqData directly.
return ioctlPtr(fd, req, unsafe.Pointer(value))
}

func ioctlPtr(fd int, req uint, arg unsafe.Pointer) (err error) {
_, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
if e1 != 0 {
err = errnoErr(e1)
}
return
}

// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return nil
case EAGAIN:
return errEAGAIN
case EINVAL:
return errEINVAL
case ENOENT:
return errENOENT
}
return e
}
24 changes: 24 additions & 0 deletions phc/unix/linux_386.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:build 386 && linux

/*
Copyright (c) Facebook, Inc. and its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package unix

type ifreq struct {
Ifrn [16]byte
Ifru [16]byte
}
24 changes: 24 additions & 0 deletions phc/unix/linux_amd64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:build amd64 && linux

/*
Copyright (c) Facebook, Inc. and its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package unix

type ifreq struct {
Ifrn [16]byte
Ifru [24]byte
}
24 changes: 24 additions & 0 deletions phc/unix/linux_arm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:build arm && linux

/*
Copyright (c) Facebook, Inc. and its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package unix

type ifreq struct {
Ifrn [16]byte
Ifru [16]byte
}
Loading

0 comments on commit ced8adb

Please sign in to comment.