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