From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 81BE345A2C; Wed, 25 Sep 2024 21:20:43 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 8009A402EF; Wed, 25 Sep 2024 21:20:33 +0200 (CEST) Received: from mail-il1-f228.google.com (mail-il1-f228.google.com [209.85.166.228]) by mails.dpdk.org (Postfix) with ESMTP id A139F402B4 for ; Wed, 25 Sep 2024 21:20:30 +0200 (CEST) Received: by mail-il1-f228.google.com with SMTP id e9e14a558f8ab-3a0cc384a85so823895ab.1 for ; Wed, 25 Sep 2024 12:20:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iol.unh.edu; s=unh-iol; t=1727292030; x=1727896830; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Dr4UOqqtNH8Ju2JkcwwIKF4w96EMHrfWB6LA+cWzy+I=; b=USWWEmANI5+DVOKrcwkKIZKFEVU5icAl6XWSLdhYzZhFws4yunZaSFcTdNK1XN53kX EB/xDI5oWaQ9EcZtBCVHRyTPUeBmmPlYyhdCAf/NJL6TO5a718RFSry0hXEtmXFPFfdk 75N97+cd2WB877dPsNwHl1mr3tw8ux53bNeJc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727292030; x=1727896830; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Dr4UOqqtNH8Ju2JkcwwIKF4w96EMHrfWB6LA+cWzy+I=; b=Y9svX28isEZ/izeZxUi3Fxln70NnzXLTwuvJIrxwnBxJyW52ceF/OG5Xo9V8jI4gU5 nt+m2hSd9dYtsiiu+yj8hiO/NlLi2v4Vm3Git114ohxlP88FUW+UtDhuG4k+iots1P3y enr7KPrCv91ErocEsbLizr2uWAPGhfwb5975kDpBDRHYOsFbjdvmTzsqh3Xph3sVD3vb QaB0ck2LO4Q4Q+y3i9lBbPa1QvdvPP67CuTTNovWJRlKTz3/WcA3C6qPLIgHnDY1/HQI 1Gh7G7tRU59Ft2mhux6jiaQ7/MiJmZrYPzR73hDL9AKa9pT5o4RKuBGXiBb6wIlxmntZ Pnjw== X-Gm-Message-State: AOJu0Yw283PxA8BhmhGT1KFblz8hYDzrwOR2WOUftahw5HQJg/nZkEoy 9EOmNodpiupoxUDYeUGkBhhWh3qXjDNavOL3/ozwyuBJ9OQ5Wapj22cEvizMiCfZ86JIuaP4FQK xla+1Ub4XbaNMuHrMZRAs8uZP35YVbJLe X-Google-Smtp-Source: AGHT+IHhdSCzfGccqWCCrctxYC8Ss1A39JySZ5TJZrYXOeHMuNin3VE8aSRHRQXy1I0WTo5IDhzXy827s/X6 X-Received: by 2002:a05:6e02:1fcc:b0:3a0:979d:843 with SMTP id e9e14a558f8ab-3a26d72c875mr44539355ab.9.1727292029767; Wed, 25 Sep 2024 12:20:29 -0700 (PDT) Received: from postal.iol.unh.edu (postal.iol.unh.edu. [132.177.123.84]) by smtp-relay.gmail.com with ESMTPS id e9e14a558f8ab-3a1a57247bbsm1589615ab.76.2024.09.25.12.20.29 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 25 Sep 2024 12:20:29 -0700 (PDT) X-Relaying-Domain: iol.unh.edu Received: from iol.unh.edu (unknown [IPv6:2606:4100:3880:1257::1083]) by postal.iol.unh.edu (Postfix) with ESMTP id D8420605C351; Wed, 25 Sep 2024 15:20:28 -0400 (EDT) From: jspewock@iol.unh.edu To: yoan.picchi@foss.arm.com, npratte@iol.unh.edu, wathsala.vithanage@arm.com, probb@iol.unh.edu, Honnappa.Nagarahalli@arm.com, paul.szczepanek@arm.com, thomas@monjalon.net, juraj.linkes@pantheon.tech, Luca.Vizzarro@arm.com, alex.chapman@arm.com Cc: dev@dpdk.org, Jeremy Spewock Subject: [PATCH v5 2/2] dts: add dynamic queue test suite Date: Wed, 25 Sep 2024 15:20:13 -0400 Message-ID: <20240925192013.17446-3-jspewock@iol.unh.edu> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20240925192013.17446-1-jspewock@iol.unh.edu> References: <20240625155332.2400-1-jspewock@iol.unh.edu> <20240925192013.17446-1-jspewock@iol.unh.edu> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Jeremy Spewock This patch adds a new test suite that is designed to test the stopping and modification of port queues at runtime. Specifically, there are test cases that display the ports ability to stop some queues but still send and receive traffic on others, as well as the ability to configure the ring size of the queue without blocking the traffic on other queues. Depends-on: patch-144441 ("dts: add send_packets to test_suite") Signed-off-by: Jeremy Spewock --- dts/framework/config/conf_yaml_schema.json | 3 +- dts/tests/TestSuite_dynamic_queue_conf.py | 286 +++++++++++++++++++++ 2 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 dts/tests/TestSuite_dynamic_queue_conf.py diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json index df390e8ae2..11c6f25aa1 100644 --- a/dts/framework/config/conf_yaml_schema.json +++ b/dts/framework/config/conf_yaml_schema.json @@ -187,7 +187,8 @@ "enum": [ "hello_world", "os_udp", - "pmd_buffer_scatter" + "pmd_buffer_scatter", + "dynamic_queue_conf" ] }, "test_target": { diff --git a/dts/tests/TestSuite_dynamic_queue_conf.py b/dts/tests/TestSuite_dynamic_queue_conf.py new file mode 100644 index 0000000000..f5c667cdeb --- /dev/null +++ b/dts/tests/TestSuite_dynamic_queue_conf.py @@ -0,0 +1,286 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2024 University of New Hampshire + +"""Dynamic configuration of port queues test suite. + +This test suite tests the support of being able to either stop or reconfigure port queues at +runtime without stopping the entire device. Previously, to configure a DPDK ethdev, the application +first specifies how many Tx and Rx queues to include in the ethdev and then application sets up +each queue individually. Only once all the queues have been set up can the application then start +the device, and at this point traffic can flow. If device stops, this halts the flow of traffic on +all queues in the ethdev completely. Dynamic queue is a capability present on some NICs that +specifies whether the NIC is able to delay the configuration of queues on its port. This capability +allows for the support of stopping and reconfiguring queues on a port at runtime without stopping +the entire device. + +Support of this capability is shown by starting the Poll Mode Driver with multiple Rx and Tx queues +configured and stopping some prior to forwarding packets, then examining whether or not the stopped +ports and the unmodified ports were able to handle traffic. In addition to just stopping the ports, +the ports must also show that they support configuration changes on their queues at runtime without +stopping the entire device. This is shown by changing the ring size of the queues. + +If the Poll Mode Driver is able to stop some queues on a port and modify them then handle traffic +on the unmodified queues while the others are stopped, then it is the case that the device properly +supports dynamic configuration of its queues. +""" + +import random +from typing import Callable, ClassVar, MutableSet + +from scapy.layers.inet import IP # type: ignore[import-untyped] +from scapy.layers.l2 import Ether # type: ignore[import-untyped] +from scapy.packet import Raw # type: ignore[import-untyped] + +from framework.exception import InteractiveCommandExecutionError +from framework.params.testpmd import PortTopology, SimpleForwardingModes +from framework.remote_session.testpmd_shell import TestPmdShell +from framework.test_suite import TestSuite + + +def setup_and_teardown_test( + test_meth: Callable[ + ["TestDynamicQueueConf", int, MutableSet, MutableSet, TestPmdShell, bool], None + ], +) -> Callable[["TestDynamicQueueConf", bool], None]: + """Decorator that provides a setup and teardown for testing methods. + + This decorator provides a method that sets up the environment for testing, runs the test + method, and then does a clean-up verification step after the queues are started again. The + decorated method will be provided with all the variables it should need to run testing + including: The ID of the port where the queues for testing reside, disjoint sets of IDs for + queues that are/aren't modified, a testpmd session to run testing with, and a flag that + indicates whether or not testing should be done on Rx or Tx queues. + + Args: + test_meth: The decorated method that tests configuration of port queues at runtime. + This method must have the following parameters in order: An int that represents a + port ID, a set of queues for testing, a set of unmodified queues, a testpmd + interactive shell, and a boolean that, when :data:`True`, does Rx testing, + otherwise does Tx testing. This method must also be a member of the + :class:`TestDynamicQueueConf` class. + + Returns: + A method that sets up the environment, runs the decorated method, then re-enables all + queues and validates they can still handle traffic. + """ + + def wrap(self: "TestDynamicQueueConf", is_rx_testing: bool) -> None: + """Setup environment, run test function, then cleanup. + + Start a testpmd shell and stop ports for testing, then call the decorated function that + performs the testing. After the decorated function is finished running its testing, + start the stopped queues and send packets to validate that these ports can properly + handle traffic after being started again. + + Args: + self: Instance of :class:`TestDynamicQueueConf` `test_meth` belongs to. + is_rx_testing: If :data:`True` then Rx queues will be the ones modified throughout + the test, otherwise Tx queues will be modified. + """ + port_id = self.rx_port_num if is_rx_testing else self.tx_port_num + queues_to_config: set[int] = set() + while len(queues_to_config) < self.num_ports_to_modify: + queues_to_config.add(random.randint(1, self.number_of_queues - 1)) + unchanged_queues = set(range(self.number_of_queues)) - queues_to_config + with TestPmdShell( + self.sut_node, + port_topology=PortTopology.chained, + rx_queues=self.number_of_queues, + tx_queues=self.number_of_queues, + ) as testpmd: + for q in queues_to_config: + testpmd.stop_port_queue(port_id, q, is_rx_testing) + testpmd.set_forward_mode(SimpleForwardingModes.mac) + + test_meth(self, port_id, queues_to_config, unchanged_queues, testpmd, is_rx_testing) + + for queue_id in queues_to_config: + testpmd.start_port_queue(port_id, queue_id, is_rx_testing) + + testpmd.start() + self.send_packets_with_different_addresses(self.number_of_packets_to_send) + forwarding_stats = testpmd.stop() + for queue_id in queues_to_config: + self.verify( + self.port_queue_in_stats(port_id, is_rx_testing, queue_id, forwarding_stats), + f"Modified queue {queue_id} on port {port_id} failed to receive traffic after" + "being started again.", + ) + + return wrap + + +class TestDynamicQueueConf(TestSuite): + """DPDK dynamic queue configuration test suite. + + Testing for the support of dynamic queue configuration is done by splitting testing by the type + of queue (either Rx or Tx) and the type of testing (testing for stopping a port at runtime vs + testing configuration changes at runtime). Testing is done by first stopping a finite number of + port queues (3 is sufficient) and either modifying the configuration or sending packets to + verify that the unmodified queues can handle traffic. Specifically, the following cases are + tested: + + 1. The application should be able to start the device with only some of the + queues set up. + 2. The application should be able to reconfigure existing queues at runtime + without calling dev_stop(). + """ + + #: + num_ports_to_modify: ClassVar[int] = 3 + #: Source IP address to use when sending packets. + src_addr: ClassVar[str] = "192.168.0.1" + #: Subnet to use for all of the destination addresses of the packets being sent. + dst_address_subnet: ClassVar[str] = "192.168.1" + #: ID of the port to modify Rx queues on. + rx_port_num: ClassVar[int] = 0 + #: ID of the port to modify Tx queues on. + tx_port_num: ClassVar[int] = 1 + #: Number of queues to start testpmd with. There will be the same number of Rx and Tx queues. + #: 8 was chosen as a number that is low enough for most NICs to accommodate while also being + #: enough to validate the usage of the queues. + number_of_queues: ClassVar[int] = 8 + #: The number of packets to send while testing. The test calls for well over the ring size - 1 + #: packets in the modification test case and the only options for ring size are 256 or 512, + #: therefore 1024 will be more than enough. + number_of_packets_to_send: ClassVar[int] = 1024 + + def send_packets_with_different_addresses(self, number_of_packets: int) -> None: + """Send a set number of packets each with different dst addresses. + + Different destination addresses are required to ensure that each queue is used. If every + packet had the same address, then they would all be processed by the same queue. Note that + this means the current implementation of this method is limited to only work for up to 254 + queues. A smaller subnet would be required to handle an increased number of queues. + + Args: + number_of_packets: The number of packets to generate and then send using the traffic + generator. + """ + packets_to_send = [ + Ether() + / IP(src=self.src_addr, dst=f"{self.dst_address_subnet}.{(i % 254) + 1}") + / Raw() + for i in range(number_of_packets) + ] + self.send_packets(packets_to_send) + + def port_queue_in_stats( + self, port_id: int, is_rx_queue: bool, queue_id: int, stats: str + ) -> bool: + """Verify if stats for a queue are in the provided output. + + Args: + port_id: ID of the port that the queue resides on. + is_rx_queue: Type of queue to scan for, if :data:`True` then search for an Rx queue, + otherwise search for a Tx queue. + queue_id: ID of the queue. + stats: Testpmd forwarding statistics to scan for the given queue. + + Returns: + If the queue appeared in the forwarding statistics. + """ + type_of_queue = "RX" if is_rx_queue else "TX" + return f"{type_of_queue} Port= {port_id}/Queue={queue_id:2d}" in stats + + @setup_and_teardown_test + def modify_ring_size( + self, + port_id: int, + queues_to_modify: MutableSet[int], + unchanged_queues: MutableSet[int], + testpmd: TestPmdShell, + is_rx_testing: bool, + ) -> None: + """Verify ring size of port queues can be configured at runtime. + + Ring size of queues in `queues_to_modify` are set to 512 unless that is already their + configured size, in which case they are instead set to 256. Queues in `queues_to_modify` + are expected to already be stopped before calling this method. `testpmd` is also expected + to already be started. + + Args: + port_id: Port where the queues reside. + queues_to_modify: IDs of stopped queues to configure in the test. + unchanged_queues: IDs of running, unmodified queues. + testpmd: Running interactive testpmd application. + is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx + queues will be modified. + """ + for queue_id in queues_to_modify: + curr_ring_size = testpmd.show_port_queue_info( + port_id, queue_id, is_rx_testing + ).ring_size + new_ring_size = 256 if curr_ring_size == 512 else 512 + try: + testpmd.set_queue_ring_size( + port_id, queue_id, new_ring_size, is_rx_testing, verify=True + ) + # The testpmd method verifies that the modification worked, so we catch that error + # and just re-raise it as a test case failure + except InteractiveCommandExecutionError: + self.verify( + False, + f"Failed to update the ring size of queue {queue_id} on port " + f"{port_id} at runtime", + ) + + @setup_and_teardown_test + def stop_queues( + self, + port_id: int, + queues_to_modify: MutableSet[int], + unchanged_queues: MutableSet[int], + testpmd: TestPmdShell, + is_rx_testing: bool, + ) -> None: + """Verify stopped queues do not handle traffic and do not block traffic on other queues. + + Queues in `queues_to_modify` are expected to already be stopped before calling this method. + `testpmd` is also expected to already be started. + + Args: + port_id: Port where the queues reside. + queues_to_modify: IDs of stopped queues to configure in the test. + unchanged_queues: IDs of running, unmodified queues. + testpmd: Running interactive testpmd application. + is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx + queues will be modified. + """ + testpmd.start() + self.send_packets_with_different_addresses(self.number_of_packets_to_send) + forwarding_stats = testpmd.stop() + + # Checking that all unmodified queues handled some packets is important because this + # test case checks for the absence of stopped queues to validate that they cannot + # receive traffic. If there are some unchanged queues that also didn't receive traffic, + # it means there could be another reason for the packets not transmitting and, + # therefore, a false positive result. + for unchanged_q_id in unchanged_queues: + self.verify( + self.port_queue_in_stats(port_id, is_rx_testing, unchanged_q_id, forwarding_stats), + f"Queue {unchanged_q_id} failed to receive traffic.", + ) + for stopped_q_id in queues_to_modify: + self.verify( + not self.port_queue_in_stats( + port_id, is_rx_testing, stopped_q_id, forwarding_stats + ), + f"Queue {stopped_q_id} should be stopped but still received traffic.", + ) + + def test_rx_queue_stop(self): + """Run method for stopping queues with flag for Rx testing set to :data:`True`.""" + self.stop_queues(True) + + def test_rx_queue_configuration(self): + """Run method for configuring queues with flag for Rx testing set to :data:`True`.""" + self.modify_ring_size(True) + + def test_tx_queue_stop(self): + """Run method for stopping queues with flag for Rx testing set to :data:`False`.""" + self.stop_queues(False) + + def test_tx_queue_configuration(self): + """Run method for configuring queues with flag for Rx testing set to :data:`False`.""" + self.modify_ring_size(False) -- 2.46.0