github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/device/manager/utils.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 manager
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/sirupsen/logrus"
    17  
    18  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    19  	"github.com/kata-containers/runtime/virtcontainers/device/drivers"
    20  )
    21  
    22  const (
    23  	vfioPath = "/dev/vfio/"
    24  )
    25  
    26  // isVFIO checks if the device provided is a vfio group.
    27  func isVFIO(hostPath string) bool {
    28  	// Ignore /dev/vfio/vfio character device
    29  	if strings.HasPrefix(hostPath, filepath.Join(vfioPath, "vfio")) {
    30  		return false
    31  	}
    32  
    33  	if strings.HasPrefix(hostPath, vfioPath) && len(hostPath) > len(vfioPath) {
    34  		return true
    35  	}
    36  
    37  	return false
    38  }
    39  
    40  // isBlock checks if the device is a block device.
    41  func isBlock(devInfo config.DeviceInfo) bool {
    42  	return devInfo.DevType == "b"
    43  }
    44  
    45  // IsVFIOLargeBarSpaceDevice checks if the device is a large bar space device.
    46  func IsVFIOLargeBarSpaceDevice(hostPath string) (bool, error) {
    47  	if !isVFIO(hostPath) {
    48  		return false, nil
    49  	}
    50  
    51  	iommuDevicesPath := filepath.Join(config.SysIOMMUPath, filepath.Base(hostPath), "devices")
    52  	deviceFiles, err := ioutil.ReadDir(iommuDevicesPath)
    53  	if err != nil {
    54  		return false, err
    55  	}
    56  
    57  	// Pass all devices in iommu group
    58  	for _, deviceFile := range deviceFiles {
    59  		vfioDeviceType := drivers.GetVFIODeviceType(deviceFile.Name())
    60  		var isLarge bool
    61  		switch vfioDeviceType {
    62  		case config.VFIODeviceNormalType:
    63  			sysfsResource := filepath.Join(iommuDevicesPath, deviceFile.Name(), "resource")
    64  			if isLarge, err = isLargeBarSpace(sysfsResource); err != nil {
    65  				return false, err
    66  			}
    67  			deviceLogger().WithFields(logrus.Fields{
    68  				"device-file":     deviceFile.Name(),
    69  				"device-type":     vfioDeviceType,
    70  				"resource":        sysfsResource,
    71  				"large-bar-space": isLarge,
    72  			}).Info("Detect large bar space device")
    73  			return isLarge, nil
    74  		case config.VFIODeviceMediatedType:
    75  			//TODO: support VFIODeviceMediatedType
    76  			deviceLogger().WithFields(logrus.Fields{
    77  				"device-file": deviceFile.Name(),
    78  				"device-type": vfioDeviceType,
    79  			}).Warn("Detect large bar space device is not yet supported for VFIODeviceMediatedType")
    80  		default:
    81  			deviceLogger().WithFields(logrus.Fields{
    82  				"device-file": deviceFile.Name(),
    83  				"device-type": vfioDeviceType,
    84  			}).Warn("Incorrect token found when detecting large bar space devices")
    85  		}
    86  	}
    87  
    88  	return false, nil
    89  }
    90  
    91  func isLargeBarSpace(resourcePath string) (bool, error) {
    92  	buf, err := ioutil.ReadFile(resourcePath)
    93  	if err != nil {
    94  		return false, fmt.Errorf("failed to read sysfs resource: %v", err)
    95  	}
    96  
    97  	// The resource file contains host addresses of PCI resources:
    98  	// For example:
    99  	// $ cat /sys/bus/pci/devices/0000:04:00.0/resource
   100  	// 0x00000000c6000000 0x00000000c6ffffff 0x0000000000040200
   101  	// 0x0000383800000000 0x0000383bffffffff 0x000000000014220c
   102  	// Refer:
   103  	// resource format: https://github.com/torvalds/linux/blob/63623fd44972d1ed2bfb6e0fb631dfcf547fd1e7/drivers/pci/pci-sysfs.c#L145
   104  	// calculate size : https://github.com/pciutils/pciutils/blob/61ecc14a327de030336f1ff3fea9c7e7e55a90ca/lspci.c#L388
   105  	for rIdx, line := range strings.Split(string(buf), "\n") {
   106  		cols := strings.Fields(line)
   107  		// start and end columns are required to calculate the size
   108  		if len(cols) < 2 {
   109  			deviceLogger().WithField("resource-line", line).Debug("not enough columns to calculate PCI size")
   110  			continue
   111  		}
   112  		start, _ := strconv.ParseUint(cols[0], 0, 64)
   113  		end, _ := strconv.ParseUint(cols[1], 0, 64)
   114  		if start > end {
   115  			deviceLogger().WithFields(logrus.Fields{
   116  				"start": start,
   117  				"end":   end,
   118  			}).Debug("start is greater than end")
   119  			continue
   120  		}
   121  		// Use right shift to convert Bytes to GBytes
   122  		// This is equivalent to ((end - start + 1) / 1024 / 1024 / 1024)
   123  		gbSize := (end - start + 1) >> 30
   124  		deviceLogger().WithFields(logrus.Fields{
   125  			"resource": resourcePath,
   126  			"region":   rIdx,
   127  			"start":    cols[0],
   128  			"end":      cols[1],
   129  			"gb-size":  gbSize,
   130  		}).Debug("Check large bar space device")
   131  		//size is large than 4G
   132  		if gbSize > 4 {
   133  			return true, nil
   134  		}
   135  	}
   136  
   137  	return false, nil
   138  }
   139  
   140  // isVhostUserBlk checks if the device is a VhostUserBlk device.
   141  func isVhostUserBlk(devInfo config.DeviceInfo) bool {
   142  	return devInfo.DevType == "b" && devInfo.Major == config.VhostUserBlkMajor
   143  }
   144  
   145  // isVhostUserSCSI checks if the device is a VhostUserSCSI device.
   146  func isVhostUserSCSI(devInfo config.DeviceInfo) bool {
   147  	return devInfo.DevType == "b" && devInfo.Major == config.VhostUserSCSIMajor
   148  }