github.com/openshift/dpu-operator@v0.0.0-20240502153209-3af840d137c2/dpu-cni/pkgs/sriov/sriov.go (about)

     1  package sriov
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/containernetworking/plugins/pkg/ns"
     9  	"k8s.io/klog/v2"
    10  
    11  	"github.com/containernetworking/cni/pkg/types"
    12  	current "github.com/containernetworking/cni/pkg/types/100"
    13  	"github.com/containernetworking/plugins/pkg/ipam"
    14  	"github.com/openshift/dpu-operator/dpu-cni/pkgs/cnitypes"
    15  	"github.com/openshift/dpu-operator/dpu-cni/pkgs/sriovconfig"
    16  	"github.com/openshift/dpu-operator/dpu-cni/pkgs/sriovutils"
    17  	"github.com/vishvananda/netlink"
    18  )
    19  
    20  const (
    21  	// TODO: Some vendors have issues with Host VLANs, thus disable the feature for now
    22  	host_vlans = false
    23  )
    24  
    25  type pciUtils interface {
    26  	GetSriovNumVfs(ifName string) (int, error)
    27  	GetVFLinkNamesFromVFID(pfName string, vfID int) ([]string, error)
    28  	GetPciAddress(ifName string, vf int) (string, error)
    29  	EnableArpAndNdiscNotify(ifName string) error
    30  }
    31  
    32  type pciUtilsImpl struct{}
    33  
    34  func (p *pciUtilsImpl) GetSriovNumVfs(ifName string) (int, error) {
    35  	return sriovutils.GetSriovNumVfs(ifName)
    36  }
    37  
    38  func (p *pciUtilsImpl) GetVFLinkNamesFromVFID(pfName string, vfID int) ([]string, error) {
    39  	return sriovutils.GetVFLinkNamesFromVFID(pfName, vfID)
    40  }
    41  
    42  func (p *pciUtilsImpl) GetPciAddress(ifName string, vf int) (string, error) {
    43  	return sriovutils.GetPciAddress(ifName, vf)
    44  }
    45  
    46  func (p *pciUtilsImpl) EnableArpAndNdiscNotify(ifName string) error {
    47  	return sriovutils.EnableArpAndNdiscNotify(ifName)
    48  }
    49  
    50  // Manager provides interface invoke sriov nic related operations
    51  type Manager interface {
    52  	SetupVF(conf *cnitypes.NetConf, podifName string, netns ns.NetNS) error
    53  	ReleaseVF(conf *cnitypes.NetConf, podifName string, netns ns.NetNS) error
    54  	ResetVFConfig(conf *cnitypes.NetConf) error
    55  	ApplyVFConfig(conf *cnitypes.NetConf) error
    56  	FillOriginalVfInfo(conf *cnitypes.NetConf) error
    57  	CmdAdd(req *cnitypes.PodRequest) (*current.Result, error)
    58  	CmdDel(req *cnitypes.PodRequest) error
    59  }
    60  
    61  type sriovManager struct {
    62  	nLink sriovutils.NetlinkManager
    63  	utils pciUtils
    64  }
    65  
    66  // NewSriovManager returns an instance of SriovManager
    67  func NewSriovManager() *sriovManager {
    68  	return &sriovManager{
    69  		nLink: &sriovutils.MyNetlink{},
    70  		utils: &pciUtilsImpl{},
    71  	}
    72  }
    73  
    74  // SetupVF sets up a VF in Pod netns
    75  func (s *sriovManager) SetupVF(conf *cnitypes.NetConf, podifName string, netns ns.NetNS) error {
    76  	linkName := conf.OrigVfState.HostIFName
    77  
    78  	linkObj, err := s.nLink.LinkByName(linkName)
    79  	if err != nil {
    80  		return fmt.Errorf("error getting VF netdevice with name %s", linkName)
    81  	}
    82  
    83  	// Save the original effective MAC address before overriding it
    84  	conf.OrigVfState.EffectiveMAC = linkObj.Attrs().HardwareAddr.String()
    85  
    86  	// tempName used as intermediary name to avoid name conflicts
    87  	tempName := fmt.Sprintf("%s%d", "temp_", linkObj.Attrs().Index)
    88  
    89  	// 1. Set link down
    90  	klog.Infof("1. Set link down %+v", linkObj)
    91  	if err := s.nLink.LinkSetDown(linkObj); err != nil {
    92  		return fmt.Errorf("failed to down vf device %q: %v", linkName, err)
    93  	}
    94  
    95  	// 2. Set temp name
    96  	klog.Infof("2. Set temp name %+v %s", linkObj, tempName)
    97  	if err := s.nLink.LinkSetName(linkObj, tempName); err != nil {
    98  		return fmt.Errorf("error setting temp IF name %s for %s", tempName, linkName)
    99  	}
   100  
   101  	// 3. Change netns
   102  	klog.Infof("3. Change netns %+v %d", linkObj, int(netns.Fd()))
   103  	if err := s.nLink.LinkSetNsFd(linkObj, int(netns.Fd())); err != nil {
   104  		return fmt.Errorf("failed to move IF %s to netns: %q", tempName, err)
   105  	}
   106  
   107  	if err := netns.Do(func(_ ns.NetNS) error {
   108  		// 4. Set Pod IF name
   109  		klog.Infof("4. Set Pod IF name %+v %s", linkObj, podifName)
   110  		if err := s.nLink.LinkSetName(linkObj, podifName); err != nil {
   111  			return fmt.Errorf("error setting container interface name %s for %s", linkName, tempName)
   112  		}
   113  
   114  		// 5. Enable IPv4 ARP notify and IPv6 Network Discovery notify
   115  		// Error is ignored here because enabling this feature is only a performance enhancement.
   116  		klog.Infof("5. Enable IPv4 ARP notify and IPv6 Network Discovery notify %s", podifName)
   117  		_ = s.utils.EnableArpAndNdiscNotify(podifName)
   118  
   119  		// 6. Set MAC address
   120  		if conf.MAC != "" {
   121  			klog.Infof("6. Set MAC address %+v %s %s", s.nLink, podifName, conf.MAC)
   122  			err = sriovutils.SetVFEffectiveMAC(s.nLink, podifName, conf.MAC)
   123  			if err != nil {
   124  				return fmt.Errorf("failed to set netlink MAC address to %s: %v", conf.MAC, err)
   125  			}
   126  		}
   127  
   128  		// 7. Bring IF up in Pod netns
   129  		klog.Infof("7. Bring IF up in Pod netns %+v", linkObj)
   130  		if err := s.nLink.LinkSetUp(linkObj); err != nil {
   131  			return fmt.Errorf("error bringing interface up in container ns: %q", err)
   132  		}
   133  
   134  		return nil
   135  	}); err != nil {
   136  		return fmt.Errorf("error setting up interface in container namespace: %q", err)
   137  	}
   138  
   139  	return nil
   140  }
   141  
   142  // ReleaseVF reset a VF from Pod netns and return it to init netns
   143  func (s *sriovManager) ReleaseVF(conf *cnitypes.NetConf, podifName string, netns ns.NetNS) error {
   144  	initns, err := ns.GetCurrentNS()
   145  	if err != nil {
   146  		return fmt.Errorf("failed to get init netns: %v", err)
   147  	}
   148  
   149  	return netns.Do(func(_ ns.NetNS) error {
   150  		// get VF device
   151  		klog.Infof("Get VF device %s", podifName)
   152  		linkObj, err := s.nLink.LinkByName(podifName)
   153  		if err != nil {
   154  			return fmt.Errorf("failed to get netlink device with name %s: %q", podifName, err)
   155  		}
   156  
   157  		// shutdown VF device
   158  		klog.Infof("Shutdown VF device %+v", linkObj)
   159  		if err = s.nLink.LinkSetDown(linkObj); err != nil {
   160  			return fmt.Errorf("failed to set link %s down: %q", podifName, err)
   161  		}
   162  
   163  		// rename VF device
   164  		klog.Infof("Rename VF device %+v %s", linkObj, conf.OrigVfState.HostIFName)
   165  		err = s.nLink.LinkSetName(linkObj, conf.OrigVfState.HostIFName)
   166  		if err != nil {
   167  			return fmt.Errorf("failed to rename link %s to host name %s: %q", podifName, conf.OrigVfState.HostIFName, err)
   168  		}
   169  
   170  		if conf.MAC != "" {
   171  			// reset effective MAC address
   172  			klog.Infof("Reset effective MAC address %+v %s %s", s.nLink, conf.OrigVfState.HostIFName, conf.OrigVfState.EffectiveMAC)
   173  			err = sriovutils.SetVFEffectiveMAC(s.nLink, conf.OrigVfState.HostIFName, conf.OrigVfState.EffectiveMAC)
   174  			if err != nil {
   175  				return fmt.Errorf("failed to restore original effective netlink MAC address %s: %v", conf.OrigVfState.EffectiveMAC, err)
   176  			}
   177  		}
   178  
   179  		// move VF device to init netns
   180  		klog.Infof("Move VF device to init netns %+v %d", linkObj, int(initns.Fd()))
   181  		if err = s.nLink.LinkSetNsFd(linkObj, int(initns.Fd())); err != nil {
   182  			return fmt.Errorf("failed to move interface %s to init netns: %v", conf.OrigVfState.HostIFName, err)
   183  		}
   184  
   185  		return nil
   186  	})
   187  }
   188  
   189  func getVfInfo(link netlink.Link, id int) *netlink.VfInfo {
   190  	attrs := link.Attrs()
   191  	for _, vf := range attrs.Vfs {
   192  		if vf.ID == id {
   193  			return &vf
   194  		}
   195  	}
   196  	return nil
   197  }
   198  
   199  // ApplyVFConfig configure a VF with parameters given in NetConf
   200  func (s *sriovManager) ApplyVFConfig(conf *cnitypes.NetConf) error {
   201  	pfLink, err := s.nLink.LinkByName(conf.Master)
   202  	if err != nil {
   203  		return fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
   204  	}
   205  	// 1. Set vlan
   206  	if host_vlans {
   207  		if err = s.nLink.LinkSetVfVlanQosProto(pfLink, conf.VFID, *conf.Vlan, *conf.VlanQoS, cnitypes.VlanProtoInt[*conf.VlanProto]); err != nil {
   208  			return fmt.Errorf("failed to set vf %d vlan configuration - id %d, qos %d and proto %s: %v", conf.VFID, *conf.Vlan, *conf.VlanQoS, *conf.VlanProto, err)
   209  		}
   210  	}
   211  
   212  	// 2. Set mac address
   213  	if conf.MAC != "" {
   214  		// when we restore the original hardware mac address we may get a device or resource busy. so we introduce retry
   215  		if err := sriovutils.SetVFHardwareMAC(s.nLink, conf.Master, conf.VFID, conf.MAC); err != nil {
   216  			return fmt.Errorf("failed to set MAC address to %s: %v", conf.MAC, err)
   217  		}
   218  	}
   219  
   220  	// 3. Set min/max tx link rate. 0 means no rate limiting. Support depends on NICs and driver.
   221  	var minTxRate, maxTxRate int
   222  	rateConfigured := false
   223  	if conf.MinTxRate != nil {
   224  		minTxRate = *conf.MinTxRate
   225  		rateConfigured = true
   226  	}
   227  
   228  	if conf.MaxTxRate != nil {
   229  		maxTxRate = *conf.MaxTxRate
   230  		rateConfigured = true
   231  	}
   232  
   233  	if rateConfigured {
   234  		if err = s.nLink.LinkSetVfRate(pfLink, conf.VFID, minTxRate, maxTxRate); err != nil {
   235  			return fmt.Errorf("failed to set vf %d min_tx_rate to %d Mbps: max_tx_rate to %d Mbps: %v",
   236  				conf.VFID, minTxRate, maxTxRate, err)
   237  		}
   238  	}
   239  
   240  	// 4. Set spoofchk flag
   241  	if conf.SpoofChk != "" {
   242  		spoofChk := false
   243  		if conf.SpoofChk == "on" {
   244  			spoofChk = true
   245  		}
   246  		if err = s.nLink.LinkSetVfSpoofchk(pfLink, conf.VFID, spoofChk); err != nil {
   247  			return fmt.Errorf("failed to set vf %d spoofchk flag to %s: %v", conf.VFID, conf.SpoofChk, err)
   248  		}
   249  	}
   250  
   251  	// 5. Set trust flag
   252  	if conf.Trust != "" {
   253  		trust := false
   254  		if conf.Trust == "on" {
   255  			trust = true
   256  		}
   257  		if err = s.nLink.LinkSetVfTrust(pfLink, conf.VFID, trust); err != nil {
   258  			return fmt.Errorf("failed to set vf %d trust flag to %s: %v", conf.VFID, conf.Trust, err)
   259  		}
   260  	}
   261  
   262  	// 6. Set link state
   263  	if conf.LinkState != "" {
   264  		var state uint32
   265  		switch conf.LinkState {
   266  		case "auto":
   267  			state = netlink.VF_LINK_STATE_AUTO
   268  		case "enable":
   269  			state = netlink.VF_LINK_STATE_ENABLE
   270  		case "disable":
   271  			state = netlink.VF_LINK_STATE_DISABLE
   272  		default:
   273  			// the value should have been validated earlier, return error if we somehow got here
   274  			return fmt.Errorf("unknown link state %s when setting it for vf %d: %v", conf.LinkState, conf.VFID, err)
   275  		}
   276  		if err = s.nLink.LinkSetVfState(pfLink, conf.VFID, state); err != nil {
   277  			return fmt.Errorf("failed to set vf %d link state to %d: %v", conf.VFID, state, err)
   278  		}
   279  	}
   280  
   281  	return nil
   282  }
   283  
   284  // FillOriginalVfInfo fills the original vf info
   285  func (s *sriovManager) FillOriginalVfInfo(conf *cnitypes.NetConf) error {
   286  	pfLink, err := s.nLink.LinkByName(conf.Master)
   287  	if err != nil {
   288  		return fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
   289  	}
   290  	// Save current the VF state before modifying it
   291  	vfState := getVfInfo(pfLink, conf.VFID)
   292  	if vfState == nil {
   293  		return fmt.Errorf("failed to find vf %d", conf.VFID)
   294  	}
   295  	conf.OrigVfState.FillFromVfInfo(vfState)
   296  
   297  	return err
   298  }
   299  
   300  // ResetVFConfig reset a VF to its original state
   301  func (s *sriovManager) ResetVFConfig(conf *cnitypes.NetConf) error {
   302  	pfLink, err := s.nLink.LinkByName(conf.Master)
   303  	if err != nil {
   304  		return fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
   305  	}
   306  
   307  	if host_vlans {
   308  		// Set 802.1q as default in case cache config does not have a value for vlan proto.
   309  		if conf.OrigVfState.VlanProto == 0 {
   310  			conf.OrigVfState.VlanProto = cnitypes.VlanProtoInt[cnitypes.Proto8021q]
   311  		}
   312  
   313  		if err = s.nLink.LinkSetVfVlanQosProto(pfLink, conf.VFID, conf.OrigVfState.Vlan, conf.OrigVfState.VlanQoS, conf.OrigVfState.VlanProto); err != nil {
   314  			return fmt.Errorf("failed to set vf %d vlan configuration - id %d, qos %d and proto %d: %v", conf.VFID, conf.OrigVfState.Vlan, conf.OrigVfState.VlanQoS, conf.OrigVfState.VlanProto, err)
   315  		}
   316  	}
   317  
   318  	// Restore spoofchk
   319  	if conf.SpoofChk != "" {
   320  		if err = s.nLink.LinkSetVfSpoofchk(pfLink, conf.VFID, conf.OrigVfState.SpoofChk); err != nil {
   321  			return fmt.Errorf("failed to restore spoofchk for vf %d: %v", conf.VFID, err)
   322  		}
   323  	}
   324  
   325  	// Restore the original administrative MAC address
   326  	if conf.MAC != "" {
   327  		// when we restore the original hardware mac address we may get a device or resource busy. so we introduce retry
   328  		if err := sriovutils.SetVFHardwareMAC(s.nLink, conf.Master, conf.VFID, conf.OrigVfState.AdminMAC); err != nil {
   329  			return fmt.Errorf("failed to restore original administrative MAC address %s: %v", conf.OrigVfState.AdminMAC, err)
   330  		}
   331  	}
   332  
   333  	// Restore VF trust
   334  	if conf.Trust != "" {
   335  		if err = s.nLink.LinkSetVfTrust(pfLink, conf.VFID, conf.OrigVfState.Trust); err != nil {
   336  			return fmt.Errorf("failed to set trust for vf %d: %v", conf.VFID, err)
   337  		}
   338  	}
   339  
   340  	// Restore rate limiting
   341  	if conf.MinTxRate != nil || conf.MaxTxRate != nil {
   342  		if err = s.nLink.LinkSetVfRate(pfLink, conf.VFID, conf.OrigVfState.MinTxRate, conf.OrigVfState.MaxTxRate); err != nil {
   343  			return fmt.Errorf("failed to disable rate limiting for vf %d %v", conf.VFID, err)
   344  		}
   345  	}
   346  
   347  	// Restore link state to `auto`
   348  	if conf.LinkState != "" {
   349  		// Reset only when link_state was explicitly specified, to  accommodate for drivers / NICs
   350  		// that don't support the netlink command (e.g. igb driver)
   351  		if err = s.nLink.LinkSetVfState(pfLink, conf.VFID, conf.OrigVfState.LinkState); err != nil {
   352  			return fmt.Errorf("failed to set link state to auto for vf %d: %v", conf.VFID, err)
   353  		}
   354  	}
   355  
   356  	return nil
   357  }
   358  
   359  func (sm *sriovManager) CmdAdd(req *cnitypes.PodRequest) (*current.Result, error) {
   360  	klog.Info("CmdAdd called")
   361  
   362  	netConf, err := sriovconfig.LoadConf(req.CNIConf)
   363  	if err != nil {
   364  		return nil, fmt.Errorf("SRIOV-CNI failed to load netconf: %v", err)
   365  	}
   366  
   367  	// RuntimeConfig takes preference than envArgs.
   368  	// This maintains compatibility of using envArgs
   369  	// for MAC config.
   370  	if netConf.RuntimeConfig.Mac != "" {
   371  		netConf.MAC = netConf.RuntimeConfig.Mac
   372  	}
   373  
   374  	// Always use lower case for mac address
   375  	netConf.MAC = strings.ToLower(netConf.MAC)
   376  
   377  	netns, err := ns.GetNS(req.Netns)
   378  	if err != nil {
   379  		return nil, fmt.Errorf("failed to open netns %q: %v", netns, err)
   380  	}
   381  	defer netns.Close()
   382  
   383  	// TODO: Some vendors have issues with Netlink, thus disable the feature for now
   384  	// err = sm.FillOriginalVfInfo(netConf)
   385  	// if err != nil {
   386  	// 	return nil, fmt.Errorf("failed to get original vf information: %v", err)
   387  	// }
   388  	defer func() {
   389  		if err != nil {
   390  			err := netns.Do(func(_ ns.NetNS) error {
   391  				_, err := netlink.LinkByName(req.IfName)
   392  				return err
   393  			})
   394  			if err == nil {
   395  				_ = sm.ReleaseVF(netConf, req.IfName, netns)
   396  			}
   397  			// Reset the VF if failure occurs before the netconf is cached
   398  			_ = sm.ResetVFConfig(netConf)
   399  		}
   400  	}()
   401  	if err := sm.ApplyVFConfig(netConf); err != nil {
   402  		return nil, fmt.Errorf("SRIOV-CNI failed to configure VF %q", err)
   403  	}
   404  
   405  	result := &current.Result{}
   406  	result.CNIVersion = netConf.CNIVersion
   407  	result.Interfaces = []*current.Interface{{
   408  		Name:    req.IfName,
   409  		Sandbox: netns.Path(),
   410  	}}
   411  
   412  	if !netConf.DPDKMode {
   413  		err = sm.SetupVF(netConf, req.IfName, netns)
   414  
   415  		if err != nil {
   416  			return nil, fmt.Errorf("failed to set up pod interface %q from the device %q: %v", req.IfName, netConf.Master, err)
   417  		}
   418  	}
   419  
   420  	result.Interfaces[0].Mac = sriovconfig.GetMacAddressForResult(netConf)
   421  
   422  	// run the IPAM plugin
   423  	if netConf.IPAM.Type != "" {
   424  		var r types.Result
   425  		r, err = ipam.ExecAdd(netConf.IPAM.Type, req.CNIReq.Config)
   426  		if err != nil {
   427  			return nil, fmt.Errorf("failed to set up IPAM plugin type %q from the device %q: %v", netConf.IPAM.Type, netConf.Master, err)
   428  		}
   429  
   430  		defer func() {
   431  			if err != nil {
   432  				_ = ipam.ExecDel(netConf.IPAM.Type, req.CNIReq.Config)
   433  			}
   434  		}()
   435  
   436  		// Convert the IPAM result into the current Result type
   437  		var newResult *current.Result
   438  		newResult, err = current.NewResultFromResult(r)
   439  		if err != nil {
   440  			return nil, err
   441  		}
   442  
   443  		if len(newResult.IPs) == 0 {
   444  			err = errors.New("IPAM plugin returned missing IP config")
   445  			return nil, err
   446  		}
   447  
   448  		newResult.Interfaces = result.Interfaces
   449  
   450  		for _, ipc := range newResult.IPs {
   451  			// All addresses apply to the container interface (move from host)
   452  			ipc.Interface = current.Int(0)
   453  		}
   454  
   455  		if !netConf.DPDKMode {
   456  			err = netns.Do(func(_ ns.NetNS) error {
   457  				err := ipam.ConfigureIface(req.IfName, newResult)
   458  				if err != nil {
   459  					return err
   460  				}
   461  
   462  				/* After IPAM configuration is done, the following needs to handle the case of an IP address being reused by a different pods.
   463  				 * This is achieved by sending Gratuitous ARPs and/or Unsolicited Neighbor Advertisements unconditionally.
   464  				 * Although we set arp_notify and ndisc_notify unconditionally on the interface (please see EnableArpAndNdiscNotify()), the kernel
   465  				 * only sends GARPs/Unsolicited NA when the interface goes from down to up, or when the link-layer address changes on the interfaces.
   466  				 * These scenarios are perfectly valid and recommended to be enabled for optimal network performance.
   467  				 * However for our specific case, which the kernel is unaware of, is the reuse of IP addresses across pods where each pod has a different
   468  				 * link-layer address for it's SRIOV interface. The ARP/Neighbor cache residing in neighbors would be invalid if an IP address is reused.
   469  				 * In order to update the cache, the GARP/Unsolicited NA packets should be sent for performance reasons. Otherwise, the neighbors
   470  				 * may be sending packets with the incorrect link-layer address. Eventually, most network stacks would send ARPs and/or Neighbor
   471  				 * Solicitation packets when the connection is unreachable. This would correct the invalid cache; however this may take a significant
   472  				 * amount of time to complete.
   473  				 *
   474  				 * The error is ignored here because enabling this feature is only a performance enhancement.
   475  				 */
   476  				_ = sriovutils.AnnounceIPs(req.IfName, newResult.IPs)
   477  				return nil
   478  			})
   479  			if err != nil {
   480  				return nil, err
   481  			}
   482  		}
   483  		result = newResult
   484  	}
   485  
   486  	// Cache NetConf for CmdDel
   487  	klog.Infof("Cache NetConf for CmdDel %s %+v", sriovconfig.DefaultCNIDir, netConf)
   488  	if err = sriovutils.SaveNetConf(req.ContainerId, sriovconfig.DefaultCNIDir, req.IfName, netConf); err != nil {
   489  		return nil, fmt.Errorf("error saving NetConf %q", err)
   490  	}
   491  
   492  	// Mark the pci address as in use.
   493  	klog.Infof("Mark the PCI address as in use %s %s", sriovconfig.DefaultCNIDir, netConf.DeviceID)
   494  	allocator := sriovutils.NewPCIAllocator(sriovconfig.DefaultCNIDir)
   495  	if err = allocator.SaveAllocatedPCI(netConf.DeviceID, req.Netns); err != nil {
   496  		return nil, fmt.Errorf("error saving the pci allocation for vf pci address %s: %v", netConf.DeviceID, err)
   497  	}
   498  
   499  	return result, nil
   500  }
   501  
   502  func (sm *sriovManager) CmdDel(req *cnitypes.PodRequest) error {
   503  	klog.Info("CmdDel called")
   504  
   505  	netConf, cRefPath, err := sriovconfig.LoadConfFromCache(req.ContainerId, req.IfName)
   506  	if err != nil {
   507  		// If cmdDel() fails, cached netconf is cleaned up by
   508  		// the followed defer call. However, subsequence calls
   509  		// of cmdDel() from kubelet fail in a dead loop due to
   510  		// cached netconf doesn't exist.
   511  		// Return nil when LoadConfFromCache fails since the rest
   512  		// of cmdDel() code relies on netconf as input argument
   513  		// and there is no meaning to continue.
   514  		klog.Errorf("Cannot load config file from cache %v", err)
   515  		return nil
   516  	}
   517  
   518  	defer func() {
   519  		if err == nil && cRefPath != "" {
   520  			_ = sriovutils.CleanCachedNetConf(cRefPath)
   521  		}
   522  	}()
   523  
   524  	if netConf.IPAM.Type != "" {
   525  		err = ipam.ExecDel(netConf.IPAM.Type, req.CNIReq.Config)
   526  		if err != nil {
   527  			return err
   528  		}
   529  	}
   530  
   531  	// https://github.com/kubernetes/kubernetes/pull/35240
   532  	if req.Netns == "" {
   533  		return nil
   534  	}
   535  
   536  	// Verify VF ID existence.
   537  	if _, err := sriovutils.GetVfid(netConf.DeviceID, netConf.Master); err != nil {
   538  		return fmt.Errorf("cmdDel() error obtaining VF ID: %q", err)
   539  	}
   540  
   541  	/* ResetVFConfig resets a VF administratively. We must run ResetVFConfig
   542  	   before ReleaseVF because some drivers will error out if we try to
   543  	   reset netdev VF with trust off. So, reset VF MAC address via PF first.
   544  	*/
   545  	if err := sm.ResetVFConfig(netConf); err != nil {
   546  		return fmt.Errorf("cmdDel() error reseting VF: %q", err)
   547  	}
   548  
   549  	if !netConf.DPDKMode {
   550  		netns, err := ns.GetNS(req.Netns)
   551  		if err != nil {
   552  			// according to:
   553  			// https://github.com/kubernetes/kubernetes/issues/43014#issuecomment-287164444
   554  			// if provided path does not exist (e.x. when node was restarted)
   555  			// plugin should silently return with success after releasing
   556  			// IPAM resources
   557  			_, ok := err.(ns.NSPathNotExistErr)
   558  			if ok {
   559  				return nil
   560  			}
   561  
   562  			return fmt.Errorf("failed to open netns %s: %q", netns, err)
   563  		}
   564  		defer netns.Close()
   565  
   566  		if err = sm.ReleaseVF(netConf, req.IfName, netns); err != nil {
   567  			return err
   568  		}
   569  	}
   570  	req.CNIConf.VFID = netConf.VFID
   571  
   572  	// Mark the pci address as released
   573  	klog.Infof("Mark the PCI address as released %s %s", sriovconfig.DefaultCNIDir, netConf.DeviceID)
   574  	allocator := sriovutils.NewPCIAllocator(sriovconfig.DefaultCNIDir)
   575  	if err = allocator.DeleteAllocatedPCI(netConf.DeviceID); err != nil {
   576  		return fmt.Errorf("error cleaning the pci allocation for vf pci address %s: %v", netConf.DeviceID, err)
   577  	}
   578  
   579  	return nil
   580  }