feat: add additional devlink port attributes

Add the following devlink port attributes:

PortNumber: the physical port number
PfNumber: the PF number
VfNumber: the VF number (index)
SfNumber: the SF number (index)
ControllerNumber: the controller number
External: if set, indicates external controller

Signed-off-by: adrianc <adrianc@nvidia.com>
This commit is contained in:
adrianc
2025-08-17 18:01:13 +03:00
parent 7af87bcf82
commit 398598b704
5 changed files with 330 additions and 15 deletions

View File

@@ -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

View File

@@ -5,12 +5,17 @@ package netlink
import (
"flag"
"fmt"
"math/rand"
"net"
"os"
"strconv"
"strings"
"syscall"
"testing"
"github.com/stretchr/testify/assert"
"github.com/vishvananda/netlink/nl"
)
@@ -49,6 +54,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 +117,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 +462,211 @@ 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) {
assert.Equal(t, "pci", port.BusName)
assert.Equal(t, "0000:08:00.0", port.DeviceName)
assert.Equal(t, uint32(131071), port.PortIndex)
assert.Equal(t, uint16(nl.DEVLINK_PORT_TYPE_ETH), port.PortType)
assert.Equal(t, "eth0", port.NetdeviceName)
assert.Equal(t, uint32(5), port.NetdevIfIndex)
assert.Equal(t, "rdma0", 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)
assert.NoError(t, err)
testAssertCommonAttrs(t, port)
assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PHYSICAL), port.PortFlavour)
assert.Equal(t, uint32(1), *port.PortNumber)
assert.Nil(t, port.Fn)
assert.Nil(t, port.PfNumber)
assert.Nil(t, port.VfNumber)
assert.Nil(t, port.SfNumber)
assert.Nil(t, port.ControllerNumber)
assert.Nil(t, port.External)
})
t.Run("flavor pcipf", func(t *testing.T) {
nlAttrs := testGetDevlinkPortCommonAttrs()
nlAttrs = testAddDevlinkPortPfAttrs(nlAttrs)
attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs)
port := &DevlinkPort{}
err := port.parseAttributes(attrs)
assert.NoError(t, err)
testAssertCommonAttrs(t, port)
assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_PF), port.PortFlavour)
assert.Equal(t, uint16(1), *port.PfNumber)
assert.Nil(t, port.Fn)
assert.Nil(t, port.PortNumber)
assert.Nil(t, port.VfNumber)
assert.Nil(t, port.SfNumber)
assert.Nil(t, port.ControllerNumber)
assert.Nil(t, port.External)
})
t.Run("flavor pcivf", func(t *testing.T) {
nlAttrs := testGetDevlinkPortCommonAttrs()
nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs)
attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs)
port := &DevlinkPort{}
err := port.parseAttributes(attrs)
assert.NoError(t, err)
testAssertCommonAttrs(t, port)
assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_VF), port.PortFlavour)
assert.Equal(t, uint16(0), *port.PfNumber)
assert.Equal(t, uint16(4), *port.VfNumber)
assert.Equal(t, "00:11:22:33:44:55", port.Fn.HwAddr.String())
assert.Nil(t, port.PortNumber)
assert.Nil(t, port.SfNumber)
assert.Nil(t, port.ControllerNumber)
assert.Nil(t, port.External)
})
t.Run("flavor pcisf", func(t *testing.T) {
nlAttrs := testGetDevlinkPortCommonAttrs()
nlAttrs = testAddDevlinkPortSfAttrs(nlAttrs)
attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs)
port := &DevlinkPort{}
err := port.parseAttributes(attrs)
assert.NoError(t, err)
testAssertCommonAttrs(t, port)
assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_SF), port.PortFlavour)
assert.Equal(t, uint16(0), *port.PfNumber)
assert.Equal(t, uint32(123), *port.SfNumber)
assert.Equal(t, "00:11:22:33:44:55", port.Fn.HwAddr.String())
assert.Nil(t, port.PortNumber)
assert.Nil(t, port.VfNumber)
assert.Nil(t, port.ControllerNumber)
assert.Nil(t, 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)
assert.NoError(t, err)
assert.Equal(t, uint32(0), *port.ControllerNumber)
assert.Equal(t, false, *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)
assert.NoError(t, err)
assert.Equal(t, uint32(1), *port.ControllerNumber)
assert.Equal(t, true, *port.External)
})
}

1
go.mod
View File

@@ -3,6 +3,7 @@ module github.com/vishvananda/netlink
go 1.12
require (
github.com/stretchr/testify v1.10.0
github.com/vishvananda/netns v0.0.5
golang.org/x/sys v0.10.0
)

19
go.sum
View File

@@ -1,5 +1,24 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -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 (