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 }