From: Ivan Malov <ivan.malov@arknetworks.am>
To: Thomas Wilks <thomas.wilks@arm.com>
Cc: dev@dpdk.org, Paul Szczepanek <paul.szczepanek@arm.com>,
Luca Vizzarro <luca.vizzarro@arm.com>,
Patrick Robb <probb@iol.unh.edu>, Dean Marx <dmarx@iol.unh.edu>
Subject: Re: [PATCH v4 2/2] dts: add PMD RSS testsuite
Date: Wed, 30 Jul 2025 19:19:35 +0400 (+04) [thread overview]
Message-ID: <ca9ebc2b-13b0-e955-b802-fc2f9f24b186@arknetworks.am> (raw)
In-Reply-To: <20250730125859.159185-3-thomas.wilks@arm.com>
Hi Thomas,
On Wed, 30 Jul 2025, Thomas Wilks wrote:
> Port over the rss_key_update, pmd_rss_reta and pmd_rss_hash
> test suites from old DTS into one file including all of the
> helper functions that are required by all of the test suites
> to work.
>
> The rss_key_update test cases verify that setting a new hash
> key when Receive Side Scaling (RSS) will result in a change
> in the packets destination queue. These test cases also
> verifies that the reported key size of the NIC is correct.
>
> The pmd_rss_reta test cases verify that Redirection Tables (RETAs)
> of different sizes function correctly in RSS. These test cases
> also verify that the reported reta size of the NIC is correct.
>
> The pmd_rss_hash test case verifies that the 4 supported types of
> hashing algorithm used in RSS function correctly and that the nic
> supports them. The four hashing algorithms being DEFAULT, TOEPLITZ,
> SYMMETRIC_TOEPLITZ and SIMPLE_XOR. These test cases also verify
> that the supported hashing algorithms reported by the NIC are correct.
This is a huge improvement, and the patch is quite neat.
Apparently, the code does not consider cases where 'l3-dst-only, 'l4-dst-only'
and similar flags get requested along with 'ipv4-udp'. Is it worth considering?
Also, the test doesn't seem to validate the literal value of the 32-bit hash to
make sure the predicted value matches the one in the packet's metadata. Though,
I admit adding such coverage on the hoof might not be good and I don't insist.
The patch is probably good as it is.
Thank you.
>
> Signed-off-by: Thomas Wilks <thomas.wilks@arm.com>
> ---
> dts/tests/TestSuite_pmd_rss.py | 383 +++++++++++++++++++++++++++++++++
> 1 file changed, 383 insertions(+)
> create mode 100644 dts/tests/TestSuite_pmd_rss.py
>
> diff --git a/dts/tests/TestSuite_pmd_rss.py b/dts/tests/TestSuite_pmd_rss.py
> new file mode 100644
> index 0000000000..4c1b026307
> --- /dev/null
> +++ b/dts/tests/TestSuite_pmd_rss.py
> @@ -0,0 +1,383 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2025 Arm Limited
> +
> +"""RSS testing suite.
> +
> +Tests different hashing algorithms by checking if packets are routed to correct queues.
> +Tests updating the RETA (Redirection Table) key to verify it takes effect and follows
> +set size constraints.
> +Tests RETA behavior under changing number of queues.
> +"""
> +
> +import random
> +
> +from scapy.layers.inet import IP, UDP
> +from scapy.layers.l2 import Ether
> +
> +from framework.exception import InteractiveCommandExecutionError
> +from framework.params.testpmd import SimpleForwardingModes
> +from framework.remote_session.testpmd_shell import (
> + FlowRule,
> + RSSOffloadTypesFlag,
> + TestPmdShell,
> + TestPmdVerbosePacket,
> +)
> +from framework.test_suite import BaseConfig, TestSuite, func_test
> +from framework.testbed_model.capability import requires
> +from framework.testbed_model.topology import TopologyType
> +from framework.utils import StrEnum
> +
> +
> +class Config(BaseConfig):
> + """Default configuration for Per Test Suite config."""
> +
> + NUM_QUEUES: int = 4
> +
> + ACTUAL_KEY_SIZE: int = 52
> +
> + ACTUAL_RETA_SIZE: int = 512
> +
> +
> +class HashAlgorithm(StrEnum):
> + """Enum of hashing algorithms."""
> +
> + DEFAULT = "default"
> + SIMPLE_XOR = "simple_xor"
> + TOEPLITZ = "toeplitz"
> + SYMMETRIC_TOEPLITZ = "symmetric_toeplitz"
> +
> +
> +@requires(topology_type=TopologyType.one_link)
> +class TestPmdRss(TestSuite):
> + """PMD RSS test suite."""
> +
> + config: Config
> +
> + def verify_hash_queue(
> + self,
> + reta: list[int],
> + received_packets: list[TestPmdVerbosePacket],
> + verify_packet_pairs: bool,
> + ) -> None:
> + """Verifies the packet hash corresponds to the packet queue.
> +
> + Given the received packets in the verbose output, iterate through each packet.
> + Use the hash to index into RETA and get its intended queue.
> + Verify the intended queue is the same as the actual queue the packet was received in.
> + If the hash algorithm is symmetric, verify that pairs of packets have the same hash,
> + as the pairs of packets sent have "mirrored" L4 ports.
> + e.g. received_packets[0, 1, 2, 3, ...] hash(0) = hash(1), hash(2) = hash(3), ...
> +
> + Args:
> + reta: Used to get predicted queue based on hash.
> + received_packets: Packets received in the verbose output of testpmd.
> + verify_packet_pairs: Verify pairs of packets have the same hash.
> +
> + Raises:
> + InteractiveCommandExecutionError: If packet_hash is None.
> + """
> + # List of packet hashes, used for symmetric algorithms
> + hash_list = []
> + for packet in received_packets:
> + # Ignore stray packets
> + if packet.port_id != 0 or packet.src_mac != "02:00:00:00:00:00":
> + continue
> + # Get packet hash
> + packet_hash = packet.rss_hash
> + if packet_hash is None:
> + raise InteractiveCommandExecutionError(
> + "Packet sent by the Traffic Generator has no RSS hash attribute."
> + )
> +
> + packet_queue = packet.rss_queue
> +
> + # Calculate the predicted packet queue
> + predicted_queue = reta[packet_hash % len(reta)]
> + self.verify(
> + predicted_queue == packet_queue,
> + "Packet sent by the Traffic Generator assigned to incorrect queue by the RSS.",
> + )
> +
> + if verify_packet_pairs:
> + hash_list.append(packet_hash)
> +
> + if verify_packet_pairs:
> + # Go through pairs of hashes in list and verify they are the same
> + for odd_hash, even_hash in zip(hash_list[0::2], hash_list[1::2]):
> + self.verify(
> + odd_hash == even_hash,
> + "Packet pair do not have same hash. Hash algorithm is not symmetric.",
> + )
> +
> + def send_test_packets(
> + self,
> + testpmd: TestPmdShell,
> + send_additional_mirrored_packet: bool = False,
> + ) -> list[TestPmdVerbosePacket]:
> + """Sends test packets.
> +
> + Send 10 packets from the TG to SUT, parsing the verbose output and returning it.
> + If the algorithm chosen is symmetric, send an additional packet for each initial
> + packet sent, which has the L4 src and dst swapped.
> +
> + Args:
> + testpmd: Used to send packets and send commands to testpmd.
> + send_additional_mirrored_packet: Send an additional mirrored packet for each packet
> + sent.
> +
> + Returns:
> + TestPmdVerbosePacket: List of packets.
> + """
> + # Create test packets
> + packets = []
> + for i in range(10):
> + packets.append(
> + Ether(src="02:00:00:00:00:00", dst="11:00:00:00:00:00")
> + / IP()
> + / UDP(sport=i, dport=i + 1),
> + )
> + if send_additional_mirrored_packet: # If symmetric, send the inverse packets
> + packets.append(
> + Ether(src="02:00:00:00:00:00", dst="11:00:00:00:00:00")
> + / IP()
> + / UDP(sport=i + 1, dport=i),
> + )
> +
> + # Set verbose packet information and start packet capture
> + testpmd.set_verbose(level=3)
> + testpmd.start()
> + testpmd.start_all_ports()
> + self.send_packets_and_capture(packets)
> +
> + # Stop packet capture and revert verbose packet information
> + testpmd_shell_out = testpmd.stop()
> + testpmd.set_verbose(level=0)
> + # Parse the packets and return them
> + return testpmd.extract_verbose_output(testpmd_shell_out)
> +
> + def setup_rss_environment(
> + self,
> + testpmd: TestPmdShell,
> + ) -> None:
> + """Sets up the testpmd environment for RSS test suites.
> +
> + Sets the testpmd forward mode to rx_only and RSS on the NIC to UDP.
> +
> + Args:
> + testpmd: Where the environment will be set.
> + """
> + # Set forward mode to receive only, to remove forwarded packets from verbose output
> + testpmd.set_forward_mode(SimpleForwardingModes.rxonly)
> +
> + # Reset RSS settings and only RSS udp packets
> + testpmd.port_config_all_rss_offload_type(RSSOffloadTypesFlag.udp)
> +
> + def configure_random_reta(self, testpmd: TestPmdShell, queue_number: int) -> list[int]:
> + """Configure RETA to have random order of queues.
> +
> + Args:
> + testpmd: The testpmd instance that will be used to set the rss environment.
> + queue_number: Number of queues that will be randomly inserted into the RETA.
> +
> + Returns:
> + List of ids matching the configured RETA table
> +
> + Raises:
> + InteractiveCommandExecutionError: If size of RETA table for driver is None.
> + """
> + reta_size = testpmd.show_port_info(port_id=0).redirection_table_size
> + if reta_size is None:
> + raise InteractiveCommandExecutionError("Size of RETA table for driver is None.")
> + reta_table: list[int] = []
> +
> + for i in range(reta_size):
> + random_id = random.randint(0, queue_number - 1)
> + reta_table.insert(i, random_id)
> + testpmd.port_config_rss_reta(port_id=0, hash_index=i, queue_id=random_id)
> + return reta_table
> +
> + def verify_rss_hash_function(
> + self,
> + testpmd: TestPmdShell,
> + hash_algorithm: HashAlgorithm,
> + flow_rule: FlowRule,
> + reta: list[int],
> + ) -> None:
> + """Verifies hash function are working by sending test packets and checking the packet queue.
> +
> + Args:
> + testpmd: The testpmd instance that will be used to set the rss environment.
> + hash_algorithm: The hash algorithm to be tested.
> + flow_rule: The flow rule that is to be validated and then created.
> + reta: Will be used to calculate the predicted packet queues.
> + """
> + is_symmetric = hash_algorithm == HashAlgorithm.SYMMETRIC_TOEPLITZ
> + self.setup_rss_environment(testpmd)
> + testpmd.flow_create(flow_rule, port_id=0)
> + # Send udp packets and ensure hash corresponds with queue
> + parsed_output = self.send_test_packets(
> + testpmd, send_additional_mirrored_packet=is_symmetric
> + )
> + self.verify_hash_queue(reta, parsed_output, is_symmetric)
> +
> + @func_test
> + def test_key_hash_algorithm(self) -> None:
> + """Hashing algorithm test.
> +
> + Steps:
> + Setup RSS environment using the chosen algorithm.
> + Send test packets for each flow rule.
> +
> + Verify:
> + Packet hash corresponds to the packet queue.
> +
> + Raises:
> + InteractiveCommandExecutionError: If size of RETA table for driver is None.
> + InteractiveCommandExecutionError: If there are no valid flow rules that can be created.
> + """
> + failed_attempts: int = 0
> + for algorithm in HashAlgorithm:
> + flow_rule = FlowRule(
> + group_id=0,
> + direction="ingress",
> + pattern=["eth / ipv4 / udp"],
> + actions=[f"rss types ipv4-udp end queues end func {algorithm.name.lower()}"],
> + )
> + with TestPmdShell(
> + rx_queues=self.config.NUM_QUEUES,
> + tx_queues=self.config.NUM_QUEUES,
> + ) as testpmd:
> + reta_table = self.configure_random_reta(testpmd, self.config.NUM_QUEUES)
> +
> + if not testpmd.flow_validate(flow_rule, port_id=0):
> + # Queues need to be specified in the flow rule on some NICs
> + queue_ids = " ".join([str(x) for x in reta_table])
> + flow_rule.actions = [
> + f"rss types ipv4-udp end queues {queue_ids} end func "
> + + algorithm.name.lower()
> + ]
> +
> + if not testpmd.flow_validate(flow_rule, port_id=0):
> + failed_attempts += 1
> + if failed_attempts == len(HashAlgorithm):
> + raise InteractiveCommandExecutionError(
> + "No Valid flow rule could be created."
> + )
> + # if neither rule format is valid then the algorithm is not supported,
> + # move to next one
> + continue
> + self.verify_rss_hash_function(testpmd, algorithm, flow_rule, reta_table)
> +
> + @func_test
> + def test_update_key_set_hash_key_short_long(self) -> None:
> + """Set hash key short long test.
> +
> + Steps:
> + Fetch the hash key size.
> + Create two random hash keys one key too short and one too long.
> +
> + Verify:
> + Verify that it is not possible to set the shorter hash key.
> + Verify that it is not possible to set the longer hash key.
> +
> + Raises:
> + InteractiveCommandExecutionError: If port info dose not contain hash key size.
> + """
> + with TestPmdShell(
> + memory_channels=4,
> + rx_queues=self.config.NUM_QUEUES,
> + tx_queues=self.config.NUM_QUEUES,
> + ) as testpmd:
> + # Get RETA and key size
> + port_info = testpmd.show_port_info(port_id=0)
> +
> + # Get hash key size
> + key_size = port_info.hash_key_size
> + if key_size is None:
> + raise InteractiveCommandExecutionError("Port info does not contain hash key size.")
> +
> + # Create 2 hash keys based on the NIC capabilities
> + short_key = "".join(
> + [random.choice("0123456789ABCDEF") for n in range(key_size * 2 - 2)]
> + )
> + long_key = "".join([random.choice("0123456789ABCDEF") for n in range(key_size * 2 + 2)])
> +
> + # Verify a short key cannot be set
> + short_key_out = testpmd.port_config_rss_hash_key(
> + 0, RSSOffloadTypesFlag.ipv4_udp, short_key, False
> + )
> + self.verify(
> + "invalid" in short_key_out,
> + "Able to set hash key shorter than specified.",
> + )
> +
> + # Verify a long key cannot be set
> + long_key_out = testpmd.port_config_rss_hash_key(
> + 0, RSSOffloadTypesFlag.ipv4_udp, long_key, False
> + )
> + self.verify("invalid" in long_key_out, "Able to set hash key longer than specified.")
> +
> + @func_test
> + def test_update_key_reported_key_size(self) -> None:
> + """Verify reported hash key size is the same as the NIC capabilities.
> +
> + Steps:
> + Fetch the hash key size and compare to the actual key size.
> +
> + Verify:
> + Reported key size is the same as the actual key size.
> + """
> + with TestPmdShell() as testpmd:
> + reported_key_size = testpmd.show_port_info(port_id=0).hash_key_size
> + self.verify(
> + reported_key_size == self.config.ACTUAL_KEY_SIZE,
> + "Reported key size is not the same as the config file.",
> + )
> +
> + @func_test
> + def test_reta_key_reta_queues(self) -> None:
> + """RETA rx/tx queues test.
> +
> + Steps:
> + For each queue size setup RSS environment and send Test packets.
> +
> + Verify:
> + Packet hash corresponds to hash queue.
> +
> + Raises:
> + InteractiveCommandExecutionError: If size of RETA table for driver is None.
> + """
> + queues_numbers = [2, 9, 16]
> + for queue_number in queues_numbers:
> + with TestPmdShell(
> + rx_queues=queue_number,
> + tx_queues=queue_number,
> + ) as testpmd:
> + # Configure the RETA with random queues
> + reta = self.configure_random_reta(testpmd, queue_number)
> +
> + self.setup_rss_environment(testpmd)
> +
> + # Send UDP packets and ensure hash corresponds with queue
> + parsed_output = self.send_test_packets(testpmd)
> + self.verify_hash_queue(reta, parsed_output, False)
> +
> + @func_test
> + def test_reta_key_reported_reta_size(self) -> None:
> + """Reported RETA size test.
> +
> + Steps:
> + Fetch reported reta size.
> +
> + Verify:
> + Reported RETA size is equal to the actual RETA size.
> + """
> + with TestPmdShell(
> + rx_queues=self.config.NUM_QUEUES,
> + tx_queues=self.config.NUM_QUEUES,
> + ) as testpmd:
> + reported_reta_size = testpmd.show_port_info(port_id=0).redirection_table_size
> + self.verify(
> + reported_reta_size == self.config.ACTUAL_RETA_SIZE,
> + "Reported RETA size is not the same as the config file.",
> + )
> --
> 2.43.0
>
>
prev parent reply other threads:[~2025-07-30 15:19 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-08-29 12:50 [PATCH] dts: add RSS functions to testpmd Alex Chapman
2024-09-06 14:29 ` Juraj Linkeš
2025-02-25 15:33 ` [PATCH v2 0/6] Added RSS functions and tests Thomas Wilks
2025-02-25 15:33 ` [PATCH v2 1/6] dts: add RSS functions to testpmd Thomas Wilks
2025-04-14 3:11 ` Patrick Robb
2025-02-25 15:33 ` [PATCH v2 2/6] dts: add utils for PMD RSS testsuites Thomas Wilks
2025-04-14 3:11 ` Patrick Robb
2025-02-25 15:33 ` [PATCH v2 3/6] dts: add PMD RSS hash testsuite Thomas Wilks
2025-04-14 3:30 ` Patrick Robb
2025-02-25 15:33 ` [PATCH v2 4/6] dts: add PMD RSS RETA testsuite Thomas Wilks
2025-02-25 15:33 ` [PATCH v2 5/6] dts: add PMD RSS key update testsuite Thomas Wilks
2025-02-25 15:33 ` [PATCH v2 6/6] dts: add NIC capabilities for hash algorithms Thomas Wilks
2025-07-18 15:03 ` [RFC PATCH v3 0/2] dts: add RSS functions and test suite Thomas Wilks
2025-07-18 15:04 ` [RFC PATCH v3 1/2] dts: add RSS functions to testpmd Thomas Wilks
2025-07-18 15:04 ` [RFC PATCH v3 2/2] dts: add PMD RSS testsuite Thomas Wilks
2025-07-18 18:37 ` Dean Marx
2025-07-20 8:00 ` Ivan Malov
2025-07-18 17:00 ` [RFC PATCH v3 0/2] dts: add RSS functions and test suite Ivan Malov
2025-07-18 18:22 ` Ivan Malov
2025-07-30 12:58 ` [PATCH v4 " Thomas Wilks
2025-07-30 12:58 ` [PATCH v4 1/2] dts: add RSS functions to testpmd Thomas Wilks
2025-07-30 12:58 ` [PATCH v4 2/2] dts: add PMD RSS testsuite Thomas Wilks
2025-07-30 15:19 ` Ivan Malov [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ca9ebc2b-13b0-e955-b802-fc2f9f24b186@arknetworks.am \
--to=ivan.malov@arknetworks.am \
--cc=dev@dpdk.org \
--cc=dmarx@iol.unh.edu \
--cc=luca.vizzarro@arm.com \
--cc=paul.szczepanek@arm.com \
--cc=probb@iol.unh.edu \
--cc=thomas.wilks@arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).