From 25bb9f753989f322afcb93029ab7c2234ebb3a0c Mon Sep 17 00:00:00 2001 From: Yury Kulazhenkov Date: Tue, 9 Jan 2024 17:06:14 +0200 Subject: [PATCH] Add additional methods to host/kernel.go New methods are: * BindDriverByBusAndDevice - binds device to the provided driver * UnbindDriverByBusAndDevice unbind device identified by bus and device ID from the driver Both methods allows to work with devices not only on PCI bus. +refactor driver-related methods +add unit-tests for changed methods Signed-off-by: Yury Kulazhenkov --- pkg/consts/constants.go | 9 +- pkg/helper/mock/mock_helper.go | 28 +++++ pkg/host/kernel.go | 222 +++++++++++++++++++++++---------- pkg/host/kernel_test.go | 216 ++++++++++++++++++++++++++++++++ pkg/host/suite_test.go | 21 ++++ 5 files changed, 429 insertions(+), 67 deletions(-) create mode 100644 pkg/host/kernel_test.go create mode 100644 pkg/host/suite_test.go diff --git a/pkg/consts/constants.go b/pkg/consts/constants.go index d56d36e7f..e7255368d 100644 --- a/pkg/consts/constants.go +++ b/pkg/consts/constants.go @@ -77,13 +77,16 @@ const ( CheckpointFileName = "sno-initial-node-state.json" Unknown = "Unknown" - SysBusPciDevices = "/sys/bus/pci/devices" - SysBusPciDrivers = "/sys/bus/pci/drivers" - SysBusPciDriversProbe = "/sys/bus/pci/drivers_probe" + SysBus = "/sys/bus" + SysBusPciDevices = SysBus + "/pci/devices" + SysBusPciDrivers = SysBus + "/pci/drivers" + SysBusPciDriversProbe = SysBus + "/pci/drivers_probe" SysClassNet = "/sys/class/net" ProcKernelCmdLine = "/proc/cmdline" NetClass = 0x02 NumVfsFile = "sriov_numvfs" + BusPci = "pci" + BusVdpa = "vdpa" UdevFolder = "/etc/udev" UdevRulesFolder = UdevFolder + "/rules.d" diff --git a/pkg/helper/mock/mock_helper.go b/pkg/helper/mock/mock_helper.go index 74814f4e6..75015a925 100644 --- a/pkg/helper/mock/mock_helper.go +++ b/pkg/helper/mock/mock_helper.go @@ -81,6 +81,20 @@ func (mr *MockHostHelpersInterfaceMockRecorder) BindDpdkDriver(arg0, arg1 interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindDpdkDriver", reflect.TypeOf((*MockHostHelpersInterface)(nil).BindDpdkDriver), arg0, arg1) } +// BindDriverByBusAndDevice mocks base method. +func (m *MockHostHelpersInterface) BindDriverByBusAndDevice(arg0, arg1, arg2 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BindDriverByBusAndDevice", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindDriverByBusAndDevice indicates an expected call of BindDriverByBusAndDevice. +func (mr *MockHostHelpersInterfaceMockRecorder) BindDriverByBusAndDevice(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindDriverByBusAndDevice", reflect.TypeOf((*MockHostHelpersInterface)(nil).BindDriverByBusAndDevice), arg0, arg1, arg2) +} + // Chroot mocks base method. func (m *MockHostHelpersInterface) Chroot(arg0 string) (func() error, error) { m.ctrl.T.Helper() @@ -994,6 +1008,20 @@ func (mr *MockHostHelpersInterfaceMockRecorder) Unbind(arg0 interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unbind", reflect.TypeOf((*MockHostHelpersInterface)(nil).Unbind), arg0) } +// UnbindDriverByBusAndDevice mocks base method. +func (m *MockHostHelpersInterface) UnbindDriverByBusAndDevice(bus, device string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnbindDriverByBusAndDevice", bus, device) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnbindDriverByBusAndDevice indicates an expected call of UnbindDriverByBusAndDevice. +func (mr *MockHostHelpersInterfaceMockRecorder) UnbindDriverByBusAndDevice(bus, device interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnbindDriverByBusAndDevice", reflect.TypeOf((*MockHostHelpersInterface)(nil).UnbindDriverByBusAndDevice), bus, device) +} + // UnbindDriverIfNeeded mocks base method. func (m *MockHostHelpersInterface) UnbindDriverIfNeeded(arg0 string, arg1 bool) error { m.ctrl.T.Helper() diff --git a/pkg/host/kernel.go b/pkg/host/kernel.go index ac7277eef..a7111d4d2 100644 --- a/pkg/host/kernel.go +++ b/pkg/host/kernel.go @@ -1,12 +1,12 @@ package host import ( + "errors" "fmt" "os" "path/filepath" "strings" - dputils "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" "sigs.k8s.io/controller-runtime/pkg/log" sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" @@ -36,12 +36,21 @@ type KernelInterface interface { BindDpdkDriver(string, string) error // BindDefaultDriver binds the virtual function to is default driver BindDefaultDriver(string) error + // BindDriverByBusAndDevice binds device to the provided driver + // bus - the bus path in the sysfs, e.g. "pci" or "vdpa" + // device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA + // driver - the name of the driver, e.g. vfio-pci or vhost_vdpa. + BindDriverByBusAndDevice(string, string, string) error // HasDriver returns try if the virtual function is bind to a driver HasDriver(string) (bool, string) // RebindVfToDefaultDriver rebinds the virtual function to is default driver RebindVfToDefaultDriver(string) error // UnbindDriverIfNeeded unbinds the virtual function from a driver if needed UnbindDriverIfNeeded(string, bool) error + // UnbindDriverByBusAndDevice unbind device identified by bus and device ID from the driver + // bus - the bus path in the sysfs, e.g. "pci" or "vdpa" + // device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA + UnbindDriverByBusAndDevice(bus, device string) error // LoadKernelModule loads a kernel module to the host LoadKernelModule(name string, args ...string) error // IsKernelModuleLoaded returns try if the requested kernel module is loaded @@ -165,18 +174,7 @@ func (k *kernel) IsKernelArgsSet(cmdLine string, karg string) bool { // Unbind unbind driver for one device func (k *kernel) Unbind(pciAddr string) error { log.Log.V(2).Info("Unbind(): unbind device driver for device", "device", pciAddr) - yes, driver := k.HasDriver(pciAddr) - if !yes { - return nil - } - - filePath := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDrivers, driver, "unbind") - err := os.WriteFile(filePath, []byte(pciAddr), os.ModeAppend) - if err != nil { - log.Log.Error(err, "Unbind(): fail to unbind driver for device", "device", pciAddr) - return err - } - return nil + return k.UnbindDriverByBusAndDevice(consts.BusPci, pciAddr) } // BindDpdkDriver bind dpdk driver for one device @@ -184,45 +182,15 @@ func (k *kernel) Unbind(pciAddr string) error { func (k *kernel) BindDpdkDriver(pciAddr, driver string) error { log.Log.V(2).Info("BindDpdkDriver(): bind device to driver", "device", pciAddr, "driver", driver) - - if yes, d := k.HasDriver(pciAddr); yes { - if driver == d { - log.Log.V(2).Info("BindDpdkDriver(): device already bound to driver", - "device", pciAddr, "driver", driver) - return nil - } - - if err := k.Unbind(pciAddr); err != nil { - return err - } - } - - driverOverridePath := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "driver_override") - err := os.WriteFile(driverOverridePath, []byte(driver), os.ModeAppend) - if err != nil { - log.Log.Error(err, "BindDpdkDriver(): fail to write driver_override for device", - "device", pciAddr, "driver", driver) - return err - } - bindPath := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDrivers, driver, "bind") - err = os.WriteFile(bindPath, []byte(pciAddr), os.ModeAppend) - if err != nil { - log.Log.Error(err, "BindDpdkDriver(): fail to bind driver for device", - "driver", driver, "device", pciAddr) - _, err := os.Readlink(filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "iommu_group")) - if err != nil { + if err := k.BindDriverByBusAndDevice(consts.BusPci, pciAddr, driver); err != nil { + _, innerErr := os.Readlink(filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "iommu_group")) + if innerErr != nil { log.Log.Error(err, "Could not read IOMMU group for device", "device", pciAddr) return fmt.Errorf( - "cannot bind driver %s to device %s, make sure IOMMU is enabled in BIOS. %w", driver, pciAddr, err) + "cannot bind driver %s to device %s, make sure IOMMU is enabled in BIOS. %w", driver, pciAddr, innerErr) } return err } - err = os.WriteFile(driverOverridePath, []byte(""), os.ModeAppend) - if err != nil { - log.Log.Error(err, "BindDpdkDriver(): failed to clear driver_override for device", "device", pciAddr) - return err - } - return nil } @@ -231,32 +199,58 @@ func (k *kernel) BindDpdkDriver(pciAddr, driver string) error { func (k *kernel) BindDefaultDriver(pciAddr string) error { log.Log.V(2).Info("BindDefaultDriver(): bind device to default driver", "device", pciAddr) - if yes, d := k.HasDriver(pciAddr); yes { - if !sriovnetworkv1.StringInArray(d, vars.DpdkDrivers) { + curDriver, err := getDriverByBusAndDevice(consts.BusPci, pciAddr) + if err != nil { + return err + } + if curDriver != "" { + if !sriovnetworkv1.StringInArray(curDriver, vars.DpdkDrivers) { log.Log.V(2).Info("BindDefaultDriver(): device already bound to default driver", - "device", pciAddr, "driver", d) + "device", pciAddr, "driver", curDriver) return nil } - if err := k.Unbind(pciAddr); err != nil { + if err := k.UnbindDriverByBusAndDevice(consts.BusPci, pciAddr); err != nil { return err } } - - driverOverridePath := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "driver_override") - err := os.WriteFile(driverOverridePath, []byte("\x00"), os.ModeAppend) - if err != nil { - log.Log.Error(err, "BindDefaultDriver(): failed to write driver_override for device", "device", pciAddr) + if err := setDriverOverride(consts.BusPci, pciAddr, ""); err != nil { + return err + } + if err := probeDriver(consts.BusPci, pciAddr); err != nil { return err } + return nil +} - pciDriversProbe := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDriversProbe) - err = os.WriteFile(pciDriversProbe, []byte(pciAddr), os.ModeAppend) +// BindDriverByBusAndDevice binds device to the provided driver +// bus - the bus path in the sysfs, e.g. "pci" or "vdpa" +// device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA +// driver - the name of the driver, e.g. vfio-pci or vhost_vdpa. +func (k *kernel) BindDriverByBusAndDevice(bus, device, driver string) error { + log.Log.V(2).Info("BindDriverByBusAndDevice(): bind device to driver", + "bus", bus, "device", device, "driver", driver) + + curDriver, err := getDriverByBusAndDevice(bus, device) if err != nil { - log.Log.Error(err, "BindDefaultDriver(): failed to bind driver for device", "device", pciAddr) return err } - - return nil + if curDriver != "" { + if curDriver == driver { + log.Log.V(2).Info("BindDriverByBusAndDevice(): device already bound to driver", + "bus", bus, "device", device, "driver", driver) + return nil + } + if err := k.UnbindDriverByBusAndDevice(bus, device); err != nil { + return err + } + } + if err := setDriverOverride(bus, device, driver); err != nil { + return err + } + if err := bindDriver(bus, device, driver); err != nil { + return err + } + return setDriverOverride(bus, device, "") } // Workaround function to handle a case where the vf default driver is stuck and not able to create the vf kernel interface. @@ -287,14 +281,33 @@ func (k *kernel) UnbindDriverIfNeeded(vfAddr string, isRdma bool) error { return nil } +// UnbindDriverByBusAndDevice unbind device identified by bus and device ID from the driver +// bus - the bus path in the sysfs, e.g. "pci" or "vdpa" +// device - the name of the device on the bus, e.g. 0000:85:1e.5 for PCI or vpda1 for VDPA +func (k *kernel) UnbindDriverByBusAndDevice(bus, device string) error { + log.Log.V(2).Info("UnbindDriverByBusAndDevice(): unbind device driver for device", "bus", bus, "device", device) + driver, err := getDriverByBusAndDevice(bus, device) + if err != nil { + return err + } + if driver == "" { + log.Log.V(2).Info("UnbindDriverByBusAndDevice(): device has no driver", "bus", bus, "device", device) + return nil + } + return unbindDriver(bus, device, driver) +} + func (k *kernel) HasDriver(pciAddr string) (bool, string) { - driver, err := dputils.GetDriverName(pciAddr) + driver, err := getDriverByBusAndDevice(consts.BusPci, pciAddr) if err != nil { log.Log.V(2).Info("HasDriver(): device driver is empty for device", "device", pciAddr) return false, "" } - log.Log.V(2).Info("HasDriver(): device driver for device", "device", pciAddr, "driver", driver) - return true, driver + if driver != "" { + log.Log.V(2).Info("HasDriver(): device driver for device", "device", pciAddr, "driver", driver) + return true, driver + } + return false, "" } func (k *kernel) TryEnableRdma() (bool, error) { @@ -625,3 +638,84 @@ func (k *kernel) IsKernelLockdownMode() bool { } return strings.Contains(stdout, "[integrity]") || strings.Contains(stdout, "[confidentiality]") } + +// returns driver for device on the bus +func getDriverByBusAndDevice(bus, device string) (string, error) { + driverLink := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "devices", device, "driver") + driverInfo, err := os.Readlink(driverLink) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + log.Log.V(2).Info("getDriverByBusAndDevice(): driver path for device not exist", "bus", bus, "device", device, "driver", driverInfo) + return "", nil + } + log.Log.Error(err, "getDriverByBusAndDevice(): error getting driver info for device", "bus", bus, "device", device) + return "", err + } + log.Log.V(2).Info("getDriverByBusAndDevice(): driver for device", "bus", bus, "device", device, "driver", driverInfo) + return filepath.Base(driverInfo), nil +} + +// binds device to the provide driver +func bindDriver(bus, device, driver string) error { + log.Log.V(2).Info("bindDriver(): bind to driver", "bus", bus, "device", device, "driver", driver) + bindPath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "drivers", driver, "bind") + err := os.WriteFile(bindPath, []byte(device), os.ModeAppend) + if err != nil { + log.Log.Error(err, "bindDriver(): failed to bind driver", "bus", bus, "device", device, "driver", driver) + return err + } + return nil +} + +// unbind device from the driver +func unbindDriver(bus, device, driver string) error { + log.Log.V(2).Info("unbindDriver(): unbind from driver", "bus", bus, "device", device, "driver", driver) + unbindPath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "drivers", driver, "unbind") + err := os.WriteFile(unbindPath, []byte(device), os.ModeAppend) + if err != nil { + log.Log.Error(err, "unbindDriver(): failed to unbind driver", "bus", bus, "device", device, "driver", driver) + return err + } + return nil +} + +// probes driver for device on the bus +func probeDriver(bus, device string) error { + log.Log.V(2).Info("probeDriver(): drivers probe", "bus", bus, "device", device) + probePath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "drivers_probe") + err := os.WriteFile(probePath, []byte(device), os.ModeAppend) + if err != nil { + log.Log.Error(err, "probeDriver(): failed to trigger driver probe", "bus", bus, "device", device) + return err + } + return nil +} + +// set driver override for the bus/device, +// resets override if override arg is "", +// if device doesn't support overriding (has no driver_override path), does nothing +func setDriverOverride(bus, device, override string) error { + driverOverridePath := filepath.Join(vars.FilesystemRoot, consts.SysBus, bus, "devices", device, "driver_override") + if _, err := os.Stat(driverOverridePath); err != nil { + if os.IsNotExist(err) { + log.Log.V(2).Info("setDriverOverride(): device doesn't support driver override, skip", "bus", bus, "device", device) + return nil + } + return err + } + var overrideData []byte + if override != "" { + log.Log.V(2).Info("setDriverOverride(): configure driver override for device", "bus", bus, "device", device, "driver", override) + overrideData = []byte(override) + } else { + log.Log.V(2).Info("setDriverOverride(): reset driver override for device", "bus", bus, "device", device) + overrideData = []byte("\x00") + } + err := os.WriteFile(driverOverridePath, overrideData, os.ModeAppend) + if err != nil { + log.Log.Error(err, "setDriverOverride(): fail to write driver_override for device", + "bus", bus, "device", device, "driver", override) + return err + } + return nil +} diff --git a/pkg/host/kernel_test.go b/pkg/host/kernel_test.go new file mode 100644 index 000000000..687cda454 --- /dev/null +++ b/pkg/host/kernel_test.go @@ -0,0 +1,216 @@ +package host + +import ( + "os" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" + "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" + "github.com/k8snetworkplumbingwg/sriov-network-operator/test/util/fakefilesystem" +) + +func assertFileContentsEquals(path, expectedContent string) { + d, err := os.ReadFile(filepath.Join(vars.FilesystemRoot, path)) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + ExpectWithOffset(1, string(d)).To(Equal(expectedContent)) +} + +var _ = Describe("Kernel", func() { + Context("Drivers", func() { + var ( + k KernelInterface + ) + configureFS := func(f *fakefilesystem.FS) { + var ( + cleanFakeFs func() + err error + ) + vars.FilesystemRoot, cleanFakeFs, err = f.Use() + Expect(err).ToNot(HaveOccurred()) + DeferCleanup(cleanFakeFs) + } + BeforeEach(func() { + k = newKernelInterface(nil) + }) + Context("Unbind, UnbindDriverByBusAndDevice", func() { + It("unknown device", func() { + Expect(k.UnbindDriverByBusAndDevice(consts.BusPci, "unknown-dev")).NotTo(HaveOccurred()) + }) + It("known device, no driver", func() { + configureFS(&fakefilesystem.FS{Dirs: []string{"/sys/bus/pci/devices/0000:d8:00.0"}}) + Expect(k.Unbind("0000:d8:00.0")).NotTo(HaveOccurred()) + }) + It("has driver, succeed", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers/test-driver/unbind": {}}, + }) + Expect(k.Unbind("0000:d8:00.0")).NotTo(HaveOccurred()) + // check that echo to unbind path was done + assertFileContentsEquals("/sys/bus/pci/drivers/test-driver/unbind", "0000:d8:00.0") + }) + It("has driver, failed to unbind", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + }) + Expect(k.Unbind("0000:d8:00.0")).To(HaveOccurred()) + }) + }) + Context("HasDriver", func() { + It("unknown device", func() { + has, driver := k.HasDriver("unknown-dev") + Expect(has).To(BeFalse()) + Expect(driver).To(BeEmpty()) + }) + It("known device, no driver", func() { + configureFS(&fakefilesystem.FS{Dirs: []string{"/sys/bus/pci/devices/0000:d8:00.0"}}) + has, driver := k.HasDriver("0000:d8:00.0") + Expect(has).To(BeFalse()) + Expect(driver).To(BeEmpty()) + }) + It("has driver", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + }) + has, driver := k.HasDriver("0000:d8:00.0") + Expect(has).To(BeTrue()) + Expect(driver).To(Equal("test-driver")) + }) + }) + Context("BindDefaultDriver", func() { + It("unknown device", func() { + Expect(k.BindDefaultDriver("unknown-dev")).To(HaveOccurred()) + }) + It("no driver", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers_probe": {}, "/sys/bus/pci/devices/0000:d8:00.0/driver_override": {}}, + }) + Expect(k.BindDefaultDriver("0000:d8:00.0")).NotTo(HaveOccurred()) + // should probe driver for dev + assertFileContentsEquals("/sys/bus/pci/drivers_probe", "0000:d8:00.0") + }) + It("already bind to default driver", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + }) + Expect(k.BindDefaultDriver("0000:d8:00.0")).NotTo(HaveOccurred()) + }) + It("bind to dpdk driver", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/vfio-pci"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/vfio-pci"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers_probe": {}, + "/sys/bus/pci/drivers/vfio-pci/unbind": {}}, + }) + Expect(k.BindDefaultDriver("0000:d8:00.0")).NotTo(HaveOccurred()) + // should unbind from dpdk driver + assertFileContentsEquals("/sys/bus/pci/drivers/vfio-pci/unbind", "0000:d8:00.0") + // should probe driver for dev + assertFileContentsEquals("/sys/bus/pci/drivers_probe", "0000:d8:00.0") + }) + }) + Context("BindDpdkDriver", func() { + It("unknown device", func() { + Expect(k.BindDpdkDriver("unknown-dev", "vfio-pci")).To(HaveOccurred()) + }) + It("no driver", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/vfio-pci"}, + Files: map[string][]byte{ + "/sys/bus/pci/devices/0000:d8:00.0/driver_override": {}}, + }) + Expect(k.BindDpdkDriver("0000:d8:00.0", "vfio-pci")).NotTo(HaveOccurred()) + // should reset driver override + assertFileContentsEquals("/sys/bus/pci/devices/0000:d8:00.0/driver_override", "\x00") + }) + It("already bind to required driver", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/vfio-pci"}, + }) + Expect(k.BindDpdkDriver("0000:d8:00.0", "vfio-pci")).NotTo(HaveOccurred()) + }) + It("bind to wrong driver", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver", + "/sys/bus/pci/drivers/vfio-pci"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers/test-driver/unbind": {}, + "/sys/bus/pci/drivers/vfio-pci/bind": {}, + "/sys/bus/pci/devices/0000:d8:00.0/driver_override": {}}, + }) + Expect(k.BindDpdkDriver("0000:d8:00.0", "vfio-pci")).NotTo(HaveOccurred()) + // should unbind from driver1 + assertFileContentsEquals("/sys/bus/pci/drivers/test-driver/unbind", "0000:d8:00.0") + // should bind to driver2 + assertFileContentsEquals("/sys/bus/pci/drivers/vfio-pci/bind", "0000:d8:00.0") + }) + It("fail to bind", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers/test-driver/unbind": {}, + "/sys/bus/pci/devices/0000:d8:00.0/driver_override": {}}, + }) + Expect(k.BindDpdkDriver("0000:d8:00.0", "vfio-pci")).To(HaveOccurred()) + }) + }) + Context("BindDriverByBusAndDevice", func() { + It("device doesn't support driver_override", func() { + configureFS(&fakefilesystem.FS{ + Dirs: []string{ + "/sys/bus/pci/devices/0000:d8:00.0", + "/sys/bus/pci/drivers/test-driver", + "/sys/bus/pci/drivers/vfio-pci"}, + Symlinks: map[string]string{ + "/sys/bus/pci/devices/0000:d8:00.0/driver": "../../../../bus/pci/drivers/test-driver"}, + Files: map[string][]byte{ + "/sys/bus/pci/drivers/test-driver/unbind": {}, + "/sys/bus/pci/drivers/vfio-pci/bind": {}}, + }) + Expect(k.BindDriverByBusAndDevice(consts.BusPci, "0000:d8:00.0", "vfio-pci")).NotTo(HaveOccurred()) + // should unbind from driver1 + assertFileContentsEquals("/sys/bus/pci/drivers/test-driver/unbind", "0000:d8:00.0") + // should bind to driver2 + assertFileContentsEquals("/sys/bus/pci/drivers/vfio-pci/bind", "0000:d8:00.0") + }) + }) + }) +}) diff --git a/pkg/host/suite_test.go b/pkg/host/suite_test.go new file mode 100644 index 000000000..674437292 --- /dev/null +++ b/pkg/host/suite_test.go @@ -0,0 +1,21 @@ +package host + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "go.uber.org/zap/zapcore" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +func TestHostManager(t *testing.T) { + log.SetLogger(zap.New( + zap.WriteTo(GinkgoWriter), + zap.Level(zapcore.Level(-2)), + zap.UseDevMode(true))) + RegisterFailHandler(Fail) + RunSpecs(t, "Package Host Suite") +}