From 694f06c69bdffa7cae0d6cf2b7052ea3ab7a6590 Mon Sep 17 00:00:00 2001 From: Karthik S Date: Wed, 5 Jul 2017 06:52:36 -0400 Subject: [PATCH] OvS 2.7 support - dpdk-devargs needs to provided for DPDK devices The pci address of the DPDK NIC needs to be specified as dpdk-devargs when adding the DPDK port. This is required for using DPDK with OvS 2.7 Change-Id: I4975130961199ee04dd002ec987081299e3ddd4a Closes-Bug: #1702457 Signed-off-by: Karthik S (cherry picked from commit 312a77a1ac353c1a135ac0b901863bbe8ee66f20) --- os_net_config/impl_ifcfg.py | 18 +++++++ os_net_config/tests/test_impl_ifcfg.py | 15 ++++++ os_net_config/tests/test_utils.py | 72 +++++++++++++++++++++++++- os_net_config/utils.py | 28 +++++++++- 4 files changed, 129 insertions(+), 4 deletions(-) diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index a6629d64..0615e743 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -294,6 +294,14 @@ class IfcfgNetConfig(os_net_config.NetConfig): data += "DEVICETYPE=ovs\n" data += "TYPE=OVSDPDKPort\n" data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name + # Validation of DPDK port having only one interface is done prior + # to this. So accesing the interface name statically. + # Also pci_address would be valid here, since + # bind_dpdk_interfaces() is invoked before this. + pci_address = utils.get_stored_pci_address( + base_opt.members[0].name, self.noop) + ovs_extra.append("set Interface $DEVICE options:dpdk-devargs=" + "%s" % pci_address) elif isinstance(base_opt, objects.OvsDpdkBond): ovs_extra.extend(base_opt.ovs_extra) # Referring to bug:1643026, the below commenting of the interfaces, @@ -307,6 +315,16 @@ class IfcfgNetConfig(os_net_config.NetConfig): data += "TYPE=OVSDPDKBond\n" data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name if base_opt.members: + for bond_member in base_opt.members: + # Validation of DPDK port having only one interface is done + # prior to this. So accesing the interface name statically. + # Also pci_address would be valid here, since + # bind_dpdk_interfaces () is invoked before this. + pci_address = utils.get_stored_pci_address( + bond_member.members[0].name, self.noop) + ovs_extra.append("set Interface %s options:" + "dpdk-devargs=%s" + % (bond_member.name, pci_address)) members = [member.name for member in base_opt.members] data += ("BOND_IFACES=\"%s\"\n" % " ".join(members)) if base_opt.ovs_options: diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py index 82ca1169..08fdb5f6 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -412,6 +412,14 @@ class TestIfcfgNetConfig(base.TestCase): def get_route6_config(self, name='em1'): return self.provider.route6_data.get(name, '') + def stub_get_stored_pci_address(self, ifname, noop): + if 'eth0' in ifname: + return "0000:00:07.0" + if 'eth1' in ifname: + return "0000:00:08.0" + if 'eth2' in ifname: + return "0000:00:09.0" + def test_add_base_interface(self): interface = objects.Interface('em1') self.provider.add_interface(interface) @@ -893,6 +901,8 @@ DNS2=5.6.7.8 self.assertEqual(driver, 'vfio-pci') self.stubs.Set(utils, 'bind_dpdk_interfaces', test_bind_dpdk_interfaces) + self.stubs.Set(utils, 'get_stored_pci_address', + self.stub_get_stored_pci_address) self.provider.add_ovs_dpdk_port(dpdk_port) self.provider.add_ovs_user_bridge(bridge) @@ -914,6 +924,7 @@ PEERDNS=no DEVICETYPE=ovs TYPE=OVSDPDKPort OVS_BRIDGE=br-link +OVS_EXTRA="set Interface $DEVICE options:dpdk-devargs=0000:00:09.0" """ self.assertEqual(br_link_config, self.provider.bridge_data['br-link']) @@ -935,6 +946,8 @@ OVS_BRIDGE=br-link self.assertEqual(driver, 'vfio-pci') self.stubs.Set(utils, 'bind_dpdk_interfaces', test_bind_dpdk_interfaces) + self.stubs.Set(utils, 'get_stored_pci_address', + self.stub_get_stored_pci_address) self.provider.add_ovs_dpdk_bond(bond) self.provider.add_ovs_user_bridge(bridge) @@ -949,6 +962,8 @@ DEVICETYPE=ovs TYPE=OVSDPDKBond OVS_BRIDGE=br-link BOND_IFACES="dpdk0 dpdk1" +OVS_EXTRA="set Interface dpdk0 options:dpdk-devargs=0000:00:08.0 \ +-- set Interface dpdk1 options:dpdk-devargs=0000:00:09.0" """ self.assertEqual(dpdk_bond_config, self.get_interface_config('dpdkbond0')) diff --git a/os_net_config/tests/test_utils.py b/os_net_config/tests/test_utils.py index 9e516b69..05e30a26 100644 --- a/os_net_config/tests/test_utils.py +++ b/os_net_config/tests/test_utils.py @@ -102,6 +102,24 @@ class TestUtils(base.TestCase): pci = utils._get_pci_address('nic2', False) self.assertEqual(None, pci) + def test_get_stored_pci_address_success(self): + def test_get_dpdk_map(): + return [{'name': 'eth1', 'pci_address': '0000:00:09.0', + 'mac_address': '01:02:03:04:05:06', + 'driver': 'vfio-pci'}] + + self.stubs.Set(utils, '_get_dpdk_map', test_get_dpdk_map) + pci = utils.get_stored_pci_address('eth1', False) + self.assertEqual('0000:00:09.0', pci) + + def test_get_stored_pci_address_empty(self): + def test_get_dpdk_map(): + return [] + + self.stubs.Set(utils, '_get_dpdk_map', test_get_dpdk_map) + pci = utils.get_stored_pci_address('eth1', False) + self.assertEqual(None, pci) + def test_bind_dpdk_interfaces(self): def test_execute(name, dummy1, dummy2=None, dummy3=None): if 'ethtool' in name: @@ -115,8 +133,10 @@ class TestUtils(base.TestCase): self.stubs.Set(processutils, 'execute', test_execute) self.stubs.Set(utils, '_get_dpdk_mac_address', test_get_dpdk_mac_address) - - utils.bind_dpdk_interfaces('nic2', 'vfio-pci', False) + try: + utils.bind_dpdk_interfaces('nic2', 'vfio-pci', False) + except utils.OvsDpdkBindException: + self.fail("Received OvsDpdkBindException unexpectedly") def test_bind_dpdk_interfaces_fail(self): def test_execute(name, dummy1, dummy2=None, dummy3=None): @@ -136,6 +156,54 @@ class TestUtils(base.TestCase): utils.bind_dpdk_interfaces, 'eth1', 'vfio-pci', False) + def test_bind_dpdk_interfaces_skip_valid_device(self): + def test_execute(name, dummy1, dummy2=None, dummy3=None): + if 'ethtool' in name: + return None, 'Error' + if 'driverctl' in name: + return None, None + + def test_get_dpdk_mac_address(name): + return '01:02:03:04:05:06' + + def test_get_dpdk_map(): + return [{'name': 'eth1', 'pci_address': '0000:00:09.0', + 'mac_address': '01:02:03:04:05:06', + 'driver': 'vfio-pci'}] + + self.stubs.Set(utils, '_get_dpdk_map', test_get_dpdk_map) + self.stubs.Set(processutils, 'execute', test_execute) + self.stubs.Set(utils, '_get_dpdk_mac_address', + test_get_dpdk_mac_address) + try: + utils.bind_dpdk_interfaces('eth1', 'vfio-pci', False) + except utils.OvsDpdkBindException: + self.fail("Received OvsDpdkBindException unexpectedly") + + def test_bind_dpdk_interfaces_fail_invalid_device(self): + def test_execute(name, dummy1, dummy2=None, dummy3=None): + if 'ethtool' in name: + return None, 'Error' + if 'driverctl' in name: + return None, None + + def test_get_dpdk_mac_address(name): + return '01:02:03:04:05:06' + + def test_get_dpdk_map(): + return [{'name': 'eth1', 'pci_address': '0000:00:09.0', + 'mac_address': '01:02:03:04:05:06', + 'driver': 'vfio-pci'}] + + self.stubs.Set(utils, '_get_dpdk_map', test_get_dpdk_map) + self.stubs.Set(processutils, 'execute', test_execute) + self.stubs.Set(utils, '_get_dpdk_mac_address', + test_get_dpdk_mac_address) + + self.assertRaises(utils.OvsDpdkBindException, + utils.bind_dpdk_interfaces, 'eth2', 'vfio-pci', + False) + def test__update_dpdk_map_new(self): utils._update_dpdk_map('eth1', '0000:03:00.0', '01:02:03:04:05:06', 'vfio-pci') diff --git a/os_net_config/utils.py b/os_net_config/utils.py index 84e719ed..fb2e7949 100644 --- a/os_net_config/utils.py +++ b/os_net_config/utils.py @@ -230,6 +230,14 @@ def bind_dpdk_interfaces(ifname, driver, noop): except processutils.ProcessExecutionError: msg = "Failed to bind interface %s with dpdk" % ifname raise OvsDpdkBindException(msg) + else: + # Check if the pci address is already fetched and stored. + # If the pci address could not be fetched from dpdk_mapping.yaml + # raise OvsDpdkBindException, since the interface is neither + # available nor bound with dpdk. + if not get_stored_pci_address(ifname, noop): + msg = "Interface %s cannot be found" % ifname + raise OvsDpdkBindException(msg) else: logger.info('Interface %(name)s bound to DPDK driver %(driver)s ' 'using driverctl command' % @@ -256,14 +264,24 @@ def _get_pci_address(ifname, noop): 'ethtool' % ifname) +def get_stored_pci_address(ifname, noop): + if not noop: + dpdk_map = _get_dpdk_map() + for dpdk_nic in dpdk_map: + if dpdk_nic['name'] == ifname: + return dpdk_nic['pci_address'] + else: + logger.info('Fetch the PCI address of the interface %s using ' + 'ethtool' % ifname) + + # Once the interface is bound to a DPDK driver, all the references to the # interface including '/sys' and '/proc', will be removed. And there is no # way to identify the nic name after it is bound. So, the DPDK bound nic info # is stored persistently in a file and is used to for nic numbering on # subsequent runs of os-net-config. def _update_dpdk_map(ifname, pci_address, mac_address, driver): - contents = get_file_data(_DPDK_MAPPING_FILE) - dpdk_map = yaml.load(contents) if contents else [] + dpdk_map = _get_dpdk_map() for item in dpdk_map: if item['pci_address'] == pci_address: item['name'] = ifname @@ -281,6 +299,12 @@ def _update_dpdk_map(ifname, pci_address, mac_address, driver): write_yaml_config(_DPDK_MAPPING_FILE, dpdk_map) +def _get_dpdk_map(): + contents = get_file_data(_DPDK_MAPPING_FILE) + dpdk_map = yaml.load(contents) if contents else [] + return dpdk_map + + def _get_dpdk_mac_address(name): contents = get_file_data(_DPDK_MAPPING_FILE) dpdk_map = yaml.load(contents) if contents else []