mirror of
https://github.com/vishvananda/netlink
synced 2025-04-01 22:58:37 +00:00
Add devlink commands for devlink device information
This patch adds very basic support for getting information about devlink devices which are typically PCI devices which exposes Networking switch or legacy devices. This information includes bus name, device name and eswitch modes. This is done through devlink family of commands via generic netlink sockets provided by Linux kernel. DevlinkDevice represents a devlink device which is identified by bus name and device name (unlike interface index for netdevices). It contains the DevlinkDevAttrs device attributes. Currently only eswitch attributes are queried. In future more attributes such as port, shared buffer, traffic class will be added. Signed-off-by: Parav Pandit <parav@mellanox.com>
This commit is contained in:
parent
cb78b18701
commit
332a6983d9
167
devlink_linux.go
Normal file
167
devlink_linux.go
Normal file
@ -0,0 +1,167 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// DevlinkDevEswitchAttr represents device's eswitch attributes
|
||||
type DevlinkDevEswitchAttr struct {
|
||||
Mode string
|
||||
InlineMode string
|
||||
EncapMode string
|
||||
}
|
||||
|
||||
// DevlinkDevAttrs represents device attributes
|
||||
type DevlinkDevAttrs struct {
|
||||
Eswitch DevlinkDevEswitchAttr
|
||||
}
|
||||
|
||||
// DevlinkDevice represents device and its attributes
|
||||
type DevlinkDevice struct {
|
||||
BusName string
|
||||
DeviceName string
|
||||
Attrs DevlinkDevAttrs
|
||||
}
|
||||
|
||||
func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
|
||||
devices := make([]*DevlinkDevice, 0, len(msgs))
|
||||
for _, m := range msgs {
|
||||
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dev := &DevlinkDevice{}
|
||||
if err = dev.parseAttributes(attrs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
devices = append(devices, dev)
|
||||
}
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func parseEswitchMode(mode uint16) string {
|
||||
var eswitchMode = map[uint16]string{
|
||||
nl.DEVLINK_ESWITCH_MODE_LEGACY: "legacy",
|
||||
nl.DEVLINK_ESWITCH_MODE_SWITCHDEV: "switchdev",
|
||||
}
|
||||
if eswitchMode[mode] == "" {
|
||||
return "unknown"
|
||||
} else {
|
||||
return eswitchMode[mode]
|
||||
}
|
||||
}
|
||||
|
||||
func parseEswitchInlineMode(inlinemode uint8) string {
|
||||
var eswitchInlineMode = map[uint8]string{
|
||||
nl.DEVLINK_ESWITCH_INLINE_MODE_NONE: "none",
|
||||
nl.DEVLINK_ESWITCH_INLINE_MODE_LINK: "link",
|
||||
nl.DEVLINK_ESWITCH_INLINE_MODE_NETWORK: "network",
|
||||
nl.DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT: "transport",
|
||||
}
|
||||
if eswitchInlineMode[inlinemode] == "" {
|
||||
return "unknown"
|
||||
} else {
|
||||
return eswitchInlineMode[inlinemode]
|
||||
}
|
||||
}
|
||||
|
||||
func parseEswitchEncapMode(encapmode uint8) string {
|
||||
var eswitchEncapMode = map[uint8]string{
|
||||
nl.DEVLINK_ESWITCH_ENCAP_MODE_NONE: "disable",
|
||||
nl.DEVLINK_ESWITCH_ENCAP_MODE_BASIC: "enable",
|
||||
}
|
||||
if eswitchEncapMode[encapmode] == "" {
|
||||
return "unknown"
|
||||
} else {
|
||||
return eswitchEncapMode[encapmode]
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DevlinkDevice) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
|
||||
for _, a := range attrs {
|
||||
switch a.Attr.Type {
|
||||
case nl.DEVLINK_ATTR_BUS_NAME:
|
||||
d.BusName = string(a.Value)
|
||||
case nl.DEVLINK_ATTR_DEV_NAME:
|
||||
d.DeviceName = string(a.Value)
|
||||
case nl.DEVLINK_ATTR_ESWITCH_MODE:
|
||||
d.Attrs.Eswitch.Mode = parseEswitchMode(native.Uint16(a.Value))
|
||||
case nl.DEVLINK_ATTR_ESWITCH_INLINE_MODE:
|
||||
d.Attrs.Eswitch.InlineMode = parseEswitchInlineMode(uint8(a.Value[0]))
|
||||
case nl.DEVLINK_ATTR_ESWITCH_ENCAP_MODE:
|
||||
d.Attrs.Eswitch.EncapMode = parseEswitchEncapMode(uint8(a.Value[0]))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dev *DevlinkDevice) parseEswitchAttrs(msgs [][]byte) {
|
||||
m := msgs[0]
|
||||
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dev.parseAttributes(attrs)
|
||||
}
|
||||
|
||||
func (h *Handle) getEswitchAttrs(family *GenlFamily, dev *DevlinkDevice) {
|
||||
msg := &nl.Genlmsg{
|
||||
Command: nl.DEVLINK_CMD_ESWITCH_GET,
|
||||
Version: nl.GENL_DEVLINK_VERSION,
|
||||
}
|
||||
req := h.newNetlinkRequest(int(family.ID), unix.NLM_F_REQUEST|unix.NLM_F_ACK)
|
||||
req.AddData(msg)
|
||||
|
||||
b := make([]byte, len(dev.BusName))
|
||||
copy(b, dev.BusName)
|
||||
data := nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, b)
|
||||
req.AddData(data)
|
||||
|
||||
b = make([]byte, len(dev.DeviceName))
|
||||
copy(b, dev.DeviceName)
|
||||
data = nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, b)
|
||||
req.AddData(data)
|
||||
|
||||
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dev.parseEswitchAttrs(msgs)
|
||||
}
|
||||
|
||||
// DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
|
||||
// otherwise returns an error code.
|
||||
func (h *Handle) DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
|
||||
f, err := h.GenlFamilyGet(nl.GENL_DEVLINK_NAME)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg := &nl.Genlmsg{
|
||||
Command: nl.DEVLINK_CMD_GET,
|
||||
Version: nl.GENL_DEVLINK_VERSION,
|
||||
}
|
||||
req := h.newNetlinkRequest(int(f.ID),
|
||||
unix.NLM_F_REQUEST|unix.NLM_F_ACK|unix.NLM_F_DUMP)
|
||||
req.AddData(msg)
|
||||
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
devices, err := parseDevLinkDeviceList(msgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, d := range devices {
|
||||
h.getEswitchAttrs(f, d)
|
||||
}
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
// DevLinkGetDeviceList provides a pointer to devlink devices and nil error,
|
||||
// otherwise returns an error code.
|
||||
func DevLinkGetDeviceList() ([]*DevlinkDevice, error) {
|
||||
return pkgHandle.DevLinkGetDeviceList()
|
||||
}
|
34
devlink_test.go
Normal file
34
devlink_test.go
Normal file
@ -0,0 +1,34 @@
|
||||
// +build linux
|
||||
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setupDevlinkKModule(t *testing.T, name string) {
|
||||
skipUnlessRoot(t)
|
||||
file, err := ioutil.ReadFile("/proc/modules")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to open /proc/modules", err)
|
||||
}
|
||||
for _, line := range strings.Split(string(file), "\n") {
|
||||
n := strings.Split(line, " ")[0]
|
||||
if n == name {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
t.Skipf("Test requires kmodule %q.", name)
|
||||
}
|
||||
|
||||
func TestDevLinkGetDeviceList(t *testing.T) {
|
||||
minKernelRequired(t, 4, 12)
|
||||
setUpNetlinkTestWithKModule(t, "devlink")
|
||||
_, err := DevLinkGetDeviceList()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
39
nl/devlink_linux.go
Normal file
39
nl/devlink_linux.go
Normal file
@ -0,0 +1,39 @@
|
||||
package nl
|
||||
|
||||
// All the following constants are coming from:
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/devlink.h
|
||||
|
||||
const (
|
||||
GENL_DEVLINK_VERSION = 1
|
||||
GENL_DEVLINK_NAME = "devlink"
|
||||
)
|
||||
|
||||
const (
|
||||
DEVLINK_CMD_GET = 1
|
||||
DEVLINK_CMD_ESWITCH_GET = 29
|
||||
)
|
||||
|
||||
const (
|
||||
DEVLINK_ATTR_BUS_NAME = 1
|
||||
DEVLINK_ATTR_DEV_NAME = 2
|
||||
DEVLINK_ATTR_ESWITCH_MODE = 25
|
||||
DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26
|
||||
DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62
|
||||
)
|
||||
|
||||
const (
|
||||
DEVLINK_ESWITCH_MODE_LEGACY = 0
|
||||
DEVLINK_ESWITCH_MODE_SWITCHDEV = 1
|
||||
)
|
||||
|
||||
const (
|
||||
DEVLINK_ESWITCH_INLINE_MODE_NONE = 0
|
||||
DEVLINK_ESWITCH_INLINE_MODE_LINK = 1
|
||||
DEVLINK_ESWITCH_INLINE_MODE_NETWORK = 2
|
||||
DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT = 3
|
||||
)
|
||||
|
||||
const (
|
||||
DEVLINK_ESWITCH_ENCAP_MODE_NONE = 0
|
||||
DEVLINK_ESWITCH_ENCAP_MODE_BASIC = 1
|
||||
)
|
Loading…
Reference in New Issue
Block a user