On 1/12/26 12:20 PM, Eelco Chaudron wrote:
> Introduce an API in dpif-offload to dump all offloaded flows.
> This API centralizes flow retrieval and is intended to
> eventually replace the existing netdev-offload flow dump
> mechanism.
> 
> Acked-by: Eli Britstein <elibr.nvidia.com>
> Signed-off-by: Eelco Chaudron <[email protected]>
> ---
>  lib/dpctl.c                 |   8 +-
>  lib/dpif-netdev.c           |   7 +-
>  lib/dpif-netlink.c          |   9 +-
>  lib/dpif-offload-provider.h |  72 ++++++++++++++
>  lib/dpif-offload-tc.c       |  48 +++++++++
>  lib/dpif-offload.c          | 188 ++++++++++++++++++++++++++++++++++++
>  lib/dpif-provider.h         |  35 ++++++-
>  lib/dpif.c                  |  26 ++++-
>  lib/dpif.h                  |   2 +-
>  9 files changed, 373 insertions(+), 22 deletions(-)
> 
> diff --git a/lib/dpctl.c b/lib/dpctl.c
> index 2a700f24a..1411c5a31 100644
> --- a/lib/dpctl.c
> +++ b/lib/dpctl.c
> @@ -964,10 +964,10 @@ determine_dpif_flow_dump_types(struct dump_types 
> *dump_types,
>                                 struct dpif_flow_dump_types *dpif_dump_types)
>  {
>      dpif_dump_types->ovs_flows = dump_types->ovs || 
> dump_types->non_offloaded;
> -    dpif_dump_types->netdev_flows = dump_types->tc || dump_types->offloaded
> -                                    || dump_types->non_offloaded
> -                                    || dump_types->dpdk
> -                                    || dump_types->partially_offloaded;
> +    dpif_dump_types->offloaded_flows = dump_types->tc || 
> dump_types->offloaded
> +                                       || dump_types->non_offloaded
> +                                       || dump_types->dpdk
> +                                       || dump_types->partially_offloaded;
>  }
>  
>  static bool
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index 07e94f5cb..d11106644 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -4482,13 +4482,12 @@ dpif_netdev_flow_dump_cast(struct dpif_flow_dump 
> *dump)
>  
>  static struct dpif_flow_dump *
>  dpif_netdev_flow_dump_create(const struct dpif *dpif_, bool terse,
> -                             struct dpif_flow_dump_types *types OVS_UNUSED)
> +                             struct dpif_flow_dump_types *types)
>  {
>      struct dpif_netdev_flow_dump *dump;
>  
>      dump = xzalloc(sizeof *dump);
> -    dpif_flow_dump_init(&dump->up, dpif_);
> -    dump->up.terse = terse;
> +    dpif_flow_dump_init(&dump->up, dpif_, terse, types);
>      ovs_mutex_init(&dump->mutex);
>  
>      return &dump->up;
> @@ -4546,7 +4545,7 @@ dpif_netdev_flow_dump_next(struct dpif_flow_dump_thread 
> *thread_,
>          = dpif_netdev_flow_dump_thread_cast(thread_);
>      struct dpif_netdev_flow_dump *dump = thread->dump;
>      struct dp_netdev_flow *netdev_flows[FLOW_DUMP_MAX_BATCH];
> -    struct dpif_netdev *dpif = dpif_netdev_cast(thread->up.dpif);
> +    struct dpif_netdev *dpif = dpif_netdev_cast(thread->up.dump->dpif);
>      struct dp_netdev *dp = get_dp_netdev(&dpif->dpif);
>      int n_flows = 0;
>      int i;
> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
> index aff90976c..90e92d3a0 100644
> --- a/lib/dpif-netlink.c
> +++ b/lib/dpif-netlink.c
> @@ -1621,7 +1621,7 @@ start_netdev_dump(const struct dpif *dpif_,
>  {
>      ovs_mutex_init(&dump->netdev_lock);
>  
> -    if (!(dump->types.netdev_flows)) {
> +    if (!(dump->types.offloaded_flows)) {
>          dump->netdev_dumps_num = 0;
>          dump->netdev_dumps = NULL;
>          return;
> @@ -1642,7 +1642,7 @@ dpif_netlink_populate_flow_dump_types(struct 
> dpif_netlink_flow_dump *dump,
>  {
>      if (!types) {
>          dump->types.ovs_flows = true;
> -        dump->types.netdev_flows = true;
> +        dump->types.offloaded_flows = true;
>      } else {
>          memcpy(&dump->types, types, sizeof *types);
>      }
> @@ -1658,7 +1658,7 @@ dpif_netlink_flow_dump_create(const struct dpif *dpif_, 
> bool terse,
>      struct ofpbuf *buf;
>  
>      dump = xmalloc(sizeof *dump);
> -    dpif_flow_dump_init(&dump->up, dpif_);
> +    dpif_flow_dump_init(&dump->up, dpif_, terse, types);
>  
>      dpif_netlink_populate_flow_dump_types(dump, types);
>  
> @@ -1675,7 +1675,6 @@ dpif_netlink_flow_dump_create(const struct dpif *dpif_, 
> bool terse,
>          ofpbuf_delete(buf);
>      }
>      atomic_init(&dump->status, 0);
> -    dump->up.terse = terse;
>  
>      start_netdev_dump(dpif_, dump);
>  
> @@ -1885,7 +1884,7 @@ dpif_netlink_flow_dump_next(struct 
> dpif_flow_dump_thread *thread_,
>      struct dpif_netlink_flow_dump_thread *thread
>          = dpif_netlink_flow_dump_thread_cast(thread_);
>      struct dpif_netlink_flow_dump *dump = thread->dump;
> -    struct dpif_netlink *dpif = dpif_netlink_cast(thread->up.dpif);
> +    struct dpif_netlink *dpif = dpif_netlink_cast(thread->up.dump->dpif);
>      int n_flows;
>  
>      ofpbuf_delete(thread->nl_actions);
> diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h
> index 8da7b12cd..4061f6e02 100644
> --- a/lib/dpif-offload-provider.h
> +++ b/lib/dpif-offload-provider.h
> @@ -60,6 +60,32 @@ struct dpif_offload {
>  };
>  
>  
> +struct dpif_offload_flow_dump {
> +    struct dpif_offload *offload;
> +    bool terse;
> +};
> +
> +static inline void
> +dpif_offload_flow_dump_init(struct dpif_offload_flow_dump *dump,
> +                            const struct dpif_offload *offload, bool terse)
> +{
> +    dump->offload = CONST_CAST(struct dpif_offload *, offload);
> +    dump->terse = terse;
> +}
> +
> +struct dpif_offload_flow_dump_thread {
> +    struct dpif_offload_flow_dump *dump;
> +};
> +
> +static inline void
> +dpif_offload_flow_dump_thread_init(
> +    struct dpif_offload_flow_dump_thread *thread,
> +    struct dpif_offload_flow_dump *dump)
> +{
> +    thread->dump = dump;
> +}
> +
> +
>  struct dpif_offload_class {
>      /* Type of DPIF offload provider in this class, e.g., "tc", "dpdk",
>       * "dummy", etc. */
> @@ -132,6 +158,44 @@ struct dpif_offload_class {
>       * successful, otherwise returns a positive errno value. */
>      int (*flow_flush)(const struct dpif_offload *);
>  
> +    /* Flow Dumping Interface for dpif-offload.
> +     *
> +     * This interface mirrors the flow dumping interface found in the dpif
> +     * layer.  For a thorough understanding of the design and expectations,
> +     * please refer to the documentation in:
> +     *   - include/openvswitch/dpif.h
> +     *   - include/openvswitch/dpif-provider.h
> +     *
> +     * The dpif-offload flow dumping interface is intended for use only when
> +     * there is a clear separation between traditional dpif flows and 
> offloaded
> +     * flows handled by an offload mechanism.
> +     *
> +     * For example:
> +     *   - The 'tc' offload provider installs flow rules via kernel tc 
> without
> +     *     creating corresponding kernel datapath (dpif) flows.  In such 
> cases,
> +     *     dumping dpif flows would not reflect the actual set of active
> +     *      offloaded flows. This interface provides a way to explicitly
> +     *      enumerate such offloaded flows.

nit: Indentation is off.  And double spaces.

> +     *
> +     * 'flow_dump_create' and 'flow_dump_thread_create' must always return
> +     * initialized and usable data structures. Specifically, they must
> +     * initialize the returned structures using dpif_offload_flow_dump_init()
> +     * and dpif_offload_flow_dump_thread_init(), respectively, and defer any
> +     * error reporting until flow_dump_destroy() is called. */
> +    struct dpif_offload_flow_dump *(*flow_dump_create)(
> +        const struct dpif_offload *, bool terse);
> +
> +    int (*flow_dump_next)(struct dpif_offload_flow_dump_thread *,
> +                          struct dpif_flow *, int max_flows);
> +
> +    int (*flow_dump_destroy)(struct dpif_offload_flow_dump *);
> +
> +    struct dpif_offload_flow_dump_thread *(*flow_dump_thread_create)(
> +        struct dpif_offload_flow_dump *);
> +
> +    void (*flow_dump_thread_destroy)(
> +        struct dpif_offload_flow_dump_thread *);

nit: Can be a single line.

> +
>      /* Returns the number of flows offloaded by the offload provider. */
>      uint64_t (*flow_get_n_offloaded)(const struct dpif_offload *);
>  
> @@ -239,6 +303,14 @@ void dpif_offload_port_del(struct dpif *, odp_port_t);
>  void dpif_offload_port_set_config(struct dpif *, odp_port_t,
>                                    const struct smap *cfg);
>  void dpif_offload_set_netdev_offload(struct netdev *, struct dpif_offload *);
> +void dpif_offload_flow_dump_create(struct dpif_flow_dump *,
> +                                   const struct dpif *, bool terse);
> +int dpif_offload_flow_dump_destroy(struct dpif_flow_dump *);
> +int dpif_offload_flow_dump_next(struct dpif_flow_dump_thread *,
> +                                struct dpif_flow *, int max_flows);
> +void dpif_offload_flow_dump_thread_create(struct dpif_flow_dump_thread *,
> +                                          struct dpif_flow_dump *);
> +void dpif_offload_flow_dump_thread_destroy(struct dpif_flow_dump_thread *);
>  
>  static inline void dpif_offload_assert_class(
>      const struct dpif_offload *dpif_offload,
> diff --git a/lib/dpif-offload-tc.c b/lib/dpif-offload-tc.c
> index 9e8da207c..d60919f2a 100644
> --- a/lib/dpif-offload-tc.c
> +++ b/lib/dpif-offload-tc.c
> @@ -281,6 +281,49 @@ dpif_offload_tc_flow_flush(const struct dpif_offload 
> *offload)
>      return err;
>  }
>  
> +static struct dpif_offload_flow_dump *
> +dpif_offload_tc_flow_dump_create(const struct dpif_offload *offload,
> +                                 bool terse)
> +{
> +    struct dpif_offload_flow_dump *dump;
> +
> +    dump = xmalloc(sizeof *dump);
> +    dpif_offload_flow_dump_init(dump, offload, terse);
> +    return dump;
> +}
> +
> +static int
> +dpif_offload_tc_flow_dump_next(struct dpif_offload_flow_dump_thread *thread,
> +                               struct dpif_flow *flows, int max_flows)
> +{
> +    ovs_assert(thread && flows && max_flows);
> +    return 0;
> +}
> +
> +static int
> +dpif_offload_tc_flow_dump_destroy(struct dpif_offload_flow_dump *dump)
> +{
> +    free(dump);
> +    return 0;
> +}
> +
> +static struct dpif_offload_flow_dump_thread *
> +dpif_offload_tc_flow_dump_thread_create(struct dpif_offload_flow_dump *dump)
> +{
> +    struct dpif_offload_flow_dump_thread *thread;
> +
> +    thread = xmalloc(sizeof *thread);
> +    dpif_offload_flow_dump_thread_init(thread, dump);
> +    return thread;
> +}
> +
> +static void
> +dpif_offload_tc_flow_dump_thread_destroy(
> +    struct dpif_offload_flow_dump_thread *thread)
> +{
> +    free(thread);
> +}
> +
>  struct dpif_offload_class dpif_offload_tc_class = {
>      .type = "tc",
>      .supported_dpif_types = (const char *const[]) {
> @@ -294,6 +337,11 @@ struct dpif_offload_class dpif_offload_tc_class = {
>      .port_add = dpif_offload_tc_port_add,
>      .port_del = dpif_offload_tc_port_del,
>      .flow_flush = dpif_offload_tc_flow_flush,
> +    .flow_dump_create = dpif_offload_tc_flow_dump_create,
> +    .flow_dump_next = dpif_offload_tc_flow_dump_next,
> +    .flow_dump_destroy = dpif_offload_tc_flow_dump_destroy,
> +    .flow_dump_thread_create = dpif_offload_tc_flow_dump_thread_create,
> +    .flow_dump_thread_destroy = dpif_offload_tc_flow_dump_thread_destroy,
>      .flow_get_n_offloaded = dpif_offload_tc_flow_get_n_offloaded,
>      .meter_set = dpif_offload_tc_meter_set,
>      .meter_get = dpif_offload_tc_meter_get,
> diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c
> index 544744c88..2591ae83f 100644
> --- a/lib/dpif-offload.c
> +++ b/lib/dpif-offload.c
> @@ -160,6 +160,17 @@ dp_offload_initialize(void)
>                     && base_dpif_offload_classes[i]->port_add
>                     && base_dpif_offload_classes[i]->port_del);
>  
> +        ovs_assert((base_dpif_offload_classes[i]->flow_dump_create &&
> +                    base_dpif_offload_classes[i]->flow_dump_next &&
> +                    base_dpif_offload_classes[i]->flow_dump_destroy &&
> +                    base_dpif_offload_classes[i]->flow_dump_thread_create &&
> +                    base_dpif_offload_classes[i]->flow_dump_thread_destroy) 
> ||
> +                   (!base_dpif_offload_classes[i]->flow_dump_create &&
> +                    !base_dpif_offload_classes[i]->flow_dump_next &&
> +                    !base_dpif_offload_classes[i]->flow_dump_destroy &&
> +                    !base_dpif_offload_classes[i]->flow_dump_thread_create &&
> +                    
> !base_dpif_offload_classes[i]->flow_dump_thread_destroy));
> +
>          dpif_offload_register_provider(base_dpif_offload_classes[i]);
>      }
>  
> @@ -838,6 +849,183 @@ dpif_offload_meter_del(const struct dpif *dpif, 
> ofproto_meter_id meter_id,
>      }
>  }
>  
> +/*
> + * Further initializes a 'struct dpif_flow_dump' that was already initialized
> + * by dpif_flow_dump_create(), preparing it for use by
> + * dpif_offload_flow_dump_next().
> + *
> + * For more details, see the documentation of dpif_flow_dump_create(). */
> +void
> +dpif_offload_flow_dump_create(struct dpif_flow_dump *dump,
> +                              const struct dpif *dpif, bool terse)
> +{
> +    struct dp_offload *dp_offload = dpif_offload_get_dp_offload(dpif);
> +    const struct dpif_offload *offload;
> +    size_t n_providers = 0;
> +    int i = 0;
> +
> +    if (!dump || !dpif_offload_is_offload_enabled() || !dp_offload) {
> +        return;
> +    }
> +
> +    LIST_FOR_EACH (offload, dpif_list_node, &dp_offload->offload_providers) {
> +        if (offload->class->flow_dump_create) {
> +            n_providers++;
> +        }
> +    }
> +
> +    if (!n_providers) {
> +        return;
> +    }
> +
> +    dump->offload_dumps = xmalloc(n_providers * sizeof(
> +        struct dpif_offload_flow_dump *));
> +
> +    LIST_FOR_EACH (offload, dpif_list_node, &dp_offload->offload_providers) {
> +        if (offload->class->flow_dump_create) {
> +            dump->offload_dumps[i++] = offload->class->flow_dump_create(
> +                offload, terse);

nit: Split the line after the '='.

> +        }
> +    }
> +    dump->n_offload_dumps = i;
> +    dump->offload_dump_index = 0;
> +}
> +
> +/* Destroys the 'dump' data associated with the offload flow dump.
> + * This function is called as part of the general dump cleanup
> + * by dpif_flow_dump_destroy().
> + *
> + * Returns 0 if all individual dpif-offload dump operations complete
> + * without error.  If one or more providers return an error, the error
> + * code from the first failing provider is returned as a positive errno
> + * value. */
> +int
> +dpif_offload_flow_dump_destroy(struct dpif_flow_dump *dump)
> +{
> +    int error = 0;
> +
> +    for (int i = 0; i < dump->n_offload_dumps; i++) {
> +        struct dpif_offload_flow_dump *offload_dump = dump->offload_dumps[i];
> +        const struct dpif_offload *offload = offload_dump->offload;
> +        int rc = offload->class->flow_dump_destroy(offload_dump);
> +
> +        if (rc && rc != EOF) {
> +            VLOG_ERR("Failed flow dumping on dpif-offload provider "
> +                "%s, error %s", dpif_offload_name(offload),
> +                ovs_strerror(rc));

nit: Align to the opening parenthesis.

> +            if (!error) {
> +                error = rc;
> +            }
> +        }
> +    }
> +    ovs_mutex_destroy(&dump->offload_dump_mutex);
> +    free(dump->offload_dumps);
> +    return error;
> +}
> +
> +static void
> +dpif_offload_advance_provider_dump(struct dpif_flow_dump_thread *thread)
> +{
> +    struct dpif_flow_dump *dump = thread->dump;
> +
> +    ovs_mutex_lock(&dump->offload_dump_mutex);
> +
> +    /* If we haven't finished (dumped all providers). */
> +    if (dump->offload_dump_index < dump->n_offload_dumps) {
> +        /* If we are the first to find that current dump is finished
> +         * advance it. */
> +        if (thread->offload_dump_index == dump->offload_dump_index) {
> +            thread->offload_dump_index = ++dump->offload_dump_index;
> +            /* Did we just finish the last dump? If so we are done. */
> +            if (dump->offload_dump_index == dump->n_offload_dumps) {
> +                thread->offload_dump_done = true;
> +            }
> +        } else {
> +            /* otherwise, we are behind, catch up */
> +            thread->offload_dump_index = dump->offload_dump_index;
> +        }
> +    } else {
> +        /* Some other thread finished. */
> +        thread->offload_dump_done = true;
> +    }
> +
> +    ovs_mutex_unlock(&dump->offload_dump_mutex);
> +}
> +
> +/* This function behaves exactly the same as dpif_flow_dump_next(),
> + * so see its documentation for details. */
> +int dpif_offload_flow_dump_next(struct dpif_flow_dump_thread *thread,
> +                                struct dpif_flow *flows, int max_flows)
> +{
> +    int n_flows = 0;
> +
> +    ovs_assert(max_flows > 0);
> +
> +    /* The logic here processes all registered offload providers and
> +     * dumps all related flows. If done (i.e., it returns 0), continue
> +     * with the next offload provider. */
> +    while (!thread->offload_dump_done) {
> +        struct dpif_offload_flow_dump_thread *offload_thread;
> +
> +        ovs_assert(thread->offload_dump_index < thread->n_offload_threads);
> +        offload_thread = thread->offload_threads[thread->offload_dump_index];
> +        n_flows = offload_thread->dump->offload->class->flow_dump_next(
> +            offload_thread, flows, max_flows);
> +
> +        if (n_flows > 0) {
> +            /* If we got some flows, we need to return due to the constraint
> +             * on returned flows, as explained in dpif_flow_dump_next(). */
> +            break;
> +        }
> +        dpif_offload_advance_provider_dump(thread);
> +    }
> +    return MAX(n_flows, 0);
> +}
> +
> +/* Further initializes a 'struct dpif_flow_dump_thread' that was already
> + * initialized by dpif_flow_dump_thread_create(), preparing it for use by
> + * dpif_offload_flow_dump_next(). */
> +void
> +dpif_offload_flow_dump_thread_create(struct dpif_flow_dump_thread *thread,
> +                                     struct dpif_flow_dump *dump)
> +{
> +    if (!dpif_offload_is_offload_enabled() || !dump
> +        || !dump->n_offload_dumps) {
> +        return;
> +    }
> +
> +    thread->n_offload_threads = dump->n_offload_dumps;
> +    thread->offload_dump_done = false;
> +    thread->offload_dump_index = 0;
> +    thread->offload_threads = xmalloc(thread->n_offload_threads * sizeof(
> +        struct dpif_offload_flow_dump_thread *));

nit:
Maybe:

    thread->offload_threads =
        xmalloc(thread->n_offload_threads *
                sizeof(struct dpif_offload_flow_dump_thread *));

?

> +
> +    for (int i = 0; i < dump->n_offload_dumps; i++) {
> +        struct dpif_offload_flow_dump *offload_dump = dump->offload_dumps[i];
> +        const struct dpif_offload *offload = offload_dump->offload;
> +
> +        thread->offload_threads[i] = offload->class->flow_dump_thread_create(
> +            offload_dump);

nit: Split after the '='

> +    }
> +}
> +
> +/* Destroys the 'thread' data associated with the offload flow dump.
> + * This function is called as part of the general thread cleanup
> + * by dpif_flow_dump_thread_destroy(). */
> +void
> +dpif_offload_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread)
> +{
> +    for (int i = 0; i < thread->n_offload_threads; i++) {
> +        struct dpif_offload_flow_dump_thread *offload_thread;
> +        const struct dpif_offload *offload;
> +
> +        offload_thread = thread->offload_threads[i];
> +        offload = offload_thread->dump->offload;
> +        offload->class->flow_dump_thread_destroy(offload_thread);
> +    }
> +    free(thread->offload_threads);
> +}
> +
>  
>  int
>  dpif_offload_netdev_flush_flows(struct netdev *netdev)
> diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
> index e99807782..91e7ddcfa 100644
> --- a/lib/dpif-provider.h
> +++ b/lib/dpif-provider.h
> @@ -65,23 +65,52 @@ static inline void dpif_assert_class(const struct dpif 
> *dpif,
>  struct dpif_flow_dump {
>      struct dpif *dpif;
>      bool terse;         /* If true, key/mask/actions may be omitted. */
> +
> +    struct ovs_mutex offload_dump_mutex;
> +    struct dpif_offload_flow_dump **offload_dumps;
> +    size_t n_offload_dumps;
> +    size_t offload_dump_index;
>  };
>  
> +void dpif_offload_flow_dump_create(struct dpif_flow_dump *,
> +                                   const struct dpif *, bool terse);
> +void dpif_offload_flow_dump_thread_create(struct dpif_flow_dump_thread *,
> +                                          struct dpif_flow_dump *);
> +
>  static inline void
> -dpif_flow_dump_init(struct dpif_flow_dump *dump, const struct dpif *dpif)
> +dpif_flow_dump_init(struct dpif_flow_dump *dump, const struct dpif *dpif,
> +                    bool terse, struct dpif_flow_dump_types *types)
>  {
>      dump->dpif = CONST_CAST(struct dpif *, dpif);
> +    dump->terse = terse;
> +    dump->offload_dumps = NULL;
> +    dump->n_offload_dumps = 0;
> +    dump->offload_dump_index = 0;
> +    ovs_mutex_init(&dump->offload_dump_mutex);
> +    if (!types || types->offloaded_flows) {
> +        dpif_offload_flow_dump_create(dump, dpif, terse);
> +    }
>  }
>  
>  struct dpif_flow_dump_thread {
> -    struct dpif *dpif;
> +    struct dpif_flow_dump *dump;
> +
> +    struct dpif_offload_flow_dump_thread **offload_threads;
> +    size_t n_offload_threads;
> +    size_t offload_dump_index;
> +    bool offload_dump_done;
>  };
>  
>  static inline void
>  dpif_flow_dump_thread_init(struct dpif_flow_dump_thread *thread,
>                             struct dpif_flow_dump *dump)
>  {
> -    thread->dpif = dump->dpif;
> +    thread->dump = dump;
> +    thread->offload_threads = NULL;
> +    thread->n_offload_threads = 0;
> +    thread->offload_dump_index = 0;
> +    thread->offload_dump_done = true;
> +    dpif_offload_flow_dump_thread_create(thread, dump);
>  }
>  
>  struct ct_dpif_dump_state;
> diff --git a/lib/dpif.c b/lib/dpif.c
> index 61cc68c8e..4dc1261dc 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1114,7 +1114,15 @@ int
>  dpif_flow_dump_destroy(struct dpif_flow_dump *dump)
>  {
>      const struct dpif *dpif = dump->dpif;
> -    int error = dpif->dpif_class->flow_dump_destroy(dump);
> +    int error;
> +    int offload_error;
> +
> +    offload_error = dpif_offload_flow_dump_destroy(dump);
> +    error = dpif->dpif_class->flow_dump_destroy(dump);
> +
> +    if (!error || error == EOF) {
> +        error = offload_error;
> +    }
>      log_operation(dpif, "flow_dump_destroy", error);
>      return error == EOF ? 0 : error;
>  }
> @@ -1130,7 +1138,8 @@ dpif_flow_dump_thread_create(struct dpif_flow_dump 
> *dump)
>  void
>  dpif_flow_dump_thread_destroy(struct dpif_flow_dump_thread *thread)
>  {
> -    thread->dpif->dpif_class->flow_dump_thread_destroy(thread);
> +    dpif_offload_flow_dump_thread_destroy(thread);
> +    thread->dump->dpif->dpif_class->flow_dump_thread_destroy(thread);
>  }
>  
>  /* Attempts to retrieve up to 'max_flows' more flows from 'thread'.  Returns > 0
> @@ -1155,11 +1164,18 @@ int
>  dpif_flow_dump_next(struct dpif_flow_dump_thread *thread,
>                      struct dpif_flow *flows, int max_flows)
>  {
> -    struct dpif *dpif = thread->dpif;
> -    int n;
> +    struct dpif *dpif = thread->dump->dpif;
> +    int n = 0;
>  
>      ovs_assert(max_flows > 0);
> -    n = dpif->dpif_class->flow_dump_next(thread, flows, max_flows);
> +
> +    if (!thread->offload_dump_done) {
> +        n = dpif_offload_flow_dump_next(thread, flows, max_flows);
> +    }
> +    if (n == 0) {
> +        n = dpif->dpif_class->flow_dump_next(thread, flows, max_flows);
> +    }
> +
>      if (n > 0) {
>          struct dpif_flow *f;
>  
> diff --git a/lib/dpif.h b/lib/dpif.h
> index 7fd3c939c..473eacb2f 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -522,7 +522,7 @@ struct dpif_flow_attrs {
>  
>  struct dpif_flow_dump_types {
>      bool ovs_flows;
> -    bool netdev_flows;
> +    bool offloaded_flows;
>  };
>  
>  void dpif_flow_stats_extract(const struct flow *, const struct dp_packet 
> *packet,

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

Reply via email to