xref: /dpdk/dts/tests/TestSuite_dynamic_queue_conf.py (revision 2eef9a80df4a2662f60ae313d779ef1e02227844)
1*2eef9a80SDean Marx# SPDX-License-Identifier: BSD-3-Clause
2*2eef9a80SDean Marx# Copyright(c) 2024 University of New Hampshire
3*2eef9a80SDean Marx
4*2eef9a80SDean Marx"""Dynamic configuration of port queues test suite.
5*2eef9a80SDean Marx
6*2eef9a80SDean MarxThis test suite tests the support of being able to either stop or reconfigure port queues at
7*2eef9a80SDean Marxruntime without stopping the entire device. Previously, to configure a DPDK ethdev, the application
8*2eef9a80SDean Marxfirst specifies how many Tx and Rx queues to include in the ethdev and then application sets up
9*2eef9a80SDean Marxeach queue individually. Only once all the queues have been set up can the application then start
10*2eef9a80SDean Marxthe device, and at this point traffic can flow. If device stops, this halts the flow of traffic on
11*2eef9a80SDean Marxall queues in the ethdev completely. Dynamic queue is a capability present on some NICs that
12*2eef9a80SDean Marxspecifies whether the NIC is able to delay the configuration of queues on its port. This capability
13*2eef9a80SDean Marxallows for the support of stopping and reconfiguring queues on a port at runtime without stopping
14*2eef9a80SDean Marxthe entire device.
15*2eef9a80SDean Marx
16*2eef9a80SDean MarxSupport of this capability is shown by starting the Poll Mode Driver with multiple Rx and Tx queues
17*2eef9a80SDean Marxconfigured and stopping some prior to forwarding packets, then examining whether or not the stopped
18*2eef9a80SDean Marxports and the unmodified ports were able to handle traffic. In addition to just stopping the ports,
19*2eef9a80SDean Marxthe ports must also show that they support configuration changes on their queues at runtime without
20*2eef9a80SDean Marxstopping the entire device. This is shown by changing the ring size of the queues.
21*2eef9a80SDean Marx
22*2eef9a80SDean MarxIf the Poll Mode Driver is able to stop some queues on a port and modify them then handle traffic
23*2eef9a80SDean Marxon the unmodified queues while the others are stopped, then it is the case that the device properly
24*2eef9a80SDean Marxsupports dynamic configuration of its queues.
25*2eef9a80SDean Marx"""
26*2eef9a80SDean Marx
27*2eef9a80SDean Marximport random
28*2eef9a80SDean Marxfrom typing import Callable, ClassVar, MutableSet
29*2eef9a80SDean Marx
30*2eef9a80SDean Marxfrom scapy.layers.inet import IP  # type: ignore[import-untyped]
31*2eef9a80SDean Marxfrom scapy.layers.l2 import Ether  # type: ignore[import-untyped]
32*2eef9a80SDean Marxfrom scapy.packet import Raw  # type: ignore[import-untyped]
33*2eef9a80SDean Marx
34*2eef9a80SDean Marxfrom framework.exception import InteractiveCommandExecutionError
35*2eef9a80SDean Marxfrom framework.params.testpmd import PortTopology, SimpleForwardingModes
36*2eef9a80SDean Marxfrom framework.remote_session.testpmd_shell import TestPmdShell
37*2eef9a80SDean Marxfrom framework.test_suite import TestSuite, func_test
38*2eef9a80SDean Marxfrom framework.testbed_model.capability import NicCapability, requires
39*2eef9a80SDean Marx
40*2eef9a80SDean Marx
41*2eef9a80SDean Marxdef setup_and_teardown_test(
42*2eef9a80SDean Marx    test_meth: Callable[
43*2eef9a80SDean Marx        ["TestDynamicQueueConf", int, MutableSet, MutableSet, TestPmdShell, bool], None
44*2eef9a80SDean Marx    ],
45*2eef9a80SDean Marx) -> Callable[["TestDynamicQueueConf", bool], None]:
46*2eef9a80SDean Marx    """Decorator that provides a setup and teardown for testing methods.
47*2eef9a80SDean Marx
48*2eef9a80SDean Marx    This decorator provides a method that sets up the environment for testing, runs the test
49*2eef9a80SDean Marx    method, and then does a clean-up verification step after the queues are started again. The
50*2eef9a80SDean Marx    decorated method will be provided with all the variables it should need to run testing
51*2eef9a80SDean Marx    including: The ID of the port where the queues for testing reside, disjoint sets of IDs for
52*2eef9a80SDean Marx    queues that are/aren't modified, a testpmd session to run testing with, and a flag that
53*2eef9a80SDean Marx    indicates whether or not testing should be done on Rx or Tx queues.
54*2eef9a80SDean Marx
55*2eef9a80SDean Marx    Args:
56*2eef9a80SDean Marx        test_meth: The decorated method that tests configuration of port queues at runtime.
57*2eef9a80SDean Marx            This method must have the following parameters in order: An int that represents a
58*2eef9a80SDean Marx            port ID, a set of queues for testing, a set of unmodified queues, a testpmd
59*2eef9a80SDean Marx            interactive shell, and a boolean that, when :data:`True`, does Rx testing,
60*2eef9a80SDean Marx            otherwise does Tx testing. This method must also be a member of the
61*2eef9a80SDean Marx            :class:`TestDynamicQueueConf` class.
62*2eef9a80SDean Marx
63*2eef9a80SDean Marx    Returns:
64*2eef9a80SDean Marx        A method that sets up the environment, runs the decorated method, then re-enables all
65*2eef9a80SDean Marx        queues and validates they can still handle traffic.
66*2eef9a80SDean Marx    """
67*2eef9a80SDean Marx
68*2eef9a80SDean Marx    def wrap(self: "TestDynamicQueueConf", is_rx_testing: bool) -> None:
69*2eef9a80SDean Marx        """Setup environment, run test function, then cleanup.
70*2eef9a80SDean Marx
71*2eef9a80SDean Marx        Start a testpmd shell and stop ports for testing, then call the decorated function that
72*2eef9a80SDean Marx        performs the testing. After the decorated function is finished running its testing,
73*2eef9a80SDean Marx        start the stopped queues and send packets to validate that these ports can properly
74*2eef9a80SDean Marx        handle traffic after being started again.
75*2eef9a80SDean Marx
76*2eef9a80SDean Marx        Args:
77*2eef9a80SDean Marx            self: Instance of :class:`TestDynamicQueueConf` `test_meth` belongs to.
78*2eef9a80SDean Marx            is_rx_testing: If :data:`True` then Rx queues will be the ones modified throughout
79*2eef9a80SDean Marx                the test, otherwise Tx queues will be modified.
80*2eef9a80SDean Marx        """
81*2eef9a80SDean Marx        port_id = self.rx_port_num if is_rx_testing else self.tx_port_num
82*2eef9a80SDean Marx        queues_to_config: set[int] = set()
83*2eef9a80SDean Marx        while len(queues_to_config) < self.num_ports_to_modify:
84*2eef9a80SDean Marx            queues_to_config.add(random.randint(1, self.number_of_queues - 1))
85*2eef9a80SDean Marx        unchanged_queues = set(range(self.number_of_queues)) - queues_to_config
86*2eef9a80SDean Marx        with TestPmdShell(
87*2eef9a80SDean Marx            self.sut_node,
88*2eef9a80SDean Marx            port_topology=PortTopology.chained,
89*2eef9a80SDean Marx            rx_queues=self.number_of_queues,
90*2eef9a80SDean Marx            tx_queues=self.number_of_queues,
91*2eef9a80SDean Marx        ) as testpmd:
92*2eef9a80SDean Marx            for q in queues_to_config:
93*2eef9a80SDean Marx                testpmd.stop_port_queue(port_id, q, is_rx_testing)
94*2eef9a80SDean Marx            testpmd.set_forward_mode(SimpleForwardingModes.mac)
95*2eef9a80SDean Marx
96*2eef9a80SDean Marx            test_meth(self, port_id, queues_to_config, unchanged_queues, testpmd, is_rx_testing)
97*2eef9a80SDean Marx
98*2eef9a80SDean Marx            for queue_id in queues_to_config:
99*2eef9a80SDean Marx                testpmd.start_port_queue(port_id, queue_id, is_rx_testing)
100*2eef9a80SDean Marx
101*2eef9a80SDean Marx            testpmd.start()
102*2eef9a80SDean Marx            self.send_packets_with_different_addresses(self.number_of_packets_to_send)
103*2eef9a80SDean Marx            forwarding_stats = testpmd.stop()
104*2eef9a80SDean Marx            for queue_id in queues_to_config:
105*2eef9a80SDean Marx                self.verify(
106*2eef9a80SDean Marx                    self.port_queue_in_stats(port_id, is_rx_testing, queue_id, forwarding_stats),
107*2eef9a80SDean Marx                    f"Modified queue {queue_id} on port {port_id} failed to receive traffic after"
108*2eef9a80SDean Marx                    "being started again.",
109*2eef9a80SDean Marx                )
110*2eef9a80SDean Marx
111*2eef9a80SDean Marx    return wrap
112*2eef9a80SDean Marx
113*2eef9a80SDean Marx
114*2eef9a80SDean Marxclass TestDynamicQueueConf(TestSuite):
115*2eef9a80SDean Marx    """DPDK dynamic queue configuration test suite.
116*2eef9a80SDean Marx
117*2eef9a80SDean Marx    Testing for the support of dynamic queue configuration is done by splitting testing by the type
118*2eef9a80SDean Marx    of queue (either Rx or Tx) and the type of testing (testing for stopping a port at runtime vs
119*2eef9a80SDean Marx    testing configuration changes at runtime). Testing is done by first stopping a finite number of
120*2eef9a80SDean Marx    port queues (3 is sufficient) and either modifying the configuration or sending packets to
121*2eef9a80SDean Marx    verify that the unmodified queues can handle traffic. Specifically, the following cases are
122*2eef9a80SDean Marx    tested:
123*2eef9a80SDean Marx
124*2eef9a80SDean Marx    1. The application should be able to start the device with only some of the
125*2eef9a80SDean Marx       queues set up.
126*2eef9a80SDean Marx    2. The application should be able to reconfigure existing queues at runtime
127*2eef9a80SDean Marx       without calling dev_stop().
128*2eef9a80SDean Marx    """
129*2eef9a80SDean Marx
130*2eef9a80SDean Marx    #:
131*2eef9a80SDean Marx    num_ports_to_modify: ClassVar[int] = 3
132*2eef9a80SDean Marx    #: Source IP address to use when sending packets.
133*2eef9a80SDean Marx    src_addr: ClassVar[str] = "192.168.0.1"
134*2eef9a80SDean Marx    #: Subnet to use for all of the destination addresses of the packets being sent.
135*2eef9a80SDean Marx    dst_address_subnet: ClassVar[str] = "192.168.1"
136*2eef9a80SDean Marx    #: ID of the port to modify Rx queues on.
137*2eef9a80SDean Marx    rx_port_num: ClassVar[int] = 0
138*2eef9a80SDean Marx    #: ID of the port to modify Tx queues on.
139*2eef9a80SDean Marx    tx_port_num: ClassVar[int] = 1
140*2eef9a80SDean Marx    #: Number of queues to start testpmd with. There will be the same number of Rx and Tx queues.
141*2eef9a80SDean Marx    #: 8 was chosen as a number that is low enough for most NICs to accommodate while also being
142*2eef9a80SDean Marx    #: enough to validate the usage of the queues.
143*2eef9a80SDean Marx    number_of_queues: ClassVar[int] = 8
144*2eef9a80SDean Marx    #: The number of packets to send while testing. The test calls for well over the ring size - 1
145*2eef9a80SDean Marx    #: packets in the modification test case and the only options for ring size are 256 or 512,
146*2eef9a80SDean Marx    #: therefore 1024 will be more than enough.
147*2eef9a80SDean Marx    number_of_packets_to_send: ClassVar[int] = 1024
148*2eef9a80SDean Marx
149*2eef9a80SDean Marx    def send_packets_with_different_addresses(self, number_of_packets: int) -> None:
150*2eef9a80SDean Marx        """Send a set number of packets each with different dst addresses.
151*2eef9a80SDean Marx
152*2eef9a80SDean Marx        Different destination addresses are required to ensure that each queue is used. If every
153*2eef9a80SDean Marx        packet had the same address, then they would all be processed by the same queue. Note that
154*2eef9a80SDean Marx        this means the current implementation of this method is limited to only work for up to 254
155*2eef9a80SDean Marx        queues. A smaller subnet would be required to handle an increased number of queues.
156*2eef9a80SDean Marx
157*2eef9a80SDean Marx        Args:
158*2eef9a80SDean Marx            number_of_packets: The number of packets to generate and then send using the traffic
159*2eef9a80SDean Marx                generator.
160*2eef9a80SDean Marx        """
161*2eef9a80SDean Marx        packets_to_send = [
162*2eef9a80SDean Marx            Ether()
163*2eef9a80SDean Marx            / IP(src=self.src_addr, dst=f"{self.dst_address_subnet}.{(i % 254) + 1}")
164*2eef9a80SDean Marx            / Raw()
165*2eef9a80SDean Marx            for i in range(number_of_packets)
166*2eef9a80SDean Marx        ]
167*2eef9a80SDean Marx        self.send_packets(packets_to_send)
168*2eef9a80SDean Marx
169*2eef9a80SDean Marx    def port_queue_in_stats(
170*2eef9a80SDean Marx        self, port_id: int, is_rx_queue: bool, queue_id: int, stats: str
171*2eef9a80SDean Marx    ) -> bool:
172*2eef9a80SDean Marx        """Verify if stats for a queue are in the provided output.
173*2eef9a80SDean Marx
174*2eef9a80SDean Marx        Args:
175*2eef9a80SDean Marx            port_id: ID of the port that the queue resides on.
176*2eef9a80SDean Marx            is_rx_queue: Type of queue to scan for, if :data:`True` then search for an Rx queue,
177*2eef9a80SDean Marx                otherwise search for a Tx queue.
178*2eef9a80SDean Marx            queue_id: ID of the queue.
179*2eef9a80SDean Marx            stats: Testpmd forwarding statistics to scan for the given queue.
180*2eef9a80SDean Marx
181*2eef9a80SDean Marx        Returns:
182*2eef9a80SDean Marx            If the queue appeared in the forwarding statistics.
183*2eef9a80SDean Marx        """
184*2eef9a80SDean Marx        type_of_queue = "RX" if is_rx_queue else "TX"
185*2eef9a80SDean Marx        return f"{type_of_queue} Port= {port_id}/Queue={queue_id:2d}" in stats
186*2eef9a80SDean Marx
187*2eef9a80SDean Marx    @setup_and_teardown_test
188*2eef9a80SDean Marx    def modify_ring_size(
189*2eef9a80SDean Marx        self,
190*2eef9a80SDean Marx        port_id: int,
191*2eef9a80SDean Marx        queues_to_modify: MutableSet[int],
192*2eef9a80SDean Marx        unchanged_queues: MutableSet[int],
193*2eef9a80SDean Marx        testpmd: TestPmdShell,
194*2eef9a80SDean Marx        is_rx_testing: bool,
195*2eef9a80SDean Marx    ) -> None:
196*2eef9a80SDean Marx        """Verify ring size of port queues can be configured at runtime.
197*2eef9a80SDean Marx
198*2eef9a80SDean Marx        Ring size of queues in `queues_to_modify` are set to 512 unless that is already their
199*2eef9a80SDean Marx        configured size, in which case they are instead set to 256. Queues in `queues_to_modify`
200*2eef9a80SDean Marx        are expected to already be stopped before calling this method. `testpmd` is also expected
201*2eef9a80SDean Marx        to already be started.
202*2eef9a80SDean Marx
203*2eef9a80SDean Marx        Args:
204*2eef9a80SDean Marx            port_id: Port where the queues reside.
205*2eef9a80SDean Marx            queues_to_modify: IDs of stopped queues to configure in the test.
206*2eef9a80SDean Marx            unchanged_queues: IDs of running, unmodified queues.
207*2eef9a80SDean Marx            testpmd: Running interactive testpmd application.
208*2eef9a80SDean Marx            is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
209*2eef9a80SDean Marx                queues will be modified.
210*2eef9a80SDean Marx        """
211*2eef9a80SDean Marx        for queue_id in queues_to_modify:
212*2eef9a80SDean Marx            curr_ring_size = testpmd.get_queue_ring_size(port_id, queue_id, is_rx_testing)
213*2eef9a80SDean Marx            new_ring_size = 256 if curr_ring_size == 512 else 512
214*2eef9a80SDean Marx            try:
215*2eef9a80SDean Marx                testpmd.set_queue_ring_size(
216*2eef9a80SDean Marx                    port_id, queue_id, new_ring_size, is_rx_testing, verify=True
217*2eef9a80SDean Marx                )
218*2eef9a80SDean Marx            # The testpmd method verifies that the modification worked, so we catch that error
219*2eef9a80SDean Marx            # and just re-raise it as a test case failure
220*2eef9a80SDean Marx            except InteractiveCommandExecutionError:
221*2eef9a80SDean Marx                self.verify(
222*2eef9a80SDean Marx                    False,
223*2eef9a80SDean Marx                    f"Failed to update the ring size of queue {queue_id} on port "
224*2eef9a80SDean Marx                    f"{port_id} at runtime",
225*2eef9a80SDean Marx                )
226*2eef9a80SDean Marx
227*2eef9a80SDean Marx    @setup_and_teardown_test
228*2eef9a80SDean Marx    def stop_queues(
229*2eef9a80SDean Marx        self,
230*2eef9a80SDean Marx        port_id: int,
231*2eef9a80SDean Marx        queues_to_modify: MutableSet[int],
232*2eef9a80SDean Marx        unchanged_queues: MutableSet[int],
233*2eef9a80SDean Marx        testpmd: TestPmdShell,
234*2eef9a80SDean Marx        is_rx_testing: bool,
235*2eef9a80SDean Marx    ) -> None:
236*2eef9a80SDean Marx        """Verify stopped queues do not handle traffic and do not block traffic on other queues.
237*2eef9a80SDean Marx
238*2eef9a80SDean Marx        Queues in `queues_to_modify` are expected to already be stopped before calling this method.
239*2eef9a80SDean Marx        `testpmd` is also expected to already be started.
240*2eef9a80SDean Marx
241*2eef9a80SDean Marx        Args:
242*2eef9a80SDean Marx            port_id: Port where the queues reside.
243*2eef9a80SDean Marx            queues_to_modify: IDs of stopped queues to configure in the test.
244*2eef9a80SDean Marx            unchanged_queues: IDs of running, unmodified queues.
245*2eef9a80SDean Marx            testpmd: Running interactive testpmd application.
246*2eef9a80SDean Marx            is_rx_testing: If :data:`True` Rx queues will be modified in the test, otherwise Tx
247*2eef9a80SDean Marx                queues will be modified.
248*2eef9a80SDean Marx        """
249*2eef9a80SDean Marx        testpmd.start()
250*2eef9a80SDean Marx        self.send_packets_with_different_addresses(self.number_of_packets_to_send)
251*2eef9a80SDean Marx        forwarding_stats = testpmd.stop()
252*2eef9a80SDean Marx
253*2eef9a80SDean Marx        # Checking that all unmodified queues handled some packets is important because this
254*2eef9a80SDean Marx        # test case checks for the absence of stopped queues to validate that they cannot
255*2eef9a80SDean Marx        # receive traffic. If there are some unchanged queues that also didn't receive traffic,
256*2eef9a80SDean Marx        # it means there could be another reason for the packets not transmitting and,
257*2eef9a80SDean Marx        # therefore, a false positive result.
258*2eef9a80SDean Marx        for unchanged_q_id in unchanged_queues:
259*2eef9a80SDean Marx            self.verify(
260*2eef9a80SDean Marx                self.port_queue_in_stats(port_id, is_rx_testing, unchanged_q_id, forwarding_stats),
261*2eef9a80SDean Marx                f"Queue {unchanged_q_id} failed to receive traffic.",
262*2eef9a80SDean Marx            )
263*2eef9a80SDean Marx        for stopped_q_id in queues_to_modify:
264*2eef9a80SDean Marx            self.verify(
265*2eef9a80SDean Marx                not self.port_queue_in_stats(
266*2eef9a80SDean Marx                    port_id, is_rx_testing, stopped_q_id, forwarding_stats
267*2eef9a80SDean Marx                ),
268*2eef9a80SDean Marx                f"Queue {stopped_q_id} should be stopped but still received traffic.",
269*2eef9a80SDean Marx            )
270*2eef9a80SDean Marx
271*2eef9a80SDean Marx    @requires(NicCapability.RUNTIME_RX_QUEUE_SETUP)
272*2eef9a80SDean Marx    @func_test
273*2eef9a80SDean Marx    def test_rx_queue_stop(self):
274*2eef9a80SDean Marx        """Run method for stopping queues with flag for Rx testing set to :data:`True`."""
275*2eef9a80SDean Marx        self.stop_queues(True)
276*2eef9a80SDean Marx
277*2eef9a80SDean Marx    @requires(NicCapability.RUNTIME_RX_QUEUE_SETUP)
278*2eef9a80SDean Marx    @func_test
279*2eef9a80SDean Marx    def test_rx_queue_configuration(self):
280*2eef9a80SDean Marx        """Run method for configuring queues with flag for Rx testing set to :data:`True`."""
281*2eef9a80SDean Marx        self.modify_ring_size(True)
282*2eef9a80SDean Marx
283*2eef9a80SDean Marx    @requires(NicCapability.RUNTIME_TX_QUEUE_SETUP)
284*2eef9a80SDean Marx    @func_test
285*2eef9a80SDean Marx    def test_tx_queue_stop(self):
286*2eef9a80SDean Marx        """Run method for stopping queues with flag for Rx testing set to :data:`False`."""
287*2eef9a80SDean Marx        self.stop_queues(False)
288*2eef9a80SDean Marx
289*2eef9a80SDean Marx    @requires(NicCapability.RUNTIME_TX_QUEUE_SETUP)
290*2eef9a80SDean Marx    @func_test
291*2eef9a80SDean Marx    def test_tx_queue_configuration(self):
292*2eef9a80SDean Marx        """Run method for configuring queues with flag for Rx testing set to :data:`False`."""
293*2eef9a80SDean Marx        self.modify_ring_size(False)
294