On 1 Apr 2026, at 13:57, Eelco Chaudron via dev wrote:

> This patch adds support for specific PMD thread initialization,
> deinitialization, and a callback execution to perform work as
> part of the PMD thread loop. This allows hardware offload
> providers to handle any specific asynchronous or batching work.
>
> This patch also adds cycle statistics for the provider-specific
> callbacks to the 'ovs-appctl dpif-netdev/pmd-perf-show' command.

Bringing back the discussion on the earlier patch between Ilya and Gaetan to 
this revision :)

Ilya:
  Hi, Eelco.  As we talked before, this infrastructure resembles the async
  work infra that was proposed in the past for the use case of async vhost
  processing.  And I don't see any real use case proposed for it here nor
  in the RFC, where the question was asked, but not replied.

Gaetan:

  Hi Ilya, Eelco,

  Thanks for the patch and for the review.

  The use-case on our side is distributed data-structures in DOCA that
  requires each participating threads to do maintenance work periodically.

  Specifically, offload threads will insert offload objects.
  Those will reserve entries in a map that can be resized. The DOCA
  implementation requires any thread that owns an entry to perform the
  work of moving it to the new bucket / space after resize is initiated.

  This is a pervasive design choice in DOCA, they write most of their APIs
  assuming participating threads are periodically calling into these
  maintenance functions.

  Some of such work is also time-sensitive, for example the current
  implementation requires a CT offload thread to receive completions after
  some hardware initialization. Until this completion is done, the CT
  offload entry is not fully usable (cannot be queried for activity /
  counters). We cannot leave batches of CT offload entry waiting for
  completion, assuming that at some later point, we will eventually
  re-execute something in our offload provider: it leaves a few stranded
  connection objects incomplete.

  This has the result of having hardware execution of a flow with CT
  actions, but no activity counters: the software datapath then deletes
  the connection and/or flow due to inactivity.

Thanks,

Eelco

> Signed-off-by: Eelco Chaudron <[email protected]>
> ---
>  lib/dpif-netdev-perf.c        |  19 ++++-
>  lib/dpif-netdev-perf.h        |   3 +-
>  lib/dpif-netdev.c             |  42 ++++++++++-
>  lib/dpif-offload-dummy.c      |  38 ++++++++++
>  lib/dpif-offload-provider.h   |  26 +++++++
>  lib/dpif-offload.c            | 133 ++++++++++++++++++++++++++++++++++
>  lib/dpif-offload.h            |  11 +++
>  tests/pmd.at                  |  32 ++++++++
>  utilities/checkpatch_dict.txt |   2 +
>  9 files changed, 298 insertions(+), 8 deletions(-)
>
> diff --git a/lib/dpif-netdev-perf.c b/lib/dpif-netdev-perf.c
> index 1cd4ee0842..39465ba819 100644
> --- a/lib/dpif-netdev-perf.c
> +++ b/lib/dpif-netdev-perf.c
> @@ -232,6 +232,7 @@ pmd_perf_format_overall_stats(struct ds *str, struct 
> pmd_perf_stats *s,
>      uint64_t busy_iter = tot_iter >= idle_iter ? tot_iter - idle_iter : 0;
>      uint64_t sleep_iter = stats[PMD_SLEEP_ITER];
>      uint64_t tot_sleep_cycles = stats[PMD_CYCLES_SLEEP];
> +    uint64_t offload_cycles = stats[PMD_CYCLES_OFFLOAD];
>
>      ds_put_format(str,
>              "  Iterations:         %12"PRIu64"  (%.2f us/it)\n"
> @@ -242,7 +243,8 @@ pmd_perf_format_overall_stats(struct ds *str, struct 
> pmd_perf_stats *s,
>              "  Sleep time (us):    %12.0f  (%3.0f us/iteration avg.)\n",
>              tot_iter,
>              tot_iter
> -                ? (tot_cycles + tot_sleep_cycles) * us_per_cycle / tot_iter
> +                ? (tot_cycles + tot_sleep_cycles + offload_cycles)
> +                  * us_per_cycle / tot_iter
>                  : 0,
>              tot_cycles, 100.0 * (tot_cycles / duration) / tsc_hz,
>              idle_iter,
> @@ -252,6 +254,13 @@ pmd_perf_format_overall_stats(struct ds *str, struct 
> pmd_perf_stats *s,
>              sleep_iter, tot_iter ? 100.0 * sleep_iter / tot_iter : 0,
>              tot_sleep_cycles * us_per_cycle,
>              sleep_iter ? (tot_sleep_cycles * us_per_cycle) / sleep_iter : 0);
> +    if (offload_cycles > 0) {
> +        ds_put_format(str,
> +            "  Offload cycles:     %12" PRIu64 "  (%5.1f %% of used 
> cycles)\n",
> +            offload_cycles,
> +            100.0 * offload_cycles / (tot_cycles + tot_sleep_cycles
> +                                      + offload_cycles));
> +    }
>      if (rx_packets > 0) {
>          ds_put_format(str,
>              "  Rx packets:         %12"PRIu64"  (%.0f Kpps, %.0f 
> cycles/pkt)\n"
> @@ -532,14 +541,14 @@ OVS_REQUIRES(s->stats_mutex)
>  void
>  pmd_perf_end_iteration(struct pmd_perf_stats *s, int rx_packets,
>                         int tx_packets, uint64_t sleep_cycles,
> -                       bool full_metrics)
> +                       uint64_t offload_cycles, bool full_metrics)
>  {
>      uint64_t now_tsc = cycles_counter_update(s);
>      struct iter_stats *cum_ms;
>      uint64_t cycles, cycles_per_pkt = 0;
>      char *reason = NULL;
>
> -    cycles = now_tsc - s->start_tsc - sleep_cycles;
> +    cycles = now_tsc - s->start_tsc - sleep_cycles - offload_cycles;
>      s->current.timestamp = s->iteration_cnt;
>      s->current.cycles = cycles;
>      s->current.pkts = rx_packets;
> @@ -558,6 +567,10 @@ pmd_perf_end_iteration(struct pmd_perf_stats *s, int 
> rx_packets,
>          pmd_perf_update_counter(s, PMD_CYCLES_SLEEP, sleep_cycles);
>      }
>
> +    if (offload_cycles) {
> +        pmd_perf_update_counter(s, PMD_CYCLES_OFFLOAD, offload_cycles);
> +    }
> +
>      if (!full_metrics) {
>          return;
>      }
> diff --git a/lib/dpif-netdev-perf.h b/lib/dpif-netdev-perf.h
> index 84beced151..2a055dacdd 100644
> --- a/lib/dpif-netdev-perf.h
> +++ b/lib/dpif-netdev-perf.h
> @@ -82,6 +82,7 @@ enum pmd_stat_type {
>      PMD_CYCLES_UPCALL,      /* Cycles spent processing upcalls. */
>      PMD_SLEEP_ITER,         /* Iterations where a sleep has taken place. */
>      PMD_CYCLES_SLEEP,       /* Total cycles slept to save power. */
> +    PMD_CYCLES_OFFLOAD,     /* Total cycles spend handling offload. */
>      PMD_N_STATS
>  };
>
> @@ -411,7 +412,7 @@ pmd_perf_start_iteration(struct pmd_perf_stats *s);
>  void
>  pmd_perf_end_iteration(struct pmd_perf_stats *s, int rx_packets,
>                         int tx_packets, uint64_t sleep_cycles,
> -                       bool full_metrics);
> +                       uint64_t offload_cycles, bool full_metrics);
>
>  /* Formatting the output of commands. */
>
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index 9df05c4c28..64531a02c0 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -329,6 +329,9 @@ struct dp_netdev {
>      uint64_t last_reconfigure_seq;
>      struct ovsthread_once once_set_config;
>
> +    /* When a reconfigure is requested, forcefully reload all PMDs. */
> +    bool force_pmd_reload;
> +
>      /* Cpu mask for pin of pmd threads. */
>      char *pmd_cmask;
>
> @@ -339,6 +342,7 @@ struct dp_netdev {
>
>      struct conntrack *conntrack;
>      struct pmd_auto_lb pmd_alb;
> +    bool offload_enabled;
>
>      /* Bonds. */
>      struct ovs_mutex bond_mutex; /* Protects updates of 'tx_bonds'. */
> @@ -4556,6 +4560,14 @@ dpif_netdev_set_config(struct dpif *dpif, const struct 
> smap *other_config)
>          log_all_pmd_sleeps(dp);
>      }
>
> +    if (!dp->offload_enabled) {
> +        dp->offload_enabled = dpif_offload_enabled();
> +        if (dp->offload_enabled) {
> +            dp->force_pmd_reload = true;
> +            dp_netdev_request_reconfigure(dp);
> +        }
> +    }
> +
>      return 0;
>  }
>
> @@ -6216,6 +6228,14 @@ reconfigure_datapath(struct dp_netdev *dp)
>          ovs_mutex_unlock(&pmd->port_mutex);
>      }
>
> +    /* Do we need to forcefully reload all threads? */
> +    if (dp->force_pmd_reload) {
> +        CMAP_FOR_EACH (pmd, node, &dp->poll_threads) {
> +            pmd->need_reload = true;
> +        }
> +        dp->force_pmd_reload = false;
> +    }
> +
>      /* Reload affected pmd threads. */
>      reload_affected_pmds(dp);
>
> @@ -6516,6 +6536,7 @@ pmd_thread_main(void *f_)
>  {
>      struct dp_netdev_pmd_thread *pmd = f_;
>      struct pmd_perf_stats *s = &pmd->perf_stats;
> +    struct dpif_offload_pmd_ctx *offload_ctx = NULL;
>      unsigned int lc = 0;
>      struct polled_queue *poll_list;
>      bool wait_for_reload = false;
> @@ -6549,6 +6570,9 @@ reload:
>          dpdk_attached = dpdk_attach_thread(pmd->core_id);
>      }
>
> +    dpif_offload_pmd_thread_reload(pmd->dp->full_name, pmd->core_id,
> +                                   pmd->numa_id, &offload_ctx);
> +
>      /* List port/core affinity */
>      for (i = 0; i < poll_cnt; i++) {
>         VLOG_DBG("Core %d processing port \'%s\' with queue-id %d\n",
> @@ -6588,7 +6612,7 @@ reload:
>      ovs_mutex_lock(&pmd->perf_stats.stats_mutex);
>      for (;;) {
>          uint64_t rx_packets = 0, tx_packets = 0;
> -        uint64_t time_slept = 0;
> +        uint64_t time_slept = 0, offload_cycles = 0;
>          uint64_t max_sleep;
>
>          pmd_perf_start_iteration(s);
> @@ -6628,6 +6652,10 @@ reload:
>                                                     ? true : false);
>          }
>
> +        /* Do work required by any of the hardware offload providers. */
> +        offload_cycles = dpif_offload_pmd_thread_do_work(offload_ctx,
> +                                                         &pmd->perf_stats);
> +
>          if (max_sleep) {
>              /* Check if a sleep should happen on this iteration. */
>              if (sleep_time) {
> @@ -6687,7 +6715,7 @@ reload:
>          }
>
>          pmd_perf_end_iteration(s, rx_packets, tx_packets, time_slept,
> -                               pmd_perf_metrics_enabled(pmd));
> +                               offload_cycles, 
> pmd_perf_metrics_enabled(pmd));
>      }
>      ovs_mutex_unlock(&pmd->perf_stats.stats_mutex);
>
> @@ -6708,6 +6736,7 @@ reload:
>          goto reload;
>      }
>
> +    dpif_offload_pmd_thread_exit(offload_ctx);
>      pmd_free_static_tx_qid(pmd);
>      dfc_cache_uninit(&pmd->flow_cache);
>      free(poll_list);
> @@ -9623,7 +9652,7 @@ dp_netdev_pmd_try_optimize(struct dp_netdev_pmd_thread 
> *pmd,
>                             struct polled_queue *poll_list, int poll_cnt)
>  {
>      struct dpcls *cls;
> -    uint64_t tot_idle = 0, tot_proc = 0, tot_sleep = 0;
> +    uint64_t tot_idle = 0, tot_proc = 0, tot_sleep = 0, tot_offload = 0;
>      unsigned int pmd_load = 0;
>
>      if (pmd->ctx.now > pmd->next_cycle_store) {
> @@ -9642,11 +9671,14 @@ dp_netdev_pmd_try_optimize(struct 
> dp_netdev_pmd_thread *pmd,
>                         pmd->prev_stats[PMD_CYCLES_ITER_BUSY];
>              tot_sleep = pmd->perf_stats.counters.n[PMD_CYCLES_SLEEP] -
>                          pmd->prev_stats[PMD_CYCLES_SLEEP];
> +            tot_offload = pmd->perf_stats.counters.n[PMD_CYCLES_OFFLOAD] -
> +                          pmd->prev_stats[PMD_CYCLES_OFFLOAD];
>
>              if (pmd_alb->is_enabled && !pmd->isolated) {
>                  if (tot_proc) {
>                      pmd_load = ((tot_proc * 100) /
> -                                    (tot_idle + tot_proc + tot_sleep));
> +                                    (tot_idle + tot_proc + tot_sleep
> +                                     + tot_offload));
>                  }
>
>                  atomic_read_relaxed(&pmd_alb->rebalance_load_thresh,
> @@ -9665,6 +9697,8 @@ dp_netdev_pmd_try_optimize(struct dp_netdev_pmd_thread 
> *pmd,
>                          pmd->perf_stats.counters.n[PMD_CYCLES_ITER_BUSY];
>          pmd->prev_stats[PMD_CYCLES_SLEEP] =
>                          pmd->perf_stats.counters.n[PMD_CYCLES_SLEEP];
> +        pmd->prev_stats[PMD_CYCLES_OFFLOAD] =
> +                        pmd->perf_stats.counters.n[PMD_CYCLES_OFFLOAD];
>
>          /* Get the cycles that were used to process each queue and store. */
>          for (unsigned i = 0; i < poll_cnt; i++) {
> diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
> index 28f1f40013..80d6ce67c3 100644
> --- a/lib/dpif-offload-dummy.c
> +++ b/lib/dpif-offload-dummy.c
> @@ -17,6 +17,7 @@
>  #include <config.h>
>  #include <errno.h>
>
> +#include "coverage.h"
>  #include "dpif.h"
>  #include "dpif-offload.h"
>  #include "dpif-offload-provider.h"
> @@ -33,6 +34,8 @@
>
>  VLOG_DEFINE_THIS_MODULE(dpif_offload_dummy);
>
> +COVERAGE_DEFINE(dummy_offload_do_work);
> +
>  struct pmd_id_data {
>      struct hmap_node node;
>      void *flow_reference;
> @@ -1020,6 +1023,40 @@ dummy_netdev_hw_offload_run(struct netdev *netdev)
>      }
>  }
>
> +static void
> +dummy_pmd_thread_work_cb(unsigned core_id OVS_UNUSED, int numa_id OVS_UNUSED,
> +                         void *ctx OVS_UNUSED)
> +{
> +    COVERAGE_INC(dummy_offload_do_work);
> +}
> +
> +static void
> +dummy_pmd_thread_lifecycle(const struct dpif_offload *dpif_offload,
> +                           bool exit, unsigned core_id, int numa_id,
> +                           dpif_offload_pmd_thread_work_cb **callback,
> +                           void **ctx)
> +{
> +    /* Only do this for the 'dummy' class, not for 'dummy_x'. */
> +    if (strcmp(dpif_offload_type(dpif_offload), "dummy")) {
> +        *callback = NULL;
> +        *ctx = NULL;
> +        return;
> +    }
> +
> +    VLOG_DBG(
> +        "pmd_thread_lifecycle; exit=%s, core=%u, numa=%d, cb=%p, ctx=%p",
> +        exit ? "true" : "false", core_id, numa_id, *callback, *ctx);
> +
> +    ovs_assert(!*callback || *callback == dummy_pmd_thread_work_cb);
> +
> +    if (exit) {
> +        free(*ctx);
> +    } else {
> +        *ctx = *ctx ? *ctx : xstrdup("DUMMY_OFFLOAD_WORK");
> +        *callback = dummy_pmd_thread_work_cb;
> +    }
> +}
> +
>  #define DEFINE_DPIF_DUMMY_CLASS(NAME, TYPE_STR)                             \
>      struct dpif_offload_class NAME = {                                      \
>          .type = TYPE_STR,                                                   \
> @@ -1039,6 +1076,7 @@ dummy_netdev_hw_offload_run(struct netdev *netdev)
>          .netdev_flow_del = dummy_flow_del,                                  \
>          .netdev_flow_stats = dummy_flow_stats,                              \
>          .register_flow_unreference_cb = dummy_register_flow_unreference_cb, \
> +        .pmd_thread_lifecycle = dummy_pmd_thread_lifecycle                  \
>  }
>
>  DEFINE_DPIF_DUMMY_CLASS(dpif_offload_dummy_class, "dummy");
> diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h
> index 02ef46cb08..259de2c299 100644
> --- a/lib/dpif-offload-provider.h
> +++ b/lib/dpif-offload-provider.h
> @@ -87,6 +87,10 @@ dpif_offload_flow_dump_thread_init(
>  }
>
>
> +/* Offload Provider specific PMD thread work callback definition. */
> +typedef void dpif_offload_pmd_thread_work_cb(unsigned core_id, int numa_id,
> +                                             void *ctx);
> +
>  struct dpif_offload_class {
>      /* Type of DPIF offload provider in this class, e.g., "tc", "dpdk",
>       * "dummy", etc. */
> @@ -305,6 +309,28 @@ struct dpif_offload_class {
>       * to netdev_flow_put() is no longer held by the offload provider. */
>      void (*register_flow_unreference_cb)(const struct dpif_offload *,
>                                           dpif_offload_flow_unreference_cb *);
> +
> +
> +    /* The API below is specific to PMD (userspace) thread lifecycle 
> handling.
> +     *
> +     * This API allows a provider to supply a callback function
> +     * (via `*callback`) and an optional context pointer (via `*ctx`) for a
> +     * PMD thread.
> +     *
> +     * The lifecycle hook may be invoked multiple times for the same PMD
> +     * thread.  For example, when the thread is reinitialized, this function
> +     * will be called again and the previous `callback` and `ctx` values will
> +     * be passed back in.  It is the provider's responsibility to decide
> +     * whether those should be reused, replaced, or cleaned up before storing
> +     * new values.
> +     *
> +     * When the PMD thread is terminating, this API is called with
> +     * `exit == true`.  At that point, the provider must release any 
> resources
> +     * associated with the previously returned `callback` and `ctx`. */
> +    void (*pmd_thread_lifecycle)(const struct dpif_offload *, bool exit,
> +                                 unsigned core_id, int numa_id,
> +                                 dpif_offload_pmd_thread_work_cb **callback,
> +                                 void **ctx);
>  };
>
>  extern struct dpif_offload_class dpif_offload_dummy_class;
> diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c
> index bb2feced9e..cbf1f6c704 100644
> --- a/lib/dpif-offload.c
> +++ b/lib/dpif-offload.c
> @@ -17,6 +17,7 @@
>  #include <config.h>
>  #include <errno.h>
>
> +#include "dpif-netdev-perf.h"
>  #include "dpif-offload.h"
>  #include "dpif-offload-provider.h"
>  #include "dpif-provider.h"
> @@ -54,6 +55,7 @@ static const struct dpif_offload_class 
> *base_dpif_offload_classes[] = {
>      &dpif_offload_dummy_x_class,
>  };
>
> +#define TOTAL_PROVIDERS ARRAY_SIZE(base_dpif_offload_classes)
>  #define DEFAULT_PROVIDER_PRIORITY_LIST "tc,dpdk,dummy,dummy_x"
>
>  static char *dpif_offload_provider_priority_list = NULL;
> @@ -1665,3 +1667,134 @@ dpif_offload_port_mgr_port_count(const struct 
> dpif_offload *offload)
>
>      return cmap_count(&offload->ports->odp_port_to_port);
>  }
> +
> +struct dpif_offload_pmd_ctx_node {
> +    const struct dpif_offload *offload;
> +    dpif_offload_pmd_thread_work_cb *callback;
> +    void *provider_ctx;
> +};
> +
> +struct dpif_offload_pmd_ctx {
> +    unsigned core_id;
> +    int numa_id;
> +    size_t n_nodes;
> +    struct dpif_offload_pmd_ctx_node nodes[TOTAL_PROVIDERS];
> +};
> +
> +void
> +dpif_offload_pmd_thread_reload(const char *dpif_name, unsigned core_id,
> +                               int numa_id, struct dpif_offload_pmd_ctx 
> **ctx_)
> +{
> +    struct dpif_offload_pmd_ctx_node old_nodes[TOTAL_PROVIDERS];
> +    struct dpif_offload_provider_collection *collection;
> +    struct dpif_offload_pmd_ctx *ctx;
> +    struct dpif_offload *offload;
> +    size_t old_n_nodes = 0;
> +
> +    if (!dpif_offload_enabled()) {
> +        ovs_assert(!*ctx_);
> +        return;
> +    }
> +
> +    ovs_mutex_lock(&dpif_offload_mutex);
> +    collection = shash_find_data(&dpif_offload_providers, dpif_name);
> +    ovs_mutex_unlock(&dpif_offload_mutex);
> +
> +    if (OVS_UNLIKELY(!collection)) {
> +        ovs_assert(!*ctx_);
> +        return;
> +    }
> +
> +    if (!*ctx_) {
> +        /* Would be nice if we have a numa specific xzalloc(). */
> +        ctx = xzalloc(sizeof *ctx);
> +        ctx->core_id = core_id;
> +        ctx->numa_id = numa_id;
> +        *ctx_ = ctx;
> +    } else {
> +        ctx = *ctx_;
> +        old_n_nodes = ctx->n_nodes;
> +
> +        if (old_n_nodes) {
> +            memcpy(old_nodes, ctx->nodes, old_n_nodes * sizeof old_nodes[0]);
> +        }
> +
> +        /* Reset active nodes array. */
> +        memset(ctx->nodes, 0, sizeof ctx->nodes);
> +        ctx->n_nodes = 0;
> +    }
> +
> +    LIST_FOR_EACH (offload, dpif_list_node, &collection->list) {
> +
> +        ovs_assert(ctx->n_nodes < TOTAL_PROVIDERS);
> +
> +        if (!offload->class->pmd_thread_lifecycle) {
> +            continue;
> +        }
> +
> +        if (old_n_nodes) {
> +            /* If this is a reload, try to find previous callback and ctx. */
> +            for (size_t i = 0; i < old_n_nodes; i++) {
> +                struct dpif_offload_pmd_ctx_node *node = &old_nodes[i];
> +
> +                if (offload == node->offload) {
> +                    ctx->nodes[ctx->n_nodes].callback = node->callback;
> +                    ctx->nodes[ctx->n_nodes].provider_ctx = 
> node->provider_ctx;
> +                    break;
> +                }
> +            }
> +        }
> +
> +        offload->class->pmd_thread_lifecycle(
> +            offload, false, core_id, numa_id,
> +            &ctx->nodes[ctx->n_nodes].callback,
> +            &ctx->nodes[ctx->n_nodes].provider_ctx);
> +
> +        if (ctx->nodes[ctx->n_nodes].callback) {
> +            ctx->nodes[ctx->n_nodes].offload = offload;
> +            ctx->n_nodes++;
> +        } else {
> +            memset(&ctx->nodes[ctx->n_nodes], 0,
> +                   sizeof ctx->nodes[ctx->n_nodes]);
> +        }
> +    }
> +}
> +
> +uint64_t
> +dpif_offload_pmd_thread_do_work(struct dpif_offload_pmd_ctx *ctx,
> +                                struct pmd_perf_stats *stats)
> +{
> +    struct cycle_timer offload_work_timer;
> +
> +    if (!ctx || !ctx->n_nodes) {
> +        return 0;
> +    }
> +
> +    cycle_timer_start(stats, &offload_work_timer);
> +
> +    for (size_t i = 0; i < ctx->n_nodes; i++) {
> +        ctx->nodes[i].callback(ctx->core_id, ctx->numa_id,
> +                               ctx->nodes[i].provider_ctx);
> +    }
> +
> +    return cycle_timer_stop(stats, &offload_work_timer);
> +}
> +
> +void
> +dpif_offload_pmd_thread_exit(struct dpif_offload_pmd_ctx *ctx)
> +{
> +    if (!ctx) {
> +        return;
> +    }
> +
> +    for (size_t i = 0; i < ctx->n_nodes; i++) {
> +        struct dpif_offload_pmd_ctx_node *node = &ctx->nodes[i];
> +
> +        node->offload->class->pmd_thread_lifecycle(node->offload, true,
> +                                                   ctx->core_id, 
> ctx->numa_id,
> +                                                   &node->callback,
> +                                                   &node->provider_ctx);
> +    }
> +
> +    free(ctx);
> +}
> diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h
> index 7fad3ebee3..0f66d8cd8e 100644
> --- a/lib/dpif-offload.h
> +++ b/lib/dpif-offload.h
> @@ -22,6 +22,7 @@
>  /* Forward declarations of private structures. */
>  struct dpif_offload_class;
>  struct dpif_offload;
> +struct pmd_perf_stats;
>
>  /* Definition of the DPIF offload implementation type.
>   *
> @@ -186,4 +187,14 @@ dpif_offload_datapath_flow_op_continue(struct 
> dpif_offload_flow_cb_data *cb,
>      }
>  }
>
> +/* PMD Thread helper functions. */
> +struct dpif_offload_pmd_ctx;
> +
> +void dpif_offload_pmd_thread_reload(const char *dpif_name,
> +                                    unsigned core_id, int numa_id,
> +                                    struct dpif_offload_pmd_ctx **);
> +uint64_t dpif_offload_pmd_thread_do_work(struct dpif_offload_pmd_ctx *,
> +                                         struct pmd_perf_stats *);
> +void dpif_offload_pmd_thread_exit(struct dpif_offload_pmd_ctx *);
> +
>  #endif /* DPIF_OFFLOAD_H */
> diff --git a/tests/pmd.at b/tests/pmd.at
> index 8254ac3b0f..54184d8c92 100644
> --- a/tests/pmd.at
> +++ b/tests/pmd.at
> @@ -1689,3 +1689,35 @@ 
> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=10.1.2.
>
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
> +
> +AT_SETUP([PMD - offload work])
> +OVS_VSWITCHD_START([], [], [], [DUMMY_NUMA],
> +                   [-- set Open_vSwitch . other_config:hw-offload=true])
> +
> +AT_CHECK([ovs-appctl vlog/set dpif_offload_dummy:dbg])
> +AT_CHECK([ovs-vsctl add-port br0 p0 -- set Interface p0 type=dummy-pmd])
> +
> +CHECK_CPU_DISCOVERED()
> +CHECK_PMD_THREADS_CREATED()
> +
> +OVS_WAIT_UNTIL(
> +  [test $(ovs-appctl coverage/read-counter dummy_offload_do_work) -gt 0])
> +
> +AT_CHECK([ovs-appctl dpif-netdev/pmd-perf-show \
> +  | grep -Eq 'Offload cycles: +[[0-9]]+  \( *[[0-9.]]+ % of used cycles\)'])
> +
> +OVS_VSWITCHD_STOP
> +
> +LOG="$(sed -n 's/.*\(pmd_thread_lifecycle.*\)/\1/p' ovs-vswitchd.log)"
> +CB=$(echo "$LOG" | sed -n '2p' | sed -n 's/.*cb=\([[^,]]*\).*/\1/p')
> +CTX=$(echo "$LOG" | sed -n '2p' | sed -n 's/.*ctx=\(.*\)$/\1/p')
> +
> +AT_CHECK([echo "$LOG" | sed -n '1p' | sed 's/(nil)/0x0/g'], [0], [dnl
> +pmd_thread_lifecycle; exit=false, core=0, numa=0, cb=0x0, ctx=0x0
> +])
> +AT_CHECK([echo "$LOG" | sed -n '2p' \
> +          | grep -q "exit=false, core=0, numa=0, cb=$CB, ctx=$CTX"])
> +AT_CHECK([echo "$LOG" | sed -n '$p' \
> +          | grep -q "exit=true, core=0, numa=0, cb=$CB, ctx=$CTX"])
> +
> +AT_CLEANUP
> diff --git a/utilities/checkpatch_dict.txt b/utilities/checkpatch_dict.txt
> index c1f43e5afa..c9b758d63c 100644
> --- a/utilities/checkpatch_dict.txt
> +++ b/utilities/checkpatch_dict.txt
> @@ -36,6 +36,7 @@ cpu
>  cpus
>  cstime
>  csum
> +ctx
>  cutime
>  cvlan
>  datapath
> @@ -46,6 +47,7 @@ decap
>  decapsulation
>  defrag
>  defragment
> +deinitialization
>  deref
>  dereference
>  dest
> -- 
> 2.52.0
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to