Fix - os-vif fails to get the correct UpLink Representor

Till kernel 5.7 PF and VF representors are exposed as virtual device.
They are not linked to its parent PCI device like how uplink
representor is linked.

Starting from kernel 5.8 due to new change [1] the PF and VF representors are
linked to their parent PCI device, and so "get_ifname_by_pci_address" fails
to get the correct UpLink Representor.

This patch modifys the behviour of "get_ifname_by_pci_address" to
check the physical port name of the netdev in
vf_pci_addr_path/physfn/net to match the formart for the uplink "p\d+".

[1] https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=123f0f53dd64b67e34142485fe866a8a581f12f1

Closes-Bug: #1892132
Change-Id: I49f6ae3f0e6bfbf555c8284bfd70371ce90da0c7
(cherry picked from commit b37de19c58)
This commit is contained in:
Mamduh Alassi 2020-08-12 16:24:47 +03:00 committed by Sean Mooney
parent 167bb030f1
commit a28aafa796
3 changed files with 74 additions and 8 deletions

View File

@ -0,0 +1,10 @@
---
fixes:
- |
Linux kernel 5.8 changed the sysfs interface that is used to
discover the interfaces used for OVS offloads for certain NIC
models. This results in network plugging failure, as described
in `bug #1892132`_. This release fixes the plugging issue by
properly handling the new sysfs structure.
.. _bug #1892132: https://bugs.launchpad.net/os-vif/+bug/1892132

View File

@ -45,6 +45,8 @@ VF_RE = re.compile(r"vf(\d+)", re.IGNORECASE)
PF_RE = re.compile(r"pf(\d+)", re.IGNORECASE)
# bus_info (bdf) contains <bus>:<dev>.<func>
PF_FUNC_RE = re.compile(r"\.(\d+)", 0)
# phys_port_name contains p##
UPLINK_PORT_RE = re.compile(r"p(\d+)", re.IGNORECASE)
_SRIOV_TOTALVFS = "sriov_totalvfs"
NIC_NAME_LEN = 14
@ -328,12 +330,28 @@ def get_ifname_by_pci_address(pci_addr, pf_interface=False, switchdev=False):
itself based on the argument of pf_interface.
"""
dev_path = _get_sysfs_netdev_path(pci_addr, pf_interface)
# make the if statement later more readable
ignore_switchdev = not switchdev
try:
for netdev in os.listdir(dev_path):
if ignore_switchdev or _is_switchdev(netdev):
return netdev
devices = os.listdir(dev_path)
# Return the first netdev in case of switchdev=False
if not switchdev:
return devices[0]
elif pf_interface:
fallback_netdev = None
for netdev in devices:
# Return the uplink representor in case of switchdev=True
if _is_switchdev(netdev):
fallback_netdev = netdev if fallback_netdev is None \
else fallback_netdev
phys_port_name = _get_phys_port_name(netdev)
if phys_port_name is not None and \
UPLINK_PORT_RE.search(phys_port_name):
return netdev
# Fallback to first switchdev netdev in case of switchdev=True
if fallback_netdev is not None:
return fallback_netdev
except Exception:
raise exception.PciDeviceNotFoundById(id=pci_addr)
raise exception.PciDeviceNotFoundById(id=pci_addr)

View File

@ -261,26 +261,64 @@ class LinuxNetTest(testtools.TestCase):
@mock.patch.object(os, 'listdir')
@mock.patch.object(linux_net, '_get_phys_switch_id')
@mock.patch.object(linux_net, "_get_phys_port_name")
def test_physical_function_interface_name(
self, mock__get_phys_switch_id, mock_listdir):
self, mock__get_phys_port_name, mock__get_phys_switch_id,
mock_listdir):
mock_listdir.return_value = ['foo', 'bar']
mock__get_phys_switch_id.side_effect = (
['', 'valid_switch'])
mock__get_phys_port_name.side_effect = (["p1"])
ifname = linux_net.get_ifname_by_pci_address(
'0000:00:00.1', pf_interface=True, switchdev=False)
self.assertEqual(ifname, 'foo')
@mock.patch.object(os, 'listdir')
@mock.patch.object(linux_net, '_get_phys_switch_id')
@mock.patch.object(linux_net, "_get_phys_switch_id")
@mock.patch.object(linux_net, "_get_phys_port_name")
def test_physical_function_interface_name_with_switchdev(
self, mock__get_phys_switch_id, mock_listdir):
self, mock__get_phys_port_name, mock__get_phys_switch_id,
mock_listdir):
mock_listdir.return_value = ['foo', 'bar']
mock__get_phys_switch_id.side_effect = (
['', 'valid_switch'])
mock__get_phys_port_name.side_effect = (["p1s0"])
ifname = linux_net.get_ifname_by_pci_address(
'0000:00:00.1', pf_interface=True, switchdev=True)
self.assertEqual(ifname, 'bar')
@mock.patch.object(os, 'listdir')
@mock.patch.object(linux_net, "_get_phys_switch_id")
@mock.patch.object(linux_net, "_get_phys_port_name")
def test_physical_function_interface_name_with_representors(
self, mock__get_phys_port_name, mock__get_phys_switch_id,
mock_listdir):
# Get the PF that matches the phys_port_name regex
mock_listdir.return_value = ['enp2s0f0_0', 'enp2s0f0_1', 'enp2s0f0']
mock__get_phys_switch_id.side_effect = (
['valid_switch', 'valid_switch', 'valid_switch'])
mock__get_phys_port_name.side_effect = (["pf0vf0", "pf0vf1", "p0"])
ifname = linux_net.get_ifname_by_pci_address(
'0000:00:00.1', pf_interface=True, switchdev=True)
self.assertEqual(ifname, 'enp2s0f0')
@mock.patch.object(os, 'listdir')
@mock.patch.object(linux_net, "_get_phys_switch_id")
@mock.patch.object(linux_net, "_get_phys_port_name")
def test_physical_function_interface_name_with_fallback_To_first_netdev(
self, mock__get_phys_port_name, mock__get_phys_switch_id,
mock_listdir):
# Try with switchdev mode to get PF but fail because there is no match
# for the phys_port_name then fallback to first interface found
mock_listdir.return_value = ['enp2s0f0_0', 'enp2s0f0_1', 'enp2s0f0']
mock__get_phys_switch_id.side_effect = (['valid_switch',
'valid_switch',
'valid_switch'])
mock__get_phys_port_name.side_effect = (["pf0vf0", "pf0vf1", "pf0vf2"])
ifname = linux_net.get_ifname_by_pci_address(
'0000:00:00.1', pf_interface=True, switchdev=True)
self.assertEqual(ifname, 'enp2s0f0_0')
@mock.patch.object(os, 'listdir')
def test_get_ifname_by_pci_address_exception(self, mock_listdir):
mock_listdir.side_effect = OSError('No such file or directory')