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  }