Skip to content

Commit 5daafaf

Browse files
ykulazhenkovaboch
authored andcommitted
Add functions to work with devlink device parameters
Functions added: DevlinkGetDeviceParams - get all parameters for device DevlinkGetDeviceParamByName - get specific parameter for device DevlinkSetDeviceParam - set parameter for device Signed-off-by: Yury Kulazhenkov <[email protected]>
1 parent 857968a commit 5daafaf

File tree

3 files changed

+382
-0
lines changed

3 files changed

+382
-0
lines changed

devlink_linux.go

+239
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,107 @@ func (dlrs *DevlinkResources) parseAttributes(attrs map[uint16]syscall.NetlinkRo
247247
return nil
248248
}
249249

250+
// DevlinkParam represents parameter of the device
251+
type DevlinkParam struct {
252+
Name string
253+
IsGeneric bool
254+
Type uint8 // possible values are in nl.DEVLINK_PARAM_TYPE_* constants
255+
Values []DevlinkParamValue
256+
}
257+
258+
// DevlinkParamValue contains values of the parameter
259+
// Data field contains specific type which can be casted by unsing info from the DevlinkParam.Type field
260+
type DevlinkParamValue struct {
261+
rawData []byte
262+
Data interface{}
263+
CMODE uint8 // possible values are in nl.DEVLINK_PARAM_CMODE_* constants
264+
}
265+
266+
// parseAttributes parses provided Netlink Attributes and populates DevlinkParam, returns error if occured
267+
func (dlp *DevlinkParam) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
268+
var valuesList [][]syscall.NetlinkRouteAttr
269+
for _, attr := range attrs {
270+
switch attr.Attr.Type {
271+
case nl.DEVLINK_ATTR_PARAM:
272+
nattrs, err := nl.ParseRouteAttr(attr.Value)
273+
if err != nil {
274+
return err
275+
}
276+
for _, nattr := range nattrs {
277+
switch nattr.Attr.Type {
278+
case nl.DEVLINK_ATTR_PARAM_NAME:
279+
dlp.Name = nl.BytesToString(nattr.Value)
280+
case nl.DEVLINK_ATTR_PARAM_GENERIC:
281+
dlp.IsGeneric = true
282+
case nl.DEVLINK_ATTR_PARAM_TYPE:
283+
if len(nattr.Value) == 1 {
284+
dlp.Type = nattr.Value[0]
285+
}
286+
case nl.DEVLINK_ATTR_PARAM_VALUES_LIST:
287+
nnattrs, err := nl.ParseRouteAttr(nattr.Value)
288+
if err != nil {
289+
return err
290+
}
291+
valuesList = append(valuesList, nnattrs)
292+
}
293+
}
294+
}
295+
}
296+
for _, valAttr := range valuesList {
297+
v := DevlinkParamValue{}
298+
if err := v.parseAttributes(valAttr, dlp.Type); err != nil {
299+
return err
300+
}
301+
dlp.Values = append(dlp.Values, v)
302+
}
303+
return nil
304+
}
305+
306+
func (dlpv *DevlinkParamValue) parseAttributes(attrs []syscall.NetlinkRouteAttr, paramType uint8) error {
307+
for _, attr := range attrs {
308+
nattrs, err := nl.ParseRouteAttr(attr.Value)
309+
if err != nil {
310+
return err
311+
}
312+
var rawData []byte
313+
for _, nattr := range nattrs {
314+
switch nattr.Attr.Type {
315+
case nl.DEVLINK_ATTR_PARAM_VALUE_DATA:
316+
rawData = nattr.Value
317+
case nl.DEVLINK_ATTR_PARAM_VALUE_CMODE:
318+
if len(nattr.Value) == 1 {
319+
dlpv.CMODE = nattr.Value[0]
320+
}
321+
}
322+
}
323+
switch paramType {
324+
case nl.DEVLINK_PARAM_TYPE_U8:
325+
dlpv.Data = uint8(0)
326+
if rawData != nil && len(rawData) == 1 {
327+
dlpv.Data = uint8(rawData[0])
328+
}
329+
case nl.DEVLINK_PARAM_TYPE_U16:
330+
dlpv.Data = uint16(0)
331+
if rawData != nil {
332+
dlpv.Data = native.Uint16(rawData)
333+
}
334+
case nl.DEVLINK_PARAM_TYPE_U32:
335+
dlpv.Data = uint32(0)
336+
if rawData != nil {
337+
dlpv.Data = native.Uint32(rawData)
338+
}
339+
case nl.DEVLINK_PARAM_TYPE_STRING:
340+
dlpv.Data = ""
341+
if rawData != nil {
342+
dlpv.Data = nl.BytesToString(rawData)
343+
}
344+
case nl.DEVLINK_PARAM_TYPE_BOOL:
345+
dlpv.Data = rawData != nil
346+
}
347+
}
348+
return nil
349+
}
350+
250351
func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
251352
devices := make([]*DevlinkDevice, 0, len(msgs))
252353
for _, m := range msgs {
@@ -635,6 +736,144 @@ func (h *Handle) DevlinkGetDeviceResources(bus string, device string) (*DevlinkR
635736
return &resources, nil
636737
}
637738

739+
// DevlinkGetDeviceParams returns parameters for devlink device
740+
// Equivalent to: `devlink dev param show <bus>/<device>`
741+
func (h *Handle) DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) {
742+
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_GET, bus, device)
743+
if err != nil {
744+
return nil, err
745+
}
746+
req.Flags |= unix.NLM_F_DUMP
747+
respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
748+
if err != nil {
749+
return nil, err
750+
}
751+
var params []*DevlinkParam
752+
for _, m := range respmsg {
753+
attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
754+
if err != nil {
755+
return nil, err
756+
}
757+
p := &DevlinkParam{}
758+
if err := p.parseAttributes(attrs); err != nil {
759+
return nil, err
760+
}
761+
params = append(params, p)
762+
}
763+
764+
return params, nil
765+
}
766+
767+
// DevlinkGetDeviceParams returns parameters for devlink device
768+
// Equivalent to: `devlink dev param show <bus>/<device>`
769+
func DevlinkGetDeviceParams(bus string, device string) ([]*DevlinkParam, error) {
770+
return pkgHandle.DevlinkGetDeviceParams(bus, device)
771+
}
772+
773+
// DevlinkGetDeviceParamByName returns specific parameter for devlink device
774+
// Equivalent to: `devlink dev param show <bus>/<device> name <param>`
775+
func (h *Handle) DevlinkGetDeviceParamByName(bus string, device string, param string) (*DevlinkParam, error) {
776+
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_GET, bus, device)
777+
if err != nil {
778+
return nil, err
779+
}
780+
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_NAME, nl.ZeroTerminated(param)))
781+
respmsg, err := req.Execute(unix.NETLINK_GENERIC, 0)
782+
if err != nil {
783+
return nil, err
784+
}
785+
if len(respmsg) == 0 {
786+
return nil, fmt.Errorf("unexpected response")
787+
}
788+
attrs, err := nl.ParseRouteAttr(respmsg[0][nl.SizeofGenlmsg:])
789+
if err != nil {
790+
return nil, err
791+
}
792+
p := &DevlinkParam{}
793+
if err := p.parseAttributes(attrs); err != nil {
794+
return nil, err
795+
}
796+
return p, nil
797+
}
798+
799+
// DevlinkGetDeviceParamByName returns specific parameter for devlink device
800+
// Equivalent to: `devlink dev param show <bus>/<device> name <param>`
801+
func DevlinkGetDeviceParamByName(bus string, device string, param string) (*DevlinkParam, error) {
802+
return pkgHandle.DevlinkGetDeviceParamByName(bus, device, param)
803+
}
804+
805+
// DevlinkSetDeviceParam set specific parameter for devlink device
806+
// Equivalent to: `devlink dev param set <bus>/<device> name <param> cmode <cmode> value <value>`
807+
// cmode argument should contain valid cmode value as uint8, modes are define in nl.DEVLINK_PARAM_CMODE_* constants
808+
// value argument should have one of the following types: uint8, uint16, uint32, string, bool
809+
func (h *Handle) DevlinkSetDeviceParam(bus string, device string, param string, cmode uint8, value interface{}) error {
810+
// retrive the param type
811+
p, err := h.DevlinkGetDeviceParamByName(bus, device, param)
812+
if err != nil {
813+
return fmt.Errorf("failed to get device param: %v", err)
814+
}
815+
paramType := p.Type
816+
817+
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_PARAM_SET, bus, device)
818+
if err != nil {
819+
return err
820+
}
821+
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_TYPE, nl.Uint8Attr(paramType)))
822+
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_NAME, nl.ZeroTerminated(param)))
823+
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_VALUE_CMODE, nl.Uint8Attr(cmode)))
824+
825+
var valueAsBytes []byte
826+
switch paramType {
827+
case nl.DEVLINK_PARAM_TYPE_U8:
828+
v, ok := value.(uint8)
829+
if !ok {
830+
return fmt.Errorf("unepected value type required: uint8, actual: %T", value)
831+
}
832+
valueAsBytes = nl.Uint8Attr(v)
833+
case nl.DEVLINK_PARAM_TYPE_U16:
834+
v, ok := value.(uint16)
835+
if !ok {
836+
return fmt.Errorf("unepected value type required: uint16, actual: %T", value)
837+
}
838+
valueAsBytes = nl.Uint16Attr(v)
839+
case nl.DEVLINK_PARAM_TYPE_U32:
840+
v, ok := value.(uint32)
841+
if !ok {
842+
return fmt.Errorf("unepected value type required: uint32, actual: %T", value)
843+
}
844+
valueAsBytes = nl.Uint32Attr(v)
845+
case nl.DEVLINK_PARAM_TYPE_STRING:
846+
v, ok := value.(string)
847+
if !ok {
848+
return fmt.Errorf("unepected value type required: string, actual: %T", value)
849+
}
850+
valueAsBytes = nl.ZeroTerminated(v)
851+
case nl.DEVLINK_PARAM_TYPE_BOOL:
852+
v, ok := value.(bool)
853+
if !ok {
854+
return fmt.Errorf("unepected value type required: bool, actual: %T", value)
855+
}
856+
if v {
857+
valueAsBytes = []byte{}
858+
}
859+
default:
860+
return fmt.Errorf("unsupported parameter type: %d", paramType)
861+
}
862+
if valueAsBytes != nil {
863+
req.AddData(nl.NewRtAttr(nl.DEVLINK_ATTR_PARAM_VALUE_DATA, valueAsBytes))
864+
}
865+
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
866+
return err
867+
}
868+
869+
// DevlinkSetDeviceParam set specific parameter for devlink device
870+
// Equivalent to: `devlink dev param set <bus>/<device> name <param> cmode <cmode> value <value>`
871+
// cmode argument should contain valid cmode value as uint8, modes are define in nl.DEVLINK_PARAM_CMODE_* constants
872+
// value argument should have one of the following types: uint8, uint16, uint32, string, bool
873+
func DevlinkSetDeviceParam(bus string, device string, param string, cmode uint8, value interface{}) error {
874+
return pkgHandle.DevlinkSetDeviceParam(bus, device, param, cmode, value)
875+
}
876+
638877
// DevLinkGetPortByIndex provides a pointer to devlink portand nil error,
639878
// otherwise returns an error code.
640879
func DevLinkGetPortByIndex(Bus string, Device string, PortIndex uint32) (*DevlinkPort, error) {

devlink_test.go

+116
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ package netlink
55

66
import (
77
"flag"
8+
"math/rand"
89
"net"
10+
"os"
11+
"strconv"
912
"testing"
13+
14+
"github.com/vishvananda/netlink/nl"
1015
)
1116

1217
func TestDevLinkGetDeviceList(t *testing.T) {
@@ -286,3 +291,114 @@ func TestDevlinkGetDeviceResources(t *testing.T) {
286291

287292
t.Logf("Resources: %+v", res)
288293
}
294+
295+
// devlink device parameters can be tested with netdevsim
296+
// function will create netdevsim/netdevsim<random_id> virtual device that can be used for testing
297+
// netdevsim module should be loaded to run devlink param tests
298+
func setupDevlinkDeviceParamTest(t *testing.T) (string, string, func()) {
299+
t.Helper()
300+
skipUnlessRoot(t)
301+
skipUnlessKModuleLoaded(t, "netdevsim")
302+
testDevID := strconv.Itoa(1000 + rand.Intn(1000))
303+
err := os.WriteFile("/sys/bus/netdevsim/new_device", []byte(testDevID), 0755)
304+
if err != nil {
305+
t.Fatalf("can't create netdevsim test device %s: %v", testDevID, err)
306+
}
307+
308+
return "netdevsim", "netdevsim" + testDevID, func() {
309+
_ = os.WriteFile("/sys/bus/netdevsim/del_device", []byte(testDevID), 0755)
310+
}
311+
}
312+
313+
func TestDevlinkGetDeviceParams(t *testing.T) {
314+
busName, deviceName, cleanupFunc := setupDevlinkDeviceParamTest(t)
315+
defer cleanupFunc()
316+
params, err := DevlinkGetDeviceParams(busName, deviceName)
317+
if err != nil {
318+
t.Fatalf("failed to get device(%s/%s) parameters. %s", busName, deviceName, err)
319+
}
320+
if len(params) == 0 {
321+
t.Fatal("parameters list is empty")
322+
}
323+
for _, p := range params {
324+
validateDeviceParams(t, p)
325+
}
326+
}
327+
328+
func TestDevlinkGetDeviceParamByName(t *testing.T) {
329+
busName, deviceName, cleanupFunc := setupDevlinkDeviceParamTest(t)
330+
defer cleanupFunc()
331+
param, err := DevlinkGetDeviceParamByName(busName, deviceName, "max_macs")
332+
if err != nil {
333+
t.Fatalf("failed to get device(%s/%s) parameter max_macs. %s", busName, deviceName, err)
334+
}
335+
validateDeviceParams(t, param)
336+
}
337+
338+
func TestDevlinkSetDeviceParam(t *testing.T) {
339+
busName, deviceName, cleanupFunc := setupDevlinkDeviceParamTest(t)
340+
defer cleanupFunc()
341+
err := DevlinkSetDeviceParam(busName, deviceName, "max_macs", nl.DEVLINK_PARAM_CMODE_DRIVERINIT, uint32(8))
342+
if err != nil {
343+
t.Fatalf("failed to set max_macs for device(%s/%s): %s", busName, deviceName, err)
344+
}
345+
param, err := DevlinkGetDeviceParamByName(busName, deviceName, "max_macs")
346+
if err != nil {
347+
t.Fatalf("failed to get device(%s/%s) parameter max_macs. %s", busName, deviceName, err)
348+
}
349+
validateDeviceParams(t, param)
350+
v, ok := param.Values[0].Data.(uint32)
351+
if !ok {
352+
t.Fatalf("unexpected value")
353+
}
354+
if v != uint32(8) {
355+
t.Fatalf("value not set")
356+
}
357+
}
358+
359+
func validateDeviceParams(t *testing.T, p *DevlinkParam) {
360+
if p.Name == "" {
361+
t.Fatal("Name field not set")
362+
}
363+
if p.Name == "max_macs" && !p.IsGeneric {
364+
t.Fatal("IsGeneric should be true for generic parameter")
365+
}
366+
// test1 is a driver-specific parameter in netdevsim device, check should
367+
// also path on HW devices
368+
if p.Name == "test1" && p.IsGeneric {
369+
t.Fatal("IsGeneric should be false for driver-specific parameter")
370+
}
371+
switch p.Type {
372+
case nl.DEVLINK_PARAM_TYPE_U8,
373+
nl.DEVLINK_PARAM_TYPE_U16,
374+
nl.DEVLINK_PARAM_TYPE_U32,
375+
nl.DEVLINK_PARAM_TYPE_STRING,
376+
nl.DEVLINK_PARAM_TYPE_BOOL:
377+
default:
378+
t.Fatal("Type has unexpected value")
379+
}
380+
if len(p.Values) == 0 {
381+
t.Fatal("Values are not set")
382+
}
383+
for _, v := range p.Values {
384+
switch v.CMODE {
385+
case nl.DEVLINK_PARAM_CMODE_RUNTIME,
386+
nl.DEVLINK_PARAM_CMODE_DRIVERINIT,
387+
nl.DEVLINK_PARAM_CMODE_PERMANENT:
388+
default:
389+
t.Fatal("CMODE has unexpected value")
390+
}
391+
if p.Name == "max_macs" {
392+
_, ok := v.Data.(uint32)
393+
if !ok {
394+
t.Fatalf("value max_macs has wrong type: %T, expected: uint32", v.Data)
395+
}
396+
}
397+
if p.Name == "test1" {
398+
_, ok := v.Data.(bool)
399+
if !ok {
400+
t.Fatalf("value test1 has wrong type: %T, expected: bool", v.Data)
401+
}
402+
}
403+
}
404+
}

0 commit comments

Comments
 (0)