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

     1  package sriovutils
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/containernetworking/plugins/pkg/ns"
     9  )
    10  
    11  type PCIAllocation interface {
    12  	SaveAllocatedPCI(string, string) error
    13  	DeleteAllocatedPCI(string) error
    14  	IsAllocated(string) error
    15  }
    16  
    17  type PCIAllocator struct {
    18  	dataDir string
    19  }
    20  
    21  // NewPCIAllocator returns a new PCI allocator
    22  // it will use the <dataDir>/pci folder to store the information about allocated PCI addresses
    23  func NewPCIAllocator(dataDir string) *PCIAllocator {
    24  	return &PCIAllocator{dataDir: filepath.Join(dataDir, "pci")}
    25  }
    26  
    27  // SaveAllocatedPCI creates a file with the pci address as a name and the network namespace as the content
    28  // return error if the file was not created
    29  func (p *PCIAllocator) SaveAllocatedPCI(pciAddress, ns string) error {
    30  	if err := os.MkdirAll(p.dataDir, 0600); err != nil {
    31  		return fmt.Errorf("failed to create the sriov data directory(%q): %v", p.dataDir, err)
    32  	}
    33  
    34  	path := filepath.Join(p.dataDir, pciAddress)
    35  	err := os.WriteFile(path, []byte(ns), 0600)
    36  	if err != nil {
    37  		return fmt.Errorf("failed to write used PCI address lock file in the path(%q): %v", path, err)
    38  	}
    39  
    40  	return err
    41  }
    42  
    43  // DeleteAllocatedPCI Remove the allocated PCI file
    44  // return error if the file doesn't exist
    45  func (p *PCIAllocator) DeleteAllocatedPCI(pciAddress string) error {
    46  	path := filepath.Join(p.dataDir, pciAddress)
    47  	if err := os.Remove(path); err != nil {
    48  		return fmt.Errorf("error removing PCI address lock file %s: %v", path, err)
    49  	}
    50  	return nil
    51  }
    52  
    53  // IsAllocated checks if the PCI address file exist
    54  // if it exists we also check the network namespace still exist if not we delete the allocation
    55  // The function will return an error if the pci is still allocated to a running pod
    56  func (p *PCIAllocator) IsAllocated(pciAddress string) (bool, error) {
    57  	path := filepath.Join(p.dataDir, pciAddress)
    58  	_, err := os.Stat(path)
    59  	if err != nil {
    60  		if os.IsNotExist(err) {
    61  			return false, nil
    62  		}
    63  
    64  		return false, fmt.Errorf("failed to check for pci address file for %s: %v", path, err)
    65  	}
    66  
    67  	dat, err := os.ReadFile(path)
    68  	if err != nil {
    69  		return false, fmt.Errorf("failed to read for pci address file for %s: %v", path, err)
    70  	}
    71  
    72  	// To prevent a locking of a PCI address for every pciAddress file we also add the netns path where it's been used
    73  	// This way if for some reason the cmdDel command was not called but the pod namespace doesn't exist anymore
    74  	// we release the PCI address
    75  	networkNamespace, err := ns.GetNS(string(dat))
    76  	if err != nil {
    77  		// FIXME: Fix Logging
    78  		//logging.Debug("Mark the PCI address as released",
    79  		//	"func", "IsAllocated",
    80  		//	"pciAddress", pciAddress)
    81  		err = p.DeleteAllocatedPCI(pciAddress)
    82  		if err != nil {
    83  			return false, fmt.Errorf("error deleting the pci allocation for vf pci address %s: %v", pciAddress, err)
    84  		}
    85  
    86  		return false, nil
    87  	}
    88  
    89  	// Close the network namespace
    90  	networkNamespace.Close()
    91  	return true, nil
    92  }