PCI: Fix PCI with fully qualified address

Specifying a PF passthrough device in the pci_passthrough_whitelist using its
fully qualified PCI address (no wildcard) causes the device to not be
properly loaded.  The PCI device is then not available to be assigned to any
guest.

In this case, the hypervisor reports the PF device without a 'parent_addr'.
But in the PciAddress, match() is using it when doing the comparison to its
own address.

This commit changes the logic of the address matching method in PciDevSpec to
only try to match the address with a physical function device when a
'parent_addr' is reported by the hypervisor.

Change-Id: I5255240871d8ad5c216500f39520339efe46e84b
Closes-Bug: #1613434
This commit is contained in:
Ludovic Beliveau 2016-08-31 14:27:43 -04:00
parent 8ecef74b27
commit d38d5767d1
2 changed files with 38 additions and 19 deletions

View File

@ -106,25 +106,36 @@ class PciAddress(object):
self._check_physical_function()
def match(self, pci_addr, pci_phys_addr):
# Assume this is called given pci_add and pci_phys_addr from libvirt,
# no attempt is made to verify pci_addr is a VF of pci_phys_addr
if self.is_physical_function:
if not pci_phys_addr:
return False
"""Match a device to this PciAddress. Assume this is called given
pci_addr and pci_phys_addr reported by libvirt, no attempt is made to
verify if pci_addr is a VF of pci_phys_addr.
:param pci_addr: PCI address of the device to match.
:param pci_phys_addr: PCI address of the parent of the device to match
(or None if the device is not a VF).
"""
# Try to match on the parent PCI address if the PciDeviceSpec is a
# PF (sriov is available) and the device to match is a VF. This
# makes possible to specify the PCI address of a PF in the
# pci_passthrough_whitelist to match any of it's VFs PCI devices.
if self.is_physical_function and pci_phys_addr:
domain, bus, slot, func = (
utils.get_pci_address_fields(pci_phys_addr))
return (self.domain == domain and self.bus == bus and
self.slot == slot and self.func == func)
else:
domain, bus, slot, func = (
utils.get_pci_address_fields(pci_addr))
conditions = [
self.domain in (ANY, domain),
self.bus in (ANY, bus),
self.slot in (ANY, slot),
self.func in (ANY, func)
]
return all(conditions)
if (self.domain == domain and self.bus == bus and
self.slot == slot and self.func == func):
return True
# Try to match on the device PCI address only.
domain, bus, slot, func = (
utils.get_pci_address_fields(pci_addr))
conditions = [
self.domain in (ANY, domain),
self.bus in (ANY, bus),
self.slot in (ANY, slot),
self.func in (ANY, func)
]
return all(conditions)
class PciDeviceSpec(object):

View File

@ -22,7 +22,7 @@ from nova import test
dev = {"vendor_id": "8086",
"product_id": "5057",
"address": "1234:5678:8988.5",
"address": "0000:0b:00.5",
"parent_addr": "0000:0a:00.0"}
@ -95,12 +95,20 @@ class PciAddressTestCase(test.NoDBTestCase):
"parent_addr": "0000:0a:00.0"}
self.assertTrue(pci.match(dev))
@mock.patch('nova.pci.utils.is_physical_function', return_value = True)
@mock.patch('nova.pci.utils.is_physical_function', return_value=True)
def test_address_is_pf(self, mock_is_physical_function):
pci_info = {"address": "0000:0a:00.0", "physical_network": "hr_net"}
pci = devspec.PciDeviceSpec(pci_info)
self.assertTrue(pci.match(dev))
@mock.patch('nova.pci.utils.is_physical_function', return_value=True)
def test_address_pf_no_parent_addr(self, mock_is_physical_function):
_dev = dev.copy()
_dev.pop('parent_addr')
pci_info = {"address": "0000:0b:00.5", "physical_network": "hr_net"}
pci = devspec.PciDeviceSpec(pci_info)
self.assertTrue(pci.match(_dev))
class PciDevSpecTestCase(test.NoDBTestCase):
def test_spec_match(self):