Skip to content

Commit

Permalink
Add functions to work with devlink dev parameters
Browse files Browse the repository at this point in the history
Signed-off-by: Yury Kulazhenkov <[email protected]>
  • Loading branch information
ykulazhenkov committed Feb 22, 2024
1 parent 535775a commit 1f15f18
Show file tree
Hide file tree
Showing 9 changed files with 381 additions and 2 deletions.
29 changes: 29 additions & 0 deletions pkg/helper/mock/mock_helper.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions pkg/host/internal/lib/netlink/mock/mock_netlink.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions pkg/host/internal/lib/netlink/netlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ type NetlinkLib interface {
// VDPANewDev adds new VDPA device
// Equivalent to: `vdpa dev add name <name> mgmtdev <mgmtBus>/mgmtName [params]`
VDPANewDev(name, mgmtBus, mgmtName string, params netlink.VDPANewDevParams) error
// DevlinkGetDeviceParamByName returns specific parameter for devlink device
// Equivalent to: `devlink dev param show <bus>/<device> name <param>`
DevlinkGetDeviceParamByName(bus string, device string, param string) (*netlink.DevlinkParam, error)
// DevlinkSetDeviceParam set specific parameter for devlink device
// Equivalent to: `devlink dev param set <bus>/<device> name <param> cmode <cmode> value <value>`
// cmode argument should contain valid cmode value as uint8, modes are define in nl.DEVLINK_PARAM_CMODE_* constants
// value argument should have one of the following types: uint8, uint16, uint32, string, bool
DevlinkSetDeviceParam(bus string, device string, param string, cmode uint8, value interface{}) error
}

type libWrapper struct{}
Expand Down Expand Up @@ -111,3 +119,17 @@ func (w *libWrapper) VDPADelDev(name string) error {
func (w *libWrapper) VDPANewDev(name, mgmtBus, mgmtName string, params netlink.VDPANewDevParams) error {
return netlink.VDPANewDev(name, mgmtBus, mgmtName, params)
}

// DevlinkGetDeviceParamByName returns specific parameter for devlink device
// Equivalent to: `devlink dev param show <bus>/<device> name <param>`
func (w *libWrapper) DevlinkGetDeviceParamByName(bus string, device string, param string) (*netlink.DevlinkParam, error) {
return netlink.DevlinkGetDeviceParamByName(bus, device, param)
}

// DevlinkSetDeviceParam set specific parameter for devlink device
// Equivalent to: `devlink dev param set <bus>/<device> name <param> cmode <cmode> value <value>`
// cmode argument should contain valid cmode value as uint8, modes are define in nl.DEVLINK_PARAM_CMODE_* constants
// value argument should have one of the following types: uint8, uint16, uint32, string, bool
func (w *libWrapper) DevlinkSetDeviceParam(bus string, device string, param string, cmode uint8, value interface{}) error {
return netlink.DevlinkSetDeviceParam(bus, device, param, cmode, value)
}
96 changes: 95 additions & 1 deletion pkg/host/internal/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
"time"

"github.com/cenkalti/backoff"
"github.com/vishvananda/netlink/nl"
"sigs.k8s.io/controller-runtime/pkg/log"

"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
dputilsPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/dputils"
netlinkPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
Expand All @@ -21,12 +23,14 @@ import (
type network struct {
utilsHelper utils.CmdInterface
dputilsLib dputilsPkg.DPUtilsLib
netlinkLib netlinkPkg.NetlinkLib
}

func New(utilsHelper utils.CmdInterface, dputilsLib dputilsPkg.DPUtilsLib) types.NetworkInterface {
func New(utilsHelper utils.CmdInterface, dputilsLib dputilsPkg.DPUtilsLib, netlinkLib netlinkPkg.NetlinkLib) types.NetworkInterface {
return &network{
utilsHelper: utilsHelper,
dputilsLib: dputilsLib,
netlinkLib: netlinkLib,
}
}

Expand Down Expand Up @@ -196,3 +200,93 @@ func (n *network) GetNetDevLinkSpeed(ifaceName string) string {

return fmt.Sprintf("%s Mb/s", strings.TrimSpace(string(data)))
}

// GetDeviceParam returns devlink parameter for the device as a string, if the parameter has multiple values
// then the function will return only first one from the list.
func (n *network) GetDevlinkDeviceParam(pciAddr, paramName string) (string, error) {
funcLog := log.Log.WithValues("device", pciAddr, "param", paramName)
funcLog.V(2).Info("GetDevlinkDeviceParam(): get device parameter")
param, err := n.netlinkLib.DevlinkGetDeviceParamByName(consts.BusPci, pciAddr, paramName)
if err != nil {
funcLog.Error(err, "GetDevlinkDeviceParam(): fail to get devlink device param")
return "", err
}
if len(param.Values) == 0 {
funcLog.Error(fmt.Errorf("param %s has no value", paramName),
"GetDevlinkDeviceParam(): error")
}
var value string
switch param.Type {
case nl.DEVLINK_PARAM_TYPE_U8, nl.DEVLINK_PARAM_TYPE_U16, nl.DEVLINK_PARAM_TYPE_U32:
var valData uint64
switch v := param.Values[0].Data.(type) {
case uint8:
valData = uint64(v)
case uint16:
valData = uint64(v)
case uint32:
valData = uint64(v)
default:
return "", fmt.Errorf("unexpected uint type type")
}
value = strconv.FormatUint(valData, 10)

case nl.DEVLINK_PARAM_TYPE_STRING:
value = param.Values[0].Data.(string)
case nl.DEVLINK_PARAM_TYPE_BOOL:
value = strconv.FormatBool(param.Values[0].Data.(bool))
default:
return "", fmt.Errorf("unknown value type: %d", param.Type)
}
funcLog.V(2).Info("GetDevlinkDeviceParam(): result", "value", value)
return value, nil
}

// SetDevlinkDeviceParam set devlink parameter for the device, accepts paramName and value
// as a string. Automatically set CMODE for the parameter and converts the value to the right
// type before submitting it.
func (n *network) SetDevlinkDeviceParam(pciAddr, paramName, value string) error {
funcLog := log.Log.WithValues("device", pciAddr, "param", paramName, "value", value)
funcLog.V(2).Info("SetDevlinkDeviceParam(): set device parameter")
param, err := n.netlinkLib.DevlinkGetDeviceParamByName(consts.BusPci, pciAddr, paramName)
if err != nil {
funcLog.Error(err, "SetDevlinkDeviceParam(): can get existing param data")
return err
}
if len(param.Values) == 0 {
err = fmt.Errorf("param %s has no value", paramName)
funcLog.Error(err, "SetDevlinkDeviceParam(): error")
return err
}
targetCMOD := param.Values[0].CMODE
var typedValue interface{}
var v uint64
switch param.Type {
case nl.DEVLINK_PARAM_TYPE_U8:
v, err = strconv.ParseUint(value, 10, 8)
typedValue = uint8(v)
case nl.DEVLINK_PARAM_TYPE_U16:
v, err = strconv.ParseUint(value, 10, 16)
typedValue = uint16(v)
case nl.DEVLINK_PARAM_TYPE_U32:
v, err = strconv.ParseUint(value, 10, 32)
typedValue = uint32(v)
case nl.DEVLINK_PARAM_TYPE_STRING:
err = nil
typedValue = value
case nl.DEVLINK_PARAM_TYPE_BOOL:
typedValue, err = strconv.ParseBool(value)
default:
return fmt.Errorf("parameter has unknown value type: %d", param.Type)
}
if err != nil {
err = fmt.Errorf("failed to convert value %s to the required type: %d", value, param.Type)
funcLog.Error(err, "SetDevlinkDeviceParam(): error")
return err
}
if err := n.netlinkLib.DevlinkSetDeviceParam(consts.BusPci, pciAddr, paramName, targetCMOD, typedValue); err != nil {
funcLog.Error(err, "SetDevlinkDeviceParam(): failed to set parameter")
return err
}
return nil
}
148 changes: 148 additions & 0 deletions pkg/host/internal/network/network_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package network

import (
"fmt"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl"

"github.com/golang/mock/gomock"

hostMockPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper/mock"
dputilsMockPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/dputils/mock"
netlinkMockPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink/mock"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types"
)

func getDevlinkParam(t uint8, value interface{}) *netlink.DevlinkParam {
return &netlink.DevlinkParam{
Name: "test_param",
Type: t,
Values: []netlink.DevlinkParamValue{
{Data: value, CMODE: nl.DEVLINK_PARAM_CMODE_DRIVERINIT}},
}
}

var _ = Describe("Network", func() {
var (
n types.NetworkInterface
netlinkLibMock *netlinkMockPkg.MockNetlinkLib
dputilsLibMock *dputilsMockPkg.MockDPUtilsLib
hostMock *hostMockPkg.MockHostHelpersInterface

testCtrl *gomock.Controller
testErr = fmt.Errorf("test")
)
BeforeEach(func() {
testCtrl = gomock.NewController(GinkgoT())
netlinkLibMock = netlinkMockPkg.NewMockNetlinkLib(testCtrl)
dputilsLibMock = dputilsMockPkg.NewMockDPUtilsLib(testCtrl)
hostMock = hostMockPkg.NewMockHostHelpersInterface(testCtrl)

n = New(hostMock, dputilsLibMock, netlinkLibMock)
})

AfterEach(func() {
testCtrl.Finish()
})
Context("GetDevlinkDeviceParam", func() {
It("get - string", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_STRING, "test_value"), nil)
result, err := n.GetDevlinkDeviceParam("0000:d8:00.1", "param_name")
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal("test_value"))
})
It("get - uint8", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_U8, uint8(8)), nil)
result, err := n.GetDevlinkDeviceParam("0000:d8:00.1", "param_name")
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal("8"))
})
It("get - uint16", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_U16, uint16(16)), nil)
result, err := n.GetDevlinkDeviceParam("0000:d8:00.1", "param_name")
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal("16"))
})
It("get - uint32", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_U32, uint32(32)), nil)
result, err := n.GetDevlinkDeviceParam("0000:d8:00.1", "param_name")
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal("32"))
})
It("get - bool", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_BOOL, false), nil)
result, err := n.GetDevlinkDeviceParam("0000:d8:00.1", "param_name")
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal("false"))
})
It("failed", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(nil, testErr)
_, err := n.GetDevlinkDeviceParam("0000:d8:00.1", "param_name")
Expect(err).To(HaveOccurred())
})
})
Context("SetDevlinkDeviceParam", func() {
It("set - string", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_STRING, "test_value"), nil)
netlinkLibMock.EXPECT().DevlinkSetDeviceParam("pci", "0000:d8:00.1", "param_name",
uint8(nl.DEVLINK_PARAM_CMODE_DRIVERINIT), "test_value").Return(nil)
err := n.SetDevlinkDeviceParam("0000:d8:00.1", "param_name", "test_value")
Expect(err).NotTo(HaveOccurred())
})
It("set - uint8", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_U8, uint8(8)), nil)
netlinkLibMock.EXPECT().DevlinkSetDeviceParam("pci", "0000:d8:00.1", "param_name",
uint8(nl.DEVLINK_PARAM_CMODE_DRIVERINIT), uint8(100)).Return(nil)
err := n.SetDevlinkDeviceParam("0000:d8:00.1", "param_name", "100")
Expect(err).NotTo(HaveOccurred())
})
It("set - uint16", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_U16, uint16(16)), nil)
netlinkLibMock.EXPECT().DevlinkSetDeviceParam("pci", "0000:d8:00.1", "param_name",
uint8(nl.DEVLINK_PARAM_CMODE_DRIVERINIT), uint16(100)).Return(nil)
err := n.SetDevlinkDeviceParam("0000:d8:00.1", "param_name", "100")
Expect(err).NotTo(HaveOccurred())
})
It("set - uint32", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_U32, uint32(32)), nil)
netlinkLibMock.EXPECT().DevlinkSetDeviceParam("pci", "0000:d8:00.1", "param_name",
uint8(nl.DEVLINK_PARAM_CMODE_DRIVERINIT), uint32(100)).Return(nil)
err := n.SetDevlinkDeviceParam("0000:d8:00.1", "param_name", "100")
Expect(err).NotTo(HaveOccurred())
})
It("set - bool", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_BOOL, false), nil)
netlinkLibMock.EXPECT().DevlinkSetDeviceParam("pci", "0000:d8:00.1", "param_name",
uint8(nl.DEVLINK_PARAM_CMODE_DRIVERINIT), true).Return(nil)
err := n.SetDevlinkDeviceParam("0000:d8:00.1", "param_name", "true")
Expect(err).NotTo(HaveOccurred())
})
It("failed to get", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
nil, testErr)
err := n.SetDevlinkDeviceParam("0000:d8:00.1", "param_name", "true")
Expect(err).To(HaveOccurred())
})
It("failed to set", func() {
netlinkLibMock.EXPECT().DevlinkGetDeviceParamByName("pci", "0000:d8:00.1", "param_name").Return(
getDevlinkParam(nl.DEVLINK_PARAM_TYPE_BOOL, false), nil)
netlinkLibMock.EXPECT().DevlinkSetDeviceParam("pci", "0000:d8:00.1", "param_name",
uint8(nl.DEVLINK_PARAM_CMODE_DRIVERINIT), true).Return(testErr)
err := n.SetDevlinkDeviceParam("0000:d8:00.1", "param_name", "true")
Expect(err).To(HaveOccurred())
})
})
})
Loading

0 comments on commit 1f15f18

Please sign in to comment.