X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FInterfaceUtil.py;h=bf3663591533873016f66a0c8e0d23f5f72caf18;hp=e751160077f4f1ecfb1a235d92cd5b3eb37df92a;hb=bf66d4dbf760614e819f7d50bf26defab3e537b5;hpb=f58d415afaacc7565f08817903b0d21f16579eb8 diff --git a/resources/libraries/python/InterfaceUtil.py b/resources/libraries/python/InterfaceUtil.py index e751160077..bf36635915 100644 --- a/resources/libraries/python/InterfaceUtil.py +++ b/resources/libraries/python/InterfaceUtil.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Cisco and/or its affiliates. +# Copyright (c) 2021 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -20,6 +20,7 @@ from ipaddress import ip_address from robot.api import logger from resources.libraries.python.Constants import Constants +from resources.libraries.python.CpuUtils import CpuUtils from resources.libraries.python.DUTSetup import DUTSetup from resources.libraries.python.IPAddress import IPAddress from resources.libraries.python.L2Util import L2Util @@ -1029,9 +1030,11 @@ class InterfaceUtil: :raises RuntimeError: If it is not possible to create loopback on the node. """ - cmd = u"create_loopback" + cmd = u"create_loopback_instance" args = dict( - mac_address=L2Util.mac_to_bin(mac) if mac else 0 + mac_address=L2Util.mac_to_bin(mac) if mac else 0, + is_specified=False, + user_instance=0, ) err_msg = f"Failed to create loopback interface on host {node[u'host']}" with PapiSocketExecutor(node) as papi_exec: @@ -1048,24 +1051,28 @@ class InterfaceUtil: return sw_if_index @staticmethod - def vpp_create_bond_interface(node, mode, load_balance=None, mac=None): + def vpp_create_bond_interface( + node, mode, load_balance=None, mac=None, gso=False): """Create bond interface on VPP node. :param node: DUT node from topology. :param mode: Link bonding mode. :param load_balance: Load balance (optional, valid for xor and lacp - modes, otherwise ignored). + modes, otherwise ignored). Default: None. :param mac: MAC address to assign to the bond interface (optional). + Default: None. + :param gso: Enable GSO support (optional). Default: False. :type node: dict :type mode: str :type load_balance: str :type mac: str + :type gso: bool :returns: Interface key (name) in topology. :rtype: str :raises RuntimeError: If it is not possible to create bond interface on the node. """ - cmd = u"bond_create" + cmd = u"bond_create2" args = dict( id=int(Constants.BITWISE_NON_ZERO), use_custom_mac=bool(mac is not None), @@ -1078,7 +1085,8 @@ class InterfaceUtil: LinkBondLoadBalanceAlgo, f"BOND_API_LB_ALGO_{load_balance.upper()}" ).value, - numa_only=False + numa_only=False, + enable_gso=gso ) err_msg = f"Failed to create bond interface on host {node[u'host']}" with PapiSocketExecutor(node) as papi_exec: @@ -1202,7 +1210,7 @@ class InterfaceUtil: node, u"set logging class rdma level debug" ) - cmd = u"rdma_create" + cmd = u"rdma_create_v2" pci_addr = Topology.get_interface_pci_addr(node, if_key) args = dict( name=InterfaceUtil.pci_to_eth(node, pci_addr), @@ -1211,6 +1219,9 @@ class InterfaceUtil: rxq_size=rxq_size, txq_size=txq_size, mode=getattr(RdmaMode, f"RDMA_API_MODE_{mode.upper()}").value, + # TODO: Set True for non-jumbo packets. + no_multi_seg=False, + max_pktlen=0, ) err_msg = f"Failed to create RDMA interface on host {node[u'host']}" with PapiSocketExecutor(node) as papi_exec: @@ -1227,8 +1238,8 @@ class InterfaceUtil: return Topology.get_interface_by_sw_index(node, sw_if_index) @staticmethod - def vpp_enslave_physical_interface(node, interface, bond_if): - """Enslave physical interface to bond interface on VPP node. + def vpp_add_bond_member(node, interface, bond_if): + """Add member interface to bond interface on VPP node. :param node: DUT node from topology. :param interface: Physical interface key from topology file. @@ -1236,18 +1247,18 @@ class InterfaceUtil: :type node: dict :type interface: str :type bond_if: str - :raises RuntimeError: If it is not possible to enslave physical - interface to bond interface on the node. + :raises RuntimeError: If it is not possible to add member to bond + interface on the node. """ - cmd = u"bond_enslave" + cmd = u"bond_add_member" args = dict( sw_if_index=Topology.get_interface_sw_index(node, interface), bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if), is_passive=False, is_long_timeout=False ) - err_msg = f"Failed to enslave physical interface {interface} to bond " \ - f"interface {bond_if} on host {node[u'host']}" + err_msg = f"Failed to add member {interface} to bond interface " \ + f"{bond_if} on host {node[u'host']}" with PapiSocketExecutor(node) as papi_exec: papi_exec.add(cmd, **args).get_reply(err_msg) @@ -1260,7 +1271,7 @@ class InterfaceUtil: :type node: dict :type verbose: bool """ - cmd = u"sw_interface_bond_dump" + cmd = u"sw_bond_interface_dump" err_msg = f"Failed to get bond interface dump on host {node[u'host']}" data = f"Bond data on node {node[u'host']}:\n" @@ -1275,26 +1286,26 @@ class InterfaceUtil: data += u" load balance: {lb}\n".format( lb=bond[u"lb"].name.replace(u"BOND_API_LB_ALGO_", u"").lower() ) - data += f" number of active slaves: {bond[u'active_slaves']}\n" + data += f" number of active members: {bond[u'active_members']}\n" if verbose: - slave_data = InterfaceUtil.vpp_bond_slave_dump( + member_data = InterfaceUtil.vpp_bond_member_dump( node, Topology.get_interface_by_sw_index( node, bond[u"sw_if_index"] ) ) - for slave in slave_data: - if not slave[u"is_passive"]: - data += f" {slave[u'interface_name']}\n" - data += f" number of slaves: {bond[u'slaves']}\n" + for member in member_data: + if not member[u"is_passive"]: + data += f" {member[u'interface_name']}\n" + data += f" number of members: {bond[u'members']}\n" if verbose: - for slave in slave_data: - data += f" {slave[u'interface_name']}\n" + for member in member_data: + data += f" {member[u'interface_name']}\n" data += f" interface id: {bond[u'id']}\n" data += f" sw_if_index: {bond[u'sw_if_index']}\n" logger.info(data) @staticmethod - def vpp_bond_slave_dump(node, interface): + def vpp_bond_member_dump(node, interface): """Get bond interface slave(s) data on VPP node. :param node: DUT node from topology. @@ -1304,7 +1315,7 @@ class InterfaceUtil: :returns: Bond slave interface data. :rtype: dict """ - cmd = u"sw_interface_slave_dump" + cmd = u"sw_member_interface_dump" args = dict( sw_if_index=Topology.get_interface_sw_index(node, interface) ) @@ -1313,7 +1324,7 @@ class InterfaceUtil: with PapiSocketExecutor(node) as papi_exec: details = papi_exec.add(cmd, **args).get_details(err_msg) - logger.debug(f"Slave data:\n{details}") + logger.debug(f"Member data:\n{details}") return details @staticmethod @@ -1546,6 +1557,25 @@ class InterfaceUtil: cmd = f"{ns_str} ip link set dev {interface} {spoof_str}" exec_cmd_no_error(node, cmd, sudo=True) + @staticmethod + def set_linux_interface_state( + node, interface, namespace=None, state=u"up"): + """Set operational state for interface in linux. + + :param node: Node where to execute command. + :param interface: Interface in namespace. + :param namespace: Execute command in namespace. Optional + :param state: Up/Down. + :type node: dict + :type interface: str + :type namespace: str + :type state: str + """ + ns_str = f"ip netns exec {namespace}" if namespace else u"" + + cmd = f"{ns_str} ip link set dev {interface} {state}" + exec_cmd_no_error(node, cmd, sudo=True) + @staticmethod def init_avf_interface(node, ifc_key, numvfs=1, osi_layer=u"L2"): """Init PCI device by creating VIFs and bind them to vfio-pci for AVF @@ -1610,6 +1640,9 @@ class InterfaceUtil: InterfaceUtil.set_linux_interface_mac( node, pf_dev, vf_mac_addr, vf_id=vf_id ) + InterfaceUtil.set_linux_interface_state( + node, pf_dev, state=u"up" + ) DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id) DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver) @@ -1650,6 +1683,19 @@ class InterfaceUtil: details = papi_exec.get_details(err_msg) return sorted(details, key=lambda k: k[u"sw_if_index"]) + @staticmethod + def vpp_sw_interface_rx_placement_dump_on_all_duts(nodes): + """Dump VPP interface RX placement on all given nodes. + + :param nodes: Nodes to run command on. + :type nodes: dict + :returns: Thread mapping information as a list of dictionaries. + :rtype: list + """ + for node in nodes.values(): + if node[u"type"] == NodeType.DUT: + InterfaceUtil.vpp_sw_interface_rx_placement_dump(node) + @staticmethod def vpp_sw_interface_set_rx_placement( node, sw_if_index, queue_id, worker_id): @@ -1679,17 +1725,29 @@ class InterfaceUtil: papi_exec.add(cmd, **args).get_reply(err_msg) @staticmethod - def vpp_round_robin_rx_placement(node, prefix): + def vpp_round_robin_rx_placement( + node, prefix, dp_worker_limit=None + ): """Set Round Robin interface RX placement on all worker threads on node. + If specified, dp_core_limit limits the number of physical cores used + for data plane I/O work. Other cores are presumed to do something else, + e.g. asynchronous crypto processing. + None means all workers are used for data plane work. + Note this keyword specifies workers, not cores. + :param node: Topology nodes. :param prefix: Interface name prefix. + :param dp_worker_limit: How many cores for data plane work. :type node: dict :type prefix: str + :type dp_worker_limit: Optional[int] """ worker_id = 0 worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1 + if dp_worker_limit is not None: + worker_cnt = min(worker_cnt, dp_worker_limit) if not worker_cnt: return for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node): @@ -1703,15 +1761,31 @@ class InterfaceUtil: worker_id += 1 @staticmethod - def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix): + def vpp_round_robin_rx_placement_on_all_duts( + nodes, prefix, dp_core_limit=None + ): """Set Round Robin interface RX placement on all worker threads on all DUTs. + If specified, dp_core_limit limits the number of physical cores used + for data plane I/O work. Other cores are presumed to do something else, + e.g. asynchronous crypto processing. + None means all cores are used for data plane work. + Note this keyword specifies cores, not workers. + :param nodes: Topology nodes. :param prefix: Interface name prefix. + :param dp_worker_limit: How many cores for data plane work. :type nodes: dict :type prefix: str + :type dp_worker_limit: Optional[int] """ for node in nodes.values(): if node[u"type"] == NodeType.DUT: - InterfaceUtil.vpp_round_robin_rx_placement(node, prefix) + dp_worker_limit = CpuUtils.worker_count_from_cores_and_smt( + phy_cores=dp_core_limit, + smt_used=CpuUtils.is_smt_enabled(node[u"cpuinfo"]), + ) + InterfaceUtil.vpp_round_robin_rx_placement( + node, prefix, dp_worker_limit + )