From: Jun Yang <[email protected]>

Add a standalone DPCON-based Rx scheduler API that lets applications
aggregate multiple ethdev Rx queues onto a single DPCON channel and
receive packets directly, without going through the eventdev framework.

The new API is exposed as experimental in rte_pmd_dpaa2.h:

- rte_dpaa2_scheduler_init():     allocate a DPCON device and select the
                                  receive path (plain or prefetch, chosen
                                  by the DPAA2_SCHEDULE_RX_PREFETCH env).
- rte_dpaa2_scheduler_add():      attach an ethdev Rx queue to the DPCON
                                  channel with a given priority.
- rte_dpaa2_scheduler_start():    enable the DPCON channel.
- rte_dpaa2_scheduler_rx():       dequeue packets from the channel.
- rte_dpaa2_scheduler_destroy():  disable the DPCON channel.
- rte_dpaa2_scheduler_max_prio(): Get the maximum supported priority.
                                  The priority specified in
                                  rte_dpaa2_scheduler_add() must not
                                  exceed this value.

Two receive implementations are provided: a plain single-buffer pull
(dpaa2_scheduler_recv) and a double-buffered prefetch variant
(dpaa2_scheduler_prefetch_recv) that overlaps the next pull with mbuf
extraction.

To support this, the fslmc bus exposes internal helpers
rte_dpaa2_dpcon_start()/stop() to enable/disable a DPCON, extends
struct dpaa2_dpcon_dev with per-lcore dequeue storage and an rx_schedule
callback, and caches the dequeue storage IOVA in
struct queue_storage_info_t to avoid repeated address translation on the
receive path.

Signed-off-by: Jun Yang <[email protected]>
---
 drivers/bus/fslmc/portal/dpaa2_hw_dpcon.c |  25 +-
 drivers/bus/fslmc/portal/dpaa2_hw_dpio.c  |   2 +
 drivers/bus/fslmc/portal/dpaa2_hw_pvt.h   |   5 +
 drivers/event/dpaa2/dpaa2_scheduler.c     | 458 ++++++++++++++++++++++
 drivers/event/dpaa2/meson.build           |   3 +-
 drivers/net/dpaa2/dpaa2_ethdev.c          |   4 +-
 drivers/net/dpaa2/dpaa2_ethdev.h          |  38 ++
 drivers/net/dpaa2/dpaa2_rxtx.c            |  21 +
 drivers/net/dpaa2/rte_pmd_dpaa2.h         |  28 ++
 9 files changed, 579 insertions(+), 5 deletions(-)
 create mode 100644 drivers/event/dpaa2/dpaa2_scheduler.c

diff --git a/drivers/bus/fslmc/portal/dpaa2_hw_dpcon.c 
b/drivers/bus/fslmc/portal/dpaa2_hw_dpcon.c
index eff8bc2445..400fb35c46 100644
--- a/drivers/bus/fslmc/portal/dpaa2_hw_dpcon.c
+++ b/drivers/bus/fslmc/portal/dpaa2_hw_dpcon.c
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: BSD-3-Clause
  *
- *   Copyright 2017, 2020, 2023 NXP
+ *   Copyright 2017, 2020, 2023, 2026 NXP
  *
  */
 
@@ -121,6 +121,29 @@ void rte_dpaa2_free_dpcon_dev(struct dpaa2_dpcon_dev 
*dpcon)
        }
 }
 
+RTE_EXPORT_INTERNAL_SYMBOL(rte_dpaa2_dpcon_start)
+int32_t rte_dpaa2_dpcon_start(struct dpaa2_dpcon_dev *dpcon_dev)
+{
+       int32_t ret;
+
+       ret = dpcon_enable(&dpcon_dev->dpcon, CMD_PRI_LOW, dpcon_dev->token);
+       if (ret)
+               DPAA2_BUS_ERR("Failed(%d) to enable dpcon.%d", ret, 
dpcon_dev->dpcon_id);
+
+       return ret;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(rte_dpaa2_dpcon_stop)
+int32_t rte_dpaa2_dpcon_stop(struct dpaa2_dpcon_dev *dpcon_dev)
+{
+       int32_t ret;
+
+       ret = dpcon_disable(&dpcon_dev->dpcon, CMD_PRI_LOW, dpcon_dev->token);
+       if (ret)
+               DPAA2_BUS_ERR("Failed(%d) to disable dpcon.%d", ret, 
dpcon_dev->dpcon_id);
+
+       return ret;
+}
 
 static void
 rte_dpaa2_close_dpcon_device(int object_id)
diff --git a/drivers/bus/fslmc/portal/dpaa2_hw_dpio.c 
b/drivers/bus/fslmc/portal/dpaa2_hw_dpio.c
index e88334088b..55dcef41d1 100644
--- a/drivers/bus/fslmc/portal/dpaa2_hw_dpio.c
+++ b/drivers/bus/fslmc/portal/dpaa2_hw_dpio.c
@@ -692,6 +692,8 @@ dpaa2_alloc_dq_storage(struct queue_storage_info_t 
*q_storage)
                        RTE_CACHE_LINE_SIZE);
                if (!q_storage->dq_storage[i])
                        goto fail;
+               q_storage->iova_dq_storage[i] =
+                       DPAA2_VADDR_TO_IOVA(q_storage->dq_storage[i]);
        }
        return 0;
 fail:
diff --git a/drivers/bus/fslmc/portal/dpaa2_hw_pvt.h 
b/drivers/bus/fslmc/portal/dpaa2_hw_pvt.h
index f80abffb40..b00e52a9b7 100644
--- a/drivers/bus/fslmc/portal/dpaa2_hw_pvt.h
+++ b/drivers/bus/fslmc/portal/dpaa2_hw_pvt.h
@@ -152,6 +152,7 @@ struct dpaa2_dpbp_dev {
 
 struct queue_storage_info_t {
        struct qbman_result *dq_storage[NUM_DQS_PER_QUEUE];
+       uint64_t iova_dq_storage[NUM_DQS_PER_QUEUE];
        struct qbman_result *active_dqs;
        uint8_t active_dpio_id;
        uint8_t toggle;
@@ -297,6 +298,10 @@ __rte_internal
 struct dpaa2_dpcon_dev *rte_dpaa2_alloc_dpcon_dev(void);
 __rte_internal
 void rte_dpaa2_free_dpcon_dev(struct dpaa2_dpcon_dev *dpcon);
+__rte_internal
+int32_t rte_dpaa2_dpcon_start(struct dpaa2_dpcon_dev *dpcon_dev);
+__rte_internal
+int32_t rte_dpaa2_dpcon_stop(struct dpaa2_dpcon_dev *dpcon_dev);
 
 /* Refer to Table 7-3 in SEC BG */
 #define QBMAN_FLE_WORD4_FMT_SBF 0x0    /* Single buffer frame */
diff --git a/drivers/event/dpaa2/dpaa2_scheduler.c 
b/drivers/event/dpaa2/dpaa2_scheduler.c
new file mode 100644
index 0000000000..e553c4d3f6
--- /dev/null
+++ b/drivers/event/dpaa2/dpaa2_scheduler.c
@@ -0,0 +1,458 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2026 NXP
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+#include <rte_string_fns.h>
+#include <rte_cycles.h>
+#include <rte_kvargs.h>
+#include <eal_export.h>
+#include <dev_driver.h>
+#include <ethdev_driver.h>
+#include <rte_event_eth_rx_adapter.h>
+
+#include <bus_fslmc_driver.h>
+#include <mc/fsl_dpcon.h>
+#include <portal/dpaa2_hw_pvt.h>
+#include <dpaa2_hw_dpio.h>
+#include <compat.h>
+#include "dpaa2_ethdev.h"
+#include "dpaa2_eventdev.h"
+#include "dpaa2_eventdev_logs.h"
+#include "rte_pmd_dpaa2.h"
+
+#define DPAA2_SCH_PORT_QUEUE_MAX_NUM 64
+struct dpaa2_sch_port_queue {
+       struct rte_eth_dev *eth_dev;
+       uint16_t port_id;
+       uint16_t rxq_id;
+       uint8_t priority;
+       struct rte_event_eth_rx_adapter_queue_conf queue_conf;
+       int configured;
+};
+
+struct dpaa2_sch_dev {
+       struct dpaa2_dpcon_dev *dpcon_dev;
+       struct dpaa2_sch_port_queue port_queue[DPAA2_SCH_PORT_QUEUE_MAX_NUM];
+       uint8_t port_queue_num;
+       struct queue_storage_info_t q_storage[RTE_MAX_LCORE];
+       uint16_t (*rx_schedule)(struct dpaa2_sch_dev *dev,
+               struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
+};
+
+static int
+dpaa2_scheduler_dq_storage_init(struct dpaa2_sch_dev *sch_dev)
+{
+       int i, ret = 0;
+
+       memset(&sch_dev->q_storage, 0,
+               sizeof(struct queue_storage_info_t) * RTE_MAX_LCORE);
+
+       for (i = 0; i < RTE_MAX_LCORE; i++) {
+               ret = dpaa2_alloc_dq_storage(&sch_dev->q_storage[i]);
+               if (ret)
+                       goto err;
+       }
+       return 0;
+err:
+       for (i = 0; i < RTE_MAX_LCORE; i++)
+               dpaa2_free_dq_storage(&sch_dev->q_storage[i]);
+
+       return ret;
+}
+
+static inline void
+dpaa2_qbman_pull_desc_channel_set(struct qbman_pull_desc *pulldesc,
+       uint32_t num, uint16_t ch_id, struct qbman_result *dq_sch_storage,
+       uint64_t iova_storage)
+{
+       qbman_pull_desc_clear(pulldesc);
+       qbman_pull_desc_set_numframes(pulldesc, num);
+       qbman_pull_desc_set_channel(pulldesc, ch_id,
+               qbman_pull_type_prio);
+       qbman_pull_desc_set_storage(pulldesc, dq_sch_storage,
+               iova_storage, 1);
+}
+
+static uint16_t
+dpaa2_scheduler_recv(struct dpaa2_sch_dev *sch_dev,
+       struct rte_mbuf **mbuf, uint16_t nb_pkts)
+{
+       struct dpaa2_dpcon_dev *dpcon_dev = sch_dev->dpcon_dev;
+       uint16_t ch_id = dpcon_dev->qbman_ch_id;
+       struct qbman_result *dq_sch_storage;
+       uint16_t total_nb_pkts = nb_pkts;
+       struct qbman_pull_desc pulldesc;
+       const struct qbman_fd *fd;
+       struct dpaa2_queue *rvq;
+       bool is_last, next_pull;
+       int ret, rcvd_pkts = 0;
+       struct qbman_swp *swp;
+       uint8_t status;
+       struct queue_storage_info_t *q_storage;
+       uint64_t iova_storage;
+       struct dpaa2_dev_priv *priv;
+
+       if (unlikely(!DPAA2_PER_LCORE_ETHRX_DPIO)) {
+               ret = dpaa2_affine_qbman_ethrx_swp();
+               if (ret) {
+                       DPAA2_EVENTDEV_ERR("Failure(%d) in affining portal", 
ret);
+                       return 0;
+               }
+       }
+       swp = DPAA2_PER_LCORE_ETHRX_PORTAL;
+       q_storage = &sch_dev->q_storage[rte_lcore_id()];
+       dq_sch_storage = q_storage->dq_storage[0];
+       iova_storage = q_storage->iova_dq_storage[0];
+
+       do {
+               is_last = false;
+               next_pull = false;
+               dpaa2_qbman_pull_desc_channel_set(&pulldesc, nb_pkts, ch_id,
+                       dq_sch_storage, iova_storage);
+
+               while (1) {
+                       if (qbman_swp_pull(swp, &pulldesc)) {
+                               DPAA2_EVENTDEV_DEBUG("Line(%d): QBMAN is busy", 
__LINE__);
+                               /* Portal was busy, try again */
+                               continue;
+                       }
+                       break;
+               }
+               /* Receive the packets till Last Dequeue entry is found with
+                * respect to the above issues PULL command.
+                */
+               while (!is_last) {
+                       /* Loop until the dq_storage is updated with
+                        * new result by QBMAN
+                        */
+                       while (!qbman_result_has_new_result(swp, 
dq_sch_storage))
+                               ;
+
+                       /* Check whether Last Pull command is Expired and
+                        * setting Condition for Loop termination
+                        */
+                       if (qbman_result_DQ_is_pull_complete(dq_sch_storage)) {
+                               is_last = true;
+                               /* Check for valid frame. */
+                               status = qbman_result_DQ_flags(dq_sch_storage);
+                               if (unlikely(!(status & 
QBMAN_DQ_STAT_VALIDFRAME))) {
+                                       next_pull = true;
+                                       DPAA2_EVENTDEV_DEBUG("No frame is 
delivered");
+                                       break;
+                               }
+                               nb_pkts = total_nb_pkts - (rcvd_pkts + 1);
+                               if (!nb_pkts)
+                                       next_pull = true;
+                       }
+
+                       fd = qbman_result_DQ_fd(dq_sch_storage);
+                       rvq = (void *)qbman_result_DQ_fqd_ctx(dq_sch_storage);
+                       priv = rvq->eth_data->dev_private;
+                       if (unlikely(DPAA2_FD_GET_FORMAT(fd) == qbman_fd_sg))
+                               mbuf[rcvd_pkts] = 
dpaa2_eth_sg_fd_to_mbuf_export(priv, fd);
+                       else
+                               mbuf[rcvd_pkts] = 
dpaa2_eth_fd_to_mbuf_export(priv, fd);
+                       dpaa2_dev_rx_mbuf_flc_offload_export(mbuf[rcvd_pkts], 
fd);
+                       dpaa2_dev_rx_print_parser_result(priv, fd, 
mbuf[rcvd_pkts]);
+                       rcvd_pkts++;
+                       dq_sch_storage++;
+                       iova_storage += sizeof(struct qbman_result);
+               }
+       } while (!next_pull);
+       /* End of Packet Rx loop */
+       DPAA2_EVENTDEV_DEBUG("DPCONC Received %d Packets", rcvd_pkts);
+
+       return rcvd_pkts;
+}
+
+static uint16_t
+dpaa2_scheduler_prefetch_recv(struct dpaa2_sch_dev *sch_dev,
+       struct rte_mbuf **mbuf, uint16_t nb_pkts)
+{
+       struct dpaa2_dpcon_dev *dpcon_dev = sch_dev->dpcon_dev;
+       uint16_t ch_id = dpcon_dev->qbman_ch_id, pull_size;
+       struct qbman_result *dq_storage, *dq_storage1 = NULL, *active;
+       struct qbman_pull_desc pulldesc;
+       struct queue_storage_info_t *q_storage;
+       uint64_t iova_storage;
+       const struct qbman_fd *fd;
+       struct dpaa2_queue *rvq;
+       int ret, rcvd_pkts = 0;
+       struct qbman_swp *swp;
+       struct dpaa2_dpio_dev *ethrx_dpio_dev;
+       uint8_t status, pending;
+       struct dpaa2_dev_priv *priv;
+
+       if (unlikely(!DPAA2_PER_LCORE_ETHRX_DPIO)) {
+               ret = dpaa2_affine_qbman_ethrx_swp();
+               if (ret) {
+                       DPAA2_EVENTDEV_ERR("Failure(%d) in affining portal", 
ret);
+                       return 0;
+               }
+       }
+       swp = DPAA2_PER_LCORE_ETHRX_PORTAL;
+       ethrx_dpio_dev = DPAA2_PER_LCORE_ETHRX_DPIO;
+       q_storage = &sch_dev->q_storage[rte_lcore_id()];
+
+       pull_size = (nb_pkts > dpaa2_dqrr_size) ?
+               dpaa2_dqrr_size : nb_pkts;
+       if (likely(q_storage->active_dqs))
+               goto pull_active_dqs;
+
+       q_storage->toggle = 0;
+       dq_storage = q_storage->dq_storage[q_storage->toggle];
+       iova_storage = q_storage->iova_dq_storage[q_storage->toggle];
+       q_storage->last_num_pkts = pull_size;
+       dpaa2_qbman_pull_desc_channel_set(&pulldesc, nb_pkts,
+               ch_id, dq_storage, iova_storage);
+       if (check_swp_active_dqs(ethrx_dpio_dev->index)) {
+               do {
+                       active = get_swp_active_dqs(ethrx_dpio_dev->index);
+                       if (qbman_check_command_complete(active))
+                               break;
+               } while (1);
+               clear_swp_active_dqs(ethrx_dpio_dev->index);
+       }
+       while (1) {
+               if (qbman_swp_pull(swp, &pulldesc)) {
+                       DPAA2_EVENTDEV_DEBUG("Line(%d): QBMAN is busy", 
__LINE__);
+                       /* Portal was busy, try again */
+                       continue;
+               }
+               break;
+       }
+       q_storage->active_dqs = dq_storage;
+       q_storage->active_dpio_id = ethrx_dpio_dev->index;
+       set_swp_active_dqs(ethrx_dpio_dev->index, dq_storage);
+
+pull_active_dqs:
+
+       dq_storage = q_storage->active_dqs;
+       rte_prefetch0((void *)(size_t)(dq_storage));
+       rte_prefetch0((void *)(size_t)(dq_storage + 1));
+
+       /* Prepare next pull descriptor. This will give space for the
+        * prefetching done on DQRR entries
+        */
+       q_storage->toggle ^= 1;
+       dq_storage1 = q_storage->dq_storage[q_storage->toggle];
+       iova_storage = q_storage->iova_dq_storage[q_storage->toggle];
+       dpaa2_qbman_pull_desc_channel_set(&pulldesc, nb_pkts,
+               ch_id, dq_storage1, iova_storage);
+
+       while (!qbman_check_command_complete(dq_storage))
+               ;
+       active = get_swp_active_dqs(q_storage->active_dpio_id);
+       if (dq_storage == active)
+               clear_swp_active_dqs(q_storage->active_dpio_id);
+
+       pending = 1;
+
+       do {
+               /* Loop until the dq_storage is updated with
+                * new token by QBMAN
+                */
+               while (!qbman_check_new_result(dq_storage))
+                       ;
+               rte_prefetch0((void *)((size_t)(dq_storage + 2)));
+               /* Check whether Last Pull command is Expired and
+                * setting Condition for Loop termination
+                */
+               if (qbman_result_DQ_is_pull_complete(dq_storage)) {
+                       pending = 0;
+                       /* Check for valid frame. */
+                       status = qbman_result_DQ_flags(dq_storage);
+                       if (unlikely(!(status & QBMAN_DQ_STAT_VALIDFRAME)))
+                               continue;
+               }
+
+               fd = qbman_result_DQ_fd(dq_storage);
+               rvq = (void *)qbman_result_DQ_fqd_ctx(dq_storage);
+               priv = rvq->eth_data->dev_private;
+               if (unlikely(DPAA2_FD_GET_FORMAT(fd) == qbman_fd_sg))
+                       mbuf[rcvd_pkts] = dpaa2_eth_sg_fd_to_mbuf_export(priv, 
fd);
+               else
+                       mbuf[rcvd_pkts] = dpaa2_eth_fd_to_mbuf_export(priv, fd);
+               dpaa2_dev_rx_mbuf_flc_offload_export(mbuf[rcvd_pkts], fd);
+               dpaa2_dev_rx_print_parser_result(priv, fd, mbuf[rcvd_pkts]);
+               rcvd_pkts++;
+
+               dq_storage++;
+       } while (pending);
+
+       if (check_swp_active_dqs(ethrx_dpio_dev->index)) {
+               do {
+                       active = get_swp_active_dqs(ethrx_dpio_dev->index);
+                       if (qbman_check_command_complete(active))
+                               break;
+               } while (1);
+               clear_swp_active_dqs(ethrx_dpio_dev->index);
+       }
+       /* issue a volatile dequeue command for next pull */
+       while (1) {
+               if (qbman_swp_pull(swp, &pulldesc)) {
+                       DPAA2_EVENTDEV_DEBUG("Line(%d): QBMAN is busy", 
__LINE__);
+                       continue;
+               }
+               break;
+       }
+       q_storage->active_dqs = dq_storage1;
+       q_storage->active_dpio_id = ethrx_dpio_dev->index;
+       set_swp_active_dqs(ethrx_dpio_dev->index, dq_storage1);
+
+       return rcvd_pkts;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_dpaa2_scheduler_init, 26.11)
+void *rte_dpaa2_scheduler_init(void)
+{
+       struct dpaa2_dpcon_dev *dpcon_dev = NULL;
+       struct dpaa2_sch_dev *sch_dev = NULL;
+       char *env = getenv("DPAA2_SCHEDULE_RX_PREFETCH");
+       int prefetch_enable = env ? atoi(env) : 1, ret, i;
+
+       sch_dev = rte_zmalloc(NULL, sizeof(struct dpaa2_sch_dev), 0);
+       if (!sch_dev) {
+               DPAA2_EVENTDEV_ERR("Failed alloc scheduler device!!");
+               goto err;
+       }
+
+       ret = dpaa2_scheduler_dq_storage_init(sch_dev);
+       if (ret)
+               goto err;
+
+       dpcon_dev = rte_dpaa2_alloc_dpcon_dev();
+       if (!dpcon_dev) {
+               DPAA2_EVENTDEV_ERR("Failed alloc dpcon device!!");
+               goto err;
+       }
+       sch_dev->dpcon_dev = dpcon_dev;
+       if (prefetch_enable)
+               sch_dev->rx_schedule = dpaa2_scheduler_prefetch_recv;
+       else
+               sch_dev->rx_schedule = dpaa2_scheduler_recv;
+
+       return sch_dev;
+err:
+       if (dpcon_dev)
+               rte_dpaa2_free_dpcon_dev(dpcon_dev);
+       if (sch_dev) {
+               for (i = 0; i < RTE_MAX_LCORE; i++)
+                       dpaa2_free_dq_storage(&sch_dev->q_storage[i]);
+               rte_free(sch_dev);
+       }
+
+       return NULL;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_dpaa2_scheduler_start, 26.11)
+int rte_dpaa2_scheduler_start(void *scheduler_handle)
+{
+       struct dpaa2_sch_dev *sch_dev = scheduler_handle;
+       int32_t ret = 0;
+
+       if (sch_dev->dpcon_dev) {
+               ret = rte_dpaa2_dpcon_start(sch_dev->dpcon_dev);
+               if (ret)
+                       DPAA2_EVENTDEV_ERR("Failed(%d) to start dpcon", ret);
+       } else {
+               DPAA2_EVENTDEV_ERR("No dpcon allocated");
+               ret = -ENODEV;
+       }
+
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_dpaa2_scheduler_destroy, 26.11)
+int rte_dpaa2_scheduler_destroy(void *scheduler_handle)
+{
+       struct dpaa2_sch_dev *sch_dev = scheduler_handle;
+       int32_t ret = 0;
+
+       if (sch_dev->dpcon_dev) {
+               ret = rte_dpaa2_dpcon_stop(sch_dev->dpcon_dev);
+               if (ret) {
+                       DPAA2_EVENTDEV_ERR("Failed(%d) to stop dpcon", ret);
+                       /** Free anyway.*/
+               }
+               rte_dpaa2_free_dpcon_dev(sch_dev->dpcon_dev);
+       }
+
+       rte_free(sch_dev);
+
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_dpaa2_scheduler_add, 26.11)
+int rte_dpaa2_scheduler_add(void *scheduler_handle,
+       uint16_t port_id, uint16_t rxq_id, uint8_t priority)
+{
+       struct dpaa2_sch_dev *sch_dev = scheduler_handle;
+       struct dpaa2_dpcon_dev *dpcon_dev;
+       struct rte_eth_dev *dev;
+       struct dpaa2_dev_priv *priv;
+       struct rte_event_eth_rx_adapter_queue_conf queue_conf;
+       int ret;
+
+       if (!rte_pmd_dpaa2_dev_is_dpaa2(port_id))
+               return -ENODEV;
+
+       dev = &rte_eth_devices[port_id];
+       priv = dev->data->dev_private;
+       if (rxq_id >= priv->nb_rx_queues) {
+               DPAA2_EVENTDEV_ERR("rxq_id(%d) >= queue number(%d)",
+                       rxq_id, priv->nb_rx_queues);
+               return -EINVAL;
+       }
+
+       if (sch_dev->port_queue_num >= DPAA2_SCH_PORT_QUEUE_MAX_NUM) {
+               DPAA2_EVENTDEV_ERR("Too many schedule queues");
+               return -EINVAL;
+       }
+
+       memset(&queue_conf, 0, sizeof(struct 
rte_event_eth_rx_adapter_queue_conf));
+       dpcon_dev = sch_dev->dpcon_dev;
+       dpaa2_eth_eventq_prio_dpcon_to_ev(priority, dpcon_dev, &queue_conf);
+       queue_conf.ev.sched_type = RTE_SCHED_TYPE_PARALLEL;
+       ret = dpaa2_eth_eventq_attach(dev, rxq_id, dpcon_dev, &queue_conf);
+       if (!ret) {
+               sch_dev->port_queue[sch_dev->port_queue_num].eth_dev = dev;
+               sch_dev->port_queue[sch_dev->port_queue_num].rxq_id = rxq_id;
+               sch_dev->port_queue[sch_dev->port_queue_num].priority = 
priority;
+               
rte_memcpy(&sch_dev->port_queue[sch_dev->port_queue_num].queue_conf,
+                       &queue_conf, sizeof(struct 
rte_event_eth_rx_adapter_queue_conf));
+               sch_dev->port_queue[sch_dev->port_queue_num].configured = true;
+               sch_dev->port_queue_num++;
+       }
+
+       return ret;
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_dpaa2_scheduler_rx, 26.11)
+uint16_t
+rte_dpaa2_scheduler_rx(void *scheduler_handle, struct rte_mbuf **mbuf,
+       uint16_t nb_pkts)
+{
+       struct dpaa2_sch_dev *sch_dev = scheduler_handle;
+
+       return sch_dev->rx_schedule(sch_dev, mbuf, nb_pkts);
+}
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_dpaa2_scheduler_max_prio, 26.11)
+uint16_t
+rte_dpaa2_scheduler_max_prio(void *scheduler_handle)
+{
+       struct dpaa2_sch_dev *sch_dev = scheduler_handle;
+
+       return sch_dev->dpcon_dev->num_priorities;
+}
diff --git a/drivers/event/dpaa2/meson.build b/drivers/event/dpaa2/meson.build
index 62b8507652..d4aca38d02 100644
--- a/drivers/event/dpaa2/meson.build
+++ b/drivers/event/dpaa2/meson.build
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: BSD-3-Clause
-# Copyright 2018 NXP
+# Copyright 2018, 2026 NXP
 
 if not is_linux
     build = false
@@ -9,6 +9,7 @@ deps += ['bus_vdev', 'net_dpaa2', 'crypto_dpaa2_sec']
 sources = files(
         'dpaa2_eventdev.c',
         'dpaa2_eventdev_selftest.c',
+        'dpaa2_scheduler.c'
 )
 
 includes += include_directories('../../crypto/dpaa2_sec/')
diff --git a/drivers/net/dpaa2/dpaa2_ethdev.c b/drivers/net/dpaa2/dpaa2_ethdev.c
index 552ac4c94d..849ab7049f 100644
--- a/drivers/net/dpaa2/dpaa2_ethdev.c
+++ b/drivers/net/dpaa2/dpaa2_ethdev.c
@@ -3332,14 +3332,12 @@ int dpaa2_eth_eventq_attach(const struct rte_eth_dev 
*dev,
        else
                return -EINVAL;
 
-       priority = (RTE_EVENT_DEV_PRIORITY_LOWEST / queue_conf->ev.priority) *
-                  (dpcon->num_priorities - 1);
-
        if (!cfg) {
                DPAA2_PMD_ERR("%s: %s-rxq%d was not setup yet!",
                        __func__, dev->data->name, eth_rx_queue_id);
                return -EINVAL;
        }
+       priority = dpaa2_eth_eventq_prio_ev_to_dpcon(dpcon, queue_conf);
        options = DPNI_QUEUE_OPT_DEST;
        cfg->destination.type = DPNI_DEST_DPCON;
        cfg->destination.id = dpcon->dpcon_id;
diff --git a/drivers/net/dpaa2/dpaa2_ethdev.h b/drivers/net/dpaa2/dpaa2_ethdev.h
index d46fbd6d7d..e4b896f17c 100644
--- a/drivers/net/dpaa2/dpaa2_ethdev.h
+++ b/drivers/net/dpaa2/dpaa2_ethdev.h
@@ -908,6 +908,33 @@ dpaa2_dpkg_insert_extract(struct dpkg_profile_cfg *kg_cfg,
        kg_cfg->num_extracts++;
 }
 
+static inline void
+dpaa2_eth_eventq_prio_dpcon_to_ev(uint8_t priority,
+       struct dpaa2_dpcon_dev *dpcon,
+       struct rte_event_eth_rx_adapter_queue_conf *queue_conf)
+{
+       uint8_t priority_step;
+       uint32_t priority_ev;
+
+       priority_step = (RTE_EVENT_DEV_PRIORITY_LOWEST + 1 -
+               RTE_EVENT_DEV_PRIORITY_HIGHEST) / dpcon->num_priorities;
+       priority_ev = priority * priority_step;
+       if (priority_ev > RTE_EVENT_DEV_PRIORITY_LOWEST)
+               priority_ev = RTE_EVENT_DEV_PRIORITY_LOWEST;
+       queue_conf->ev.priority = priority_ev;
+}
+
+static inline uint8_t
+dpaa2_eth_eventq_prio_ev_to_dpcon(struct dpaa2_dpcon_dev *dpcon,
+       const struct rte_event_eth_rx_adapter_queue_conf *queue_conf)
+{
+       uint8_t priority_step;
+
+       priority_step = (RTE_EVENT_DEV_PRIORITY_LOWEST + 1 -
+               RTE_EVENT_DEV_PRIORITY_HIGHEST) / dpcon->num_priorities;
+       return queue_conf->ev.priority / priority_step;
+}
+
 __rte_internal
 int dpaa2_eth_eventq_attach(const struct rte_eth_dev *dev,
                int eth_rx_queue_id,
@@ -971,5 +998,16 @@ int dpaa2_timesync_read_tx_timestamp(struct rte_eth_dev 
*dev,
 int dpaa2_dev_recycle_config(struct rte_eth_dev *eth_dev);
 int dpaa2_dev_recycle_deconfig(struct rte_eth_dev *eth_dev);
 
+__rte_internal
+struct rte_mbuf *
+dpaa2_eth_fd_to_mbuf_export(struct dpaa2_dev_priv *priv, const struct qbman_fd 
*fd);
+__rte_internal
+struct rte_mbuf *
+dpaa2_eth_sg_fd_to_mbuf_export(struct dpaa2_dev_priv *priv, const struct 
qbman_fd *fd);
+__rte_internal
+void
+dpaa2_dev_rx_mbuf_flc_offload_export(struct rte_mbuf *m,
+       const struct qbman_fd *fd);
+
 int dpaa2_mtr_ops_get(struct rte_eth_dev *dev, void *ops);
 #endif /* _DPAA2_ETHDEV_H */
diff --git a/drivers/net/dpaa2/dpaa2_rxtx.c b/drivers/net/dpaa2/dpaa2_rxtx.c
index d0d4536ab4..0d4fc18455 100644
--- a/drivers/net/dpaa2/dpaa2_rxtx.c
+++ b/drivers/net/dpaa2/dpaa2_rxtx.c
@@ -165,6 +165,13 @@ dpaa2_dev_rx_mbuf_flc_offload(struct rte_mbuf *m,
        }
 }
 
+RTE_EXPORT_INTERNAL_SYMBOL(dpaa2_dev_rx_mbuf_flc_offload_export)
+void dpaa2_dev_rx_mbuf_flc_offload_export(struct rte_mbuf *m,
+       const struct qbman_fd *fd)
+{
+       dpaa2_dev_rx_mbuf_flc_offload(m, fd);
+}
+
 static inline void
 dpaa2_dev_rx_read_timestamp(struct dpaa2_dev_priv *priv,
        struct rte_mbuf *m)
@@ -479,6 +486,13 @@ dpaa2_eth_sg_fd_to_mbuf(struct dpaa2_dev_priv *priv, const 
struct qbman_fd *fd)
        return (void *)first_seg;
 }
 
+RTE_EXPORT_INTERNAL_SYMBOL(dpaa2_eth_sg_fd_to_mbuf_export)
+struct rte_mbuf *
+dpaa2_eth_sg_fd_to_mbuf_export(struct dpaa2_dev_priv *priv, const struct 
qbman_fd *fd)
+{
+       return dpaa2_eth_sg_fd_to_mbuf(priv, fd);
+}
+
 static inline struct rte_mbuf *__rte_hot
 dpaa2_eth_fd_to_mbuf(struct dpaa2_dev_priv *priv, const struct qbman_fd *fd)
 {
@@ -534,6 +548,13 @@ dpaa2_eth_fd_to_mbuf(struct dpaa2_dev_priv *priv, const 
struct qbman_fd *fd)
        return mbuf;
 }
 
+RTE_EXPORT_INTERNAL_SYMBOL(dpaa2_eth_fd_to_mbuf_export)
+struct rte_mbuf *
+dpaa2_eth_fd_to_mbuf_export(struct dpaa2_dev_priv *priv, const struct qbman_fd 
*fd)
+{
+       return dpaa2_eth_fd_to_mbuf(priv, fd);
+}
+
 static void dpaa2_dev_tx_enable_tstamp(struct qbman_fd *fd)
 {
        struct dpaa2_faead *fd_faead;
diff --git a/drivers/net/dpaa2/rte_pmd_dpaa2.h 
b/drivers/net/dpaa2/rte_pmd_dpaa2.h
index b28962c5f8..eb038011c9 100644
--- a/drivers/net/dpaa2/rte_pmd_dpaa2.h
+++ b/drivers/net/dpaa2/rte_pmd_dpaa2.h
@@ -129,6 +129,34 @@ int
 rte_pmd_dpaa2_rx_get_offset(uint16_t port_id, struct rte_mbuf *m,
        uint8_t *l3_off, uint8_t *l4_off, uint8_t *l5_off);
 
+__rte_experimental
+void *
+rte_dpaa2_scheduler_init(void);
+__rte_experimental
+int
+rte_dpaa2_scheduler_start(void *scheduler_handle);
+__rte_experimental
+int
+rte_dpaa2_scheduler_destroy(void *scheduler_handle);
+__rte_experimental
+uint16_t
+rte_dpaa2_scheduler_max_prio(void *scheduler_handle);
+__rte_experimental
+int
+rte_dpaa2_scheduler_add(void *scheduler_handle,
+       uint16_t port_id, uint16_t rxq_id, uint8_t priority);
+
+/* rte_dpaa2_scheduler_rx()- DPCON scheduler receive function
+ * @scheduler_handle: DPCON scheduler handle
+ * @mbuf:             Packet mbuf
+ * @nb_pkts:          Number of packets to be received.
+ * Return Number of received packet.
+ */
+__rte_experimental
+uint16_t
+rte_dpaa2_scheduler_rx(void *scheduler_handle, struct rte_mbuf **mbuf,
+       uint16_t nb_pkts);
+
 #define RTE_DPAA2_EVENT_PORT_CFG_ATOMIC RTE_BIT32(31)
 
 /** RTE_DPAA2_MIX_GROUP_FLOW has same behavio as RTE_DPAA2_FLAT_GROUP_FLOW*/
-- 
2.43.0

Reply via email to