I think this patch looks good. Thanks Thomas and thanks Ivan for your insights.
Reviewed-by: Patrick Robb <pr...@iol.unh.edu> On Wed, Jul 30, 2025 at 8:59 AM Thomas Wilks <thomas.wi...@arm.com> wrote: > From: Alex Chapman <alex.chap...@arm.com> > > This patch adds the required functionality for the RSS key_update, > RETA, and hash test suites. This includes: > The setting of custom RETA values for routing packets to specific > queues. > The setting of the RSS mode on all ports, to specify how to hash > the packets. > The updating of the RSS hash key used during the hashing process. > > Alongside this, there is the addition of a __str__ method to the > RSSOffloadTypesFlags class, so that when flag names are cast to > a string they will use '-' as separators, instead of '_'. > This allows them to be directly used within testpmd RSS commands > without any further changes. > > Signed-off-by: Alex Chapman <alex.chap...@arm.com> > Signed-off-by: Thomas Wilks <thomas.wi...@arm.com> > > Reviewed-by: Paul Szczepanek <paul.szczepa...@arm.com> > Reviewed-by: Patrick Robb <pr...@iol.unh.edu> > Tested-by: Patrick Robb <pr...@iol.unh.edu> > --- > dts/framework/remote_session/testpmd_shell.py | 104 ++++++++++++++++-- > 1 file changed, 97 insertions(+), 7 deletions(-) > > diff --git a/dts/framework/remote_session/testpmd_shell.py > b/dts/framework/remote_session/testpmd_shell.py > index ad8cb273dc..473333f8fb 100644 > --- a/dts/framework/remote_session/testpmd_shell.py > +++ b/dts/framework/remote_session/testpmd_shell.py > @@ -343,6 +343,12 @@ def make_parser(cls) -> ParserFn: > RSSOffloadTypesFlag.from_list_string, > ) > > + def __str__(self): > + """Replaces underscores with hyphens to produce valid testpmd > value.""" > + if self.name is None: > + return "" > + return self.name.replace("_", "-") > + > > class DeviceCapabilitiesFlag(Flag): > """Flag representing the device capabilities.""" > @@ -644,11 +650,13 @@ class TestPmdPort(TextParser): > ) > #: Maximum number of VFs > max_vfs_num: int | None = field( > - default=None, metadata=TextParser.find_int(r"Maximum number of > VFs: (\d+)") > + default=None, > + metadata=TextParser.find_int(r"Maximum number of VFs: (\d+)"), > ) > #: Maximum number of VMDq pools > max_vmdq_pools_num: int | None = field( > - default=None, metadata=TextParser.find_int(r"Maximum number of > VMDq pools: (\d+)") > + default=None, > + metadata=TextParser.find_int(r"Maximum number of VMDq pools: > (\d+)"), > ) > > #: > @@ -1734,6 +1742,82 @@ def close_all_ports(self, verify: bool = True) -> > None: > if not all(f"Port {p_id} is closed" in port_close_output for > p_id in range(num_ports)): > raise InteractiveCommandExecutionError("Ports were not > closed successfully.") > > + def port_config_rss_reta( > + self, port_id: int, hash_index: int, queue_id: int, verify: bool > = True > + ) -> None: > + """Configure a port's RSS redirection table. > + > + Args: > + port_id: The port where the redirection table will be > configured. > + hash_index: The index into the redirection table associated > with the destination queue. > + queue_id: The destination queue of the packet. > + verify: If :data:`True`, verifies if a port's redirection > table > + was correctly configured. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True` > + Testpmd failed to config RSS reta. > + """ > + out = self.send_command(f"port config {port_id} rss reta > ({hash_index},{queue_id})") > + if verify: > + if f"The reta size of port {port_id} is" not in out: > + self._logger.debug(f"Failed to config RSS reta: \n{out}") > + raise InteractiveCommandExecutionError("Testpmd failed to > config RSS reta.") > + > + def port_config_all_rss_offload_type( > + self, flag: RSSOffloadTypesFlag, verify: bool = True > + ) -> None: > + """Set the RSS mode on all ports. > + > + Args: > + flag: The RSS iptype all ports will be configured to. > + verify: If :data:`True`, it verifies if all ports RSS offload > type > + was correctly configured. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True` > + Testpmd failed to config the RSS mode on all ports. > + """ > + out = self.send_command(f"port config all rss {flag.name}") > + if verify: > + if "error" in out: > + self._logger.debug(f"Failed to config the RSS mode on all > ports: \n{out}") > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to change RSS mode to {flag.name}" > + ) > + > + def port_config_rss_hash_key( > + self, > + port_id: int, > + offload_type: RSSOffloadTypesFlag, > + hex_str: str, > + verify: bool = True, > + ) -> str: > + """Sets the RSS hash key for the specified port. > + > + Args: > + port_id: The port that will have the hash key applied to. > + offload_type: The offload type the hash key will be applied > to. > + hex_str: The hash key to set. > + verify: If :data:`True`, verify that RSS has the key. > + > + Raises: > + InteractiveCommandExecutionError: If `verify` is :data:`True` > + Testpmd failed to set the RSS hash key. > + """ > + output = self.send_command( > + f"port config {port_id} rss-hash-key {offload_type} > {hex_str}", > + skip_first_line=True, > + ) > + > + if verify: > + if output.strip(): > + self._logger.debug(f"Failed to set rss hash key: > \n{output}") > + raise InteractiveCommandExecutionError( > + f"Testpmd failed to set {hex_str} on {port_id} with a > flag of {offload_type}." > + ) > + return output > + > def show_port_info_all(self) -> list[TestPmdPort]: > """Returns the information of all the ports. > > @@ -1952,22 +2036,28 @@ def csum_set_hw( > > {port_id}:\n{csum_output}""" > ) > > - def flow_create(self, flow_rule: FlowRule, port_id: int) -> int: > + def flow_create(self, flow_rule: FlowRule, port_id: int, verify: bool > = True) -> int: > """Creates a flow rule in the testpmd session. > > - This command is implicitly verified as needed to return the > created flow rule id. > - > Args: > flow_rule: :class:`FlowRule` object used for creating testpmd > flow rule. > port_id: Integer representing the port to use. > + verify: If :data:`True`, the output of the command is scanned > + to ensure the flow rule was created successfully. > > Raises: > InteractiveCommandExecutionError: If flow rule is invalid. > > Returns: > - Id of created flow rule. > + Id of created flow rule as an integer. > """ > flow_output = self.send_command(f"flow create {port_id} > {flow_rule}") > + if verify: > + if "created" not in flow_output: > + self._logger.debug(f"Failed to create flow > rule:\n{flow_output}") > + raise InteractiveCommandExecutionError( > + f"Failed to create flow rule:\n{flow_output}" > + ) > match = re.search(r"#(\d+)", flow_output) > if match is not None: > match_str = match.group(1) > @@ -1996,7 +2086,7 @@ def flow_delete(self, flow_id: int, port_id: int, > verify: bool = True) -> None: > """Deletes the specified flow rule from the testpmd session. > > Args: > - flow_id: ID of the flow to remove. > + flow_id: :class:`FlowRule` id used for deleting testpmd flow > rule. > port_id: Integer representing the port to use. > verify: If :data:`True`, the output of the command is scanned > to ensure the flow rule was deleted successfully. > -- > 2.43.0 > >