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  }