+
+ @staticmethod
+ def vpp_sw_interface_rx_placement_dump(node):
+ """Dump VPP interface RX placement on node.
+
+ :param node: Node to run command on.
+ :type node: dict
+ :returns: Thread mapping information as a list of dictionaries.
+ :rtype: list
+ """
+ cmd = u"sw_interface_rx_placement_dump"
+ err_msg = f"Failed to run '{cmd}' PAPI command on host {node[u'host']}!"
+ with PapiSocketExecutor(node) as papi_exec:
+ for ifc in node[u"interfaces"].values():
+ if ifc[u"vpp_sw_index"] is not None:
+ papi_exec.add(cmd, sw_if_index=ifc[u"vpp_sw_index"])
+ 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):
+ """Set interface RX placement to worker on node.
+
+ :param node: Node to run command on.
+ :param sw_if_index: VPP SW interface index.
+ :param queue_id: VPP interface queue ID.
+ :param worker_id: VPP worker ID (indexing from 0).
+ :type node: dict
+ :type sw_if_index: int
+ :type queue_id: int
+ :type worker_id: int
+ :raises RuntimeError: If failed to run command on host or if no API
+ reply received.
+ """
+ cmd = u"sw_interface_set_rx_placement"
+ err_msg = f"Failed to set interface RX placement to worker " \
+ f"on host {node[u'host']}!"
+ args = dict(
+ sw_if_index=sw_if_index,
+ queue_id=queue_id,
+ worker_id=worker_id,
+ is_main=False
+ )
+ with PapiSocketExecutor(node) as papi_exec:
+ papi_exec.add(cmd, **args).get_reply(err_msg)
+
+ @staticmethod
+ def vpp_round_robin_rx_placement(
+ node, prefix, workers=None):
+ """Set Round Robin interface RX placement on all worker threads
+ on node.
+
+ If specified, workers 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.
+
+ :param node: Topology nodes.
+ :param prefix: Interface name prefix.
+ :param workers: Comma separated worker index numbers intended for
+ dataplane work.
+ :type node: dict
+ :type prefix: str
+ :type workers: str
+ """
+ thread_data = VPPUtil.vpp_show_threads(node)
+ worker_cnt = len(thread_data) - 1
+ if not worker_cnt:
+ return
+ worker_ids = list()
+ if workers:
+ for item in thread_data:
+ if str(item.cpu_id) in workers.split(u","):
+ worker_ids.append(item.id)
+ else:
+ for item in thread_data:
+ if u"vpp_main" not in item.name:
+ worker_ids.append(item.id)
+
+ worker_idx = 0
+ for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
+ for interface in node[u"interfaces"].values():
+ if placement[u"sw_if_index"] == interface[u"vpp_sw_index"] \
+ and prefix in interface[u"name"]:
+ InterfaceUtil.vpp_sw_interface_set_rx_placement(
+ node, placement[u"sw_if_index"], placement[u"queue_id"],
+ worker_ids[worker_idx % len(worker_ids)] - 1
+ )
+ worker_idx += 1
+
+ @staticmethod
+ def vpp_round_robin_rx_placement_on_all_duts(
+ nodes, prefix, workers=None):
+ """Set Round Robin interface RX placement on worker threads
+ on all DUTs.
+
+ If specified, workers 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.
+
+ :param nodes: Topology nodes.
+ :param prefix: Interface name prefix.
+ :param workers: Comma separated worker index numbers intended for
+ dataplane work.
+ :type nodes: dict
+ :type prefix: str
+ :type workers: str
+ """
+ for node in nodes.values():
+ if node[u"type"] == NodeType.DUT:
+ InterfaceUtil.vpp_round_robin_rx_placement(
+ node, prefix, workers
+ )