From c3ad968c875ae7fa67e7a1f8d321f1fc622897bc Mon Sep 17 00:00:00 2001 From: Dmitrii Shcherbakov Date: Tue, 15 Mar 2022 21:11:24 +0300 Subject: [PATCH] Fix the PCI device capability dict creation VPD handling was added in ab49f97b2c08294234c7bfd3dedb75780ca519e6 and introduced a regression which was not caught by unit or functional tests: if a VPD capability is present for a PCI device, it overrides the network capabilities obtained from the associated netdev. This change fixes the issue and adds additional test coverage. Closes-Bug: #1964995 Change-Id: Ifa9ae93530339828333dd3e23cdffe9f85e96a70 --- nova/tests/fixtures/libvirt_data.py | 21 ++++++++ nova/tests/unit/virt/libvirt/test_host.py | 3 ++ nova/virt/libvirt/host.py | 61 +++++++++++++---------- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/nova/tests/fixtures/libvirt_data.py b/nova/tests/fixtures/libvirt_data.py index 43fcc8c950ae..1302510ab9d1 100644 --- a/nova/tests/fixtures/libvirt_data.py +++ b/nova/tests/fixtures/libvirt_data.py @@ -2163,6 +2163,27 @@ _fake_NodeDevXml = { + """, + "net_enp130s0f0v0_36_33_10_a3_94_64": """ + + net_enp130s0f0v0_36_33_10_a3_94_64 + /sys/devices/pci0000:80/0000:80:03.0/0000:82:00.3/net/enp130s0f0v0 + pci_0000_82_00_3 + + enp130s0f0v0 +
36:33:10:a3:94:64
+ + + + + + + + + + + +
""", # noqa:E501 } diff --git a/nova/tests/unit/virt/libvirt/test_host.py b/nova/tests/unit/virt/libvirt/test_host.py index d71d13ab3721..53f1dfbda615 100644 --- a/nova/tests/unit/virt/libvirt/test_host.py +++ b/nova/tests/unit/virt/libvirt/test_host.py @@ -1280,7 +1280,10 @@ Active: 8381604 kB "vendor_id": "15b3", "label": "label_15b3_101e", "dev_type": obj_fields.PciDeviceType.SRIOV_VF, + "parent_ifname": "ens1", "capabilities": { + "network": ["rx", "tx", "sg", "tso", "gso", "gro", "rxvlan", + "txvlan", "rxhash"], # Should be obtained from the parent PF in this case. "vpd": {"card_serial_number": "MT2113X00000"}}, } diff --git a/nova/virt/libvirt/host.py b/nova/virt/libvirt/host.py index cdf47008de45..8403497af5e4 100644 --- a/nova/virt/libvirt/host.py +++ b/nova/virt/libvirt/host.py @@ -1336,31 +1336,11 @@ class Host(object): return {'dev_type': fields.PciDeviceType.STANDARD} - def _get_device_capabilities( - device_dict: dict, - device: 'libvirt.virNodeDevice', - net_devs: ty.List['libvirt.virNodeDevice'] - ) -> ty.Dict[str, ty.Dict[str, ty.Any]]: - """Get PCI VF device's additional capabilities. - - If a PCI device is a virtual function, this function reads the PCI - parent's network capabilities (must be always a NIC device) and - appends this information to the device's dictionary. - """ - caps: ty.Dict[str, ty.Dict[str, ty.Any]] = {} - - if device_dict.get('dev_type') == fields.PciDeviceType.SRIOV_VF: - pcinet_info = self._get_pcinet_info(device, net_devs) - if pcinet_info: - return {'capabilities': {'network': pcinet_info}} - - return caps - def _get_vpd_details( device_dict: dict, device: 'libvirt.virNodeDevice', pci_devs: ty.List['libvirt.virNodeDevice'] - ) -> ty.Dict[str, ty.Dict[str, ty.Any]]: + ) -> ty.Dict[str, ty.Any]: """Get information from PCI VPD (if present). PCI/PCIe devices may include the optional VPD capability. It may @@ -1373,7 +1353,7 @@ class Host(object): the VPD capability or not may be controlled via a vendor-specific firmware setting. """ - caps: ty.Dict[str, ty.Dict[str, ty.Any]] = {} + vpd_info: ty.Dict[str, ty.Any] = {} # At the time of writing only the serial number had a clear # use-case. However, the set of fields may be extended. card_serial_number = self._get_vpd_card_serial_number(device) @@ -1388,7 +1368,7 @@ class Host(object): LOG.warning("A VF device dict does not have a parent PF " "address in it which is unexpected. Skipping " "serial number retrieval") - return caps + return vpd_info formatted_addr = pf_addr.replace('.', '_').replace(':', '_') vpd_cap = self._get_vf_parent_pci_vpd_info( @@ -1397,8 +1377,35 @@ class Host(object): card_serial_number = vpd_cap.card_serial_number if card_serial_number: - caps = {'capabilities': { - 'vpd': {"card_serial_number": card_serial_number}}} + vpd_info = {'card_serial_number': card_serial_number} + return vpd_info + + def _get_device_capabilities( + device_dict: dict, + device: 'libvirt.virNodeDevice', + pci_devs: ty.List['libvirt.virNodeDevice'], + net_devs: ty.List['libvirt.virNodeDevice'] + ) -> ty.Dict[str, ty.Any]: + """Get PCI VF device's additional capabilities. + + If a PCI device is a virtual function, this function reads the PCI + parent's network capabilities (must be always a NIC device) and + appends this information to the device's dictionary. + """ + caps: ty.Dict[str, ty.Any] = {} + + if device_dict.get('dev_type') == fields.PciDeviceType.SRIOV_VF: + pcinet_info = self._get_pcinet_info(device, net_devs) + if pcinet_info: + caps['network'] = pcinet_info + + vpd_info = _get_vpd_details(device_dict, device, pci_devs) + if vpd_info: + caps['vpd'] = vpd_info + + if caps: + return {'capabilities': caps} + return caps xmlstr = dev.XMLDesc(0) @@ -1424,8 +1431,8 @@ class Host(object): device['label'] = 'label_%(vendor_id)s_%(product_id)s' % device device.update( _get_device_type(cfgdev, address, dev, net_devs, vdpa_devs)) - device.update(_get_device_capabilities(device, dev, net_devs)) - device.update(_get_vpd_details(device, dev, pci_devs)) + device.update(_get_device_capabilities(device, dev, + pci_devs, net_devs)) return device def get_vdpa_nodedev_by_address(