diff --git a/devlink_linux.go b/devlink_linux.go index 45d8ee4b..5c795c0b 100644 --- a/devlink_linux.go +++ b/devlink_linux.go @@ -46,15 +46,21 @@ type DevlinkPortFnSetAttrs struct { // DevlinkPort represents port and its attributes type DevlinkPort struct { - BusName string - DeviceName string - PortIndex uint32 - PortType uint16 - NetdeviceName string - NetdevIfIndex uint32 - RdmaDeviceName string - PortFlavour uint16 - Fn *DevlinkPortFn + BusName string + DeviceName string + PortIndex uint32 + PortType uint16 + NetdeviceName string + NetdevIfIndex uint32 + RdmaDeviceName string + PortFlavour uint16 + Fn *DevlinkPortFn + PortNumber *uint32 + PfNumber *uint16 + VfNumber *uint16 + SfNumber *uint32 + ControllerNumber *uint32 + External *bool } type DevLinkPortAddAttrs struct { @@ -629,6 +635,24 @@ func (port *DevlinkPort) parseAttributes(attrs []syscall.NetlinkRouteAttr) error port.Fn.OpState = uint8(nested.Value[0]) } } + case nl.DEVLINK_ATTR_PORT_NUMBER: + val := native.Uint32(a.Value) + port.PortNumber = &val + case nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER: + val := native.Uint16(a.Value) + port.PfNumber = &val + case nl.DEVLINK_ATTR_PORT_PCI_VF_NUMBER: + val := native.Uint16(a.Value) + port.VfNumber = &val + case nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER: + val := native.Uint32(a.Value) + port.SfNumber = &val + case nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER: + val := native.Uint32(a.Value) + port.ControllerNumber = &val + case nl.DEVLINK_ATTR_PORT_EXTERNAL: + val := uint8(a.Value[0]) != 0 + port.External = &val } } return nil diff --git a/devlink_test.go b/devlink_test.go index cf6961c7..d008aa28 100644 --- a/devlink_test.go +++ b/devlink_test.go @@ -5,10 +5,13 @@ package netlink import ( "flag" + "fmt" "math/rand" "net" "os" "strconv" + "strings" + "syscall" "testing" "github.com/vishvananda/netlink/nl" @@ -49,6 +52,61 @@ func TestDevLinkSetEswitchMode(t *testing.T) { } } +func logPort(t *testing.T, port *DevlinkPort) { + type field struct { + key string + value string + } + + fields := []field{} + + fields = append(fields, field{key: "bus", value: port.BusName}) + fields = append(fields, field{key: "device", value: port.DeviceName}) + fields = append(fields, field{key: "port_index", value: strconv.Itoa(int(port.PortIndex))}) + fields = append(fields, field{key: "port_type", value: strconv.Itoa(int(port.PortType))}) + fields = append(fields, field{key: "port_flavour", value: strconv.Itoa(int(port.PortFlavour))}) + fields = append(fields, field{key: "netdev_name", value: port.NetdeviceName}) + fields = append(fields, field{key: "netdev_index", value: strconv.Itoa(int(port.NetdevIfIndex))}) + fields = append(fields, field{key: "rdma_dev_name", value: port.RdmaDeviceName}) + + if port.Fn != nil { + fields = append(fields, field{key: "hw_addr", value: port.Fn.HwAddr.String()}) + fields = append(fields, field{key: "state", value: strconv.Itoa(int(port.Fn.State))}) + fields = append(fields, field{key: "op_state", value: strconv.Itoa(int(port.Fn.OpState))}) + } + + if port.PortNumber != nil { + fields = append(fields, field{key: "port_number", value: strconv.Itoa(int(*port.PortNumber))}) + } + + if port.PfNumber != nil { + fields = append(fields, field{key: "pf_number", value: strconv.Itoa(int(*port.PfNumber))}) + } + + if port.VfNumber != nil { + fields = append(fields, field{key: "vf_number", value: strconv.Itoa(int(*port.VfNumber))}) + } + + if port.SfNumber != nil { + fields = append(fields, field{key: "sf_number", value: strconv.Itoa(int(*port.SfNumber))}) + } + + if port.ControllerNumber != nil { + fields = append(fields, field{key: "controller_number", value: strconv.Itoa(int(*port.ControllerNumber))}) + } + + if port.External != nil { + fields = append(fields, field{key: "external", value: strconv.FormatBool(*port.External)}) + } + + fieldsStr := []string{} + for _, field := range fields { + fieldsStr = append(fieldsStr, fmt.Sprintf("%s=%s", field.key, field.value)) + } + + t.Log(strings.Join(fieldsStr, " ")) +} + func TestDevLinkGetAllPortList(t *testing.T) { minKernelRequired(t, 5, 4) ports, err := DevLinkGetAllPortList() @@ -57,7 +115,7 @@ func TestDevLinkGetAllPortList(t *testing.T) { } t.Log("devlink port count = ", len(ports)) for _, port := range ports { - t.Log(*port) + logPort(t, port) } } @@ -402,3 +460,309 @@ func validateDeviceParams(t *testing.T, p *DevlinkParam) { } } } + +func testGetDevlinkPortCommonAttrs() []*nl.RtAttr { + nlAttrs := []*nl.RtAttr{} + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, nl.ZeroTerminated("pci")), + nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, nl.ZeroTerminated("0000:08:00.0")), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(131071)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_TYPE, nl.Uint16Attr(nl.DEVLINK_PORT_TYPE_ETH)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_NETDEV_NAME, nl.ZeroTerminated("eth0")), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX, nl.Uint32Attr(5)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_IBDEV_NAME, nl.ZeroTerminated("rdma0")), + ) + + return nlAttrs +} + +func testAddDevlinkPortPhysicalAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PHYSICAL)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_NUMBER, nl.Uint32Attr(1)), + ) + + return nlAttrs +} + +func testAddDevlinkPortPfAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PCI_PF)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(1)), + ) + + return nlAttrs +} + +func testAddDevlinkPortVfAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PCI_VF)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(0)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_VF_NUMBER, nl.Uint16Attr(4)), + ) + + nlAttrs = testAddDevlinkPortFnAttrs(nlAttrs) + return nlAttrs +} + +func testAddDevlinkPortSfAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PCI_SF)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(0)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(123)), + ) + + nlAttrs = testAddDevlinkPortFnAttrs(nlAttrs) + return nlAttrs +} + +func testNlAttrsToNetlinkRouteAttrs(nlAttrs []*nl.RtAttr) []syscall.NetlinkRouteAttr { + attrs := []syscall.NetlinkRouteAttr{} + for _, attr := range nlAttrs { + attrs = append(attrs, syscall.NetlinkRouteAttr{Attr: syscall.RtAttr(attr.RtAttr), Value: attr.Data}) + } + return attrs +} + +func testAddDevlinkPortFnAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { + hwAddr, _ := net.ParseMAC("00:11:22:33:44:55") + hwAddrAttr := nl.NewRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(hwAddr)) + raw := hwAddrAttr.Serialize() + + nlAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION, raw) + return append(nlAttrs, nlAttr) +} + +func testAddDevlinkPortControllerAttrs(nlAttrs []*nl.RtAttr, controllerNumber uint32, external bool) []*nl.RtAttr { + extVal := uint8(0) + if external { + extVal = 1 + } + + nlAttrs = append(nlAttrs, + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(controllerNumber)), + nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_EXTERNAL, nl.Uint8Attr(extVal)), + ) + + return nlAttrs +} +func testAssertCommonAttrs(t *testing.T, port *DevlinkPort) { + if port.BusName != "pci" { + t.Errorf("expected BusName to be 'pci', got %s", port.BusName) + } + if port.DeviceName != "0000:08:00.0" { + t.Errorf("expected DeviceName to be '0000:08:00.0', got %s", port.DeviceName) + } + if port.PortIndex != uint32(131071) { + t.Errorf("expected PortIndex to be 131071, got %d", port.PortIndex) + } + if port.PortType != uint16(nl.DEVLINK_PORT_TYPE_ETH) { + t.Errorf("expected PortType to be %d, got %d", nl.DEVLINK_PORT_TYPE_ETH, port.PortType) + } + if port.NetdeviceName != "eth0" { + t.Errorf("expected NetdeviceName to be 'eth0', got %s", port.NetdeviceName) + } + if port.NetdevIfIndex != uint32(5) { + t.Errorf("expected NetdevIfIndex to be 5, got %d", port.NetdevIfIndex) + } + if port.RdmaDeviceName != "rdma0" { + t.Errorf("expected RdmaDeviceName to be 'rdma0', got %s", port.RdmaDeviceName) + } +} + +func TestDevlinkPortParseAttributes(t *testing.T) { + t.Run("flavor physical", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortPhysicalAttrs(nlAttrs) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + testAssertCommonAttrs(t, port) + if port.PortFlavour != uint16(nl.DEVLINK_PORT_FLAVOUR_PHYSICAL) { + t.Errorf("expected PortFlavour to be %d, got %d", nl.DEVLINK_PORT_FLAVOUR_PHYSICAL, port.PortFlavour) + } + if *port.PortNumber != uint32(1) { + t.Errorf("expected PortNumber to be 1, got %d", *port.PortNumber) + } + + if port.Fn != nil { + t.Errorf("expected Fn to be nil, got %v", port.Fn) + } + if port.PfNumber != nil { + t.Errorf("expected PfNumber to be nil, got %v", port.PfNumber) + } + if port.VfNumber != nil { + t.Errorf("expected VfNumber to be nil, got %v", port.VfNumber) + } + if port.SfNumber != nil { + t.Errorf("expected SfNumber to be nil, got %v", port.SfNumber) + } + if port.ControllerNumber != nil { + t.Errorf("expected ControllerNumber to be nil, got %v", port.ControllerNumber) + } + if port.External != nil { + t.Errorf("expected External to be nil, got %v", port.External) + } + }) + + t.Run("flavor pcipf", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortPfAttrs(nlAttrs) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + testAssertCommonAttrs(t, port) + if port.PortFlavour != uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_PF) { + t.Errorf("expected PortFlavour to be %d, got %d", nl.DEVLINK_PORT_FLAVOUR_PCI_PF, port.PortFlavour) + } + if *port.PfNumber != uint16(1) { + t.Errorf("expected PfNumber to be 1, got %d", *port.PfNumber) + } + + if port.Fn != nil { + t.Errorf("expected Fn to be nil, got %v", port.Fn) + } + if port.PortNumber != nil { + t.Errorf("expected PortNumber to be nil, got %v", port.PortNumber) + } + if port.VfNumber != nil { + t.Errorf("expected VfNumber to be nil, got %v", port.VfNumber) + } + if port.SfNumber != nil { + t.Errorf("expected SfNumber to be nil, got %v", port.SfNumber) + } + if port.ControllerNumber != nil { + t.Errorf("expected ControllerNumber to be nil, got %v", port.ControllerNumber) + } + if port.External != nil { + t.Errorf("expected External to be nil, got %v", port.External) + } + }) + t.Run("flavor pcivf", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + testAssertCommonAttrs(t, port) + if port.PortFlavour != uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_VF) { + t.Errorf("expected PortFlavour to be %d, got %d", nl.DEVLINK_PORT_FLAVOUR_PCI_VF, port.PortFlavour) + } + if *port.PfNumber != uint16(0) { + t.Errorf("expected PfNumber to be 0, got %d", *port.PfNumber) + } + if *port.VfNumber != uint16(4) { + t.Errorf("expected VfNumber to be 4, got %d", *port.VfNumber) + } + if port.Fn.HwAddr.String() != "00:11:22:33:44:55" { + t.Errorf("expected HwAddr to be '00:11:22:33:44:55', got %s", port.Fn.HwAddr.String()) + } + + if port.PortNumber != nil { + t.Errorf("expected PortNumber to be nil, got %v", port.PortNumber) + } + if port.SfNumber != nil { + t.Errorf("expected SfNumber to be nil, got %v", port.SfNumber) + } + if port.ControllerNumber != nil { + t.Errorf("expected ControllerNumber to be nil, got %v", port.ControllerNumber) + } + if port.External != nil { + t.Errorf("expected External to be nil, got %v", port.External) + } + }) + + t.Run("flavor pcisf", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortSfAttrs(nlAttrs) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + testAssertCommonAttrs(t, port) + if port.PortFlavour != uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_SF) { + t.Errorf("expected PortFlavour to be %d, got %d", nl.DEVLINK_PORT_FLAVOUR_PCI_SF, port.PortFlavour) + } + if *port.PfNumber != uint16(0) { + t.Errorf("expected PfNumber to be 0, got %d", *port.PfNumber) + } + if *port.SfNumber != uint32(123) { + t.Errorf("expected SfNumber to be 123, got %d", *port.SfNumber) + } + if port.Fn.HwAddr.String() != "00:11:22:33:44:55" { + t.Errorf("expected HwAddr to be '00:11:22:33:44:55', got %s", port.Fn.HwAddr.String()) + } + + if port.PortNumber != nil { + t.Errorf("expected PortNumber to be nil, got %v", port.PortNumber) + } + if port.VfNumber != nil { + t.Errorf("expected VfNumber to be nil, got %v", port.VfNumber) + } + if port.ControllerNumber != nil { + t.Errorf("expected ControllerNumber to be nil, got %v", port.ControllerNumber) + } + if port.External != nil { + t.Errorf("expected External to be nil, got %v", port.External) + } + }) + + t.Run("port with controller - external false", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs) + nlAttrs = testAddDevlinkPortControllerAttrs(nlAttrs, 0, false) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if *port.ControllerNumber != uint32(0) { + t.Errorf("expected ControllerNumber to be 0, got %d", *port.ControllerNumber) + } + if *port.External != false { + t.Errorf("expected External to be false, got %t", *port.External) + } + }) + + t.Run("port with controller - external true", func(t *testing.T) { + nlAttrs := testGetDevlinkPortCommonAttrs() + nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs) + nlAttrs = testAddDevlinkPortControllerAttrs(nlAttrs, 1, true) + attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) + + port := &DevlinkPort{} + err := port.parseAttributes(attrs) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if *port.ControllerNumber != uint32(1) { + t.Errorf("expected ControllerNumber to be 1, got %d", *port.ControllerNumber) + } + if *port.External != true { + t.Errorf("expected External to be true, got %t", *port.External) + } + }) +} diff --git a/nl/devlink_linux.go b/nl/devlink_linux.go index 956367b2..896fc9c1 100644 --- a/nl/devlink_linux.go +++ b/nl/devlink_linux.go @@ -47,7 +47,8 @@ const ( DEVLINK_ATTR_RESOURCE_OCC = 74 /* u64 */ DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_ID = 75 /* u64 */ DEVLINK_ATTR_DPIPE_TABLE_RESOURCE_UNITS = 76 /* u64 */ - DEVLINK_ATTR_PORT_FLAVOUR = 77 + DEVLINK_ATTR_PORT_FLAVOUR = 77 /* u16 */ + DEVLINK_ATTR_PORT_NUMBER = 78 /* u32 */ DEVLINK_ATTR_INFO_DRIVER_NAME = 98 DEVLINK_ATTR_INFO_SERIAL_NUMBER = 99 DEVLINK_ATTR_INFO_VERSION_FIXED = 100 @@ -55,10 +56,12 @@ const ( DEVLINK_ATTR_INFO_VERSION_STORED = 102 DEVLINK_ATTR_INFO_VERSION_NAME = 103 DEVLINK_ATTR_INFO_VERSION_VALUE = 104 - DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127 - DEVLINK_ATTR_PORT_FUNCTION = 145 - DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150 - DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 164 + DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127 /* u16 */ + DEVLINK_ATTR_PORT_PCI_VF_NUMBER = 128 /* u16 */ + DEVLINK_ATTR_PORT_FUNCTION = 145 /* nested */ + DEVLINK_ATTR_PORT_EXTERNAL = 149 /* u8 */ + DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150 /* u32 */ + DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 164 /* u32 */ ) const (