From 3a2cae6f28db1f3a3e8c823f21639390c0d95835 Mon Sep 17 00:00:00 2001 From: adrianc Date: Mon, 8 Dec 2025 13:47:01 +0200 Subject: [PATCH] feat: add support for vhost-net for AuxNetDevice add support for mounting /dev/vhost-net and /dev/net/tun for AuxNetDevice similarly to PciNetDevice. Signed-off-by: adrianc --- README.md | 17 ++-- pkg/auxnetdevice/auxNetDevice.go | 8 ++ pkg/auxnetdevice/auxNetDevice_test.go | 134 ++++++++++++++++++++++++++ pkg/netdevice/pciNetDevice.go | 2 +- pkg/netdevice/pciNetDevice_test.go | 5 +- pkg/types/types.go | 18 ++-- 6 files changed, 164 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index ca7722d5f..c630317ad 100644 --- a/README.md +++ b/README.md @@ -348,14 +348,15 @@ These selectors are applicable when "deviceType" is "auxNetDevice". | Field | Required | Description | Type/Defaults | Example/Accepted values | |---------------|----------|----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|--------------------------------------------------------------------------------------------------| -| "vendors" | N | Target device's vendor Hex code as string | `string` list Default: `null` | "vendors": ["8086", "15b3"] | -| "devices" | N | Parent PCI device Hex code as string | `string` list Default: `null` | "devices": ["154c", "1889", "1018"] | -| "drivers" | N | Target device driver names as string | `string` list Default: `null` | "drivers": ["vfio-pci"] | -| "pfNames" | N | functions from PF matches list of PF names | `string` list Default: `null` | "pfNames": ["enp2s2f0"] (See follow-up sections for some advance usage of "pfNames") | -| "rootDevices" | N | functions from PF matches list of PF PCI addresses | `string` list Default: `null` | "rootDevices": ["0000:86:00.0"] (See follow-up sections for some advance usage of "rootDevices") | -| "linkTypes" | N | The link type of the net device associated with the PCI device | `string` list Default: `null` | "linkTypes": ["ether"] | -| "isRdma" | N | Mount RDMA resources. Incompatible with vdpaType | `bool` values `true` or `false` Default: `false` | "isRdma": `true` | -| "auxTypes" | N | List of vendor specific auxiliary network device types. Device type can be determined by its name: .. | `string` list Default: `null` | "auxTypes": ["sf", "eth"] | +| "vendors" | N | Target device's vendor Hex code as string | `string` list Default: `null` | "vendors": ["8086", "15b3"] | +| "devices" | N | Parent PCI device Hex code as string | `string` list Default: `null` | "devices": ["154c", "1889", "1018"] | +| "drivers" | N | Target device driver names as string | `string` list Default: `null` | "drivers": ["vfio-pci"] | +| "pfNames" | N | functions from PF matches list of PF names | `string` list Default: `null` | "pfNames": ["enp2s2f0"] (See follow-up sections for some advance usage of "pfNames") | +| "rootDevices" | N | functions from PF matches list of PF PCI addresses | `string` list Default: `null` | "rootDevices": ["0000:86:00.0"] (See follow-up sections for some advance usage of "rootDevices") | +| "linkTypes" | N | The link type of the net device associated with the PCI device | `string` list Default: `null` | "linkTypes": ["ether"] | +| "isRdma" | N | Mount RDMA resources. Incompatible with vdpaType | `bool` values `true` or `false` Default: `false` | "isRdma": `true` | +| "needVhostNet" | N | Share /dev/vhost-net and /dev/net/tun | `bool` values `true` or `false` Default: `false` | "needVhostNet": `true` | +| "auxTypes" | N | List of vendor-specific auxiliary network device types. Device type can be determined by its name: .. | `string` list Default: `null` | "auxTypes": ["sf", "eth"] | [//]: # (The tables above generated using: https://ozh.github.io/ascii-tables/) diff --git a/pkg/auxnetdevice/auxNetDevice.go b/pkg/auxnetdevice/auxNetDevice.go index 2ced517f6..5ba2d5e57 100644 --- a/pkg/auxnetdevice/auxNetDevice.go +++ b/pkg/auxnetdevice/auxNetDevice.go @@ -65,6 +65,14 @@ func NewAuxNetDevice(dev *ghw.PCIDevice, deviceID string, rFactory types.Resourc glog.Warningf("RDMA resources for %s not found. Are RDMA modules loaded?", deviceID) } } + + if nf.NeedVhostNet { + if infoprovider.VhostNetDeviceExist() { + infoProviders = append(infoProviders, infoprovider.NewVhostNetInfoProvider()) + } else { + glog.Warningf("vhost-net is required in the configuration for %s but /dev/vhost-net doesn't exist", deviceID) + } + } } hostDev, err := devices.NewHostDeviceImpl(dev, deviceID, rFactory, rc, infoProviders) diff --git a/pkg/auxnetdevice/auxNetDevice_test.go b/pkg/auxnetdevice/auxNetDevice_test.go index 89d17f051..ddc8f1bca 100644 --- a/pkg/auxnetdevice/auxNetDevice_test.go +++ b/pkg/auxnetdevice/auxNetDevice_test.go @@ -18,6 +18,7 @@ package auxnetdevice_test import ( + "path/filepath" "testing" "github.com/jaypipes/ghw" @@ -28,6 +29,7 @@ import ( "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/auxnetdevice" "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/factory" + "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/infoprovider" "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types" tmocks "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/types/mocks" "github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/utils" @@ -299,5 +301,137 @@ var _ = Describe("AuxNetDevice", func() { fakeSriovnetProvider.AssertExpectations(t) }) }) + + Context("with needVhostNet", func() { + It("should provide expected environment variables and mounts", func() { + fs := &utils.FakeFilesystem{ + Dirs: []string{ + "sys/bus/pci/devices/0000:00:00.1/net/net0", + "sys/bus/pci/drivers/mlx5_core", + "dev/net/tun", + "dev/vhost-net", + }, + Symlinks: map[string]string{ + "sys/bus/pci/devices/0000:00:00.1/driver": "../../../../bus/pci/drivers/mlx5_core", + }, + Files: map[string][]byte{"sys/bus/pci/devices/0000:00:00.1/numa_node": []byte("0")}, + } + defer fs.Use()() + + infoprovider.HostNet = filepath.Join(fs.RootDir, "dev/vhost-net") + infoprovider.HostTun = filepath.Join(fs.RootDir, "dev/net/tun") + + utils.SetDefaultMockNetlinkProvider() + auxDevID := "mlx5_core.sf.0" + fakeSriovnetProvider := mocks.SriovnetProvider{} + fakeSriovnetProvider. + On("GetUplinkRepresentorFromAux", auxDevID).Return("net0", nil). + On("GetPfPciFromAux", auxDevID).Return("0000:00:00.1", nil). + On("GetSfIndexByAuxDev", auxDevID).Return(1, nil). + On("GetNetDevicesFromAux", auxDevID).Return([]string{"eth0"}, nil) + utils.SetSriovnetProviderInst(&fakeSriovnetProvider) + + f := factory.NewResourceFactory("fake", "fake", true, false) + in := newPciDevice("0000:00:00.1") + rc := &types.ResourceConfig{ + ResourceName: "fake", + ResourcePrefix: "fake", + DeviceType: types.AuxNetDeviceType, + SelectorObjs: []interface{}{&types.AuxNetDeviceSelectors{ + GenericNetDeviceSelectors: types.GenericNetDeviceSelectors{ + NeedVhostNet: true, + }, + }}} + + dev, err := auxnetdevice.NewAuxNetDevice(in, auxDevID, f, rc, 0) + Expect(err).NotTo(HaveOccurred()) + Expect(dev).NotTo(BeNil()) + Expect(dev.GetDriver()).To(Equal("mlx5_core")) + Expect(dev.GetNetName()).To(Equal("eth0")) + + envs := dev.GetEnvVal() + Expect(envs).To(HaveLen(2)) + // validate device ID env var + Expect(envs).To(HaveKey("generic")) + Expect(envs["generic"]).To(HaveKeyWithValue("deviceID", "mlx5_core.sf.0")) + // validate vhost env vars + Expect(envs).To(HaveKey("vhost")) + Expect(envs["vhost"]).To(HaveKeyWithValue("net-mount", "/dev/vhost-net")) + Expect(envs["vhost"]).To(HaveKeyWithValue("tun-mount", "/dev/net/tun")) + + // validate mounts + devSpecs := dev.GetDeviceSpecs() + Expect(devSpecs).To(HaveLen(2)) + Expect(devSpecs).To(ConsistOf( + And(HaveField("HostPath", "/dev/vhost-net"), HaveField("ContainerPath", "/dev/vhost-net")), + And(HaveField("HostPath", "/dev/net/tun"), HaveField("ContainerPath", "/dev/net/tun")), + )) + + Expect(dev.GetLinkType()).To(Equal("fakeLinkType")) + Expect(dev.GetFuncID()).To(Equal(1)) + Expect(dev.GetAPIDevice().Topology.Nodes[0].ID).To(Equal(int64(0))) + Expect(dev.GetAuxType()).To(Equal("sf")) + fakeSriovnetProvider.AssertExpectations(t) + }) + + It("should not contain vhost related environment variables and mounts if vhost-net device does not exist", func() { + fs := &utils.FakeFilesystem{ + Dirs: []string{ + "sys/bus/pci/devices/0000:00:00.1/net/net0", + "sys/bus/pci/drivers/mlx5_core", + }, + Symlinks: map[string]string{ + "sys/bus/pci/devices/0000:00:00.1/driver": "../../../../bus/pci/drivers/mlx5_core", + }, + Files: map[string][]byte{"sys/bus/pci/devices/0000:00:00.1/numa_node": []byte("0")}, + } + defer fs.Use()() + + infoprovider.HostNet = filepath.Join(fs.RootDir, "dev/vhost-net") + infoprovider.HostTun = filepath.Join(fs.RootDir, "dev/net/tun") + + utils.SetDefaultMockNetlinkProvider() + auxDevID := "mlx5_core.sf.0" + fakeSriovnetProvider := mocks.SriovnetProvider{} + fakeSriovnetProvider. + On("GetUplinkRepresentorFromAux", auxDevID).Return("net0", nil). + On("GetPfPciFromAux", auxDevID).Return("0000:00:00.1", nil). + On("GetSfIndexByAuxDev", auxDevID).Return(1, nil). + On("GetNetDevicesFromAux", auxDevID).Return([]string{"eth0"}, nil) + utils.SetSriovnetProviderInst(&fakeSriovnetProvider) + + f := factory.NewResourceFactory("fake", "fake", true, false) + in := newPciDevice("0000:00:00.1") + rc := &types.ResourceConfig{ + ResourceName: "fake", + ResourcePrefix: "fake", + DeviceType: types.AuxNetDeviceType, + SelectorObjs: []interface{}{&types.AuxNetDeviceSelectors{ + GenericNetDeviceSelectors: types.GenericNetDeviceSelectors{ + NeedVhostNet: true, + }, + }}} + + dev, err := auxnetdevice.NewAuxNetDevice(in, auxDevID, f, rc, 0) + Expect(err).NotTo(HaveOccurred()) + Expect(dev).NotTo(BeNil()) + Expect(dev.GetDriver()).To(Equal("mlx5_core")) + Expect(dev.GetNetName()).To(Equal("eth0")) + + envs := dev.GetEnvVal() + Expect(envs).To(HaveLen(1)) + // validate device ID env var + Expect(envs).To(HaveKey("generic")) + Expect(envs["generic"]).To(HaveKeyWithValue("deviceID", "mlx5_core.sf.0")) + + // validate no mounts + Expect(dev.GetDeviceSpecs()).To(BeEmpty()) + Expect(dev.GetLinkType()).To(Equal("fakeLinkType")) + Expect(dev.GetFuncID()).To(Equal(1)) + Expect(dev.GetAPIDevice().Topology.Nodes[0].ID).To(Equal(int64(0))) + Expect(dev.GetAuxType()).To(Equal("sf")) + fakeSriovnetProvider.AssertExpectations(t) + }) + }) }) }) diff --git a/pkg/netdevice/pciNetDevice.go b/pkg/netdevice/pciNetDevice.go index aa9ae8f7a..5ca95ac76 100644 --- a/pkg/netdevice/pciNetDevice.go +++ b/pkg/netdevice/pciNetDevice.go @@ -78,7 +78,7 @@ func NewPciNetDevice(dev *ghw.PCIDevice, if infoprovider.VhostNetDeviceExist() { infoProviders = append(infoProviders, infoprovider.NewVhostNetInfoProvider()) } else { - glog.Errorf("GetDeviceSpecs(): vhost-net is required in the configuration but /dev/vhost-net doesn't exist") + glog.Warningf("vhost-net is required in the configuration for %s but /dev/vhost-net doesn't exist", dev.Address) } } } diff --git a/pkg/netdevice/pciNetDevice_test.go b/pkg/netdevice/pciNetDevice_test.go index 9c58ec383..1b8583e3a 100644 --- a/pkg/netdevice/pciNetDevice_test.go +++ b/pkg/netdevice/pciNetDevice_test.go @@ -244,8 +244,9 @@ var _ = Describe("PciNetDevice", func() { ResourcePrefix: "fake", SelectorObjs: []interface{}{&types.NetDeviceSelectors{}, &types.NetDeviceSelectors{ - NeedVhostNet: true, - }, + GenericNetDeviceSelectors: types.GenericNetDeviceSelectors{ + NeedVhostNet: true, + }}, }} no_vhost_net_selector_index := 0 vhost_net_selector_index := 1 diff --git a/pkg/types/types.go b/pkg/types/types.go index f02e15a09..52e9f0e83 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -122,11 +122,12 @@ type GenericPciDeviceSelectors struct { // GenericNetDeviceSelectors contains common net device selectors fields type GenericNetDeviceSelectors struct { - PfNames []string `json:"pfNames,omitempty"` - RootDevices []string `json:"rootDevices,omitempty"` - LinkTypes []string `json:"linkTypes,omitempty"` - IsRdma bool // the resource support rdma - AcpiIndexes []string `json:"acpiIndexes,omitempty"` + PfNames []string `json:"pfNames,omitempty"` + RootDevices []string `json:"rootDevices,omitempty"` + LinkTypes []string `json:"linkTypes,omitempty"` + IsRdma bool // the resource support rdma + AcpiIndexes []string `json:"acpiIndexes,omitempty"` + NeedVhostNet bool `json:"needVhostNet,omitempty"` // share vhost-net along the selected resource } // NetDeviceSelectors contains network device related selectors fields @@ -134,10 +135,9 @@ type NetDeviceSelectors struct { DeviceSelectors GenericPciDeviceSelectors GenericNetDeviceSelectors - DDPProfiles []string `json:"ddpProfiles,omitempty"` - NeedVhostNet bool // share vhost-net along the selected resource - VdpaType VdpaType `json:"vdpaType,omitempty"` - PKeys []string `json:"pKeys,omitempty"` + DDPProfiles []string `json:"ddpProfiles,omitempty"` + VdpaType VdpaType `json:"vdpaType,omitempty"` + PKeys []string `json:"pKeys,omitempty"` } // AccelDeviceSelectors contains accelerator(FPGA etc.) related selectors fields