<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>