From: Pravin Pathak <[email protected]>

Add driver self-test coverage and documentation for the Software
Load Balancer PMD.

The self-test verifies parallel queues, atomic queue flow ordering,
ordered queues with forward sequence restoration, single-link queues,
and device stop/restart event draining. Register the self-test in
app/test as a fast test.

Add PMD user guide, feature matrix table, release notes entry for
DPDK 26.11, and update MAINTAINERS.

Signed-off-by: Pravin Pathak <[email protected]>
---
 MAINTAINERS                           |   2 +
 app/test/test_eventdev.c              |   7 +
 doc/guides/eventdevs/features/slb.ini |  26 ++
 doc/guides/eventdevs/index.rst        |   1 +
 doc/guides/eventdevs/slb.rst          |  68 ++++
 drivers/event/slb/meson.build         |   1 +
 drivers/event/slb/slb_evdev.c         |   1 +
 drivers/event/slb/slb_evdev.h         |   4 +
 drivers/event/slb/slb_selftest.c      | 522 ++++++++++++++++++++++++++
 9 files changed, 632 insertions(+)
 create mode 100644 doc/guides/eventdevs/features/slb.ini
 create mode 100644 doc/guides/eventdevs/slb.rst
 create mode 100644 drivers/event/slb/slb_selftest.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 89ea53695a..bc46e3e8b8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1536,6 +1536,8 @@ F: doc/guides/eventdevs/dsw.rst
 Software Load Balancer (SLB) Eventdev PMD
 M: Pravin Pathak <[email protected]>
 F: drivers/event/slb/
+F: doc/guides/eventdevs/slb.rst
+F: doc/guides/eventdevs/features/slb.ini
 
 Software OPDL Eventdev PMD
 M: Liang Ma <[email protected]>
diff --git a/app/test/test_eventdev.c b/app/test/test_eventdev.c
index 1241da38cb..58ec440674 100644
--- a/app/test/test_eventdev.c
+++ b/app/test/test_eventdev.c
@@ -1527,12 +1527,19 @@ test_eventdev_selftest_cn20k(void)
        return test_eventdev_selftest_impl("event_cn20k", "");
 }
 
+static int
+test_eventdev_selftest_slb(void)
+{
+       return test_eventdev_selftest_impl("event_slb", "");
+}
+
 #endif /* !RTE_EXEC_ENV_WINDOWS */
 
 REGISTER_FAST_TEST(eventdev_common_autotest, NOHUGE_OK, ASAN_OK, 
test_eventdev_common);
 
 #ifndef RTE_EXEC_ENV_WINDOWS
 REGISTER_FAST_TEST(eventdev_selftest_sw, NOHUGE_OK, ASAN_OK, 
test_eventdev_selftest_sw);
+REGISTER_FAST_TEST(eventdev_selftest_slb, NOHUGE_OK, ASAN_OK, 
test_eventdev_selftest_slb);
 REGISTER_DRIVER_TEST(eventdev_selftest_octeontx, 
test_eventdev_selftest_octeontx);
 REGISTER_DRIVER_TEST(eventdev_selftest_dpaa2, test_eventdev_selftest_dpaa2);
 REGISTER_DRIVER_TEST(eventdev_selftest_dlb2, test_eventdev_selftest_dlb2);
diff --git a/doc/guides/eventdevs/features/slb.ini 
b/doc/guides/eventdevs/features/slb.ini
new file mode 100644
index 0000000000..0364840bc9
--- /dev/null
+++ b/doc/guides/eventdevs/features/slb.ini
@@ -0,0 +1,26 @@
+;
+; Supported features of the 'software load balancer' (event_slb) eventdev 
driver.
+;
+; Refer to default.ini for the full list of available PMD features.
+;
+[Scheduling Features]
+atomic_scheduling          = Y
+ordered_scheduling         = Y
+parallel_scheduling        = Y
+distributed_sched          = Y
+burst_mode                 = Y
+implicit_release_disable   = Y
+nonseq_mode                = Y
+runtime_port_link          = Y
+multiple_queue_port        = Y
+carry_flow_id              = Y
+maintenance_free           = Y
+independent_enq            = Y
+
+[Eth Rx adapter Features]
+
+[Eth Tx adapter Features]
+
+[Crypto adapter Features]
+
+[Timer adapter Features]
diff --git a/doc/guides/eventdevs/index.rst b/doc/guides/eventdevs/index.rst
index 69a5ebf812..fdaf846503 100644
--- a/doc/guides/eventdevs/index.rst
+++ b/doc/guides/eventdevs/index.rst
@@ -20,3 +20,4 @@ application through the eventdev API.
     sw
     octeontx
     opdl
+    slb
diff --git a/doc/guides/eventdevs/slb.rst b/doc/guides/eventdevs/slb.rst
new file mode 100644
index 0000000000..d319b94920
--- /dev/null
+++ b/doc/guides/eventdevs/slb.rst
@@ -0,0 +1,68 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2026 Pravin Pathak <[email protected]>
+
+Software Load Balancer (SLB) Eventdev Driver
+============================================
+
+The Software Load Balancer (SLB) event device is a poll-mode eventdev driver
+which distributes events among multiple worker ports and cores without 
requiring
+dedicated scheduling service cores or hardware coprocessors.
+
+Features
+--------
+
+Queues
+~~~~~~
+
+* **Parallel**: Lock-free multi-producer, multi-consumer ring-based queue 
allowing
+  concurrent dequeues across all serving worker ports.
+* **Atomic**: Flow-pinned scheduling guaranteeing strict per-flow packet 
ordering.
+  Includes dynamic load tracking and automatic flow migration to rebalance 
overloaded
+  ports while preserving in-flight sequence integrity.
+* **Ordered**: Ticket-based sequence tracking and reorder buffer providing 
end-to-end
+  packet order restoration across parallel processing workers.
+* **Single-Link**: Dedicated direct queue mapping from a producer port to a 
single
+  consumer port using optimized single-consumer rings.
+
+Ports & Scheduling Capabilities
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* **Distributed Scheduling**: No dedicated service core required; scheduling 
work is
+  performed inline during enqueue and dequeue operations.
+* **Prefix Sum Burst Enqueue**: Interleaved multi-queue bursts are grouped and 
scattered
+  via prefix-sum offsets, dispatching single batched bursts to queue rings to 
eliminate
+  atomic Compare-And-Swap contention on multi-producer rings.
+* **Dynamic Flow Migration**: Atomic queues monitor port dequeue loads and 
migrate
+  eligible victim flows from overloaded ports to underloaded ports with 
cooling cycles.
+* **Credit Pool Management**: Centralized event credit pool with per-port 
credit caching
+  to enforce device-wide in-flight event limits (``max_num_events``).
+* **Runtime Port Linking**: Supports dynamic linking and unlinking of ports 
and queues
+  both when stopped and at runtime.
+* **Maintenance Free**: Operates without requiring periodic maintenance calls
+  (``RTE_EVENT_DEV_CAP_MAINTENANCE_FREE``).
+
+Configuration and Options
+-------------------------
+
+The SLB event device is a virtual device (vdev) and can be initialized from the
+application code or from the EAL command line:
+
+* From application code:
+
+  .. code-block:: c
+
+      rte_vdev_init("event_slb0", NULL);
+
+* From EAL command-line options:
+
+  .. code-block:: console
+
+      ./your_dpdk_application --vdev="event_slb0"
+
+Limitations
+-----------
+
+* **Flow Hash Table Size**: Atomic queues use an internal flow mapping table
+  dimensioned to 2048 concurrent active flows per queue (``SLB_ATOMIC_FLOWS``).
+* **Maximum Ports and Queues**: Configured for up to 64 ports 
(``SLB_MAX_PORTS``)
+  and 64 queues (``SLB_MAX_QUEUES``).
diff --git a/drivers/event/slb/meson.build b/drivers/event/slb/meson.build
index d698a58f16..6caf48720f 100644
--- a/drivers/event/slb/meson.build
+++ b/drivers/event/slb/meson.build
@@ -11,6 +11,7 @@ sources = files(
         'slb_evdev.c',
         'slb_event.c',
         'slb_xstats.c',
+        'slb_selftest.c',
 )
 
 deps += ['bus_vdev', 'ring']
diff --git a/drivers/event/slb/slb_evdev.c b/drivers/event/slb/slb_evdev.c
index 04a2aba165..c86b934b03 100644
--- a/drivers/event/slb/slb_evdev.c
+++ b/drivers/event/slb/slb_evdev.c
@@ -758,6 +758,7 @@ static struct eventdev_ops slb_evdev_ops = {
        .xstats_get = slb_xstats_get,
        .xstats_get_names = slb_xstats_get_names,
        .xstats_get_by_name = slb_xstats_get_by_name,
+       .dev_selftest = test_slb_eventdev,
 };
 
 static int
diff --git a/drivers/event/slb/slb_evdev.h b/drivers/event/slb/slb_evdev.h
index 17f3154efa..f727a1b83c 100644
--- a/drivers/event/slb/slb_evdev.h
+++ b/drivers/event/slb/slb_evdev.h
@@ -242,6 +242,8 @@ int slb_xstats_get(const struct rte_eventdev *dev,
 uint64_t slb_xstats_get_by_name(const struct rte_eventdev *dev,
                                const char *name, uint64_t *id);
 
+int test_slb_eventdev(void);
+
 static inline struct slb_evdev *
 slb_pmd_priv(const struct rte_eventdev *eventdev)
 {
@@ -250,6 +252,8 @@ slb_pmd_priv(const struct rte_eventdev *eventdev)
 
 extern int event_slb_logtype;
 #define RTE_LOGTYPE_EVENT_SLB event_slb_logtype
+#define SLB_LOG_LINE(level, ...) \
+       RTE_LOG_LINE(level, EVENT_SLB, __VA_ARGS__)
 #define SLB_LOG_DP_LINE(level, fmt, ...)                               \
        RTE_LOG_DP_LINE(level, EVENT_SLB, "%s() line %u: " fmt,         \
                   __func__, __LINE__, ## __VA_ARGS__)
diff --git a/drivers/event/slb/slb_selftest.c b/drivers/event/slb/slb_selftest.c
new file mode 100644
index 0000000000..4a072b811b
--- /dev/null
+++ b/drivers/event/slb/slb_selftest.c
@@ -0,0 +1,522 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Pravin Pathak <[email protected]>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <rte_bus_vdev.h>
+#include <rte_common.h>
+#include <rte_cycles.h>
+#include <rte_eventdev.h>
+
+#include "slb_evdev.h"
+
+#define NUM_PORTS 4
+#define NUM_QUEUES 4
+#define PORT_PROD 0
+#define PORT_W1   1
+#define PORT_W2   2
+#define PORT_CONS 3
+
+#define Q_PARALLEL 0
+#define Q_ATOMIC   1
+#define Q_ORDERED  2
+#define Q_SINGLE   3
+
+#define NUM_FLOWS 4
+#define EVENTS_PER_FLOW 16
+#define TOTAL_ATOMIC_EVENTS (NUM_FLOWS * EVENTS_PER_FLOW)
+
+static int g_dev_id = -1;
+
+static int
+init_selftest_device(void)
+{
+       struct rte_event_dev_config dev_conf = {
+               .dequeue_timeout_ns = 0,
+               .nb_event_queues = NUM_QUEUES,
+               .nb_event_ports = NUM_PORTS,
+               .nb_events_limit = 4096,
+               .nb_event_queue_flows = 1024,
+               .nb_event_port_dequeue_depth = 64,
+               .nb_event_port_enqueue_depth = 64,
+       };
+       struct rte_event_queue_conf q_conf;
+       struct rte_event_port_conf p_conf = {
+               .dequeue_depth = 64,
+               .enqueue_depth = 64,
+               .new_event_threshold = 2048,
+               .event_port_cfg = 0
+       };
+       struct rte_event_dev_info info;
+       uint8_t ldb_queues[] = { Q_PARALLEL, Q_ATOMIC, Q_ORDERED };
+       uint8_t single_q = Q_SINGLE;
+       int ret;
+       int p;
+
+       g_dev_id = rte_event_dev_get_dev_id(SLB_PMD_NAME);
+       if (g_dev_id < 0) {
+               ret = rte_vdev_init("event_slb_selftest", NULL);
+               if (ret < 0) {
+                       SLB_LOG_LINE(ERR, "Failed to init vdev 
event_slb_selftest");
+                       return -1;
+               }
+               g_dev_id = rte_event_dev_get_dev_id("event_slb_selftest");
+               if (g_dev_id < 0) {
+                       SLB_LOG_LINE(ERR, "Cannot find initialized vdev");
+                       return -1;
+               }
+       }
+
+       ret = rte_event_dev_info_get(g_dev_id, &info);
+       if (ret < 0) {
+               SLB_LOG_LINE(ERR, "Failed to get dev info");
+               return -1;
+       }
+
+       ret = rte_event_dev_configure(g_dev_id, &dev_conf);
+       if (ret < 0) {
+               SLB_LOG_LINE(ERR, "Failed to configure device, ret=%d", ret);
+               return -1;
+       }
+
+       /* Setup Queues */
+       memset(&q_conf, 0, sizeof(q_conf));
+       q_conf.nb_atomic_flows = 1024;
+       q_conf.nb_atomic_order_sequences = 1024;
+       q_conf.priority = RTE_EVENT_DEV_PRIORITY_NORMAL;
+
+       /* Q0: Parallel */
+       q_conf.schedule_type = RTE_SCHED_TYPE_PARALLEL;
+       q_conf.event_queue_cfg = 0;
+       ret = rte_event_queue_setup(g_dev_id, Q_PARALLEL, &q_conf);
+       if (ret < 0)
+               return -1;
+
+       /* Q1: Atomic */
+       q_conf.schedule_type = RTE_SCHED_TYPE_ATOMIC;
+       q_conf.event_queue_cfg = 0;
+       ret = rte_event_queue_setup(g_dev_id, Q_ATOMIC, &q_conf);
+       if (ret < 0)
+               return -1;
+
+       /* Q2: Ordered */
+       q_conf.schedule_type = RTE_SCHED_TYPE_ORDERED;
+       q_conf.event_queue_cfg = 0;
+       ret = rte_event_queue_setup(g_dev_id, Q_ORDERED, &q_conf);
+       if (ret < 0)
+               return -1;
+
+       /* Q3: Single-link */
+       q_conf.schedule_type = RTE_SCHED_TYPE_PARALLEL;
+       q_conf.event_queue_cfg = RTE_EVENT_QUEUE_CFG_SINGLE_LINK;
+       ret = rte_event_queue_setup(g_dev_id, Q_SINGLE, &q_conf);
+       if (ret < 0)
+               return -1;
+
+       /* Setup Ports */
+       for (p = 0; p < NUM_PORTS; p++) {
+               if (p == PORT_CONS)
+                       p_conf.event_port_cfg = RTE_EVENT_PORT_CFG_SINGLE_LINK;
+               else
+                       p_conf.event_port_cfg = 0;
+
+               ret = rte_event_port_setup(g_dev_id, p, &p_conf);
+               if (ret < 0)
+                       return -1;
+       }
+
+       /* Link Queues */
+       ret = rte_event_port_link(g_dev_id, PORT_W1, ldb_queues, NULL, 3);
+       if (ret != 3)
+               return -1;
+
+       ret = rte_event_port_link(g_dev_id, PORT_W2, ldb_queues, NULL, 3);
+       if (ret != 3)
+               return -1;
+
+       ret = rte_event_port_link(g_dev_id, PORT_CONS, &single_q, NULL, 1);
+       if (ret != 1)
+               return -1;
+
+       ret = rte_event_dev_start(g_dev_id);
+       if (ret < 0)
+               return -1;
+
+       return 0;
+}
+
+static void
+cleanup_selftest_device(void)
+{
+       if (g_dev_id >= 0) {
+               rte_event_dev_stop(g_dev_id);
+               rte_event_dev_close(g_dev_id);
+               g_dev_id = -1;
+       }
+}
+
+static int
+test_parallel_queue(void)
+{
+       struct rte_event rx_ev[32];
+       struct rte_event ev[32];
+       uint16_t enqd, deqd = 0;
+       uint16_t n = 0;
+       int tries = 0;
+       int i;
+
+       memset(ev, 0, sizeof(ev));
+
+       for (i = 0; i < 32; i++) {
+               ev[i].op = RTE_EVENT_OP_NEW;
+               ev[i].sched_type = RTE_SCHED_TYPE_PARALLEL;
+               ev[i].queue_id = Q_PARALLEL;
+               ev[i].u64 = 1000 + i;
+       }
+
+       enqd = rte_event_enqueue_burst(g_dev_id, PORT_PROD, ev, 32);
+       if (enqd != 32) {
+               SLB_LOG_LINE(ERR, "Parallel enqueue failed, expected 32, got 
%u", enqd);
+               return -1;
+       }
+
+       while (deqd < 32 && tries++ < 1000) {
+               n = rte_event_dequeue_burst(g_dev_id, PORT_W1, rx_ev + deqd,
+                                           32 - deqd, 0);
+               deqd += n;
+               if (deqd < 32) {
+                       n = rte_event_dequeue_burst(g_dev_id, PORT_W2,
+                                                   rx_ev + deqd, 32 - deqd, 0);
+                       deqd += n;
+               }
+       }
+
+       if (deqd != 32) {
+               SLB_LOG_LINE(ERR, "Parallel dequeue failed, expected 32, got 
%u", deqd);
+               return -1;
+       }
+
+       /* Release events */
+       for (i = 0; i < 32; i++)
+               rx_ev[i].op = RTE_EVENT_OP_RELEASE;
+       rte_event_enqueue_burst(g_dev_id, PORT_W1, rx_ev, 32);
+
+       return 0;
+}
+
+static int
+test_atomic_queue(void)
+{
+       struct rte_event rx_ev[64];
+       struct rte_event ev[64];
+       int expected_seq[NUM_FLOWS];
+       uint16_t enqd = 0;
+       uint16_t deqd = 0;
+       int tries = 0;
+       int idx = 0;
+       int e, f;
+       uint16_t i, n;
+
+       memset(ev, 0, sizeof(ev));
+
+       for (e = 0; e < EVENTS_PER_FLOW; e++) {
+               for (f = 0; f < NUM_FLOWS; f++) {
+                       ev[idx].op = RTE_EVENT_OP_NEW;
+                       ev[idx].sched_type = RTE_SCHED_TYPE_ATOMIC;
+                       ev[idx].queue_id = Q_ATOMIC;
+                       ev[idx].flow_id = f;
+                       ev[idx].u64 = e; /* sequence number */
+                       idx++;
+               }
+       }
+
+       while (enqd < TOTAL_ATOMIC_EVENTS) {
+               n = rte_event_enqueue_burst(g_dev_id, PORT_PROD, ev + enqd,
+                                           TOTAL_ATOMIC_EVENTS - enqd);
+               if (n == 0)
+                       break;
+               enqd += n;
+       }
+       if (enqd != TOTAL_ATOMIC_EVENTS) {
+               SLB_LOG_LINE(ERR, "Atomic enqueue failed, expected %d, got %u",
+                            TOTAL_ATOMIC_EVENTS, enqd);
+               return -1;
+       }
+
+       for (f = 0; f < NUM_FLOWS; f++)
+               expected_seq[f] = 0;
+
+       while (deqd < TOTAL_ATOMIC_EVENTS && tries++ < 2000) {
+               n = rte_event_dequeue_burst(g_dev_id, PORT_W1, rx_ev, 16, 0);
+               for (i = 0; i < n; i++) {
+                       int fid = rx_ev[i].flow_id;
+
+                       if (rx_ev[i].u64 != (uint64_t)expected_seq[fid]) {
+                               SLB_LOG_LINE(ERR,
+                                            "Flow %d out of order! Expected %d 
got %" PRIu64,
+                                            fid, expected_seq[fid], 
rx_ev[i].u64);
+                               return -1;
+                       }
+                       expected_seq[fid]++;
+                       rx_ev[i].op = RTE_EVENT_OP_RELEASE;
+               }
+               if (n > 0)
+                       rte_event_enqueue_burst(g_dev_id, PORT_W1, rx_ev, n);
+               deqd += n;
+
+               n = rte_event_dequeue_burst(g_dev_id, PORT_W2, rx_ev, 16, 0);
+               for (i = 0; i < n; i++) {
+                       int fid = rx_ev[i].flow_id;
+
+                       if (rx_ev[i].u64 != (uint64_t)expected_seq[fid]) {
+                               SLB_LOG_LINE(ERR,
+                                            "Flow %d out of order! Expected %d 
got %" PRIu64,
+                                            fid, expected_seq[fid], 
rx_ev[i].u64);
+                               return -1;
+                       }
+                       expected_seq[fid]++;
+                       rx_ev[i].op = RTE_EVENT_OP_RELEASE;
+               }
+               if (n > 0)
+                       rte_event_enqueue_burst(g_dev_id, PORT_W2, rx_ev, n);
+               deqd += n;
+       }
+
+       if (deqd != TOTAL_ATOMIC_EVENTS) {
+               SLB_LOG_LINE(ERR, "Atomic dequeue failed, expected %d got %u",
+                            TOTAL_ATOMIC_EVENTS, deqd);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+test_ordered_forward(void)
+{
+       const int num_events = 32;
+       struct rte_event rx[16];
+       struct rte_event ev[32];
+       uint16_t enqd;
+       uint16_t fwd = 0;
+       uint16_t cons_rx = 0;
+       int tries = 0;
+       int i;
+       uint16_t n;
+
+       memset(ev, 0, sizeof(ev));
+
+       for (i = 0; i < num_events; i++) {
+               ev[i].op = RTE_EVENT_OP_NEW;
+               ev[i].sched_type = RTE_SCHED_TYPE_ORDERED;
+               ev[i].queue_id = Q_ORDERED;
+               ev[i].u64 = i;
+       }
+
+       enqd = rte_event_enqueue_burst(g_dev_id, PORT_PROD, ev, num_events);
+       if (enqd != num_events)
+               return -1;
+
+       /* Workers dequeue and forward to Q_SINGLE */
+       while (fwd < num_events && tries++ < 2000) {
+               n = rte_event_dequeue_burst(g_dev_id, PORT_W1, rx, 16, 0);
+               for (i = 0; i < n; i++) {
+                       rx[i].op = RTE_EVENT_OP_FORWARD;
+                       rx[i].queue_id = Q_SINGLE;
+                       rx[i].sched_type = RTE_SCHED_TYPE_PARALLEL;
+               }
+               if (n > 0)
+                       rte_event_enqueue_burst(g_dev_id, PORT_W1, rx, n);
+               fwd += n;
+
+               n = rte_event_dequeue_burst(g_dev_id, PORT_W2, rx, 16, 0);
+               for (i = 0; i < n; i++) {
+                       rx[i].op = RTE_EVENT_OP_FORWARD;
+                       rx[i].queue_id = Q_SINGLE;
+                       rx[i].sched_type = RTE_SCHED_TYPE_PARALLEL;
+               }
+               if (n > 0)
+                       rte_event_enqueue_burst(g_dev_id, PORT_W2, rx, n);
+               fwd += n;
+       }
+
+       if (fwd != num_events) {
+               SLB_LOG_LINE(ERR, "Ordered workers failed to forward all events 
(%u/%d)",
+                            fwd, num_events);
+               return -1;
+       }
+
+       /* Consumer on PORT_CONS must receive all events in exact sequence 
0..num_events-1 */
+       tries = 0;
+       while (cons_rx < num_events && tries++ < 2000) {
+               n = rte_event_dequeue_burst(g_dev_id, PORT_CONS, rx, 16, 0);
+               for (i = 0; i < n; i++) {
+                       if (rx[i].u64 != cons_rx) {
+                               SLB_LOG_LINE(ERR,
+                                            "Ordered seq violation! Expected 
%u got %" PRIu64,
+                                            cons_rx, rx[i].u64);
+                               return -1;
+                       }
+                       cons_rx++;
+                       rx[i].op = RTE_EVENT_OP_RELEASE;
+               }
+               if (n > 0)
+                       rte_event_enqueue_burst(g_dev_id, PORT_CONS, rx, n);
+       }
+
+       if (cons_rx != num_events) {
+               SLB_LOG_LINE(ERR, "Consumer received %u/%d ordered events",
+                            cons_rx, num_events);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+test_single_link(void)
+{
+       struct rte_event rx[16];
+       struct rte_event ev[16];
+       uint16_t enqd;
+       uint16_t deqd = 0;
+       int tries = 0;
+       int i;
+       uint16_t n;
+
+       memset(ev, 0, sizeof(ev));
+
+       for (i = 0; i < 16; i++) {
+               ev[i].op = RTE_EVENT_OP_NEW;
+               ev[i].sched_type = RTE_SCHED_TYPE_PARALLEL;
+               ev[i].queue_id = Q_SINGLE;
+               ev[i].u64 = 5000 + i;
+       }
+
+       enqd = rte_event_enqueue_burst(g_dev_id, PORT_PROD, ev, 16);
+       if (enqd != 16)
+               return -1;
+
+       while (deqd < 16 && tries++ < 1000) {
+               n = rte_event_dequeue_burst(g_dev_id, PORT_CONS,
+                                           rx + deqd, 16 - deqd, 0);
+               deqd += n;
+       }
+
+       if (deqd != 16) {
+               SLB_LOG_LINE(ERR, "Single-link dequeue failed (%u/16)", deqd);
+               return -1;
+       }
+
+       for (i = 0; i < 16; i++) {
+               if (rx[i].u64 != (uint64_t)(5000 + i))
+                       return -1;
+               rx[i].op = RTE_EVENT_OP_RELEASE;
+       }
+       rte_event_enqueue_burst(g_dev_id, PORT_CONS, rx, 16);
+
+       return 0;
+}
+
+static int
+test_stop_flush(void)
+{
+       struct rte_event rx[16];
+       struct rte_event ev[16];
+       uint16_t enqd;
+       uint16_t deqd;
+       int ret;
+       int i;
+
+       memset(ev, 0, sizeof(ev));
+
+       for (i = 0; i < 16; i++) {
+               ev[i].op = RTE_EVENT_OP_NEW;
+               ev[i].sched_type = RTE_SCHED_TYPE_PARALLEL;
+               ev[i].queue_id = Q_PARALLEL;
+               ev[i].u64 = 9000 + i;
+       }
+
+       enqd = rte_event_enqueue_burst(g_dev_id, PORT_PROD, ev, 16);
+       if (enqd != 16)
+               return -1;
+
+       /* Stop device flushes all queued events */
+       rte_event_dev_stop(g_dev_id);
+
+       /* Restart device */
+       ret = rte_event_dev_start(g_dev_id);
+       if (ret < 0)
+               return -1;
+
+       /* Verify no leftover events */
+       deqd = rte_event_dequeue_burst(g_dev_id, PORT_W1, rx, 16, 0);
+       if (deqd != 0) {
+               SLB_LOG_LINE(ERR, "Stop flush failed, received %u events after 
restart",
+                            deqd);
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+test_slb_eventdev(void)
+{
+       SLB_LOG_LINE(INFO, "=== Running SLB Eventdev Selftest ===");
+
+       if (init_selftest_device() < 0) {
+               SLB_LOG_LINE(ERR, "Initialization failed");
+               cleanup_selftest_device();
+               return -1;
+       }
+
+       SLB_LOG_LINE(INFO, "  [1/5] Testing Parallel Queue...");
+       if (test_parallel_queue() < 0) {
+               SLB_LOG_LINE(ERR, "  FAILED: Parallel Queue test failed");
+               cleanup_selftest_device();
+               return -1;
+       }
+       SLB_LOG_LINE(INFO, "  [1/5] Parallel Queue: PASS");
+
+       SLB_LOG_LINE(INFO, "  [2/5] Testing Atomic Queue...");
+       if (test_atomic_queue() < 0) {
+               SLB_LOG_LINE(ERR, "  FAILED: Atomic Queue test failed");
+               cleanup_selftest_device();
+               return -1;
+       }
+       SLB_LOG_LINE(INFO, "  [2/5] Atomic Queue: PASS");
+
+       SLB_LOG_LINE(INFO, "  [3/5] Testing Ordered Queue...");
+       if (test_ordered_forward() < 0) {
+               SLB_LOG_LINE(ERR, "  FAILED: Ordered Queue test failed");
+               cleanup_selftest_device();
+               return -1;
+       }
+       SLB_LOG_LINE(INFO, "  [3/5] Ordered Queue: PASS");
+
+       SLB_LOG_LINE(INFO, "  [4/5] Testing Single-Link Queue...");
+       if (test_single_link() < 0) {
+               SLB_LOG_LINE(ERR, "  FAILED: Single-Link Queue test failed");
+               cleanup_selftest_device();
+               return -1;
+       }
+       SLB_LOG_LINE(INFO, "  [4/5] Single-Link Queue: PASS");
+
+       SLB_LOG_LINE(INFO, "  [5/5] Testing Device Stop & Flush...");
+       if (test_stop_flush() < 0) {
+               SLB_LOG_LINE(ERR, "  FAILED: Stop Flush test failed");
+               cleanup_selftest_device();
+               return -1;
+       }
+       SLB_LOG_LINE(INFO, "  [5/5] Device Stop & Flush: PASS");
+
+       cleanup_selftest_device();
+       SLB_LOG_LINE(INFO, "=== SLB Eventdev Selftest PASSED ===");
+       return 0;
+}
-- 
2.53.0

Reply via email to