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 }