github.com/k8snetworkplumbingwg/sriov-network-operator@v1.2.1-0.20240408194816-2d2e5a45d453/pkg/host/internal/vdpa/vdpa.go (about) 1 package vdpa 2 3 import ( 4 "errors" 5 "fmt" 6 "syscall" 7 8 "github.com/vishvananda/netlink" 9 "sigs.k8s.io/controller-runtime/pkg/log" 10 11 constants "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts" 12 netlinkLibPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink" 13 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" 14 ) 15 16 const ( 17 VhostVdpaDriver = "vhost_vdpa" 18 VirtioVdpaDriver = "virtio_vdpa" 19 ) 20 21 type vdpa struct { 22 kernel types.KernelInterface 23 netlinkLib netlinkLibPkg.NetlinkLib 24 } 25 26 func New(k types.KernelInterface, netlinkLib netlinkLibPkg.NetlinkLib) types.VdpaInterface { 27 return &vdpa{kernel: k, netlinkLib: netlinkLib} 28 } 29 30 // CreateVDPADevice creates VDPA device for VF with required type, 31 // pciAddr - PCI address of the VF 32 // vdpaType - type of the VDPA device to create: virtio of vhost 33 func (v *vdpa) CreateVDPADevice(pciAddr, vdpaType string) error { 34 expectedVDPAName := generateVDPADevName(pciAddr) 35 funcLog := log.Log.WithValues("device", pciAddr, "vdpaType", vdpaType, "name", expectedVDPAName) 36 funcLog.V(2).Info("CreateVDPADevice(): create VDPA device for VF") 37 expectedDriver := vdpaTypeToDriver(vdpaType) 38 if expectedDriver == "" { 39 return fmt.Errorf("unknown VDPA device type: %s", vdpaType) 40 } 41 _, err := v.netlinkLib.VDPAGetDevByName(expectedVDPAName) 42 if err != nil { 43 if !errors.Is(err, syscall.ENODEV) { 44 funcLog.Error(err, "CreateVDPADevice(): fail to check if VDPA device exist") 45 return err 46 } 47 // first try to create VDPA device with MaxVQP parameter set to 32 to exactly match HW offloading use-case with the 48 // old swtichdev implementation. Create device without MaxVQP parameter if it is not supported. 49 if err := v.netlinkLib.VDPANewDev(expectedVDPAName, constants.BusPci, pciAddr, netlink.VDPANewDevParams{MaxVQP: 32}); err != nil { 50 if !errors.Is(err, syscall.ENOTSUP) { 51 funcLog.Error(err, "CreateVDPADevice(): fail to create VDPA device with MaxVQP parameter") 52 return err 53 } 54 funcLog.V(2).Info("failed to create VDPA device with MaxVQP parameter, try without it") 55 if err := v.netlinkLib.VDPANewDev(expectedVDPAName, constants.BusPci, pciAddr, netlink.VDPANewDevParams{}); err != nil { 56 funcLog.Error(err, "CreateVDPADevice(): fail to create VDPA device without MaxVQP parameter") 57 return err 58 } 59 } 60 } 61 err = v.kernel.BindDriverByBusAndDevice(constants.BusVdpa, expectedVDPAName, expectedDriver) 62 if err != nil { 63 funcLog.Error(err, "CreateVDPADevice(): fail to bind VDPA device to the driver") 64 return err 65 } 66 return nil 67 } 68 69 // DeleteVDPADevice removes VDPA device for provided pci address 70 // pciAddr - PCI address of the VF 71 func (v *vdpa) DeleteVDPADevice(pciAddr string) error { 72 expectedVDPAName := generateVDPADevName(pciAddr) 73 funcLog := log.Log.WithValues("device", pciAddr, "name", expectedVDPAName) 74 funcLog.V(2).Info("DeleteVDPADevice(): delete VDPA device for VF") 75 76 if err := v.netlinkLib.VDPADelDev(expectedVDPAName); err != nil { 77 if errors.Is(err, syscall.ENODEV) { 78 funcLog.V(2).Info("DeleteVDPADevice(): VDPA device not found") 79 return nil 80 } 81 if errors.Is(err, syscall.ENOENT) { 82 funcLog.V(2).Info("DeleteVDPADevice(): VDPA module is not loaded") 83 return nil 84 } 85 funcLog.Error(err, "DeleteVDPADevice(): fail to remove VDPA device") 86 return err 87 } 88 return nil 89 } 90 91 // DiscoverVDPAType returns type of existing VDPA device for VF, 92 // returns empty string if VDPA device not found or unknown driver is in use 93 // pciAddr - PCI address of the VF 94 func (v *vdpa) DiscoverVDPAType(pciAddr string) string { 95 expectedVDPAName := generateVDPADevName(pciAddr) 96 funcLog := log.Log.WithValues("device", pciAddr, "name", expectedVDPAName) 97 funcLog.V(2).Info("DiscoverVDPAType() discover device type") 98 _, err := v.netlinkLib.VDPAGetDevByName(expectedVDPAName) 99 if err != nil { 100 if errors.Is(err, syscall.ENODEV) { 101 funcLog.V(2).Info("DiscoverVDPAType(): VDPA device for VF not found") 102 return "" 103 } 104 if errors.Is(err, syscall.ENOENT) { 105 funcLog.V(2).Info("DiscoverVDPAType(): VDPA module is not loaded") 106 return "" 107 } 108 funcLog.Error(err, "DiscoverVDPAType(): unable to get VF VDPA devices") 109 return "" 110 } 111 driverName, err := v.kernel.GetDriverByBusAndDevice(constants.BusVdpa, expectedVDPAName) 112 if err != nil { 113 funcLog.Error(err, "DiscoverVDPAType(): unable to get driver info for VF VDPA devices") 114 return "" 115 } 116 if driverName == "" { 117 funcLog.V(2).Info("DiscoverVDPAType(): VDPA device has no driver") 118 return "" 119 } 120 vdpaType := vdpaDriverToType(driverName) 121 if vdpaType == "" { 122 funcLog.Error(nil, "DiscoverVDPAType(): WARNING: unknown VDPA device type for VF, ignore") 123 } 124 return vdpaType 125 } 126 127 // generates predictable name for VDPA device, example: vpda:0000:03:00.1 128 func generateVDPADevName(pciAddr string) string { 129 return "vdpa:" + pciAddr 130 } 131 132 // vdpa type to driver name conversion 133 func vdpaTypeToDriver(vdpaType string) string { 134 switch vdpaType { 135 case constants.VdpaTypeVhost: 136 return VhostVdpaDriver 137 case constants.VdpaTypeVirtio: 138 return VirtioVdpaDriver 139 default: 140 return "" 141 } 142 } 143 144 // vdpa driver name to type conversion 145 func vdpaDriverToType(driver string) string { 146 switch driver { 147 case VhostVdpaDriver: 148 return constants.VdpaTypeVhost 149 case VirtioVdpaDriver: 150 return constants.VdpaTypeVirtio 151 default: 152 return "" 153 } 154 }