diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 34f9c4139..4687b47bb 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -23,6 +23,8 @@ import ( "regexp" "strconv" "strings" + + "github.com/golang/glog" ) var ( @@ -31,8 +33,9 @@ var ( ) const ( - totalVfFile = "sriov_totalvfs" - configuredVfFile = "sriov_numvfs" + totalVfFile = "sriov_totalvfs" + configuredVfFile = "sriov_numvfs" + eswitchModeSwitchdev = "switchdev" ) // DetectPluginWatchMode returns true if plugins registry directory exist @@ -60,6 +63,29 @@ func GetPfAddr(pciAddr string) (string, error) { // GetPfName returns SRIOV PF name for the given VF // If device is not VF then it will return its own ifname if exist else empty string func GetPfName(pciAddr string) (string, error) { + pfEswitchMode, err := GetPfEswitchMode(pciAddr) + if err != nil { + errString := strings.ToLower(fmt.Sprint(err)) + // If device doesn't support eswitch mode query or doesn't have sriov enabled, + // fall back to the default implementation + if strings.Contains(errString, "no such device") { + glog.Warningf("Devlink query for eswitch mode is not supported for device %s", pciAddr) + } else if strings.Contains(errString, "operation not supported") { + glog.Warningf("Devlink query for is not supported for non-sriov device %s", pciAddr) + } else { + return "", err + } + } + if pfEswitchMode == eswitchModeSwitchdev { + name, err := GetSriovnetProvider().GetUplinkRepresentor(pciAddr) + + if err != nil { + return "", err + } + + return name, nil + } + path := filepath.Join(sysBusPci, pciAddr, "physfn", "net") files, err := ioutil.ReadDir(path) if err != nil { @@ -393,3 +419,17 @@ func GetVFID(pciAddr string) (vfID int, err error) { vfID = -1 return vfID, nil } + +// GetPfEswitchMode returns PF's eswitch mode for the given VF +// If device is not VF then it will return its own eswitch mode +func GetPfEswitchMode(pciAddr string) (string, error) { + pfAddr, err := GetPfAddr(pciAddr) + if err != nil { + return "", fmt.Errorf("error getting PF PCI address for device %s %v", pciAddr, err) + } + devLinkDeviceAttrs, err := GetNetlinkProvider().GetDevLinkDeviceEswitchAttrs(pfAddr) + if err != nil { + return "", err + } + return devLinkDeviceAttrs.Mode, nil +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 45dfca867..9b8984ca3 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -1,9 +1,15 @@ package utils import ( + "fmt" + . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" + "github.com/stretchr/testify/mock" + nl "github.com/vishvananda/netlink" + + mocks "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils/mocks" ) func assertShouldFail(err error, shouldFail bool) { @@ -395,8 +401,15 @@ var _ = Describe("In the utils package", func() { ), ) - DescribeTable("getting PF names", + DescribeTable("getting PF names legacy", func(fs *FakeFilesystem, device string, expected string, shouldFail bool) { + fakeNetlinkProvider := mocks.NetlinkProvider{} + fakeNetlinkProvider. + On("GetDevLinkDeviceEswitchAttrs", + mock.AnythingOfType("string")). + Return(&nl.DevlinkDevEswitchAttr{Mode: "legacy"}, nil) + SetNetlinkProviderInst(&fakeNetlinkProvider) + defer fs.Use()() actual, err := GetPfName(device) Expect(actual).To(Equal(expected)) @@ -404,8 +417,15 @@ var _ = Describe("In the utils package", func() { }, Entry("device doesn't exist", &FakeFilesystem{}, "0000:01:10.0", nil, true), Entry("device is a VF and interface name exists", - &FakeFilesystem{Dirs: []string{"sys/bus/pci/devices/0000:01:10.0/physfn/net/fakePF"}}, - "0000:01:10.0", "fakePF", false, + &FakeFilesystem{ + Dirs: []string{ + "sys/bus/pci/devices/0000:01:10.0", + "sys/bus/pci/devices/0000:01:00.0/net/fakePF", + }, + Symlinks: map[string]string{ + "sys/bus/pci/devices/0000:01:10.0/physfn/": "../0000:01:00.0", + }, + }, "0000:01:10.0", "fakePF", false, ), Entry("device is a VF and interface name does not exist", &FakeFilesystem{Dirs: []string{"sys/bus/pci/devices/0000:01:10.0/physfn/net/"}}, @@ -428,6 +448,66 @@ var _ = Describe("In the utils package", func() { ), ) + DescribeTable("getting PF names switchdev", + func(fs *FakeFilesystem, device string, expected string, shouldFail bool) { + fakeNetlinkProvider := mocks.NetlinkProvider{} + fakeNetlinkProvider. + On("GetDevLinkDeviceEswitchAttrs", "devlinkDeviceSwitchdev"). + Return(&nl.DevlinkDevEswitchAttr{Mode: "switchdev"}, nil). + On("GetDevLinkDeviceEswitchAttrs", "nonDevlinkDevice"). + Return(nil, fmt.Errorf("devlink error: no such device")). + On("GetDevLinkDeviceEswitchAttrs", "nonSriovDevice"). + Return(nil, fmt.Errorf("devlink error: operation not supported")). + On("GetDevLinkDeviceEswitchAttrs", "unknownDevice"). + Return(nil, fmt.Errorf("unknown error")) + SetNetlinkProviderInst(&fakeNetlinkProvider) + + fakeSriovnetProvider := mocks.SriovnetProvider{} + fakeSriovnetProvider. + On("GetUplinkRepresentor", mock.AnythingOfType("string")). + Return("fakeSwitchdevPF", nil) + SetSriovnetProviderInst(&fakeSriovnetProvider) + + defer fs.Use()() + actual, err := GetPfName(device) + Expect(actual).To(Equal(expected)) + assertShouldFail(err, shouldFail) + }, + Entry("device is a VF and PF is in switchdev mode", + &FakeFilesystem{ + Dirs: []string{ + "sys/bus/pci/devices/0000:01:10.0", + "sys/bus/pci/devices/devlinkDeviceSwitchdev/net/fakePF", + "sys/bus/pci/devices/devlinkDeviceSwitchdev/net/fakeVF", + }, + Symlinks: map[string]string{ + "sys/bus/pci/devices/0000:01:10.0/physfn/": "../devlinkDeviceSwitchdev", + }, + }, + "0000:01:10.0", "fakeSwitchdevPF", false, + ), + Entry("device is a PF in switchdev mode", + &FakeFilesystem{ + Dirs: []string{ + "sys/bus/pci/devices/devlinkDeviceSwitchdev/net/fakePF", + }, + }, + "devlinkDeviceSwitchdev", "fakeSwitchdevPF", false, + ), + Entry("device is a PF and doesn't support eswitch mode query", + &FakeFilesystem{Dirs: []string{"sys/bus/pci/devices/nonDevlinkDevice/net/fakeIF"}}, + "nonDevlinkDevice", "fakeIF", false, + ), + Entry("device is a PF and doesn't support eswitch mode query", + &FakeFilesystem{Dirs: []string{"sys/bus/pci/devices/nonSriovDevice/net/fakeIF"}}, + "nonSriovDevice", "fakeIF", false, + ), + Entry("device is a PF and eswitch query failed", + &FakeFilesystem{Dirs: []string{"sys/bus/pci/devices/unknownDevice/net/fakeIF"}}, + "unknownDevice", "", true, + ), + ) + DescribeTable("getting ID of VF", func(fs *FakeFilesystem, device string, expected int, shouldFail bool) { defer fs.Use()()