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

     1  package sriovconfig
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/openshift/dpu-operator/dpu-cni/pkgs/cnitypes"
    10  	"github.com/openshift/dpu-operator/dpu-cni/pkgs/sriovutils"
    11  )
    12  
    13  var (
    14  	// DefaultCNIDir used for caching NetConf
    15  	DefaultCNIDir = "/var/lib/cni/dpusriov"
    16  )
    17  
    18  // LoadConf parses and validates stdin netconf and returns NetConf object
    19  func LoadConf(n *cnitypes.NetConf) (*cnitypes.NetConf, error) {
    20  	// DeviceID takes precedence; if we are given a VF pciaddr then work from there
    21  	if n.DeviceID != "" {
    22  		// Get rest of the VF information
    23  		pfName, vfID, err := getVfInfo(n.DeviceID)
    24  		if err != nil {
    25  			return nil, fmt.Errorf("LoadConf(): failed to get VF information: %q", err)
    26  		}
    27  		n.VFID = vfID
    28  		n.Master = pfName
    29  	} else {
    30  		return nil, fmt.Errorf("LoadConf(): VF pci addr is required")
    31  	}
    32  
    33  	// Check if the device is already allocated.
    34  	// This is to prevent issues where kubelet request to delete a pod and in the same time a new pod using the same
    35  	// vf is started. we can have an issue where the cmdDel of the old pod is called AFTER the cmdAdd of the new one
    36  	// This will block the new pod creation until the cmdDel is done.
    37  	// FIXME: Fix Logging
    38  	//logging.Debug("Check if the device is already allocated",
    39  	//	"func", "LoadConf",
    40  	//	"DefaultCNIDir", DefaultCNIDir,
    41  	//	"n.DeviceID", n.DeviceID)
    42  	allocator := sriovutils.NewPCIAllocator(DefaultCNIDir)
    43  	isAllocated, err := allocator.IsAllocated(n.DeviceID)
    44  	if err != nil {
    45  		return n, err
    46  	}
    47  
    48  	if isAllocated {
    49  		return n, fmt.Errorf("pci address %s is already allocated", n.DeviceID)
    50  	}
    51  
    52  	// Assuming VF is netdev interface; Get interface name(s)
    53  	hostIFName, err := sriovutils.GetVFLinkName(n.DeviceID)
    54  	if err != nil || hostIFName == "" {
    55  		// VF interface not found; check if VF has dpdk driver
    56  		hasDpdkDriver, err := sriovutils.HasDpdkDriver(n.DeviceID)
    57  		if err != nil {
    58  			return nil, fmt.Errorf("LoadConf(): failed to detect if VF %s has dpdk driver %q", n.DeviceID, err)
    59  		}
    60  		n.DPDKMode = hasDpdkDriver
    61  	}
    62  
    63  	if hostIFName != "" {
    64  		n.OrigVfState.HostIFName = hostIFName
    65  	}
    66  
    67  	if hostIFName == "" && !n.DPDKMode {
    68  		return nil, fmt.Errorf("LoadConf(): the VF %s does not have a interface name or a dpdk driver", n.DeviceID)
    69  	}
    70  
    71  	if n.Vlan == nil {
    72  		vlan := 0
    73  		n.Vlan = &vlan
    74  	}
    75  
    76  	// validate vlan id range
    77  	if *n.Vlan < 0 || *n.Vlan > 4094 {
    78  		return nil, fmt.Errorf("LoadConf(): vlan id %d invalid: value must be in the range 0-4094", *n.Vlan)
    79  	}
    80  
    81  	if n.VlanQoS == nil {
    82  		qos := 0
    83  		n.VlanQoS = &qos
    84  	}
    85  
    86  	// validate that VLAN QoS is in the 0-7 range
    87  	if *n.VlanQoS < 0 || *n.VlanQoS > 7 {
    88  		return nil, fmt.Errorf("LoadConf(): vlan QoS PCP %d invalid: value must be in the range 0-7", *n.VlanQoS)
    89  	}
    90  
    91  	// validate non-zero value for vlan id if vlan qos is set to a non-zero value
    92  	if *n.VlanQoS != 0 && *n.Vlan == 0 {
    93  		return nil, fmt.Errorf("LoadConf(): non-zero vlan id must be configured to set vlan QoS to a non-zero value")
    94  	}
    95  
    96  	if n.VlanProto == nil {
    97  		proto := cnitypes.Proto8021q
    98  		n.VlanProto = &proto
    99  	}
   100  
   101  	*n.VlanProto = strings.ToLower(*n.VlanProto)
   102  	if *n.VlanProto != cnitypes.Proto8021ad && *n.VlanProto != cnitypes.Proto8021q {
   103  		return nil, fmt.Errorf("LoadConf(): vlan Proto %s invalid: value must be '802.1Q' or '802.1ad'", *n.VlanProto)
   104  	}
   105  
   106  	// validate non-zero value for vlan id if vlan proto is set to 802.1ad
   107  	if *n.VlanProto == cnitypes.Proto8021ad && *n.Vlan == 0 {
   108  		return nil, fmt.Errorf("LoadConf(): non-zero vlan id must be configured to set vlan proto 802.1ad")
   109  	}
   110  
   111  	// validate that link state is one of supported values
   112  	if n.LinkState != "" && n.LinkState != "auto" && n.LinkState != "enable" && n.LinkState != "disable" {
   113  		return nil, fmt.Errorf("LoadConf(): invalid link_state value: %s", n.LinkState)
   114  	}
   115  
   116  	return n, nil
   117  }
   118  
   119  func getVfInfo(vfPci string) (string, int, error) {
   120  	var vfID int
   121  
   122  	pf, err := sriovutils.GetPfName(vfPci)
   123  	if err != nil {
   124  		return "", vfID, err
   125  	}
   126  
   127  	vfID, err = sriovutils.GetVfid(vfPci, pf)
   128  	if err != nil {
   129  		return "", vfID, err
   130  	}
   131  
   132  	return pf, vfID, nil
   133  }
   134  
   135  // LoadConfFromCache retrieves cached NetConf returns it along with a handle for removal
   136  func LoadConfFromCache(containerID string, ifName string) (*cnitypes.NetConf, string, error) {
   137  	netConf := &cnitypes.NetConf{}
   138  
   139  	s := []string{containerID, ifName}
   140  	cRef := strings.Join(s, "-")
   141  	cRefPath := filepath.Join(DefaultCNIDir, cRef)
   142  
   143  	netConfBytes, err := sriovutils.ReadScratchNetConf(cRefPath)
   144  	if err != nil {
   145  		return nil, "", fmt.Errorf("error reading cached NetConf in %s with name %s", DefaultCNIDir, cRef)
   146  	}
   147  
   148  	if err = json.Unmarshal(netConfBytes, netConf); err != nil {
   149  		return nil, "", fmt.Errorf("failed to parse NetConf: %q", err)
   150  	}
   151  
   152  	return netConf, cRefPath, nil
   153  }
   154  
   155  // GetMacAddressForResult return the mac address we should report to the CNI call return object
   156  // if the device is on kernel mode we report that one back
   157  // if not we check the administrative mac address on the PF
   158  // if it is set and is not zero, report it.
   159  func GetMacAddressForResult(netConf *cnitypes.NetConf) string {
   160  	if netConf.MAC != "" {
   161  		return netConf.MAC
   162  	}
   163  	if !netConf.DPDKMode {
   164  		return netConf.OrigVfState.EffectiveMAC
   165  	}
   166  	if netConf.OrigVfState.AdminMAC != "00:00:00:00:00:00" {
   167  		return netConf.OrigVfState.AdminMAC
   168  	}
   169  
   170  	return ""
   171  }