Add an API for the dpdk-test-eventdev application, run as a
blocking app with the topology's Ethernet ports as event
producers. Only the event_sw virtual device is currently supported, with
its scheduler on a dedicated service core. Eventdev testing existed on old-dts 
but not new-dts. This API was written so the eventdev performance testsuite 
from old dts can be ported over.

Signed-off-by: Dillon Ladd <[email protected]>
---
 dts/api/eventdev/__init__.py | 95 ++++++++++++++++++++++++++++++++++++
 dts/api/eventdev/config.py   | 41 ++++++++++++++++
 dts/api/params/types.py      | 11 +++++
 3 files changed, 147 insertions(+)
 create mode 100644 dts/api/eventdev/__init__.py
 create mode 100644 dts/api/eventdev/config.py

diff --git a/dts/api/eventdev/__init__.py b/dts/api/eventdev/__init__.py
new file mode 100644
index 0000000000..202ddcb77c
--- /dev/null
+++ b/dts/api/eventdev/__init__.py
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2026 University of New Hampshire
+
+"""dpdk-test-eventdev application.
+
+Typical usage example in a TestSuite::
+
+    with Eventdev(num_workers=2, stlist="a"):
+        stats = assess_performance_by_packet(packet=packet, duration=5)
+"""
+
+from typing import TYPE_CHECKING
+
+from typing_extensions import Self, Unpack
+
+from api.context import get_ctx
+from api.eventdev.config import EventdevParams
+from api.exception import (
+    InteractiveCommandExecutionError,
+    SkippedTestException,
+)
+from api.params import Params
+from api.testbed_model.cpu import LogicalCoreCount, LogicalCoreList
+from api.testbed_model.virtual_device import VirtualDevice
+from framework.remote_session.blocking_app import BlockingApp
+from framework.remote_session.dpdk_shell import compute_eal_params
+
+if TYPE_CHECKING:
+    from api.params.types import EventdevParamsDict
+
+
+class Eventdev(BlockingApp[EventdevParams]):
+    """Blocking dpdk-test-eventdev application.
+
+    Attributes:
+        _app_params: app parameters to pass to dpdk-test-eventdev application
+    """
+
+    _app_params: EventdevParams
+
+    def __init__(self, num_workers: int = 1, **app_params: 
Unpack["EventdevParamsDict"]) -> None:
+        """Initialize the eventdev application.
+
+        Args:
+            num_workers: number of worker cores to run the application with.
+            app_params: The application parameters as keyword arguments.
+
+        Raises:
+            ValueError: if the build environment is `None`
+            SkippedTestException: if there are not enough cores on one socket
+        """
+        ctx = get_ctx()
+        dpdk = ctx.dpdk.build
+        if dpdk is None:
+            raise ValueError("No DPDK build environment exists.")
+        self._app_path = dpdk.get_app("test-eventdev")
+
+        # One main lcore, one service lcore for the software event scheduler, 
and the workers.
+        num_lcores = num_workers + 2
+        try:
+            lcores = ctx.dpdk.filter_lcores(
+                LogicalCoreCount(cores_per_socket=num_lcores), 
ctx.local.ascending_cores
+            )
+        except ValueError as e:
+            raise SkippedTestException(
+                f"dpdk-test-eventdev needs {num_lcores} cores on one socket: 
{e}"
+            )
+
+        main, service, *workers = lcores
+        app_params.setdefault("other_eal_param", Params.from_str(f"-S 
{service.lcore}"))
+        app_params.setdefault("vdevs", [VirtualDevice("event_sw0")])
+        app_params.setdefault("lcore_list", LogicalCoreList(lcores))
+        app_params.setdefault("wlcores", str(LogicalCoreList(workers)))
+        app_params.setdefault("prod_type_ethdev", True)
+        params = EventdevParams(**app_params)
+        compute_eal_params(params)
+        self._num_eth_ports = len(params.allowed_ports or [])
+        super().__init__(ctx.sut_node, self._app_path, app_params=params, 
privileged=True)
+
+    def __enter__(self) -> Self:
+        """Start the application and wait until ready.
+
+        Raises:
+            InteractiveCommandExecutionError: If there is an error starting 
the application.
+
+        Returns:
+            The started application.
+        """
+        last_port = self._num_eth_ports - 1
+        try:
+            return self.wait_until_ready(
+                f"Port[{last_port}] using Tx adapter[{last_port}] 
Configured\x1b[0m"
+            )
+        except InteractiveCommandExecutionError as e:
+            raise e
diff --git a/dts/api/eventdev/config.py b/dts/api/eventdev/config.py
new file mode 100644
index 0000000000..acdbc88743
--- /dev/null
+++ b/dts/api/eventdev/config.py
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2026 University of New Hampshire
+
+"""Module containing types and parameter classes for the dpdk-test-eventdev 
application."""
+
+from dataclasses import dataclass, field
+
+from api.params import Params, Switch
+from api.params.eal import EalParams
+
+
+@dataclass(slots=True, kw_only=True)
+class EventdevParams(EalParams):
+    """Parameters for the dpdk-test-eventdev application.
+
+    Attributes:
+        test: Name of the test to run.
+        wlcores: List of worker lcores.
+        stlist: List of scheduled types of the pipeline stages.
+        pool_sz: Size of the mbuf mempool.
+        nb_eth_queues: Number of Ethernet Rx queues.
+        prod_type_ethdev: Use an Ethernet device as the event producer.
+    """
+
+    test: str = field(default="pipeline_queue", metadata=Params.long("test"))
+    wlcores: str | None = field(default=None, metadata=Params.long("wlcores"))
+    stlist: str | None = field(default=None, metadata=Params.long("stlist"))
+    pool_sz: int | None = field(default=None, metadata=Params.long("pool_sz"))
+    nb_eth_queues: int | None = field(default=None, 
metadata=Params.long("nb_eth_queues"))
+    prod_type_ethdev: Switch = field(default=None, 
metadata=Params.long("prod_type_ethdev"))
+
+    def __str__(self) -> str:
+        """Convert the parameters to a command line string.
+
+        Returns:
+            The command line arguments, with underscored option names as the 
application expects.
+        """
+        params = Params.__str__(self)
+        for name in ("pool_sz", "nb_eth_queues", "prod_type_ethdev"):
+            params = params.replace(f"--{name.replace('_', '-')}", f"--{name}")
+        return params
diff --git a/dts/api/params/types.py b/dts/api/params/types.py
index c4b718eebe..1ba7e9bf52 100644
--- a/dts/api/params/types.py
+++ b/dts/api/params/types.py
@@ -203,3 +203,14 @@ class TestPmdParamsDict(EalParamsDict, total=False):
     mempool_allocation_mode: SimpleMempoolAllocationMode | 
AnonMempoolAllocationMode | None
     record_core_cycles: Switch
     record_burst_status: Switch
+
+
+class EventdevParamsDict(EalParamsDict, total=False):
+    """:class:`TypedDict` equivalent of 
:class:`~.eventdev.config.EventdevParams`."""
+
+    test: str
+    wlcores: str | None
+    stlist: str | None
+    pool_sz: int | None
+    nb_eth_queues: int | None
+    prod_type_ethdev: Switch
-- 
2.53.0

Reply via email to