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 := ¤t.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 }