Merge "libvirt: report pci Type-PF type even when VFs are disabled"

This commit is contained in:
Jenkins 2016-01-05 03:20:33 +00:00 committed by Gerrit Code Review
commit 995f9ece39
5 changed files with 280 additions and 117 deletions

View File

@ -67,7 +67,8 @@ class PciAddress(object):
def _check_physical_function(self):
if ANY in (self.domain, self.bus, self.slot, self.func):
return
self.is_physical_function = utils.is_physical_function(self)
self.is_physical_function = utils.is_physical_function(
self.domain, self.bus, self.slot, self.func)
def _init_address_fields(self, pci_addr):
if self.is_physical_function:

View File

@ -23,7 +23,6 @@ from oslo_log import log as logging
import six
from nova import exception
from nova.i18n import _LE
LOG = logging.getLogger(__name__)
@ -33,7 +32,7 @@ _PCI_ADDRESS_PATTERN = ("^(hex{4}):(hex{2}):(hex{2}).(oct{1})$".
replace("oct", "[0-7]"))
_PCI_ADDRESS_REGEX = re.compile(_PCI_ADDRESS_PATTERN)
_VIRTFN_RE = re.compile("virtfn\d+")
_SRIOV_TOTALVFS = "sriov_totalvfs"
def pci_device_prop_match(pci_dev, specs):
@ -74,33 +73,32 @@ def get_function_by_ifname(ifname):
"""Given the device name, returns the PCI address of a device
and returns True if the address in a physical function.
"""
try:
dev_path = "/sys/class/net/%s/device" % ifname
dev_info = os.listdir(dev_path)
for dev_file in dev_info:
if _VIRTFN_RE.match(dev_file):
return os.readlink(dev_path).strip("./"), True
else:
dev_path = "/sys/class/net/%s/device" % ifname
sriov_totalvfs = 0
if os.path.isdir(dev_path):
try:
# sriov_totalvfs contains the maximum possible VFs for this PF
with open(dev_path + _SRIOV_TOTALVFS) as fd:
sriov_totalvfs = int(fd.read())
return (os.readlink(dev_path).strip("./"),
sriov_totalvfs > 0)
except (IOError, ValueError):
return os.readlink(dev_path).strip("./"), False
except Exception:
LOG.error(_LE("PCI device %s not found") % ifname)
return None, False
return None, False
def is_physical_function(pci_addr):
def is_physical_function(domain, bus, slot, function):
dev_path = "/sys/bus/pci/devices/%(d)s:%(b)s:%(s)s.%(f)s/" % {
"d": pci_addr.domain, "b": pci_addr.bus,
"s": pci_addr.slot, "f": pci_addr.func}
try:
dev_info = os.listdir(dev_path)
for dev_file in dev_info:
if _VIRTFN_RE.match(dev_file):
return True
else:
return False
except Exception:
LOG.error(_LE("PCI device %s not found") % dev_path)
return False
"d": domain, "b": bus, "s": slot, "f": function}
if os.path.isdir(dev_path):
sriov_totalvfs = 0
try:
with open(dev_path + _SRIOV_TOTALVFS) as fd:
sriov_totalvfs = int(fd.read())
return sriov_totalvfs > 0
except (IOError, ValueError):
pass
return False
def get_ifname_by_pci_address(pci_addr, pf_interface=False):

View File

@ -18,6 +18,7 @@ import glob
import os
import mock
from six.moves import builtins
from nova import exception
from nova.pci import utils
@ -68,27 +69,28 @@ class PciDeviceAddressParserTestCase(test.NoDBTestCase):
class GetFunctionByIfnameTestCase(test.NoDBTestCase):
@mock.patch('os.path.isdir', return_value=True)
@mock.patch.object(os, 'readlink')
@mock.patch.object(os, 'listdir')
def test_virtual_function(self, mock_listdir, mock_readlink):
mock_listdir.return_value = ['foo', 'bar']
def test_virtual_function(self, mock_readlink, *args):
mock_readlink.return_value = '../../../0000.00.00.1'
address, physical_function = utils.get_function_by_ifname('eth0')
self.assertEqual(address, '0000.00.00.1')
self.assertFalse(physical_function)
with mock.patch.object(
builtins, 'open', side_effect=IOError()):
address, physical_function = utils.get_function_by_ifname('eth0')
self.assertEqual(address, '0000.00.00.1')
self.assertFalse(physical_function)
@mock.patch('os.path.isdir', return_value=True)
@mock.patch.object(os, 'readlink')
@mock.patch.object(os, 'listdir')
def test_physical_function(self, mock_listdir, mock_readlink):
mock_listdir.return_value = ['foo', 'virtfn1', 'bar']
def test_physical_function(self, mock_readlink, *args):
mock_readlink.return_value = '../../../0000:00:00.1'
address, physical_function = utils.get_function_by_ifname('eth0')
self.assertEqual(address, '0000:00:00.1')
self.assertTrue(physical_function)
with mock.patch.object(
builtins, 'open', mock.mock_open(read_data='4')):
address, physical_function = utils.get_function_by_ifname('eth0')
self.assertEqual(address, '0000:00:00.1')
self.assertTrue(physical_function)
@mock.patch.object(os, 'listdir')
def test_exception(self, mock_listdir):
mock_listdir.side_effect = OSError('No such file or directory')
@mock.patch('os.path.isdir', return_value=False)
def test_exception(self, *args):
address, physical_function = utils.get_function_by_ifname('lo')
self.assertIsNone(address)
self.assertFalse(physical_function)
@ -96,31 +98,25 @@ class GetFunctionByIfnameTestCase(test.NoDBTestCase):
class IsPhysicalFunctionTestCase(test.NoDBTestCase):
class FakePciAddress(object):
def __init__(self):
self.domain = 0
self.bus = 0
self.slot = 0
self.func = 0
def setUp(self):
super(IsPhysicalFunctionTestCase, self).setUp()
self.pci_address = self.FakePciAddress()
self.pci_args = utils.get_pci_address_fields('0000:00:00.1')
@mock.patch.object(os, 'listdir')
def test_virtual_function(self, mock_listdir):
mock_listdir.return_value = ['foo', 'bar']
self.assertFalse(utils.is_physical_function(self.pci_address))
@mock.patch('os.path.isdir', return_value=True)
def test_virtual_function(self, *args):
with mock.patch.object(
builtins, 'open', side_effect=IOError()):
self.assertFalse(utils.is_physical_function(*self.pci_args))
@mock.patch.object(os, 'listdir')
def test_physical_function(self, mock_listdir):
mock_listdir.return_value = ['foo', 'virtfn1', 'bar']
self.assertTrue(utils.is_physical_function(self.pci_address))
@mock.patch('os.path.isdir', return_value=True)
def test_physical_function(self, *args):
with mock.patch.object(
builtins, 'open', mock.mock_open(read_data='4')):
self.assertTrue(utils.is_physical_function(*self.pci_args))
@mock.patch.object(os, 'listdir')
def test_exception(self, mock_listdir):
mock_listdir.side_effect = OSError('No such file or directory')
self.assertFalse(utils.is_physical_function(self.pci_address))
@mock.patch('os.path.isdir', return_value=False)
def test_exception(self, *args):
self.assertFalse(utils.is_physical_function(*self.pci_args))
class GetIfnameByPciAddressTestCase(test.NoDBTestCase):

View File

@ -65,6 +65,7 @@ from nova.network import model as network_model
from nova import objects
from nova.objects import fields
from nova.pci import manager as pci_manager
from nova.pci import utils as pci_utils
from nova import test
from nova.tests.unit import fake_block_device
from nova.tests.unit import fake_instance
@ -180,7 +181,94 @@ _fake_NodeDevXml = \
<capability type='virt_functions'>
</capability>
</capability>
</device>"""}
</device>""",
"pci_0000_04_00_1": """
<device>
<name>pci_0000_04_00_1</name>
<path>/sys/devices/pci0000:00/0000:00:02.0/0000:04:00.1</path>
<parent>pci_0000_00_02_0</parent>
<driver>
<name>mlx5_core</name>
</driver>
<capability type='pci'>
<domain>0</domain>
<bus>4</bus>
<slot>0</slot>
<function>1</function>
<product id='0x1013'>MT27700 Family [ConnectX-4]</product>
<vendor id='0x15b3'>Mellanox Technologies</vendor>
<iommuGroup number='15'>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x1'/>
</iommuGroup>
<numa node='0'/>
<pci-express>
<link validity='cap' port='0' speed='8' width='16'/>
<link validity='sta' speed='8' width='16'/>
</pci-express>
</capability>
</device>""",
# libvirt >= 1.3.0 nodedev-dumpxml
"pci_0000_03_00_0": """
<device>
<name>pci_0000_03_00_0</name>
<path>/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0</path>
<parent>pci_0000_00_02_0</parent>
<driver>
<name>mlx5_core</name>
</driver>
<capability type='pci'>
<domain>0</domain>
<bus>3</bus>
<slot>0</slot>
<function>0</function>
<product id='0x1013'>MT27700 Family [ConnectX-4]</product>
<vendor id='0x15b3'>Mellanox Technologies</vendor>
<capability type='virt_functions' maxCount='16'>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x2'/>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x3'/>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x4'/>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x5'/>
</capability>
<iommuGroup number='15'>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x1'/>
</iommuGroup>
<numa node='0'/>
<pci-express>
<link validity='cap' port='0' speed='8' width='16'/>
<link validity='sta' speed='8' width='16'/>
</pci-express>
</capability>
</device>""",
"pci_0000_03_00_1": """
<device>
<name>pci_0000_03_00_1</name>
<path>/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.1</path>
<parent>pci_0000_00_02_0</parent>
<driver>
<name>mlx5_core</name>
</driver>
<capability type='pci'>
<domain>0</domain>
<bus>3</bus>
<slot>0</slot>
<function>1</function>
<product id='0x1013'>MT27700 Family [ConnectX-4]</product>
<vendor id='0x15b3'>Mellanox Technologies</vendor>
<capability type='virt_functions' maxCount='16'/>
<iommuGroup number='15'>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
<address domain='0x0000' bus='0x03' slot='0x00' function='0x1'/>
</iommuGroup>
<numa node='0'/>
<pci-express>
<link validity='cap' port='0' speed='8' width='16'/>
<link validity='sta' speed='8' width='16'/>
</pci-express>
</capability>
</device>""",
}
_fake_cpu_info = {
"arch": "test_arch",
@ -9867,43 +9955,108 @@ class LibvirtConnTestCase(test.NoDBTestCase):
host.Host.device_lookup_by_name = fake_nodeDeviceLookupByName
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
actualvf = drvr._get_pcidev_info("pci_0000_04_00_3")
expect_vf = {
"dev_id": "pci_0000_04_00_3",
"address": "0000:04:00.3",
"product_id": '1521',
"numa_node": None,
"vendor_id": '8086',
"label": 'label_8086_1521',
"dev_type": fields.PciDeviceType.SRIOV_PF,
}
with mock.patch.object(
fakelibvirt.Connection, 'getLibVersion') as mock_lib_version:
mock_lib_version.return_value = (
versionutils.convert_version_to_int(
libvirt_driver.MIN_LIBVIRT_PF_WITH_NO_VFS_CAP_VERSION) - 1)
self.assertEqual(expect_vf, actualvf)
actualvf = drvr._get_pcidev_info("pci_0000_04_10_7")
expect_vf = {
"dev_id": "pci_0000_04_10_7",
"address": "0000:04:10.7",
"product_id": '1520',
"numa_node": None,
"vendor_id": '8086',
"label": 'label_8086_1520',
"dev_type": fields.PciDeviceType.SRIOV_VF,
"phys_function": '0000:04:00.3',
}
self.assertEqual(expect_vf, actualvf)
actualvf = drvr._get_pcidev_info("pci_0000_04_11_7")
expect_vf = {
"dev_id": "pci_0000_04_11_7",
"address": "0000:04:11.7",
"product_id": '1520',
"vendor_id": '8086',
"numa_node": 0,
"label": 'label_8086_1520',
"dev_type": fields.PciDeviceType.SRIOV_VF,
"phys_function": '0000:04:00.3',
}
actualvf = drvr._get_pcidev_info("pci_0000_04_00_3")
expect_vf = {
"dev_id": "pci_0000_04_00_3",
"address": "0000:04:00.3",
"product_id": '1521',
"numa_node": None,
"vendor_id": '8086',
"label": 'label_8086_1521',
"dev_type": fields.PciDeviceType.SRIOV_PF,
}
self.assertEqual(expect_vf, actualvf)
self.assertEqual(expect_vf, actualvf)
actualvf = drvr._get_pcidev_info("pci_0000_04_10_7")
expect_vf = {
"dev_id": "pci_0000_04_10_7",
"address": "0000:04:10.7",
"product_id": '1520',
"numa_node": None,
"vendor_id": '8086',
"label": 'label_8086_1520',
"dev_type": fields.PciDeviceType.SRIOV_VF,
"phys_function": '0000:04:00.3',
}
self.assertEqual(expect_vf, actualvf)
actualvf = drvr._get_pcidev_info("pci_0000_04_11_7")
expect_vf = {
"dev_id": "pci_0000_04_11_7",
"address": "0000:04:11.7",
"product_id": '1520',
"vendor_id": '8086',
"numa_node": 0,
"label": 'label_8086_1520',
"dev_type": fields.PciDeviceType.SRIOV_VF,
"phys_function": '0000:04:00.3',
}
self.assertEqual(expect_vf, actualvf)
with mock.patch.object(
pci_utils, 'is_physical_function', return_value=True):
actualvf = drvr._get_pcidev_info("pci_0000_04_00_1")
expect_vf = {
"dev_id": "pci_0000_04_00_1",
"address": "0000:04:00.1",
"product_id": '1013',
"numa_node": 0,
"vendor_id": '15b3',
"label": 'label_15b3_1013',
"dev_type": fields.PciDeviceType.SRIOV_PF,
}
self.assertEqual(expect_vf, actualvf)
with mock.patch.object(
pci_utils, 'is_physical_function', return_value=False):
actualvf = drvr._get_pcidev_info("pci_0000_04_00_1")
expect_vf = {
"dev_id": "pci_0000_04_00_1",
"address": "0000:04:00.1",
"product_id": '1013',
"numa_node": 0,
"vendor_id": '15b3',
"label": 'label_15b3_1013',
"dev_type": fields.PciDeviceType.STANDARD,
}
self.assertEqual(expect_vf, actualvf)
with mock.patch.object(
fakelibvirt.Connection, 'getLibVersion') as mock_lib_version:
mock_lib_version.return_value = (
versionutils.convert_version_to_int(
libvirt_driver.MIN_LIBVIRT_PF_WITH_NO_VFS_CAP_VERSION))
actualvf = drvr._get_pcidev_info("pci_0000_03_00_0")
expect_vf = {
"dev_id": "pci_0000_03_00_0",
"address": "0000:03:00.0",
"product_id": '1013',
"numa_node": 0,
"vendor_id": '15b3',
"label": 'label_15b3_1013',
"dev_type": fields.PciDeviceType.SRIOV_PF,
}
self.assertEqual(expect_vf, actualvf)
actualvf = drvr._get_pcidev_info("pci_0000_03_00_1")
expect_vf = {
"dev_id": "pci_0000_03_00_1",
"address": "0000:03:00.1",
"product_id": '1013',
"numa_node": 0,
"vendor_id": '15b3',
"label": 'label_15b3_1013',
"dev_type": fields.PciDeviceType.SRIOV_PF,
}
self.assertEqual(expect_vf, actualvf)
def test_list_devices_not_supported(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)

View File

@ -421,6 +421,12 @@ MIN_LIBVIRT_SET_ADMIN_PASSWD = (1, 2, 16)
MIN_LIBVIRT_KVM_S390_VERSION = (1, 2, 13)
MIN_QEMU_S390_VERSION = (2, 3, 0)
# libvirt < 1.3 reported virt_functions capability
# only when VFs are enabled.
# libvirt 1.3 fix f391889f4e942e22b9ef8ecca492de05106ce41e
MIN_LIBVIRT_PF_WITH_NO_VFS_CAP_VERSION = (1, 3, 0)
# Names of the types that do not get compressed during migration
NO_COMPRESSION_TYPES = ('qcow2',)
@ -4765,7 +4771,7 @@ class LibvirtDriver(driver.ComputeDriver):
def _get_pcidev_info(self, devname):
"""Returns a dict of PCI device."""
def _get_device_type(cfgdev):
def _get_device_type(cfgdev, pci_address):
"""Get a PCI device's device type.
An assignable PCI device can be a normal PCI device,
@ -4773,26 +4779,35 @@ class LibvirtDriver(driver.ComputeDriver):
Function (VF). Only normal PCI devices or SR-IOV VFs
are assignable, while SR-IOV PFs are always owned by
hypervisor.
Please notice that a PCI device with SR-IOV
capability but not enabled is reported as normal PCI device.
"""
for fun_cap in cfgdev.pci_capability.fun_capability:
if len(fun_cap.device_addrs) != 0:
if fun_cap.type == 'virt_functions':
return {
'dev_type': fields.PciDeviceType.SRIOV_PF,
}
if fun_cap.type == 'phys_function':
phys_address = "%04x:%02x:%02x.%01x" % (
fun_cap.device_addrs[0][0],
fun_cap.device_addrs[0][1],
fun_cap.device_addrs[0][2],
fun_cap.device_addrs[0][3])
return {
'dev_type': fields.PciDeviceType.SRIOV_VF,
'phys_function': phys_address,
}
if fun_cap.type == 'virt_functions':
return {
'dev_type': fields.PciDeviceType.SRIOV_PF,
}
if (fun_cap.type == 'phys_function' and
len(fun_cap.device_addrs) != 0):
phys_address = "%04x:%02x:%02x.%01x" % (
fun_cap.device_addrs[0][0],
fun_cap.device_addrs[0][1],
fun_cap.device_addrs[0][2],
fun_cap.device_addrs[0][3])
return {
'dev_type': fields.PciDeviceType.SRIOV_VF,
'phys_function': phys_address,
}
# Note(moshele): libvirt < 1.3 reported virt_functions capability
# only when VFs are enabled. The check below is a workaround
# to get the correct report regardless of whether or not any
# VFs are enabled for the device.
if not self._host.has_min_version(
MIN_LIBVIRT_PF_WITH_NO_VFS_CAP_VERSION):
is_physical_function = pci_utils.is_physical_function(
*pci_utils.get_pci_address_fields(pci_address))
if is_physical_function:
return {'dev_type': fields.PciDeviceType.SRIOV_PF}
return {'dev_type': fields.PciDeviceType.STANDARD}
virtdev = self._host.device_lookup_by_name(devname)
@ -4817,7 +4832,7 @@ class LibvirtDriver(driver.ComputeDriver):
# requirement by DataBase Model
device['label'] = 'label_%(vendor_id)s_%(product_id)s' % device
device.update(_get_device_type(cfgdev))
device.update(_get_device_type(cfgdev, address))
return device
def _get_pci_passthrough_devices(self):