> -----Original Message-----
> From: Mattias Rönnblom <hof...@lysator.liu.se>
> Sent: Tuesday, September 19, 2023 2:51 PM
> To: Naga Harish K, S V <s.v.naga.haris...@intel.com>; mattias.ronnblom
> <mattias.ronnb...@ericsson.com>; dev@dpdk.org
> Cc: Jerin Jacob <jer...@marvell.com>; techbo...@dpdk.org; Van Haaren,
> Harry <harry.van.haa...@intel.com>; Nilsson, Peter
> <peter.j.nils...@ericsson.com>; Heng Wang <heng.w...@ericsson.com>;
> Pavan Nikhilesh <pbhagavat...@marvell.com>; Gujjar, Abhinandan S
> <abhinandan.guj...@intel.com>; Carrillo, Erik G <erik.g.carri...@intel.com>;
> Shijith Thotton <sthot...@marvell.com>; Hemant Agrawal
> <hemant.agra...@nxp.com>; Sachin Saxena <sachin.sax...@oss.nxp.com>;
> Liang Ma <lian...@liangbit.com>; Mccarthy, Peter
> <peter.mccar...@intel.com>; Yan, Zhirun <zhirun....@intel.com>
> Subject: Re: [PATCH v3 1/3] lib: introduce dispatcher library
> 
> On 2023-09-17 18:46, Naga Harish K, S V wrote:
> >
> >
> >> -----Original Message-----
> >> From: Mattias Rönnblom <mattias.ronnb...@ericsson.com>
> >> Sent: Monday, September 4, 2023 6:33 PM
> >> To: dev@dpdk.org
> >> Cc: Jerin Jacob <jer...@marvell.com>; techbo...@dpdk.org; Van Haaren,
> >> Harry <harry.van.haa...@intel.com>; hof...@lysator.liu.se; Nilsson,
> >> Peter <peter.j.nils...@ericsson.com>; Heng Wang
> >> <heng.w...@ericsson.com>; Naga Harish K, S V
> >> <s.v.naga.haris...@intel.com>; Pavan Nikhilesh
> >> <pbhagavat...@marvell.com>; Gujjar, Abhinandan S
> >> <abhinandan.guj...@intel.com>; Carrillo, Erik G
> >> <erik.g.carri...@intel.com>; Shijith Thotton <sthot...@marvell.com>;
> >> Hemant Agrawal <hemant.agra...@nxp.com>; Sachin Saxena
> >> <sachin.sax...@oss.nxp.com>; Liang Ma <lian...@liangbit.com>;
> >> Mccarthy, Peter <peter.mccar...@intel.com>; Yan, Zhirun
> >> <zhirun....@intel.com>; mattias.ronnblom
> >> <mattias.ronnb...@ericsson.com>
> >> Subject: [PATCH v3 1/3] lib: introduce dispatcher library
> >>
> >> The purpose of the dispatcher library is to help reduce coupling in
> >> an Eventdev-based DPDK application.
> >>
> >> In addition, the dispatcher also provides a convenient and flexible
> >> way for the application to use service cores for application-level 
> >> processing.
> >>
> >> Signed-off-by: Mattias Rönnblom <mattias.ronnb...@ericsson.com>
> >> Tested-by: Peter Nilsson <peter.j.nils...@ericsson.com>
> >> Reviewed-by: Heng Wang <heng.w...@ericsson.com>
> >>
> >> --
> >>
> >> PATCH v3:
> >>   o To underline its optional character and since it does not provide
> >>     hardware abstraction, the event dispatcher is now a separate
> >>     library.
> >>   o Change name from rte_event_dispatcher -> rte_dispatcher, to make it
> >>     shorter and to avoid the rte_event_* namespace.
> >>
> >
> > Rte_dispatcher is basically dispatching events but it feels like the name 
> > does
> not convey that.
> > Also, it is like any other adapter service that can reside within the 
> > eventdev
> directory.
> >
> > I can see some discussion in previous threads related to the placement of 
> > the
> dispatcher library.
> >
> > It is an optional eventdev application service, not enforcing this
> programming model to the application.
> > The documentation may need to be updated and mention that this is
> optional.
> >
> > If any hardware comes up with the dispatcher feature, then this library may
> need to be moved inside eventdev library later.
> >
> 
> It seems to me that the deciding factor for what functionality goes into a 
> DPDK
> library or not is not such much dependent on if it's implemented in hardware,
> in software, or some combination thereof. The important thing is that the
> library is be able to present a coherent API to the application (or other
> libraries).
> 
> That said, as I've mentioned before, I have no strong opionion on this 
> subject.
> 

What is the next step here? 
The response is not conclusive as It looks like both yes and no to change the 
directory structure.


> > So, It makes sense to keep this optional service in the eventdev folder as 
> > an
> optional feature.
> >
> >> PATCH v2:
> >>   o Add dequeue batch count statistic.
> >>   o Add statistics reset function to API.
> >>   o Clarify MT safety guarantees (or lack thereof) in the API 
> >> documentation.
> >>   o Change loop variable type in evd_lcore_get_handler_by_id() to uint16_t,
> >>     to be consistent with similar loops elsewhere in the dispatcher.
> >>   o Fix variable names in finalizer unregister function.
> >>
> >> PATCH:
> >>   o Change prefix from RED to EVD, to avoid confusion with random
> >>     early detection.
> >>
> >> RFC v4:
> >>   o Move handlers to per-lcore data structures.
> >>   o Introduce mechanism which rearranges handlers so that often-used
> >>     handlers tend to be tried first.
> >>   o Terminate dispatch loop in case all events are delivered.
> >>   o To avoid the dispatcher's service function hogging the CPU, process
> >>     only one batch per call.
> >>   o Have service function return -EAGAIN if no work is performed.
> >>   o Events delivered in the process function is no longer marked 'const',
> >>     since modifying them may be useful for the application and cause
> >>     no difficulties for the dispatcher.
> >>   o Various minor API documentation improvements.
> >>
> >> RFC v3:
> >>   o Add stats_get() function to the version.map file.
> >> ---
> >>   MAINTAINERS                     |   3 +
> >>   lib/dispatcher/meson.build      |  17 +
> >>   lib/dispatcher/rte_dispatcher.c | 791
> >> ++++++++++++++++++++++++++++++++  lib/dispatcher/rte_dispatcher.h
> |
> >> 480 +++++++++++++++++++
> >>   lib/dispatcher/version.map      |  20 +
> >>   lib/meson.build                 |   2 +
> >>   6 files changed, 1313 insertions(+)
> >>   create mode 100644 lib/dispatcher/meson.build  create mode 100644
> >> lib/dispatcher/rte_dispatcher.c  create mode 100644
> >> lib/dispatcher/rte_dispatcher.h  create mode 100644
> >> lib/dispatcher/version.map
> >>
> >> diff --git a/MAINTAINERS b/MAINTAINERS index a926155f26..6704cd5b2c
> >> 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -1726,6 +1726,9 @@ M: Nithin Dabilpuram
> <ndabilpu...@marvell.com>
> >>   M: Pavan Nikhilesh <pbhagavat...@marvell.com>
> >>   F: lib/node/
> >>
> >> +Dispatcher - EXPERIMENTAL
> >> +M: Mattias Rönnblom <mattias.ronnb...@ericsson.com>
> >> +F: lib/dispatcher/
> >>
> >>   Test Applications
> >>   -----------------
> >> diff --git a/lib/dispatcher/meson.build b/lib/dispatcher/meson.build
> >> new file mode 100644 index 0000000000..c6054a3a5d
> >> --- /dev/null
> >> +++ b/lib/dispatcher/meson.build
> >> @@ -0,0 +1,17 @@
> >> +# SPDX-License-Identifier: BSD-3-Clause # Copyright(c) 2023 Ericsson
> >> +AB
> >> +
> >> +if is_windows
> >> +    build = false
> >> +    reason = 'not supported on Windows'
> >> +    subdir_done()
> >> +endif
> >> +
> >> +sources = files(
> >> +        'rte_dispatcher.c',
> >> +)
> >> +headers = files(
> >> +        'rte_dispatcher.h',
> >> +)
> >> +
> >> +deps += ['eventdev']
> >> diff --git a/lib/dispatcher/rte_dispatcher.c
> >> b/lib/dispatcher/rte_dispatcher.c new file mode 100644 index
> >> 0000000000..3319fe09f2
> >> --- /dev/null
> >> +++ b/lib/dispatcher/rte_dispatcher.c
> >> @@ -0,0 +1,791 @@
> >> +/* SPDX-License-Identifier: BSD-3-Clause
> >> + * Copyright(c) 2023 Ericsson AB
> >> + */
> >> +
> >> +#include <stdbool.h>
> >> +#include <stdint.h>
> >> +
> >> +#include <rte_branch_prediction.h>
> >> +#include <rte_common.h>
> >> +#include <rte_lcore.h>
> >> +#include <rte_random.h>
> >> +#include <rte_service_component.h>
> >> +
> >> +#include "eventdev_pmd.h"
> >> +
> >> +#include <rte_dispatcher.h>
> >> +
> >> +#define EVD_MAX_PORTS_PER_LCORE 4
> >> +#define EVD_MAX_HANDLERS 32
> >> +#define EVD_MAX_FINALIZERS 16
> >> +#define EVD_AVG_PRIO_INTERVAL 2000
> >> +
> >> +struct rte_dispatcher_lcore_port {
> >> +  uint8_t port_id;
> >> +  uint16_t batch_size;
> >> +  uint64_t timeout;
> >> +};
> >> +
> >> +struct rte_dispatcher_handler {
> >> +  int id;
> >> +  rte_dispatcher_match_t match_fun;
> >> +  void *match_data;
> >> +  rte_dispatcher_process_t process_fun;
> >> +  void *process_data;
> >> +};
> >> +
> >> +struct rte_dispatcher_finalizer {
> >> +  int id;
> >> +  rte_dispatcher_finalize_t finalize_fun;
> >> +  void *finalize_data;
> >> +};
> >> +
> >> +struct rte_dispatcher_lcore {
> >> +  uint8_t num_ports;
> >> +  uint16_t num_handlers;
> >> +  int32_t prio_count;
> >> +  struct rte_dispatcher_lcore_port
> >> ports[EVD_MAX_PORTS_PER_LCORE];
> >> +  struct rte_dispatcher_handler handlers[EVD_MAX_HANDLERS];
> >> +  struct rte_dispatcher_stats stats;
> >> +} __rte_cache_aligned;
> >> +
> >> +struct rte_dispatcher {
> >> +  uint8_t id;
> >> +  uint8_t event_dev_id;
> >> +  int socket_id;
> >> +  uint32_t service_id;
> >> +  struct rte_dispatcher_lcore lcores[RTE_MAX_LCORE];
> >> +  uint16_t num_finalizers;
> >> +  struct rte_dispatcher_finalizer finalizers[EVD_MAX_FINALIZERS]; };
> >> +
> >> +static struct rte_dispatcher *dispatchers[UINT8_MAX];
> >> +
> >> +static bool
> >> +evd_has_dispatcher(uint8_t id)
> >> +{
> >> +  return dispatchers[id] != NULL;
> >> +}
> >> +
> >> +static struct rte_dispatcher *
> >> +evd_get_dispatcher(uint8_t id)
> >> +{
> >> +  return dispatchers[id];
> >> +}
> >> +
> >> +static void
> >> +evd_set_dispatcher(uint8_t id, struct rte_dispatcher *dispatcher) {
> >> +  dispatchers[id] = dispatcher;
> >> +}
> >> +
> >> +#define EVD_VALID_ID_OR_RET_EINVAL(id)
> >>    \
> >> +  do {                                                            \
> >> +          if (unlikely(!evd_has_dispatcher(id))) {                \
> >> +                  RTE_EDEV_LOG_ERR("Invalid dispatcher id %d\n", id);
> >> \
> >> +                  return -EINVAL;                                 \
> >> +          }                                                       \
> >> +  } while (0)
> >> +
> >> +static int
> >> +evd_lookup_handler_idx(struct rte_dispatcher_lcore *lcore,
> >> +                 const struct rte_event *event) {
> >> +  uint16_t i;
> >> +
> >> +  for (i = 0; i < lcore->num_handlers; i++) {
> >> +          struct rte_dispatcher_handler *handler =
> >> +                  &lcore->handlers[i];
> >> +
> >> +          if (handler->match_fun(event, handler->match_data))
> >> +                  return i;
> >> +  }
> >> +
> >> +  return -1;
> >> +}
> >> +
> >> +static void
> >> +evd_prioritize_handler(struct rte_dispatcher_lcore *lcore,
> >> +                 int handler_idx)
> >> +{
> >> +  struct rte_dispatcher_handler tmp;
> >> +
> >> +  if (handler_idx == 0)
> >> +          return;
> >> +
> >> +  /* Let the lucky handler "bubble" up the list */
> >> +
> >> +  tmp = lcore->handlers[handler_idx - 1];
> >> +
> >> +  lcore->handlers[handler_idx - 1] = lcore->handlers[handler_idx];
> >> +
> >> +  lcore->handlers[handler_idx] = tmp; }
> >> +
> >> +static inline void
> >> +evd_consider_prioritize_handler(struct rte_dispatcher_lcore *lcore,
> >> +                          int handler_idx, uint16_t handler_events) {
> >> +  lcore->prio_count -= handler_events;
> >> +
> >> +  if (unlikely(lcore->prio_count <= 0)) {
> >> +          evd_prioritize_handler(lcore, handler_idx);
> >> +
> >> +          /*
> >> +           * Randomize the interval in the unlikely case
> >> +           * the traffic follow some very strict pattern.
> >> +           */
> >> +          lcore->prio_count =
> >> +                  rte_rand_max(EVD_AVG_PRIO_INTERVAL) +
> >> +                  EVD_AVG_PRIO_INTERVAL / 2;
> >> +  }
> >> +}
> >> +
> >> +static inline void
> >> +evd_dispatch_events(struct rte_dispatcher *dispatcher,
> >> +              struct rte_dispatcher_lcore *lcore,
> >> +              struct rte_dispatcher_lcore_port *port,
> >> +              struct rte_event *events, uint16_t num_events) {
> >> +  int i;
> >> +  struct rte_event bursts[EVD_MAX_HANDLERS][num_events];
> >> +  uint16_t burst_lens[EVD_MAX_HANDLERS] = { 0 };
> >> +  uint16_t drop_count = 0;
> >> +  uint16_t dispatch_count;
> >> +  uint16_t dispatched = 0;
> >> +
> >> +  for (i = 0; i < num_events; i++) {
> >> +          struct rte_event *event = &events[i];
> >> +          int handler_idx;
> >> +
> >> +          handler_idx = evd_lookup_handler_idx(lcore, event);
> >> +
> >> +          if (unlikely(handler_idx < 0)) {
> >> +                  drop_count++;
> >> +                  continue;
> >> +          }
> >> +
> >> +          bursts[handler_idx][burst_lens[handler_idx]] = *event;
> >> +          burst_lens[handler_idx]++;
> >> +  }
> >> +
> >> +  dispatch_count = num_events - drop_count;
> >> +
> >> +  for (i = 0; i < lcore->num_handlers &&
> >> +           dispatched < dispatch_count; i++) {
> >> +          struct rte_dispatcher_handler *handler =
> >> +                  &lcore->handlers[i];
> >> +          uint16_t len = burst_lens[i];
> >> +
> >> +          if (len == 0)
> >> +                  continue;
> >> +
> >> +          handler->process_fun(dispatcher->event_dev_id, port-
> >>> port_id,
> >> +                               bursts[i], len, handler->process_data);
> >> +
> >> +          dispatched += len;
> >> +
> >> +          /*
> >> +           * Safe, since any reshuffling will only involve
> >> +           * already-processed handlers.
> >> +           */
> >> +          evd_consider_prioritize_handler(lcore, i, len);
> >> +  }
> >> +
> >> +  lcore->stats.ev_batch_count++;
> >> +  lcore->stats.ev_dispatch_count += dispatch_count;
> >> +  lcore->stats.ev_drop_count += drop_count;
> >> +
> >> +  for (i = 0; i < dispatcher->num_finalizers; i++) {
> >> +          struct rte_dispatcher_finalizer *finalizer =
> >> +                  &dispatcher->finalizers[i];
> >> +
> >> +          finalizer->finalize_fun(dispatcher->event_dev_id,
> >> +                                  port->port_id,
> >> +                                  finalizer->finalize_data);
> >> +  }
> >> +}
> >> +
> >> +static __rte_always_inline uint16_t
> >> +evd_port_dequeue(struct rte_dispatcher *dispatcher,
> >> +           struct rte_dispatcher_lcore *lcore,
> >> +           struct rte_dispatcher_lcore_port *port) {
> >> +  uint16_t batch_size = port->batch_size;
> >> +  struct rte_event events[batch_size];
> >> +  uint16_t n;
> >> +
> >> +  n = rte_event_dequeue_burst(dispatcher->event_dev_id, port-
> >>> port_id,
> >> +                              events, batch_size, port->timeout);
> >> +
> >> +  if (likely(n > 0))
> >> +          evd_dispatch_events(dispatcher, lcore, port, events, n);
> >> +
> >> +  lcore->stats.poll_count++;
> >> +
> >> +  return n;
> >> +}
> >> +
> >> +static __rte_always_inline uint16_t
> >> +evd_lcore_process(struct rte_dispatcher *dispatcher,
> >> +            struct rte_dispatcher_lcore *lcore) {
> >> +  uint16_t i;
> >> +  uint16_t event_count = 0;
> >> +
> >> +  for (i = 0; i < lcore->num_ports; i++) {
> >> +          struct rte_dispatcher_lcore_port *port =
> >> +                  &lcore->ports[i];
> >> +
> >> +          event_count += evd_port_dequeue(dispatcher, lcore, port);
> >> +  }
> >> +
> >> +  return event_count;
> >> +}
> >> +
> >> +static int32_t
> >> +evd_process(void *userdata)
> >> +{
> >> +  struct rte_dispatcher *dispatcher = userdata;
> >> +  unsigned int lcore_id = rte_lcore_id();
> >> +  struct rte_dispatcher_lcore *lcore =
> >> +          &dispatcher->lcores[lcore_id];
> >> +  uint64_t event_count;
> >> +
> >> +  event_count = evd_lcore_process(dispatcher, lcore);
> >> +
> >> +  if (unlikely(event_count == 0))
> >> +          return -EAGAIN;
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +static int
> >> +evd_service_register(struct rte_dispatcher *dispatcher) {
> >> +  struct rte_service_spec service = {
> >> +          .callback = evd_process,
> >> +          .callback_userdata = dispatcher,
> >> +          .capabilities = RTE_SERVICE_CAP_MT_SAFE,
> >> +          .socket_id = dispatcher->socket_id
> >> +  };
> >> +  int rc;
> >> +
> >> +  snprintf(service.name, RTE_SERVICE_NAME_MAX - 1, "evd_%d",
> >> +           dispatcher->id);
> >> +
> >> +  rc = rte_service_component_register(&service,
> >> +&dispatcher->service_id);
> >> +
> >> +  if (rc)
> >> +          RTE_EDEV_LOG_ERR("Registration of dispatcher service "
> >> +                           "%s failed with error code %d\n",
> >> +                           service.name, rc);
> >> +
> >> +  return rc;
> >> +}
> >> +
> >> +static int
> >> +evd_service_unregister(struct rte_dispatcher *dispatcher) {
> >> +  int rc;
> >> +
> >> +  rc = rte_service_component_unregister(dispatcher->service_id);
> >> +
> >> +  if (rc)
> >> +          RTE_EDEV_LOG_ERR("Unregistration of dispatcher service "
> >> +                           "failed with error code %d\n", rc);
> >> +
> >> +  return rc;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_create(uint8_t id, uint8_t event_dev_id) {
> >> +  int socket_id;
> >> +  struct rte_dispatcher *dispatcher;
> >> +  int rc;
> >> +
> >> +  if (evd_has_dispatcher(id)) {
> >> +          RTE_EDEV_LOG_ERR("Dispatcher with id %d already exists\n",
> >> +                           id);
> >> +          return -EEXIST;
> >> +  }
> >> +
> >> +  socket_id = rte_event_dev_socket_id(event_dev_id);
> >> +
> >> +  dispatcher =
> >> +          rte_malloc_socket("dispatcher", sizeof(struct rte_dispatcher),
> >> +                            RTE_CACHE_LINE_SIZE, socket_id);
> >> +
> >> +  if (dispatcher == NULL) {
> >> +          RTE_EDEV_LOG_ERR("Unable to allocate memory for
> >> dispatcher\n");
> >> +          return -ENOMEM;
> >> +  }
> >> +
> >> +  *dispatcher = (struct rte_dispatcher) {
> >> +          .id = id,
> >> +          .event_dev_id = event_dev_id,
> >> +          .socket_id = socket_id
> >> +  };
> >> +
> >> +  rc = evd_service_register(dispatcher);
> >> +
> >> +  if (rc < 0) {
> >> +          rte_free(dispatcher);
> >> +          return rc;
> >> +  }
> >> +
> >> +  evd_set_dispatcher(id, dispatcher);
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_free(uint8_t id)
> >> +{
> >> +  struct rte_dispatcher *dispatcher;
> >> +  int rc;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >> +  rc = evd_service_unregister(dispatcher);
> >> +
> >> +  if (rc)
> >> +          return rc;
> >> +
> >> +  evd_set_dispatcher(id, NULL);
> >> +
> >> +  rte_free(dispatcher);
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_service_id_get(uint8_t id, uint32_t *service_id) {
> >> +  struct rte_dispatcher *dispatcher;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >
> > Service_id pointer needs to be validated for NULL before accessing
> >
> >
> 
> Noted.
> 
> Returning error codes on API violations is bad practice (such should be met
> with assertions), but it is DPDK standard practice and how Eventdev does
> things as well, so I'll change it.
> 
> It would also be consistent with allowing invalid 'id' parameter values.
> 
> >> +  *service_id = dispatcher->service_id;
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +static int
> >> +lcore_port_index(struct rte_dispatcher_lcore *lcore,
> >> +           uint8_t event_port_id)
> >> +{
> >> +  uint16_t i;
> >> +
> >> +  for (i = 0; i < lcore->num_ports; i++) {
> >> +          struct rte_dispatcher_lcore_port *port =
> >> +                  &lcore->ports[i];
> >> +
> >> +          if (port->port_id == event_port_id)
> >> +                  return i;
> >> +  }
> >> +
> >> +  return -1;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_bind_port_to_lcore(uint8_t id, uint8_t event_port_id,
> >> +                                  uint16_t batch_size, uint64_t
> >> timeout,
> >> +                                  unsigned int lcore_id)
> >> +{
> >> +  struct rte_dispatcher *dispatcher;
> >> +  struct rte_dispatcher_lcore *lcore;
> >> +  struct rte_dispatcher_lcore_port *port;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >> +  lcore = &dispatcher->lcores[lcore_id];
> >> +
> >> +  if (lcore->num_ports == EVD_MAX_PORTS_PER_LCORE)
> >> +          return -ENOMEM;
> >> +
> >> +  if (lcore_port_index(lcore, event_port_id) >= 0)
> >> +          return -EEXIST;
> >> +
> >> +  port = &lcore->ports[lcore->num_ports];
> >> +
> >> +  *port = (struct rte_dispatcher_lcore_port) {
> >> +          .port_id = event_port_id,
> >> +          .batch_size = batch_size,
> >> +          .timeout = timeout
> >> +  };
> >> +
> >> +  lcore->num_ports++;
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_unbind_port_from_lcore(uint8_t id, uint8_t
> event_port_id,
> >> +                                      unsigned int lcore_id)
> >> +{
> >> +  struct rte_dispatcher *dispatcher;
> >> +  struct rte_dispatcher_lcore *lcore;
> >> +  int port_idx;
> >> +  struct rte_dispatcher_lcore_port *port;
> >> +  struct rte_dispatcher_lcore_port *last;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >> +  lcore = &dispatcher->lcores[lcore_id];
> >> +
> >> +  port_idx = lcore_port_index(lcore, event_port_id);
> >> +
> >> +  if (port_idx < 0)
> >> +          return -ENOENT;
> >> +
> >> +  port = &lcore->ports[port_idx];
> >> +  last = &lcore->ports[lcore->num_ports - 1];
> >> +
> >> +  if (port != last)
> >> +          *port = *last;
> >> +
> >> +  lcore->num_ports--;
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +static struct rte_dispatcher_handler*
> >> +evd_lcore_get_handler_by_id(struct rte_dispatcher_lcore *lcore,
> >> +                      int handler_id)
> >> +{
> >> +  uint16_t i;
> >> +
> >> +  for (i = 0; i < lcore->num_handlers; i++) {
> >> +          struct rte_dispatcher_handler *handler =
> >> +                  &lcore->handlers[i];
> >> +
> >> +          if (handler->id == handler_id)
> >> +                  return handler;
> >> +  }
> >> +
> >> +  return NULL;
> >> +}
> >> +
> >> +static int
> >> +evd_alloc_handler_id(struct rte_dispatcher *dispatcher) {
> >> +  int handler_id = 0;
> >> +  struct rte_dispatcher_lcore *reference_lcore =
> >> +          &dispatcher->lcores[0];
> >> +
> >> +  if (reference_lcore->num_handlers == EVD_MAX_HANDLERS)
> >> +          return -1;
> >> +
> >> +  while (evd_lcore_get_handler_by_id(reference_lcore, handler_id) !=
> >> NULL)
> >> +          handler_id++;
> >> +
> >> +  return handler_id;
> >> +}
> >> +
> >> +static void
> >> +evd_lcore_install_handler(struct rte_dispatcher_lcore *lcore,
> >> +              const struct rte_dispatcher_handler *handler) {
> >> +  int handler_idx = lcore->num_handlers;
> >> +
> >> +  lcore->handlers[handler_idx] = *handler;
> >> +  lcore->num_handlers++;
> >> +}
> >> +
> >> +static void
> >> +evd_install_handler(struct rte_dispatcher *dispatcher,
> >> +              const struct rte_dispatcher_handler *handler) {
> >> +  int i;
> >> +
> >> +  for (i = 0; i < RTE_MAX_LCORE; i++) {
> >> +          struct rte_dispatcher_lcore *lcore =
> >> +                  &dispatcher->lcores[i];
> >> +          evd_lcore_install_handler(lcore, handler);
> >> +  }
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_register(uint8_t id,
> >> +                        rte_dispatcher_match_t match_fun,
> >> +                        void *match_data,
> >> +                        rte_dispatcher_process_t process_fun,
> >> +                        void *process_data)
> >> +{
> >> +  struct rte_dispatcher *dispatcher;
> >> +  struct rte_dispatcher_handler handler = {
> >> +          .match_fun = match_fun,
> >> +          .match_data = match_data,
> >
> > We can have a default function which uses queue_id as matching data.
> > This reduces the application load to provide two callbacks, one for matching
> and one for processing the event.
> > Application can pass NULL parameter for "match_fun", in that case default
> function pointer can be used here.
> >
> 
> But which queue id would this default function pointer match?
> 
> I think you need more API calls to allow for something like this. I've 
> discussed
> this kind of API in some previous messager on this list, if I recall 
> correctly.
> 

Agree, it may require some more APIs to implement this functionality.
I am fine to continue with the current logic.


> >
> >> +          .process_fun = process_fun,
> >> +          .process_data = process_data
> >> +  };
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >> +  handler.id = evd_alloc_handler_id(dispatcher);
> >> +
> >> +  if (handler.id < 0)
> >> +          return -ENOMEM;
> >> +
> >> +  evd_install_handler(dispatcher, &handler);
> >> +
> >> +  return handler.id;
> >> +}
> >> +
> >> +static int
> >> +evd_lcore_uninstall_handler(struct rte_dispatcher_lcore *lcore,
> >> +                      int handler_id)
> >> +{
> >> +  struct rte_dispatcher_handler *unreg_handler;
> >> +  int handler_idx;
> >> +  uint16_t last_idx;
> >> +
> >> +  unreg_handler = evd_lcore_get_handler_by_id(lcore, handler_id);
> >> +
> >> +  if (unreg_handler == NULL)
> >> +          return -EINVAL;
> >> +
> >
> > Shouldn't the logic be " handler_idx = unreg_handler - &lcore->handlers[0];"
> > Because, unreg_handler will be a higher or equal address to the
> > handler base address (&lcore->handlers[0])
> >
> 
> True. Will fix.
> 
> >
> >> +  handler_idx = &lcore->handlers[0] - unreg_handler;
> >> +
> >> +  last_idx = lcore->num_handlers - 1;
> >> +
> >> +  if (handler_idx != last_idx) {
> >> +          /* move all handlers to maintain handler order */
> >> +          int n = last_idx - handler_idx;
> >> +          memmove(unreg_handler, unreg_handler + 1,
> >> +                  sizeof(struct rte_dispatcher_handler) * n);
> >> +  }
> >> +
> >> +  lcore->num_handlers--;
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +static int
> >> +evd_uninstall_handler(struct rte_dispatcher *dispatcher,
> >> +                int handler_id)
> >> +{
> >> +  unsigned int lcore_id;
> >> +
> >> +  for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> >> +          struct rte_dispatcher_lcore *lcore =
> >> +                  &dispatcher->lcores[lcore_id];
> >> +          int rc;
> >> +
> >> +          rc = evd_lcore_uninstall_handler(lcore, handler_id);
> >> +
> >> +          if (rc < 0)
> >> +                  return rc;
> >> +  }
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_unregister(uint8_t id, int handler_id) {
> >> +  struct rte_dispatcher *dispatcher;
> >> +  int rc;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >> +  rc = evd_uninstall_handler(dispatcher, handler_id);
> >> +
> >> +  return rc;
> >> +}
> >> +
> >> +static struct rte_dispatcher_finalizer*
> >> +evd_get_finalizer_by_id(struct rte_dispatcher *dispatcher,
> >> +                 int handler_id)
> >> +{
> >> +  int i;
> >> +
> >> +  for (i = 0; i < dispatcher->num_finalizers; i++) {
> >> +          struct rte_dispatcher_finalizer *finalizer =
> >> +                  &dispatcher->finalizers[i];
> >> +
> >> +          if (finalizer->id == handler_id)
> >> +                  return finalizer;
> >> +  }
> >> +
> >> +  return NULL;
> >> +}
> >> +
> >> +static int
> >> +evd_alloc_finalizer_id(struct rte_dispatcher *dispatcher) {
> >> +  int finalizer_id = 0;
> >> +
> >> +  while (evd_get_finalizer_by_id(dispatcher, finalizer_id) != NULL)
> >> +          finalizer_id++;
> >> +
> >> +  return finalizer_id;
> >> +}
> >> +
> >> +static struct rte_dispatcher_finalizer * evd_alloc_finalizer(struct
> >> +rte_dispatcher *dispatcher) {
> >> +  int finalizer_idx;
> >> +  struct rte_dispatcher_finalizer *finalizer;
> >> +
> >> +  if (dispatcher->num_finalizers == EVD_MAX_FINALIZERS)
> >> +          return NULL;
> >> +
> >> +  finalizer_idx = dispatcher->num_finalizers;
> >> +  finalizer = &dispatcher->finalizers[finalizer_idx];
> >> +
> >> +  finalizer->id = evd_alloc_finalizer_id(dispatcher);
> >> +
> >> +  dispatcher->num_finalizers++;
> >> +
> >> +  return finalizer;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_finalize_register(uint8_t id,
> >> +                        rte_dispatcher_finalize_t finalize_fun,
> >> +                        void *finalize_data)
> >> +{
> >> +  struct rte_dispatcher *dispatcher;
> >> +  struct rte_dispatcher_finalizer *finalizer;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >> +  finalizer = evd_alloc_finalizer(dispatcher);
> >> +
> >> +  if (finalizer == NULL)
> >> +          return -ENOMEM;
> >> +
> >> +  finalizer->finalize_fun = finalize_fun;
> >> +  finalizer->finalize_data = finalize_data;
> >> +
> >> +  return finalizer->id;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_finalize_unregister(uint8_t id, int handler_id) {
> >> +  struct rte_dispatcher *dispatcher;
> >> +  struct rte_dispatcher_finalizer *unreg_finalizer;
> >> +  int finalizer_idx;
> >> +  uint16_t last_idx;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >> +  unreg_finalizer = evd_get_finalizer_by_id(dispatcher, handler_id);
> >> +
> >> +  if (unreg_finalizer == NULL)
> >> +          return -EINVAL;
> >> +
> >
> > Same as above comment in rte_dispatcher_unregister, base address needs
> > to be subtracted from unreg_finalizer
> >
> 
> Yes.
> 
> >
> >> +  finalizer_idx = &dispatcher->finalizers[0] - unreg_finalizer;
> >> +
> >> +  last_idx = dispatcher->num_finalizers - 1;
> >> +
> >> +  if (finalizer_idx != last_idx) {
> >> +          /* move all finalizers to maintain order */
> >> +          int n = last_idx - finalizer_idx;
> >> +          memmove(unreg_finalizer, unreg_finalizer + 1,
> >> +                  sizeof(struct rte_dispatcher_finalizer) * n);
> >> +  }
> >> +
> >> +  dispatcher->num_finalizers--;
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +static int
> >> +evd_set_service_runstate(uint8_t id, int state) {
> >> +  struct rte_dispatcher *dispatcher;
> >> +  int rc;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >> +  rc = rte_service_component_runstate_set(dispatcher->service_id,
> >> +                                          state);
> >> +
> >> +  if (rc != 0) {
> >> +          RTE_EDEV_LOG_ERR("Unexpected error %d occurred while
> >> setting "
> >> +                           "service component run state to %d\n", rc,
> >> +                           state);
> >> +          RTE_ASSERT(0);
> >> +  }
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_start(uint8_t id)
> >> +{
> >> +  return evd_set_service_runstate(id, 1); }
> >> +
> >> +int
> >> +rte_dispatcher_stop(uint8_t id)
> >> +{
> >> +  return evd_set_service_runstate(id, 0); }
> >> +
> >> +static void
> >> +evd_aggregate_stats(struct rte_dispatcher_stats *result,
> >> +              const struct rte_dispatcher_stats *part) {
> >> +  result->poll_count += part->poll_count;
> >> +  result->ev_batch_count += part->ev_batch_count;
> >> +  result->ev_dispatch_count += part->ev_dispatch_count;
> >> +  result->ev_drop_count += part->ev_drop_count; }
> >> +
> >> +int
> >> +rte_dispatcher_stats_get(uint8_t id,
> >> +                         struct rte_dispatcher_stats *stats) {
> >> +  struct rte_dispatcher *dispatcher;
> >> +  unsigned int lcore_id;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >
> > Stats pointer needs to be validated for NULL before accessing
> >
> >
> 
> Yes.
> 
> Thanks a lot for your review comments.
> 
> >> +  *stats = (struct rte_dispatcher_stats) {};
> >> +
> >> +  for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> >> +          struct rte_dispatcher_lcore *lcore =
> >> +                  &dispatcher->lcores[lcore_id];
> >> +
> >> +          evd_aggregate_stats(stats, &lcore->stats);
> >> +  }
> >> +
> >> +  return 0;
> >> +}
> >> +
> >> +int
> >> +rte_dispatcher_stats_reset(uint8_t id) {
> >> +  struct rte_dispatcher *dispatcher;
> >> +  unsigned int lcore_id;
> >> +
> >> +  EVD_VALID_ID_OR_RET_EINVAL(id);
> >> +  dispatcher = evd_get_dispatcher(id);
> >> +
> >> +
> >> +  for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> >> +          struct rte_dispatcher_lcore *lcore =
> >> +                  &dispatcher->lcores[lcore_id];
> >> +
> >> +          lcore->stats = (struct rte_dispatcher_stats) {};
> >> +  }
> >> +
> >> +  return 0;
> >> +
> >> +}
> >> diff --git a/lib/dispatcher/rte_dispatcher.h
> >> b/lib/dispatcher/rte_dispatcher.h new file mode 100644 index
> >> 0000000000..6712687a08
> >> --- /dev/null
> >> +++ b/lib/dispatcher/rte_dispatcher.h
> >> @@ -0,0 +1,480 @@
> >> +/* SPDX-License-Identifier: BSD-3-Clause
> >> + * Copyright(c) 2023 Ericsson AB
> >> + */
> >> +
> >> +#ifndef __RTE_DISPATCHER_H__
> >> +#define __RTE_DISPATCHER_H__
> >> +
> >> +/**
> >> + * @file
> >> + *
> >> + * RTE Dispatcher
> >> + *
> >> + * The purpose of the dispatcher is to help decouple different parts
> >> + * of an application (e.g., modules), sharing the same underlying
> >> + * event device.
> >> + */
> >> +
> >> +#ifdef __cplusplus
> >> +extern "C" {
> >> +#endif
> >> +
> >> +#include <rte_eventdev.h>
> >> +
> >> +/**
> >> + * Function prototype for match callbacks.
> >> + *
> >> + * Match callbacks are used by an application to decide how the
> >> + * dispatcher distributes events to different parts of the
> >> + * application.
> >> + *
> >> + * The application is not expected to process the event at the point
> >> + * of the match call. Such matters should be deferred to the process
> >> + * callback invocation.
> >> + *
> >> + * The match callback may be used as an opportunity to prefetch data.
> >> + *
> >> + * @param event
> >> + *  Pointer to event
> >> + *
> >> + * @param cb_data
> >> + *  The pointer supplied by the application in
> >> + *  rte_dispatcher_register().
> >> + *
> >> + * @return
> >> + *   Returns true in case this events should be delivered (via
> >> + *   the process callback), and false otherwise.
> >> + */
> >> +typedef bool
> >> +(*rte_dispatcher_match_t)(const struct rte_event *event, void
> >> +*cb_data);
> >> +
> >> +/**
> >> + * Function prototype for process callbacks.
> >> + *
> >> + * The process callbacks are used by the dispatcher to deliver
> >> + * events for processing.
> >> + *
> >> + * @param event_dev_id
> >> + *  The originating event device id.
> >> + *
> >> + * @param event_port_id
> >> + *  The originating event port.
> >> + *
> >> + * @param events
> >> + *  Pointer to an array of events.
> >> + *
> >> + * @param num
> >> + *  The number of events in the @p events array.
> >> + *
> >> + * @param cb_data
> >> + *  The pointer supplied by the application in
> >> + *  rte_dispatcher_register().
> >> + */
> >> +
> >> +typedef void
> >> +(*rte_dispatcher_process_t)(uint8_t event_dev_id, uint8_t event_port_id,
> >> +                            struct rte_event *events, uint16_t num,
> >> +                            void *cb_data);
> >> +
> >> +/**
> >> + * Function prototype for finalize callbacks.
> >> + *
> >> + * The finalize callbacks are used by the dispatcher to notify the
> >> + * application it has delivered all events from a particular batch
> >> + * dequeued from the event device.
> >> + *
> >> + * @param event_dev_id
> >> + *  The originating event device id.
> >> + *
> >> + * @param event_port_id
> >> + *  The originating event port.
> >> + *
> >> + * @param cb_data
> >> + *  The pointer supplied by the application in
> >> + *  rte_dispatcher_finalize_register().
> >> + */
> >> +
> >> +typedef void
> >> +(*rte_dispatcher_finalize_t)(uint8_t event_dev_id, uint8_t event_port_id,
> >> +                             void *cb_data);
> >> +
> >> +/**
> >> + * Dispatcher statistics
> >> + */
> >> +struct rte_dispatcher_stats {
> >> +  uint64_t poll_count;
> >> +  /**< Number of event dequeue calls made toward the event device. */
> >> +  uint64_t ev_batch_count;
> >> +  /**< Number of non-empty event batches dequeued from event
> >> device.*/
> >> +  uint64_t ev_dispatch_count;
> >> +  /**< Number of events dispatched to a handler.*/
> >> +  uint64_t ev_drop_count;
> >> +  /**< Number of events dropped because no handler was found. */ };
> >> +
> >> +/**
> >> + * Create a dispatcher with the specified id.
> >> + *
> >> + * @param id
> >> + *  An application-specified, unique (across all dispatcher
> >> + *  instances) identifier.
> >> + *
> >> + * @param event_dev_id
> >> + *  The identifier of the event device from which this dispatcher
> >> + *  will dequeue events.
> >> + *
> >> + * @return
> >> + *   - 0: Success
> >> + *   - <0: Error code on failure
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_create(uint8_t id, uint8_t event_dev_id);
> >> +
> >> +/**
> >> + * Free a dispatcher.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @return
> >> + *  - 0: Success
> >> + *  - <0: Error code on failure
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_free(uint8_t id);
> >> +
> >> +/**
> >> + * Retrieve the service identifier of a dispatcher.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @param [out] service_id
> >> + *  A pointer to a caller-supplied buffer where the dispatcher's
> >> + *  service id will be stored.
> >> + *
> >> + * @return
> >> + *  - 0: Success
> >> + *  - <0: Error code on failure.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_service_id_get(uint8_t id, uint32_t *service_id);
> >> +
> >> +/**
> >> + * Binds an event device port to a specific lcore on the specified
> >> + * dispatcher.
> >> + *
> >> + * This function configures the event port id to be used by the
> >> +event
> >> + * dispatcher service, if run on the specified lcore.
> >> + *
> >> + * Multiple event device ports may be bound to the same lcore. A
> >> + * particular port must not be bound to more than one lcore.
> >> + *
> >> + * If the dispatcher service is mapped (with
> >> +rte_service_map_lcore_set())
> >> + * to a lcore to which no ports are bound, the service function will
> >> +be a
> >> + * no-operation.
> >> + *
> >> + * This function may be called by any thread (including unregistered
> >> + * non-EAL threads), but not while the dispatcher is running on
> >> +lcore
> >> + * specified by @c lcore_id.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @param event_port_id
> >> + *  The event device port identifier.
> >> + *
> >> + * @param batch_size
> >> + *  The batch size to use in rte_event_dequeue_burst(), for the
> >> + *  configured event device port and lcore.
> >> + *
> >> + * @param timeout
> >> + *  The timeout parameter to use in rte_event_dequeue_burst(), for
> >> +the
> >> + *  configured event device port and lcore.
> >> + *
> >> + * @param lcore_id
> >> + *  The lcore by which this event port will be used.
> >> + *
> >> + * @return
> >> + *  - 0: Success
> >> + *  - -ENOMEM: Unable to allocate sufficient resources.
> >> + *  - -EEXISTS: Event port is already configured.
> >> + *  - -EINVAL: Invalid arguments.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_bind_port_to_lcore(uint8_t id, uint8_t event_port_id,
> >> +                                  uint16_t batch_size, uint64_t
> >> timeout,
> >> +                                  unsigned int lcore_id);
> >> +
> >> +/**
> >> + * Unbind an event device port from a specific lcore.
> >> + *
> >> + * This function may be called by any thread (including unregistered
> >> + * non-EAL threads), but not while the dispatcher is running on
> >> + * lcore specified by @c lcore_id.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @param event_port_id
> >> + *  The event device port identifier.
> >> + *
> >> + * @param lcore_id
> >> + *  The lcore which was using this event port.
> >> + *
> >> + * @return
> >> + *  - 0: Success
> >> + *  - -EINVAL: Invalid @c id.
> >> + *  - -ENOENT: Event port id not bound to this @c lcore_id.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_unbind_port_from_lcore(uint8_t id, uint8_t
> event_port_id,
> >> +                                      unsigned int lcore_id);
> >> +
> >> +/**
> >> + * Register an event handler.
> >> + *
> >> + * The match callback function is used to select if a particular
> >> +event
> >> + * should be delivered, using the corresponding process callback
> >> + * function.
> >> + *
> >> + * The reason for having two distinct steps is to allow the
> >> +dispatcher
> >> + * to deliver all events as a batch. This in turn will cause
> >> + * processing of a particular kind of events to happen in a
> >> + * back-to-back manner, improving cache locality.
> >> + *
> >> + * The list of handler callback functions is shared among all
> >> +lcores,
> >> + * but will only be executed on lcores which has an eventdev port
> >> + * bound to them, and which are running the dispatcher service.
> >> + *
> >> + * An event is delivered to at most one handler. Events where no
> >> + * handler is found are dropped.
> >> + *
> >> + * The application must not depend on the order of which the match
> >> + * functions are invoked.
> >> + *
> >> + * Ordering of events is not guaranteed to be maintained between
> >> + * different deliver callbacks. For example, suppose there are two
> >> + * callbacks registered, matching different subsets of events
> >> +arriving
> >> + * on an atomic queue. A batch of events [ev0, ev1, ev2] are
> >> +dequeued
> >> + * on a particular port, all pertaining to the same flow. The match
> >> + * callback for registration A returns true for ev0 and ev2, and the
> >> + * matching function for registration B for ev1. In that scenario,
> >> +the
> >> + * dispatcher may choose to deliver first [ev0, ev2] using A's
> >> +deliver
> >> + * function, and then [ev1] to B - or vice versa.
> >> + *
> >> + * rte_dispatcher_register() may be called by any thread
> >> + * (including unregistered non-EAL threads), but not while the event
> >> + * dispatcher is running on any service lcore.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @param match_fun
> >> + *  The match callback function.
> >> + *
> >> + * @param match_cb_data
> >> + *  A pointer to some application-specific opaque data (or NULL),
> >> + *  which is supplied back to the application when match_fun is
> >> + *  called.
> >> + *
> >> + * @param process_fun
> >> + *  The process callback function.
> >> + *
> >> + * @param process_cb_data
> >> + *  A pointer to some application-specific opaque data (or NULL),
> >> + *  which is supplied back to the application when process_fun is
> >> + *  called.
> >> + *
> >> + * @return
> >> + *  - >= 0: The identifier for this registration.
> >> + *  - -ENOMEM: Unable to allocate sufficient resources.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_register(uint8_t id,
> >> +                        rte_dispatcher_match_t match_fun,
> >> +                        void *match_cb_data,
> >> +                        rte_dispatcher_process_t process_fun,
> >> +                        void *process_cb_data);
> >> +
> >> +/**
> >> + * Unregister an event handler.
> >> + *
> >> + * This function may be called by any thread (including unregistered
> >> + * non-EAL threads), but not while the dispatcher is running on
> >> + * any service lcore.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @param handler_id
> >> + *  The handler registration id returned by the original
> >> + *  rte_dispatcher_register() call.
> >> + *
> >> + * @return
> >> + *  - 0: Success
> >> + *  - -EINVAL: The @c id and/or the @c handler_id parameter was invalid.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_unregister(uint8_t id, int handler_id);
> >> +
> >> +/**
> >> + * Register a finalize callback function.
> >> + *
> >> + * An application may optionally install one or more finalize
> >> + * callbacks.
> >> + *
> >> + * All finalize callbacks are invoked by the dispatcher when a
> >> + * complete batch of events (retrieve using
> >> +rte_event_dequeue_burst())
> >> + * have been delivered to the application (or have been dropped).
> >> + *
> >> + * The finalize callback is not tied to any particular handler.
> >> + *
> >> + * The finalize callback provides an opportunity for the application
> >> + * to do per-batch processing. One case where this may be useful is
> >> +if
> >> + * an event output buffer is used, and is shared among several
> >> + * handlers. In such a case, proper output buffer flushing may be
> >> + * assured using a finalize callback.
> >> + *
> >> + * rte_dispatcher_finalize_register() may be called by any thread
> >> + * (including unregistered non-EAL threads), but not while the
> >> + * dispatcher is running on any service lcore.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @param finalize_fun
> >> + *  The function called after completing the processing of a
> >> + *  dequeue batch.
> >> + *
> >> + * @param finalize_data
> >> + *  A pointer to some application-specific opaque data (or NULL),
> >> + *  which is supplied back to the application when @c finalize_fun
> >> +is
> >> + *  called.
> >> + *
> >> + * @return
> >> + *  - >= 0: The identifier for this registration.
> >> + *  - -ENOMEM: Unable to allocate sufficient resources.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_finalize_register(uint8_t id,
> >> +                           rte_dispatcher_finalize_t finalize_fun,
> >> +                           void *finalize_data);
> >> +
> >> +/**
> >> + * Unregister a finalize callback.
> >> + *
> >> + * This function may be called by any thread (including unregistered
> >> + * non-EAL threads), but not while the dispatcher is running on
> >> + * any service lcore.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @param reg_id
> >> + *  The finalize registration id returned by the original
> >> + *  rte_dispatcher_finalize_register() call.
> >> + *
> >> + * @return
> >> + *  - 0: Success
> >> + *  - -EINVAL: The @c id and/or the @c reg_id parameter was invalid.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_finalize_unregister(uint8_t id, int reg_id);
> >> +
> >> +/**
> >> + * Start a dispatcher instance.
> >> + *
> >> + * Enables the dispatcher service.
> >> + *
> >> + * The underlying event device must have been started prior to
> >> +calling
> >> + * rte_dispatcher_start().
> >> + *
> >> + * For the dispatcher to actually perform work (i.e., dispatch
> >> + * events), its service must have been mapped to one or more service
> >> + * lcores, and its service run state set to '1'. A dispatcher's
> >> + * service is retrieved using rte_dispatcher_service_id_get().
> >> + *
> >> + * Each service lcore to which the dispatcher is mapped should
> >> + * have at least one event port configured. Such configuration is
> >> + * performed by calling rte_dispatcher_bind_port_to_lcore(), prior
> >> +to
> >> + * starting the dispatcher.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @return
> >> + *  - 0: Success
> >> + *  - -EINVAL: Invalid @c id.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_start(uint8_t id);
> >> +
> >> +/**
> >> + * Stop a running dispatcher instance.
> >> + *
> >> + * Disables the dispatcher service.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @return
> >> + *  - 0: Success
> >> + *  - -EINVAL: Invalid @c id.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_stop(uint8_t id);
> >> +
> >> +/**
> >> + * Retrieve statistics for a dispatcher instance.
> >> + *
> >> + * This function is MT safe and may be called by any thread
> >> + * (including unregistered non-EAL threads).
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + * @param[out] stats
> >> + *   A pointer to a structure to fill with statistics.
> >> + * @return
> >> + *  - 0: Success
> >> + *  - -EINVAL: The @c id parameter was invalid.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_stats_get(uint8_t id,
> >> +                         struct rte_dispatcher_stats *stats);
> >> +
> >> +/**
> >> + * Reset statistics for a dispatcher instance.
> >> + *
> >> + * This function may be called by any thread (including unregistered
> >> + * non-EAL threads), but may not produce the correct result if the
> >> + * dispatcher is running on any service lcore.
> >> + *
> >> + * @param id
> >> + *  The dispatcher identifier.
> >> + *
> >> + * @return
> >> + *  - 0: Success
> >> + *  - -EINVAL: The @c id parameter was invalid.
> >> + */
> >> +__rte_experimental
> >> +int
> >> +rte_dispatcher_stats_reset(uint8_t id);
> >> +
> >> +#ifdef __cplusplus
> >> +}
> >> +#endif
> >> +
> >> +#endif /* __RTE_DISPATCHER__ */
> >> diff --git a/lib/dispatcher/version.map b/lib/dispatcher/version.map
> >> new file mode 100644 index 0000000000..8f9ad96522
> >> --- /dev/null
> >> +++ b/lib/dispatcher/version.map
> >> @@ -0,0 +1,20 @@
> >> +EXPERIMENTAL {
> >> +  global:
> >> +
> >> +  # added in 23.11
> >> +  rte_dispatcher_create;
> >> +  rte_dispatcher_free;
> >> +  rte_dispatcher_service_id_get;
> >> +  rte_dispatcher_bind_port_to_lcore;
> >> +  rte_dispatcher_unbind_port_from_lcore;
> >> +  rte_dispatcher_register;
> >> +  rte_dispatcher_unregister;
> >> +  rte_dispatcher_finalize_register;
> >> +  rte_dispatcher_finalize_unregister;
> >> +  rte_dispatcher_start;
> >> +  rte_dispatcher_stop;
> >> +  rte_dispatcher_stats_get;
> >> +  rte_dispatcher_stats_reset;
> >> +
> >> +  local: *;
> >> +};
> >> diff --git a/lib/meson.build b/lib/meson.build index
> >> 099b0ed18a..3093b338d2 100644
> >> --- a/lib/meson.build
> >> +++ b/lib/meson.build
> >> @@ -35,6 +35,7 @@ libraries = [
> >>           'distributor',
> >>           'efd',
> >>           'eventdev',
> >> +        'dispatcher', # dispatcher depends on eventdev
> >>           'gpudev',
> >>           'gro',
> >>           'gso',
> >> @@ -81,6 +82,7 @@ optional_libs = [
> >>           'cfgfile',
> >>           'compressdev',
> >>           'cryptodev',
> >> +        'dispatcher',
> >>           'distributor',
> >>           'dmadev',
> >>           'efd',
> >> --
> >> 2.34.1
> >

Reply via email to