gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/device/config/config.go (about) 1 // Copyright (c) 2017-2018 Intel Corporation 2 // Copyright (c) 2018 Huawei Corporation 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 package config 8 9 import ( 10 "fmt" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "strconv" 15 "strings" 16 17 "github.com/go-ini/ini" 18 "golang.org/x/sys/unix" 19 ) 20 21 // DeviceType indicates device type 22 type DeviceType string 23 24 const ( 25 // DeviceVFIO is the VFIO device type 26 DeviceVFIO DeviceType = "vfio" 27 28 // DeviceBlock is the block device type 29 DeviceBlock DeviceType = "block" 30 31 // DeviceGeneric is a generic device type 32 DeviceGeneric DeviceType = "generic" 33 34 //VhostUserSCSI - SCSI based vhost-user type 35 VhostUserSCSI = "vhost-user-scsi-pci" 36 37 //VhostUserNet - net based vhost-user type 38 VhostUserNet = "virtio-net-pci" 39 40 //VhostUserBlk represents a block vhostuser device type 41 VhostUserBlk = "vhost-user-blk-pci" 42 43 //VhostUserFS represents a virtio-fs vhostuser device type 44 VhostUserFS = "vhost-user-fs-pci" 45 ) 46 47 const ( 48 // VirtioMmio means use virtio-mmio for mmio based drives 49 VirtioMmio = "virtio-mmio" 50 51 // VirtioBlock means use virtio-blk for hotplugging drives 52 VirtioBlock = "virtio-blk" 53 54 // VirtioBlockCCW means use virtio-blk for hotplugging drives 55 VirtioBlockCCW = "virtio-blk-ccw" 56 57 // VirtioSCSI means use virtio-scsi for hotplugging drives 58 VirtioSCSI = "virtio-scsi" 59 60 // Nvdimm means use nvdimm for hotplugging drives 61 Nvdimm = "nvdimm" 62 ) 63 64 const ( 65 // Virtio9P means use virtio-9p for the shared file system 66 Virtio9P = "virtio-9p" 67 68 // VirtioFS means use virtio-fs for the shared file system 69 VirtioFS = "virtio-fs" 70 ) 71 72 const ( 73 // The OCI spec requires the major-minor number to be provided for a 74 // device. We have chosen the below major numbers to represent 75 // vhost-user devices. 76 VhostUserBlkMajor = 241 77 VhostUserSCSIMajor = 242 78 ) 79 80 // Defining these as a variable instead of a const, to allow 81 // overriding this in the tests. 82 83 // SysDevPrefix is static string of /sys/dev 84 var SysDevPrefix = "/sys/dev" 85 86 // SysIOMMUPath is static string of /sys/kernel/iommu_groups 87 var SysIOMMUPath = "/sys/kernel/iommu_groups" 88 89 // SysBusPciDevicesPath is static string of /sys/bus/pci/devices 90 var SysBusPciDevicesPath = "/sys/bus/pci/devices" 91 92 // SysBusPciSlotsPath is static string of /sys/bus/pci/slots 93 var SysBusPciSlotsPath = "/sys/bus/pci/slots" 94 95 var getSysDevPath = getSysDevPathImpl 96 97 // DeviceInfo is an embedded type that contains device data common to all types of devices. 98 type DeviceInfo struct { 99 // Hostpath is device path on host 100 HostPath string 101 102 // ContainerPath is device path inside container 103 ContainerPath string `json:"-"` 104 105 // Type of device: c, b, u or p 106 // c , u - character(unbuffered) 107 // p - FIFO 108 // b - block(buffered) special file 109 // More info in mknod(1). 110 DevType string 111 112 // Major, minor numbers for device. 113 Major int64 114 Minor int64 115 116 // Pmem enabled persistent memory. Use HostPath as backing file 117 // for a nvdimm device in the guest. 118 Pmem bool 119 120 // FileMode permission bits for the device. 121 FileMode os.FileMode 122 123 // id of the device owner. 124 UID uint32 125 126 // id of the device group. 127 GID uint32 128 129 // ID for the device that is passed to the hypervisor. 130 ID string 131 132 // DriverOptions is specific options for each device driver 133 // for example, for BlockDevice, we can set DriverOptions["blockDriver"]="virtio-blk" 134 DriverOptions map[string]string 135 } 136 137 // BlockDrive represents a block storage drive which may be used in case the storage 138 // driver has an underlying block storage device. 139 type BlockDrive struct { 140 // File is the path to the disk-image/device which will be used with this drive 141 File string 142 143 // Format of the drive 144 Format string 145 146 // ID is used to identify this drive in the hypervisor options. 147 ID string 148 149 // Index assigned to the drive. In case of virtio-scsi, this is used as SCSI LUN index 150 Index int 151 152 // MmioAddr is used to identify the slot at which the drive is attached (order?). 153 MmioAddr string 154 155 // PCIAddr is the PCI address used to identify the slot at which the drive is attached. 156 PCIAddr string 157 158 // SCSI Address of the block device, in case the device is attached using SCSI driver 159 // SCSI address is in the format SCSI-Id:LUN 160 SCSIAddr string 161 162 // NvdimmID is the nvdimm id inside the VM 163 NvdimmID string 164 165 // VirtPath at which the device appears inside the VM, outside of the container mount namespace 166 VirtPath string 167 168 // DevNo identifies the css bus id for virtio-blk-ccw 169 DevNo string 170 171 // ShareRW enables multiple qemu instances to share the File 172 ShareRW bool 173 174 // ReadOnly sets the device file readonly 175 ReadOnly bool 176 177 // Pmem enables persistent memory. Use File as backing file 178 // for a nvdimm device in the guest 179 Pmem bool 180 } 181 182 // VFIODeviceType indicates VFIO device type 183 type VFIODeviceType uint32 184 185 const ( 186 // VFIODeviceErrorType is the error type of VFIO device 187 VFIODeviceErrorType VFIODeviceType = iota 188 189 // VFIODeviceNormalType is a normal VFIO device type 190 VFIODeviceNormalType 191 192 // VFIODeviceMediatedType is a VFIO mediated device type 193 VFIODeviceMediatedType 194 ) 195 196 // VFIODev represents a VFIO drive used for hotplugging 197 type VFIODev struct { 198 // IsPCIe specifies device is PCIe or PCI 199 IsPCIe bool 200 201 // Type of VFIO device 202 Type VFIODeviceType 203 204 // ID is used to identify this drive in the hypervisor options. 205 ID string 206 207 // BDF (Bus:Device.Function) of the PCI address 208 BDF string 209 210 // sysfsdev of VFIO mediated device 211 SysfsDev string 212 213 // VendorID specifies vendor id 214 VendorID string 215 216 // DeviceID specifies device id 217 DeviceID string 218 219 // PCI Class Code 220 Class string 221 222 // Bus of VFIO PCIe device 223 Bus string 224 } 225 226 // RNGDev represents a random number generator device 227 type RNGDev struct { 228 // ID is used to identify the device in the hypervisor options. 229 ID string 230 // Filename is the file to use as entropy source. 231 Filename string 232 } 233 234 // VhostUserDeviceAttrs represents data shared by most vhost-user devices 235 type VhostUserDeviceAttrs struct { 236 DevID string 237 SocketPath string 238 Type DeviceType 239 240 // MacAddress is only meaningful for vhost user net device 241 MacAddress string 242 243 // These are only meaningful for vhost user fs devices 244 Tag string 245 CacheSize uint32 246 Cache string 247 248 // PCIAddr is the PCI address used to identify the slot at which the drive is attached. 249 // It is only meaningful for vhost user block devices 250 PCIAddr string 251 252 // Block index of the device if assigned 253 Index int 254 } 255 256 // GetHostPathFunc is function pointer used to mock GetHostPath in tests. 257 var GetHostPathFunc = GetHostPath 258 259 // GetVhostUserNodeStatFunc is function pointer used to mock GetVhostUserNodeStat 260 // in tests. Through this functon, user can get device type information. 261 var GetVhostUserNodeStatFunc = GetVhostUserNodeStat 262 263 // GetHostPath is used to fetch the host path for the device. 264 // The path passed in the spec refers to the path that should appear inside the container. 265 // We need to find the actual device path on the host based on the major-minor numbers of the device. 266 func GetHostPath(devInfo DeviceInfo, vhostUserStoreEnabled bool, vhostUserStorePath string) (string, error) { 267 if devInfo.ContainerPath == "" { 268 return "", fmt.Errorf("Empty path provided for device") 269 } 270 271 // Filter out vhost-user storage devices by device Major numbers. 272 if vhostUserStoreEnabled && devInfo.DevType == "b" && 273 (devInfo.Major == VhostUserSCSIMajor || devInfo.Major == VhostUserBlkMajor) { 274 return getVhostUserHostPath(devInfo, vhostUserStorePath) 275 } 276 277 ueventPath := filepath.Join(getSysDevPath(devInfo), "uevent") 278 if _, err := os.Stat(ueventPath); err != nil { 279 // Some devices(eg. /dev/fuse, /dev/cuse) do not always implement sysfs interface under /sys/dev 280 // These devices are passed by default by docker. 281 // 282 // Simply return the path passed in the device configuration, this does mean that no device renames are 283 // supported for these devices. 284 285 if os.IsNotExist(err) { 286 return devInfo.ContainerPath, nil 287 } 288 289 return "", err 290 } 291 292 content, err := ini.Load(ueventPath) 293 if err != nil { 294 return "", err 295 } 296 297 devName, err := content.Section("").GetKey("DEVNAME") 298 if err != nil { 299 return "", err 300 } 301 302 return filepath.Join("/dev", devName.String()), nil 303 } 304 305 // getBackingFile is used to fetch the backing file for the device. 306 func getBackingFile(devInfo DeviceInfo) (string, error) { 307 backingFilePath := filepath.Join(getSysDevPath(devInfo), "loop", "backing_file") 308 data, err := ioutil.ReadFile(backingFilePath) 309 if err != nil { 310 return "", err 311 } 312 313 return strings.TrimSpace(string(data)), nil 314 } 315 316 func getSysDevPathImpl(devInfo DeviceInfo) string { 317 var pathComp string 318 319 switch devInfo.DevType { 320 case "c", "u": 321 pathComp = "char" 322 case "b": 323 pathComp = "block" 324 default: 325 // Unsupported device types. Return nil error to ignore devices 326 // that cannot be handled currently. 327 return "" 328 } 329 330 format := strconv.FormatInt(devInfo.Major, 10) + ":" + strconv.FormatInt(devInfo.Minor, 10) 331 return filepath.Join(SysDevPrefix, pathComp, format) 332 } 333 334 // getVhostUserHostPath is used to fetch host path for the vhost-user device. 335 // For vhost-user block device like vhost-user-blk or vhost-user-scsi, its 336 // socket should be under directory "<vhostUserStorePath>/block/sockets/"; 337 // its corresponding device node should be under directory 338 // "<vhostUserStorePath>/block/devices/" 339 func getVhostUserHostPath(devInfo DeviceInfo, vhostUserStorePath string) (string, error) { 340 vhostUserDevNodePath := filepath.Join(vhostUserStorePath, "/block/devices/") 341 vhostUserSockPath := filepath.Join(vhostUserStorePath, "/block/sockets/") 342 343 sockFileName, err := getVhostUserDevName(vhostUserDevNodePath, 344 uint32(devInfo.Major), uint32(devInfo.Minor)) 345 if err != nil { 346 return "", err 347 } 348 349 // Locate socket path of vhost-user device 350 sockFilePath := filepath.Join(vhostUserSockPath, sockFileName) 351 if _, err = os.Stat(sockFilePath); os.IsNotExist(err) { 352 return "", err 353 } 354 355 return sockFilePath, nil 356 } 357 358 func GetVhostUserNodeStat(devNodePath string, devNodeStat *unix.Stat_t) (err error) { 359 return unix.Stat(devNodePath, devNodeStat) 360 } 361 362 // Filter out name of the device node whose device type is Major:Minor from directory 363 func getVhostUserDevName(dirname string, majorNum, minorNum uint32) (string, error) { 364 files, err := ioutil.ReadDir(dirname) 365 if err != nil { 366 return "", err 367 } 368 369 for _, file := range files { 370 var devStat unix.Stat_t 371 372 devFilePath := filepath.Join(dirname, file.Name()) 373 err = GetVhostUserNodeStatFunc(devFilePath, &devStat) 374 if err != nil { 375 return "", err 376 } 377 378 devMajor := unix.Major(devStat.Rdev) 379 devMinor := unix.Minor(devStat.Rdev) 380 if devMajor == majorNum && devMinor == minorNum { 381 return file.Name(), nil 382 } 383 } 384 385 return "", fmt.Errorf("Required device node (%d:%d) doesn't exist under directory %s", 386 majorNum, minorNum, dirname) 387 }