gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/physical_endpoint.go (about)

     1  // Copyright (c) 2018 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package virtcontainers
     7  
     8  import (
     9  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    16  	"github.com/kata-containers/runtime/virtcontainers/device/drivers"
    17  	persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api"
    18  	"github.com/safchain/ethtool"
    19  )
    20  
    21  // PhysicalEndpoint gathers a physical network interface and its properties
    22  type PhysicalEndpoint struct {
    23  	IfaceName          string
    24  	HardAddr           string
    25  	EndpointProperties NetworkInfo
    26  	EndpointType       EndpointType
    27  	BDF                string
    28  	Driver             string
    29  	VendorDeviceID     string
    30  	PCIAddr            string
    31  }
    32  
    33  // Properties returns the properties of the physical interface.
    34  func (endpoint *PhysicalEndpoint) Properties() NetworkInfo {
    35  	return endpoint.EndpointProperties
    36  }
    37  
    38  // HardwareAddr returns the mac address of the physical network interface.
    39  func (endpoint *PhysicalEndpoint) HardwareAddr() string {
    40  	return endpoint.HardAddr
    41  }
    42  
    43  // Name returns name of the physical interface.
    44  func (endpoint *PhysicalEndpoint) Name() string {
    45  	return endpoint.IfaceName
    46  }
    47  
    48  // Type indentifies the endpoint as a physical endpoint.
    49  func (endpoint *PhysicalEndpoint) Type() EndpointType {
    50  	return endpoint.EndpointType
    51  }
    52  
    53  // PciAddr returns the PCI address of the endpoint.
    54  func (endpoint *PhysicalEndpoint) PciAddr() string {
    55  	return endpoint.PCIAddr
    56  }
    57  
    58  // SetPciAddr sets the PCI address of the endpoint.
    59  func (endpoint *PhysicalEndpoint) SetPciAddr(pciAddr string) {
    60  	endpoint.PCIAddr = pciAddr
    61  }
    62  
    63  // SetProperties sets the properties of the physical endpoint.
    64  func (endpoint *PhysicalEndpoint) SetProperties(properties NetworkInfo) {
    65  	endpoint.EndpointProperties = properties
    66  }
    67  
    68  // NetworkPair returns the network pair of the endpoint.
    69  func (endpoint *PhysicalEndpoint) NetworkPair() *NetworkInterfacePair {
    70  	return nil
    71  }
    72  
    73  // Attach for physical endpoint binds the physical network interface to
    74  // vfio-pci and adds device to the hypervisor with vfio-passthrough.
    75  func (endpoint *PhysicalEndpoint) Attach(h hypervisor) error {
    76  	// Unbind physical interface from host driver and bind to vfio
    77  	// so that it can be passed to qemu.
    78  	if err := bindNICToVFIO(endpoint); err != nil {
    79  		return err
    80  	}
    81  
    82  	// TODO: use device manager as general device management entrance
    83  	var vendorID, deviceID string
    84  	if splits := strings.Split(endpoint.VendorDeviceID, " "); len(splits) == 2 {
    85  		vendorID = splits[0]
    86  		deviceID = splits[1]
    87  	}
    88  
    89  	d := config.VFIODev{
    90  		BDF:      endpoint.BDF,
    91  		VendorID: vendorID,
    92  		DeviceID: deviceID,
    93  	}
    94  
    95  	return h.addDevice(d, vfioDev)
    96  }
    97  
    98  // Detach for physical endpoint unbinds the physical network interface from vfio-pci
    99  // and binds it back to the saved host driver.
   100  func (endpoint *PhysicalEndpoint) Detach(netNsCreated bool, netNsPath string) error {
   101  	// Bind back the physical network interface to host.
   102  	// We need to do this even if a new network namespace has not
   103  	// been created by virtcontainers.
   104  
   105  	// We do not need to enter the network namespace to bind back the
   106  	// physical interface to host driver.
   107  	return bindNICToHost(endpoint)
   108  }
   109  
   110  // HotAttach for physical endpoint not supported yet
   111  func (endpoint *PhysicalEndpoint) HotAttach(h hypervisor) error {
   112  	return fmt.Errorf("PhysicalEndpoint does not support Hot attach")
   113  }
   114  
   115  // HotDetach for physical endpoint not supported yet
   116  func (endpoint *PhysicalEndpoint) HotDetach(h hypervisor, netNsCreated bool, netNsPath string) error {
   117  	return fmt.Errorf("PhysicalEndpoint does not support Hot detach")
   118  }
   119  
   120  // isPhysicalIface checks if an interface is a physical device.
   121  // We use ethtool here to not rely on device sysfs inside the network namespace.
   122  func isPhysicalIface(ifaceName string) (bool, error) {
   123  	if ifaceName == "lo" {
   124  		return false, nil
   125  	}
   126  
   127  	ethHandle, err := ethtool.NewEthtool()
   128  	if err != nil {
   129  		return false, err
   130  	}
   131  	defer ethHandle.Close()
   132  
   133  	bus, err := ethHandle.BusInfo(ifaceName)
   134  	if err != nil {
   135  		return false, nil
   136  	}
   137  
   138  	// Check for a pci bus format
   139  	tokens := strings.Split(bus, ":")
   140  	if len(tokens) != 3 {
   141  		return false, nil
   142  	}
   143  
   144  	return true, nil
   145  }
   146  
   147  var sysPCIDevicesPath = "/sys/bus/pci/devices"
   148  
   149  func createPhysicalEndpoint(netInfo NetworkInfo) (*PhysicalEndpoint, error) {
   150  	// Get ethtool handle to derive driver and bus
   151  	ethHandle, err := ethtool.NewEthtool()
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	defer ethHandle.Close()
   156  
   157  	// Get BDF
   158  	bdf, err := ethHandle.BusInfo(netInfo.Iface.Name)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	// Get driver by following symlink /sys/bus/pci/devices/$bdf/driver
   164  	driverPath := filepath.Join(sysPCIDevicesPath, bdf, "driver")
   165  	link, err := os.Readlink(driverPath)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	driver := filepath.Base(link)
   171  
   172  	// Get vendor and device id from pci space (sys/bus/pci/devices/$bdf)
   173  
   174  	ifaceDevicePath := filepath.Join(sysPCIDevicesPath, bdf, "device")
   175  	contents, err := ioutil.ReadFile(ifaceDevicePath)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  
   180  	deviceID := strings.TrimSpace(string(contents))
   181  
   182  	// Vendor id
   183  	ifaceVendorPath := filepath.Join(sysPCIDevicesPath, bdf, "vendor")
   184  	contents, err = ioutil.ReadFile(ifaceVendorPath)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	vendorID := strings.TrimSpace(string(contents))
   190  	vendorDeviceID := fmt.Sprintf("%s %s", vendorID, deviceID)
   191  	vendorDeviceID = strings.TrimSpace(vendorDeviceID)
   192  
   193  	physicalEndpoint := &PhysicalEndpoint{
   194  		IfaceName:      netInfo.Iface.Name,
   195  		HardAddr:       netInfo.Iface.HardwareAddr.String(),
   196  		VendorDeviceID: vendorDeviceID,
   197  		EndpointType:   PhysicalEndpointType,
   198  		Driver:         driver,
   199  		BDF:            bdf,
   200  	}
   201  
   202  	return physicalEndpoint, nil
   203  }
   204  
   205  func bindNICToVFIO(endpoint *PhysicalEndpoint) error {
   206  	return drivers.BindDevicetoVFIO(endpoint.BDF, endpoint.Driver, endpoint.VendorDeviceID)
   207  }
   208  
   209  func bindNICToHost(endpoint *PhysicalEndpoint) error {
   210  	return drivers.BindDevicetoHost(endpoint.BDF, endpoint.Driver, endpoint.VendorDeviceID)
   211  }
   212  
   213  func (endpoint *PhysicalEndpoint) save() persistapi.NetworkEndpoint {
   214  	return persistapi.NetworkEndpoint{
   215  		Type: string(endpoint.Type()),
   216  
   217  		Physical: &persistapi.PhysicalEndpoint{
   218  			BDF:            endpoint.BDF,
   219  			Driver:         endpoint.Driver,
   220  			VendorDeviceID: endpoint.VendorDeviceID,
   221  		},
   222  	}
   223  }
   224  
   225  func (endpoint *PhysicalEndpoint) load(s persistapi.NetworkEndpoint) {
   226  	endpoint.EndpointType = PhysicalEndpointType
   227  
   228  	if s.Physical != nil {
   229  		endpoint.BDF = s.Physical.BDF
   230  		endpoint.Driver = s.Physical.Driver
   231  		endpoint.VendorDeviceID = s.Physical.VendorDeviceID
   232  	}
   233  }