Add support to get devlink resources

- Update nl package with new netlink attribute types and consts
- Define structs to model devlink device resources
- Add DevlinkGetDeviceResources method to return device resources
- Add basic test

Signed-off-by: adrianc <adrianc@nvidia.com>
This commit is contained in:
adrianc 2023-11-13 18:12:25 +02:00 committed by Alessandro Boch
parent 2bbba08be2
commit 36b61ad22c
3 changed files with 265 additions and 30 deletions

View File

@ -84,6 +84,169 @@ type DevlinkDeviceInfo struct {
FwUndi string FwUndi string
} }
// DevlinkResource represents a device resource
type DevlinkResource struct {
Name string
ID uint64
Size uint64
SizeNew uint64
SizeMin uint64
SizeMax uint64
SizeGranularity uint64
PendingChange bool
Unit uint8
SizeValid bool
OCCValid bool
OCCSize uint64
Parent *DevlinkResource
Children []DevlinkResource
}
// parseAttributes parses provided Netlink Attributes and populates DevlinkResource, returns error if occured
func (dlr *DevlinkResource) parseAttributes(attrs map[uint16]syscall.NetlinkRouteAttr) error {
var attr syscall.NetlinkRouteAttr
var ok bool
// mandatory attributes
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_ID]
if !ok {
return fmt.Errorf("missing resource id")
}
dlr.ID = native.Uint64(attr.Value)
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_NAME]
if !ok {
return fmt.Errorf("missing resource name")
}
dlr.Name = nl.BytesToString(attr.Value)
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE]
if !ok {
return fmt.Errorf("missing resource size")
}
dlr.Size = native.Uint64(attr.Value)
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_GRAN]
if !ok {
return fmt.Errorf("missing resource size granularity")
}
dlr.SizeGranularity = native.Uint64(attr.Value)
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_UNIT]
if !ok {
return fmt.Errorf("missing resource unit")
}
dlr.Unit = uint8(attr.Value[0])
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_MIN]
if !ok {
return fmt.Errorf("missing resource size min")
}
dlr.SizeMin = native.Uint64(attr.Value)
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_MAX]
if !ok {
return fmt.Errorf("missing resource size max")
}
dlr.SizeMax = native.Uint64(attr.Value)
// optional attributes
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_OCC]
if ok {
dlr.OCCSize = native.Uint64(attr.Value)
dlr.OCCValid = true
}
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_VALID]
if ok {
dlr.SizeValid = uint8(attr.Value[0]) != 0
}
dlr.SizeNew = dlr.Size
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_SIZE_NEW]
if ok {
dlr.SizeNew = native.Uint64(attr.Value)
}
dlr.PendingChange = dlr.Size != dlr.SizeNew
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_LIST]
if ok {
// handle nested resoruces recursively
subResources, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return err
}
for _, subresource := range subResources {
resource := DevlinkResource{Parent: dlr}
attrs, err := nl.ParseRouteAttrAsMap(subresource.Value)
if err != nil {
return err
}
err = resource.parseAttributes(attrs)
if err != nil {
return fmt.Errorf("failed to parse child resource, parent:%s. %w", dlr.Name, err)
}
dlr.Children = append(dlr.Children, resource)
}
}
return nil
}
// DevlinkResources represents all devlink resources of a devlink device
type DevlinkResources struct {
Bus string
Device string
Resources []DevlinkResource
}
// parseAttributes parses provided Netlink Attributes and populates DevlinkResources, returns error if occured
func (dlrs *DevlinkResources) parseAttributes(attrs map[uint16]syscall.NetlinkRouteAttr) error {
var attr syscall.NetlinkRouteAttr
var ok bool
// Bus
attr, ok = attrs[nl.DEVLINK_ATTR_BUS_NAME]
if !ok {
return fmt.Errorf("missing bus name")
}
dlrs.Bus = nl.BytesToString(attr.Value)
// Device
attr, ok = attrs[nl.DEVLINK_ATTR_DEV_NAME]
if !ok {
return fmt.Errorf("missing device name")
}
dlrs.Device = nl.BytesToString(attr.Value)
// Resource List
attr, ok = attrs[nl.DEVLINK_ATTR_RESOURCE_LIST]
if !ok {
return fmt.Errorf("missing resource list")
}
resourceAttrs, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return err
}
for _, resourceAttr := range resourceAttrs {
resource := DevlinkResource{}
attrs, err := nl.ParseRouteAttrAsMap(resourceAttr.Value)
if err != nil {
return err
}
err = resource.parseAttributes(attrs)
if err != nil {
return fmt.Errorf("failed to parse root resoruces, %w", err)
}
dlrs.Resources = append(dlrs.Resources, resource)
}
return nil
}
func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) { func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
devices := make([]*DevlinkDevice, 0, len(msgs)) devices := make([]*DevlinkDevice, 0, len(msgs))
for _, m := range msgs { for _, m := range msgs {
@ -443,6 +606,35 @@ func (h *Handle) DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint
return port, err return port, err
} }
// DevlinkGetDeviceResources returns devlink device resources
func DevlinkGetDeviceResources(bus string, device string) (*DevlinkResources, error) {
return pkgHandle.DevlinkGetDeviceResources(bus, device)
}
// DevlinkGetDeviceResources returns devlink device resources
func (h *Handle) DevlinkGetDeviceResources(bus string, device string) (*DevlinkResources, error) {
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_RESOURCE_DUMP, bus, device)
if err != nil {
return nil, err
}
respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
if err != nil {
return nil, err
}
var resources DevlinkResources
for _, m := range respmsg {
attrs, err := nl.ParseRouteAttrAsMap(m[nl.SizeofGenlmsg:])
if err != nil {
return nil, err
}
resources.parseAttributes(attrs)
}
return &resources, nil
}
// DevLinkGetPortByIndex provides a pointer to devlink portand nil error, // DevLinkGetPortByIndex provides a pointer to devlink portand nil error,
// otherwise returns an error code. // otherwise returns an error code.
func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) { func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {

View File

@ -1,3 +1,4 @@
//go:build linux
// +build linux // +build linux
package netlink package netlink
@ -262,3 +263,26 @@ func areInfoStructsEqual(first *DevlinkDeviceInfo, second *DevlinkDeviceInfo) bo
} }
return true return true
} }
func TestDevlinkGetDeviceResources(t *testing.T) {
minKernelRequired(t, 5, 11)
tearDown := setUpNetlinkTestWithKModule(t, "devlink")
defer tearDown()
if bus == "" || device == "" {
//TODO: setup netdevsim device instead of getting device from flags
t.Log("devlink bus and device are empty, skipping test")
t.SkipNow()
}
res, err := DevlinkGetDeviceResources(bus, device)
if err != nil {
t.Fatalf("failed to get device(%s/%s) resources. %s", bus, device, err)
}
if res.Bus != bus || res.Device != device {
t.Fatalf("missmatching bus/device")
}
t.Logf("Resources: %+v", res)
}

View File

@ -9,39 +9,54 @@ const (
) )
const ( const (
DEVLINK_CMD_GET = 1 DEVLINK_CMD_GET = 1
DEVLINK_CMD_PORT_GET = 5 DEVLINK_CMD_PORT_GET = 5
DEVLINK_CMD_PORT_SET = 6 DEVLINK_CMD_PORT_SET = 6
DEVLINK_CMD_PORT_NEW = 7 DEVLINK_CMD_PORT_NEW = 7
DEVLINK_CMD_PORT_DEL = 8 DEVLINK_CMD_PORT_DEL = 8
DEVLINK_CMD_ESWITCH_GET = 29 DEVLINK_CMD_ESWITCH_GET = 29
DEVLINK_CMD_ESWITCH_SET = 30 DEVLINK_CMD_ESWITCH_SET = 30
DEVLINK_CMD_INFO_GET = 51 DEVLINK_CMD_RESOURCE_DUMP = 36
DEVLINK_CMD_INFO_GET = 51
) )
const ( const (
DEVLINK_ATTR_BUS_NAME = 1 DEVLINK_ATTR_BUS_NAME = 1
DEVLINK_ATTR_DEV_NAME = 2 DEVLINK_ATTR_DEV_NAME = 2
DEVLINK_ATTR_PORT_INDEX = 3 DEVLINK_ATTR_PORT_INDEX = 3
DEVLINK_ATTR_PORT_TYPE = 4 DEVLINK_ATTR_PORT_TYPE = 4
DEVLINK_ATTR_PORT_NETDEV_IFINDEX = 6 DEVLINK_ATTR_PORT_NETDEV_IFINDEX = 6
DEVLINK_ATTR_PORT_NETDEV_NAME = 7 DEVLINK_ATTR_PORT_NETDEV_NAME = 7
DEVLINK_ATTR_PORT_IBDEV_NAME = 8 DEVLINK_ATTR_PORT_IBDEV_NAME = 8
DEVLINK_ATTR_ESWITCH_MODE = 25 DEVLINK_ATTR_ESWITCH_MODE = 25
DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26 DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26
DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62 DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62
DEVLINK_ATTR_PORT_FLAVOUR = 77 DEVLINK_ATTR_RESOURCE_LIST = 63 /* nested */
DEVLINK_ATTR_INFO_DRIVER_NAME = 98 DEVLINK_ATTR_RESOURCE = 64 /* nested */
DEVLINK_ATTR_INFO_SERIAL_NUMBER = 99 DEVLINK_ATTR_RESOURCE_NAME = 65 /* string */
DEVLINK_ATTR_INFO_VERSION_FIXED = 100 DEVLINK_ATTR_RESOURCE_ID = 66 /* u64 */
DEVLINK_ATTR_INFO_VERSION_RUNNING = 101 DEVLINK_ATTR_RESOURCE_SIZE = 67 /* u64 */
DEVLINK_ATTR_INFO_VERSION_STORED = 102 DEVLINK_ATTR_RESOURCE_SIZE_NEW = 68 /* u64 */
DEVLINK_ATTR_INFO_VERSION_NAME = 103 DEVLINK_ATTR_RESOURCE_SIZE_VALID = 69 /* u8 */
DEVLINK_ATTR_INFO_VERSION_VALUE = 104 DEVLINK_ATTR_RESOURCE_SIZE_MIN = 70 /* u64 */
DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127 DEVLINK_ATTR_RESOURCE_SIZE_MAX = 71 /* u64 */
DEVLINK_ATTR_PORT_FUNCTION = 145 DEVLINK_ATTR_RESOURCE_SIZE_GRAN = 72 /* u64 */
DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150 DEVLINK_ATTR_RESOURCE_UNIT = 73 /* u8 */
DEVLINK_ATTR_PORT_PCI_SF_NUMBER = 164 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_INFO_DRIVER_NAME = 98
DEVLINK_ATTR_INFO_SERIAL_NUMBER = 99
DEVLINK_ATTR_INFO_VERSION_FIXED = 100
DEVLINK_ATTR_INFO_VERSION_RUNNING = 101
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
) )
const ( const (
@ -94,3 +109,7 @@ const (
DEVLINK_PORT_FN_OPSTATE_DETACHED = 0 DEVLINK_PORT_FN_OPSTATE_DETACHED = 0
DEVLINK_PORT_FN_OPSTATE_ATTACHED = 1 DEVLINK_PORT_FN_OPSTATE_ATTACHED = 1
) )
const (
DEVLINK_RESOURCE_UNIT_ENTRY uint8 = 0
)