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