<div dir="ltr"><div>Thanks Thomas and Alex.</div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Tue, Feb 25, 2025 at 10:34 AM Thomas Wilks <<a href="mailto:thomas.wilks@arm.com">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>
To reduce the amount of maintenance and code duplication,<br>
common functionality between the rss_key_update, pmd_rss_reta<br>
and pmd_rss_hash test suites has been collated into a single<br>
file called rss_utils.<br>
<br>
It contains 3 main functions:<br>
1. verify that a packets RSS hash correctly associates with<br>
    the packets RSS queue.<br>
2. Send test packets specific to RSS, such as symmetric<br>
    packets that have the L4 port src and dst swapped.<br>
3. The setting up of the RSS environment which is common<br>
    between all 3 tets suites.<br></blockquote><div><br></div><div>tets -> test.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<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>
---<br>
 dts/tests/pmd_rss_utils.py | 195 +++++++++++++++++++++++++++++++++++++<br>
 1 file changed, 195 insertions(+)<br>
 create mode 100644 dts/tests/pmd_rss_utils.py<br>
<br>
diff --git a/dts/tests/pmd_rss_utils.py b/dts/tests/pmd_rss_utils.py<br>
new file mode 100644<br>
index 0000000000..2d9b333859<br>
--- /dev/null<br>
+++ b/dts/tests/pmd_rss_utils.py<br>
@@ -0,0 +1,195 @@<br>
+# SPDX-License-Identifier: BSD-3-Clause<br>
+# Copyright(c) 2025 Arm Limited<br>
+<br>
+"""PMD RSS Test Suite Utils.<br>
+<br>
+Utility functions for the pmd_rss_... test suite series<br>
+"""<br>
+<br>
+import random<br>
+<br>
+from scapy.layers.inet import IP, UDP<br>
+from scapy.layers.l2 import Ether<br>
+<br>
+from framework.exception import InteractiveCommandExecutionError<br>
+from framework.params.testpmd import SimpleForwardingModes<br>
+from framework.remote_session.testpmd_shell import (<br>
+    FlowRule,<br>
+    RSSOffloadTypesFlag,<br>
+    TestPmdShell,<br>
+    TestPmdVerbosePacket,<br>
+)<br>
+from framework.test_suite import TestSuite<br>
+<br>
+<br>
+def VerifyHashQueue(<br></blockquote><div><br></div><div>Should be snake case - VerifyHashQueue -> verify_hash_queue.</div><div><br></div><div>And that applies to the later functions in this patch also.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+    test_suite: TestSuite,<br>
+    reta: list[int],<br>
+    received_packets: list[TestPmdVerbosePacket],<br>
+    verify_packet_pairs: bool,<br>
+) -> None:<br>
+    """Verifies the packet hash corresponds to the packet queue.<br>
+<br>
+    Given the received packets in the verbose output, iterate through each packet.<br>
+    Lookup the packet hash in the RETA and get its intended queue.<br>
+    Verify the intended queue is the same as the actual queue the packet was received in.<br>
+    If the hash algorithm is symmetric, verify that pairs of packets have the same hash,<br>
+    as the pairs of packets sent have "mirrored" L4 ports.<br>
+    e.g. received_packets[0, 1, 2, 3, ...] hash(0) = hash(1), hash(2) = hash(3), ...<br>
+<br>
+    Args:<br>
+        test_suite: The reference to the currently running test suite.<br>
+        reta: Used to get predicted queue based on hash.<br>
+        received_packets: Packets received in the verbose output of testpmd.<br>
+        verify_packet_pairs: Verify pairs of packets have the same hash.<br>
+<br>
+    Raises:<br>
+        InteractiveCommandExecutionError: If packet_hash is None.<br>
+    """<br>
+    # List of packet hashes, used for symmetric algorithms<br>
+    hash_list = []<br>
+    for packet in received_packets:<br>
+        if packet.port_id != 0 or packet.src_mac != "02:00:00:00:00:00":<br>
+            continue<br>
+<br>
+        # Get packet hash<br>
+        packet_hash = packet.rss_hash<br>
+        if packet_hash is None:<br>
+            raise InteractiveCommandExecutionError(<br>
+                "Packet sent by the Traffic Generator has no RSS hash attribute."<br>
+            )<br>
+<br>
+        packet_queue = packet.rss_queue<br>
+<br>
+        # Calculate the predicted packet queue<br>
+        predicted_reta_index = packet_hash % len(reta)<br>
+        predicted_queue = reta[predicted_reta_index]<br>
+<br>
+        # Verify packets are in the correct queue<br>
+        test_suite.verify(<br>
+            predicted_queue == packet_queue,<br>
+            "Packet sent by the Traffic Generator has no RSS queue attribute.",<br>
+        )<br>
+<br>
+        if verify_packet_pairs:<br>
+            hash_list.append(packet_hash)<br>
+<br>
+    if verify_packet_pairs:<br>
+        # Go through pairs of hashes in list and verify they are the same<br>
+        for odd_hash, even_hash in zip(hash_list[0::2], hash_list[1::2]):<br>
+            test_suite.verify(<br>
+                odd_hash == even_hash,<br>
+                "Packet pair do not have same hash. Hash algorithm is not symmetric.",<br>
+            )<br>
+<br>
+<br>
+def SendTestPackets(<br></blockquote><div><br></div><div>Aside from the snake case, I think this function name could more clearly indicate its purpose as it relates to RSS functions.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+    TestSuite: TestSuite,<br>
+    testpmd: TestPmdShell,<br>
+    send_additional_mirrored_packet: bool,<br>
+) -> list[TestPmdVerbosePacket]:<br>
+    """Sends test packets.<br>
+<br>
+    Send 10 packets from the TG to SUT, parsing the verbose output and returning it.<br>
+    If the algorithm chosen is symmetric, send an additional packet for each initial<br>
+    packet sent, which has the L4 src and dst swapped.<br>
+<br>
+    Args:<br>
+        TestSuite: The reference to the currently running test suite.<br>
+        testpmd: Used to send packets and send commands to testpmd.<br>
+        send_additional_mirrored_packet: Send an additional mirrored packet for each packet sent.<br>
+<br>
+    Returns:<br>
+        TestPmdVerbosePacket: List of packets.<br>
+    """<br>
+    # Create test packets<br>
+    packets = []<br>
+    for i in range(10):<br>
+        packets.append(<br>
+            Ether(src="02:00:00:00:00:00", dst="11:00:00:00:00:00")<br>
+            / IP()<br>
+            / UDP(sport=i, dport=i + 1),<br>
+        )<br>
+        if send_additional_mirrored_packet:  # If symmetric, send the inverse packets<br>
+            packets.append(<br>
+                Ether(src="02:00:00:00:00:00", dst="11:00:00:00:00:00")<br>
+                / IP()<br>
+                / UDP(sport=i + 1, dport=i),<br>
+            )<br>
+<br>
+    # Set verbose packet information and start packet capture<br>
+    testpmd.set_verbose(3)<br>
+    testpmd.start()<br>
+    testpmd.start_all_ports()<br>
+    TestSuite.send_packets_and_capture(packets)<br>
+<br>
+    # Stop packet capture and revert verbose packet information<br>
+    testpmd_shell_out = testpmd.stop()<br>
+    testpmd.set_verbose(0)<br>
+    # Parse the packets and return them<br>
+    return testpmd.extract_verbose_output(testpmd_shell_out)<br>
+<br>
+<br>
+def SetupRssEnvironment(<br>
+    TestSuite: TestSuite,<br>
+    testpmd: TestPmdShell,<br>
+    num_queues: int,<br>
+    flow_rule: FlowRule | None,<br>
+) -> tuple[list[int], int]:<br>
+    """Sets up the testpmd environment for RSS test suites.<br>
+<br>
+    This involves:<br>
+    1. Setting the testpmd forward mode to rx_only.<br>
+    2. Setting RSS on the NIC to UDP.<br>
+    3. Creating a flow if provided.<br>
+    4. Configuring RETA.<br>
+<br>
+    The reta and key_size of the NIC are then returned<br>
+<br>
+    Args:<br>
+        TestSuite: TestSuite environment.<br>
+        testpmd: Where the environment will be set.<br>
+        num_queues: Number of queues in the RETA table.<br>
+        flow_rule: The flow rule for altering packet fate.<br>
+<br>
+    Raises:<br>
+        InteractiveCommandExecutionError: If size of hash key for driver is None.<br>
+        InteractiveCommandExecutionError: If size of RETA table for driver is None.<br>
+<br>
+    Returns:<br>
+        reta: Configured Redirection Table.<br>
+        key_size: key size supported by NIC.<br>
+    """<br>
+    ports = []<br>
+    for port_id, _ in enumerate(TestSuite.topology.sut_ports):<br>
+        ports.append(port_id)<br>
+<br>
+    port_info = testpmd.show_port_info(ports[0])<br>
+<br>
+    # Get hash key size<br>
+    key_size = port_info.hash_key_size<br>
+    if key_size is None:<br>
+        raise InteractiveCommandExecutionError("Size of hash key for driver is None.")<br>
+<br>
+    # Get RETA table size<br>
+    reta_size = port_info.redirection_table_size<br>
+    if reta_size is None:<br>
+        raise InteractiveCommandExecutionError("Size of RETA table for driver is None.")<br>
+<br>
+    # Set forward mode to receive only, to remove forwarded packets from verbose output<br>
+    testpmd.set_forward_mode(SimpleForwardingModes.rxonly)<br>
+<br>
+    # Reset RSS settings and only RSS udp packets<br>
+    testpmd.port_config_all_rss_offload_type(RSSOffloadTypesFlag.udp)<br>
+<br>
+    # Create flow rule<br>
+    if flow_rule is not None:<br>
+        testpmd.flow_create(flow_rule, ports[0])<br>
+<br>
+    # Configure the RETA with random queues<br>
+    reta: list[int] = []<br>
+    for i in range(reta_size):<br>
+        reta.insert(i, random.randint(0, num_queues - 1))<br>
+        testpmd.port_config_rss_reta(ports[0], i, reta[i])<br>
+<br>
+    return reta, key_size<br>
-- <br>
2.43.0<br>
<br></blockquote><div><br></div><div>Thanks, the function logic seems good.</div><div><br></div><div>I do want to reduce code duplication where possible, so having these functions makes sense in principle. But, we have tried to maintain such functions as a part of the testsuite class. So, I think that it would be fine to move these functions to the testsuite class - but let me know what you think.</div><div><br></div><div>Happy to discuss on Thursday at the DPDK CI meeting.</div><div> </div></div></div>