On 1/12/26 12:20 PM, Eelco Chaudron wrote:
> This patch introduces a common port management layer for
> offload providers and integrates it into the dpif-offload
> subsystem. Existing dummy offload provider is updated to
> use the new APIs.
>
> Acked-by: Eli Britstein <elibr.nvidia.com>
> Signed-off-by: Eelco Chaudron <[email protected]>
> ---
>
> v2 changes:
> - Fixed indentation issues.
> - Added comment to dpif_offload_attach_providers().
> - Fix netdev reference issue in dpif_offload_port_mgr_add().
>
> v3 changes:
> - Fixed indentation issues.
> - Removed unused arguments.
> - Fixed { alignment.
> - Removed unnecessary ?: constructs.
> - Free port netdev in rcu callback.
>
> v4 changes:
> - Removed leftover netdev_close(port->netdev) causing
> netdev reference count issues.
>
> v5 changes:
> - Moved DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH() to this patch
> from patch 18.
> ---
> lib/dpif-offload-dpdk.c | 17 +++
> lib/dpif-offload-dummy.c | 155 ++++++++++++++++++++-
> lib/dpif-offload-provider.h | 73 ++++++++++
> lib/dpif-offload-tc.c | 20 +++
> lib/dpif-offload.c | 265 +++++++++++++++++++++++++++++++++++-
> lib/dpif.c | 11 +-
> lib/dummy.h | 3 +
> lib/netdev-dpdk.c | 4 +-
> lib/netdev-dpdk.h | 2 +-
> lib/netdev-dummy.c | 20 +--
> lib/netdev-offload-dpdk.c | 4 +-
> lib/netdev-provider.h | 1 +
> 12 files changed, 552 insertions(+), 23 deletions(-)
>
> diff --git a/lib/dpif-offload-dpdk.c b/lib/dpif-offload-dpdk.c
> index 2a5e67ea6..e6e467629 100644
> --- a/lib/dpif-offload-dpdk.c
> +++ b/lib/dpif-offload-dpdk.c
> @@ -18,6 +18,8 @@
>
> #include "dpif-offload.h"
> #include "dpif-offload-provider.h"
> +#include "netdev-provider.h"
> +#include "netdev-vport.h"
> #include "util.h"
>
> #include "openvswitch/vlog.h"
> @@ -97,6 +99,20 @@ dpif_offload_dpdk_set_config(struct dpif_offload *offload_,
> }
> }
>
> +static bool
> +dpif_offload_dpdk_can_offload(struct dpif_offload *offload OVS_UNUSED,
> + struct netdev *netdev)
> +{
> + if (netdev_vport_is_vport_class(netdev->netdev_class)
> + && strcmp(netdev_get_dpif_type(netdev), "netdev")) {
> + VLOG_DBG("%s: vport doesn't belong to the netdev datapath, skipping",
> + netdev_get_name(netdev));
> + return false;
> + }
> +
> + return netdev_dpdk_flow_api_supported(netdev, true);
> +}
> +
> struct dpif_offload_class dpif_offload_dpdk_class = {
> .type = "dpdk",
> .supported_dpif_types = (const char *const[]) {
> @@ -105,6 +121,7 @@ struct dpif_offload_class dpif_offload_dpdk_class = {
> .open = dpif_offload_dpdk_open,
> .close = dpif_offload_dpdk_close,
> .set_config = dpif_offload_dpdk_set_config,
> + .can_offload = dpif_offload_dpdk_can_offload,
> };
>
> /* XXX: Temporary functions below, which will be removed once fully
> diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
> index 4ff63bdb6..795c82a6c 100644
> --- a/lib/dpif-offload-dummy.c
> +++ b/lib/dpif-offload-dummy.c
> @@ -15,27 +15,170 @@
> */
>
> #include <config.h>
> +#include <errno.h>
>
> #include "dpif.h"
> #include "dpif-offload.h"
> #include "dpif-offload-provider.h"
> +#include "dummy.h"
> +#include "netdev-provider.h"
> #include "util.h"
>
> +struct dpif_offload_dummy {
> + struct dpif_offload offload;
> + struct dpif_offload_port_mgr *port_mgr;
> +
> + /* Configuration specific variables. */
> + struct ovsthread_once once_enable; /* Track first-time enablement. */
> +};
> +
> +static struct dpif_offload_dummy *
> +dpif_offload_dummy_cast(const struct dpif_offload *offload)
> +{
> + return CONTAINER_OF(offload, struct dpif_offload_dummy, offload);
> +}
> +
> +static void
> +dpif_offload_dummy_enable_offload(struct dpif_offload *dpif_offload,
> + struct dpif_offload_port_mgr_port *port)
> +{
> + dpif_offload_set_netdev_offload(port->netdev, dpif_offload);
> +}
> +
> +static void
> +dpif_offload_dummy_cleanup_offload(struct dpif_offload_port_mgr_port *port)
> +{
> + dpif_offload_set_netdev_offload(port->netdev, NULL);
> +}
> +
> +static int
> +dpif_offload_dummy_port_add(struct dpif_offload *dpif_offload,
> + struct netdev *netdev, odp_port_t port_no)
> +{
> + struct dpif_offload_port_mgr_port *port = xmalloc(sizeof *port);
> + struct dpif_offload_dummy *offload_dummy;
> +
> + offload_dummy = dpif_offload_dummy_cast(dpif_offload);
> + if (dpif_offload_port_mgr_add(offload_dummy->port_mgr, port, netdev,
> + port_no, false)) {
> +
> + if (dpif_offload_is_offload_enabled()) {
> + dpif_offload_dummy_enable_offload(dpif_offload, port);
> + }
> + return 0;
> + }
> +
> + free(port);
> + return EEXIST;
> +}
> +
> +static void
> +dpif_offload_dummy_free_port(struct dpif_offload_port_mgr_port *port)
> +{
> + netdev_close(port->netdev);
> + free(port);
> +}
> +
> +static int
> +dpif_offload_dummy_port_del(struct dpif_offload *dpif_offload,
> + odp_port_t port_no)
> +{
> + struct dpif_offload_dummy *offload_dummy;
> + struct dpif_offload_port_mgr_port *port;
> +
> + offload_dummy = dpif_offload_dummy_cast(dpif_offload);
> +
> + port = dpif_offload_port_mgr_remove(offload_dummy->port_mgr, port_no,
> + true);
> + if (port) {
> + if (dpif_offload_is_offload_enabled()) {
> + dpif_offload_dummy_cleanup_offload(port);
> + }
> + ovsrcu_postpone(dpif_offload_dummy_free_port, port);
> + }
> + return 0;
> +}
> +
> static int
> dpif_offload_dummy_open(const struct dpif_offload_class *offload_class,
> struct dpif *dpif, struct dpif_offload
> **dpif_offload)
> {
> - struct dpif_offload *offload = xmalloc(sizeof *offload);
> + struct dpif_offload_dummy *offload_dummy;
>
> - dpif_offload_init(offload, offload_class, dpif);
> - *dpif_offload = offload;
> + offload_dummy = xmalloc(sizeof *offload_dummy);
> +
> + dpif_offload_init(&offload_dummy->offload, offload_class, dpif);
> + offload_dummy->port_mgr = dpif_offload_port_mgr_init();
Is there a reason why port manager is given to the provider to maintain
and not part of the generic dpif_offload structure? The only reason for
this seems to be that we could have a provider that doesn't use this
implementation of the port manager, but uses something else. Are we
anticipating such a provider? All of the existing ones use the port manager.
Asking because it's a little awkward that provider owns the port manager,
but doesn't know how it works, and the generic dpif_offload code knows how
the port manager works but doesn't own one. So, when provider needs a port,
it asks the generic code to access the port manager, and when the generic
code wants to access ports it asks providers to call back. We could probbaly
drop the dpif_offload_port_dump_* family of functions we could just iterateover
the cmap without talking to the provider. We'd just need an accessor for the
provider to get the port manager. In this case we would also not need the
separate dpif_offload_port structure and could rename the port manager's
dpif_offload_port_mgr_port structure into dpif_offload_port instead.
It's more of a question and we can keep the logic as-is for now and maybe
refine it later, but I just want to understand.
But also, the abstraction seems a bit broken as providers are using both the
dpif_offload_port_mgr_traverse_ports() and DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH,
which kind of requires knowledge of the structure of the port manager. So, I'm
not sure why we have both the iterator and the traversal function, as the
traversal function is just a wrapper around the iterator.
> + offload_dummy->once_enable = (struct ovsthread_once)
> + OVSTHREAD_ONCE_INITIALIZER;
nit: Might be better to move the cast to the next line as well.
Same for other providers.
> +
> + *dpif_offload = &offload_dummy->offload;
> return 0;
> }
>
> +static bool
> +dpif_offload_dummy_cleanup_port(struct dpif_offload_port_mgr_port *port,
> + void *aux)
> +{
> + struct dpif_offload *offload = aux;
> +
> + dpif_offload_dummy_port_del(offload, port->port_no);
> + return false;
> +}
> +
> static void
> dpif_offload_dummy_close(struct dpif_offload *dpif_offload)
> {
> - free(dpif_offload);
> + struct dpif_offload_dummy *offload_dummy;
> +
> + offload_dummy = dpif_offload_dummy_cast(dpif_offload);
> +
> + /* The ofproto layer may not call dpif_port_del() for all ports,
> + * especially internal ones, so we need to clean up any remaining ports.
> */
> + dpif_offload_port_mgr_traverse_ports(offload_dummy->port_mgr,
> + dpif_offload_dummy_cleanup_port,
> + dpif_offload);
> +
> + dpif_offload_port_mgr_uninit(offload_dummy->port_mgr);
> + free(offload_dummy);
> +}
> +
> +static bool
> +dpif_offload_dummy_late_enable(struct dpif_offload_port_mgr_port *port,
> + void *aux)
> +{
> + dpif_offload_dummy_enable_offload(aux, port);
> + return false;
> +}
> +
> +static void
> +dpif_offload_dummy_set_config(struct dpif_offload *dpif_offload,
> + const struct smap *other_cfg)
> +{
> + struct dpif_offload_dummy *offload_dummy;
> +
> + offload_dummy = dpif_offload_dummy_cast(dpif_offload);
> +
> + /* We maintain the existing behavior where global configurations
> + * are only accepted when hardware offload is initially enabled.
> + * Once enabled, they cannot be updated or reconfigured. */
You removed this comment form the other providers after the Aaron's
review. Should we remove this one as well?
> + if (smap_get_bool(other_cfg, "hw-offload", false)) {
> + if (ovsthread_once_start(&offload_dummy->once_enable)) {
> +
> + dpif_offload_port_mgr_traverse_ports(
> + offload_dummy->port_mgr, dpif_offload_dummy_late_enable,
> + dpif_offload);
> +
> + ovsthread_once_done(&offload_dummy->once_enable);
> + }
> + }
> +}
> +
> +static bool
> +dpif_offload_dummy_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED,
> + struct netdev *netdev)
> +{
> + return is_dummy_netdev_class(netdev->netdev_class);
> }
>
> #define DEFINE_DPIF_DUMMY_CLASS(NAME, TYPE_STR) \
> @@ -46,6 +189,10 @@ dpif_offload_dummy_close(struct dpif_offload
> *dpif_offload)
> NULL}, \
> .open = dpif_offload_dummy_open, \
> .close = dpif_offload_dummy_close, \
> + .set_config = dpif_offload_dummy_set_config, \
> + .can_offload = dpif_offload_dummy_can_offload, \
> + .port_add = dpif_offload_dummy_port_add, \
> + .port_del = dpif_offload_dummy_port_del, \
> }
>
> DEFINE_DPIF_DUMMY_CLASS(dpif_offload_dummy_class, "dummy");
> diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h
> index f0c0ea232..d53cc9f18 100644
> --- a/lib/dpif-offload-provider.h
> +++ b/lib/dpif-offload-provider.h
> @@ -17,6 +17,7 @@
> #ifndef DPIF_OFFLOAD_PROVIDER_H
> #define DPIF_OFFLOAD_PROVIDER_H
>
> +#include "cmap.h"
> #include "dpif-provider.h"
> #include "ovs-thread.h"
> #include "smap.h"
> @@ -92,6 +93,31 @@ struct dpif_offload_class {
> * called. */
> void (*set_config)(struct dpif_offload *,
> const struct smap *other_config);
> +
> + /* Verifies whether the offload provider supports offloading flows for
> the
> + * given 'netdev'. Returns 'false' if the provider lacks the
> capabilities
> + * to offload on this port, otherwise returns 'true'. */
> + bool (*can_offload)(struct dpif_offload *,
> + struct netdev *);
> +
> + /* This callback is invoked when a 'netdev' port has been successfully
> + * added to the dpif and should be handled by this offload provider.
> + * It is assumed that the `can_offload` callback was previously called
nit: Other comments use normal single quotes.
> + * and returned 'true' before this function is executed. */
> + int (*port_add)(struct dpif_offload *, struct netdev *,
> + odp_port_t port_no);
> +
> + /* This callback is invoked when the 'port_no' port has been successfully
> + * removed from the dpif. Note that it is called for every deleted port,
> + * even if 'port_added' was never called, as the framework does not track
> + * added ports. */
> + int (*port_del)(struct dpif_offload *, odp_port_t port_no);
> +
> + /* Refreshes the configuration of 'port_no' port. The implementation
> might
> + * postpone applying the changes until run() is called. The same note
There is no run() in this class.
> + * as above in 'port_deleted' applies here.*/
> + void (*port_set_config)(struct dpif_offload *, odp_port_t port_no,
> + const struct smap *cfg);
> };
>
>
> @@ -101,9 +127,56 @@ extern struct dpif_offload_class dpif_offload_dpdk_class;
> extern struct dpif_offload_class dpif_offload_tc_class;
>
>
> +/* Structure used by the common dpif port management library functions. */
> +struct dpif_offload_port_mgr {
> + struct ovs_mutex cmap_mod_lock;
> +
> + struct cmap odp_port_to_port;
> + struct cmap netdev_to_port;
> + struct cmap ifindex_to_port;
> +};
> +
> +struct dpif_offload_port_mgr_port {
> + struct cmap_node odp_port_node;
> + struct cmap_node netdev_node;
> + struct cmap_node ifindex_node;
> + struct netdev *netdev;
> + odp_port_t port_no;
> + int ifindex;
> +};
> +
> +
> +/* Global dpif port management library functions. */
> +struct dpif_offload_port_mgr *dpif_offload_port_mgr_init(void);
> +bool dpif_offload_port_mgr_add(struct dpif_offload_port_mgr *,
> + struct dpif_offload_port_mgr_port *,
> + struct netdev *netdev, odp_port_t,
> + bool need_ifindex);
> +struct dpif_offload_port_mgr_port *dpif_offload_port_mgr_remove(
> + struct dpif_offload_port_mgr *, odp_port_t, bool keep_netdev_ref);
> +void dpif_offload_port_mgr_uninit(struct dpif_offload_port_mgr *);
> +struct dpif_offload_port_mgr_port *dpif_offload_port_mgr_find_by_ifindex(
> + struct dpif_offload_port_mgr *, int ifindex);
> +struct dpif_offload_port_mgr_port *dpif_offload_port_mgr_find_by_netdev(
> + struct dpif_offload_port_mgr *, struct netdev *);
> +struct dpif_offload_port_mgr_port *dpif_offload_port_mgr_find_by_odp_port(
> + struct dpif_offload_port_mgr *, odp_port_t);
> +void dpif_offload_port_mgr_traverse_ports(
> + struct dpif_offload_port_mgr *mgr,
> + bool (*cb)(struct dpif_offload_port_mgr_port *, void *),
> + void *aux);
> +
> +#define DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH(PORT, PORT_MGR) \
> + CMAP_FOR_EACH (PORT, odp_port_node, &(PORT_MGR)->odp_port_to_port)
> +
> /* Global functions, called by the dpif layer or offload providers. */
> void dp_offload_initialize(void);
> void dpif_offload_set_config(struct dpif *, const struct smap *other_cfg);
> +void dpif_offload_port_add(struct dpif *, struct netdev *, odp_port_t);
> +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 *);
>
> 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 6f504b2c2..f5541473f 100644
> --- a/lib/dpif-offload-tc.c
> +++ b/lib/dpif-offload-tc.c
> @@ -18,9 +18,15 @@
>
> #include "dpif-offload.h"
> #include "dpif-offload-provider.h"
> +#include "netdev-provider.h"
> +#include "netdev-vport.h"
> #include "tc.h"
> #include "util.h"
>
> +#include "openvswitch/vlog.h"
> +
> +VLOG_DEFINE_THIS_MODULE(dpif_offload_tc);
> +
> /* dpif offload interface for the tc implementation. */
> struct dpif_offload_tc {
> struct dpif_offload offload;
> @@ -77,6 +83,19 @@ dpif_offload_tc_set_config(struct dpif_offload *offload,
> }
> }
>
> +static bool
> +dpif_offload_tc_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED,
> + struct netdev *netdev)
> +{
> + if (netdev_vport_is_vport_class(netdev->netdev_class) &&
> + strcmp(netdev_get_dpif_type(netdev), "system")) {
> + VLOG_DBG("%s: vport doesn't belong to the system datapath, skipping",
> + netdev_get_name(netdev));
> + return false;
> + }
> + return true;
> +}
> +
> struct dpif_offload_class dpif_offload_tc_class = {
> .type = "tc",
> .supported_dpif_types = (const char *const[]) {
> @@ -85,4 +104,5 @@ struct dpif_offload_class dpif_offload_tc_class = {
> .open = dpif_offload_tc_open,
> .close = dpif_offload_tc_close,
> .set_config = dpif_offload_tc_set_config,
> + .can_offload = dpif_offload_tc_can_offload,
> };
> diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c
> index 901980c03..c8cbe327b 100644
> --- a/lib/dpif-offload.c
> +++ b/lib/dpif-offload.c
> @@ -20,6 +20,7 @@
> #include "dpif-offload.h"
> #include "dpif-offload-provider.h"
> #include "dpif-provider.h"
> +#include "netdev-provider.h"
> #include "unixctl.h"
> #include "util.h"
> #include "openvswitch/dynamic-string.h"
> @@ -149,7 +150,8 @@ dp_offload_initialize(void)
>
> for (int i = 0; i < ARRAY_SIZE(base_dpif_offload_classes); i++) {
> ovs_assert(base_dpif_offload_classes[i]->open
> - && base_dpif_offload_classes[i]->close);
> + && base_dpif_offload_classes[i]->close
> + && base_dpif_offload_classes[i]->can_offload);
>
> dpif_offload_register_provider(base_dpif_offload_classes[i]);
> }
> @@ -173,7 +175,7 @@ dpif_offload_attach_provider_to_dp_offload__(struct
> dp_offload *dp_offload,
> LIST_FOR_EACH (offload_entry, dpif_list_node, providers_list) {
> if (offload_entry == offload || !strcmp(offload->name,
> offload_entry->name)) {
> - return EEXIST;
> + return -EEXIST;
> }
> }
>
> @@ -297,11 +299,14 @@ dpif_offload_attach_providers_(struct dpif *dpif)
> return 0;
> }
>
> +/* This function returns 0 if a new provider set was attached to the dpif,
> + * returns EEXIST if an existing set of providers was attached, and
> + * returns a negative error code on error. */
> int
> dpif_offload_attach_providers(struct dpif *dpif)
> {
> struct dp_offload *dp_offload;
> - int rc = 0;
> + int rc = EEXIST;
>
> ovs_mutex_lock(&dpif_offload_mutex);
>
> @@ -417,6 +422,92 @@ dpif_offload_is_offload_rebalance_policy_enabled(void)
> return enabled;
> }
>
> +void
> +dpif_offload_set_netdev_offload(struct netdev *netdev,
> + struct dpif_offload *offload)
> +{
> + ovsrcu_set(&netdev->dpif_offload, offload);
> +}
> +
> +void
> +dpif_offload_port_add(struct dpif *dpif, struct netdev *netdev,
> + odp_port_t port_no)
> +{
> + struct dp_offload *dp_offload = dpif_offload_get_dp_offload(dpif);
> + struct dpif_offload *offload;
> +
> + if (!dp_offload) {
> + return;
> + }
> +
> + LIST_FOR_EACH (offload, dpif_list_node, &dp_offload->offload_providers) {
> + if (!offload->class->port_add) {
> + continue;
> + }
> +
> + if (offload->class->can_offload(offload, netdev)) {
> + int err = offload->class->port_add(offload, netdev, port_no);
> + if (!err) {
> + VLOG_DBG("netdev %s added to dpif-offload provider %s",
> + netdev_get_name(netdev),
> dpif_offload_name(offload));
> + break;
> + } else {
> + VLOG_ERR("Failed adding netdev %s to dpif-offload provider "
> + "%s, error %s",
> + netdev_get_name(netdev), dpif_offload_name(offload),
> + ovs_strerror(err));
> + }
> + } else {
> + VLOG_DBG(
> + "netdev %s failed can_offload for dpif-offload provider %s",
> + netdev_get_name(netdev), dpif_offload_name(offload));
> + }
> + }
> +}
> +
> +void
> +dpif_offload_port_del(struct dpif *dpif, odp_port_t port_no)
> +{
> + struct dp_offload *dp_offload = dpif_offload_get_dp_offload(dpif);
> + struct dpif_offload *offload;
> +
> + if (!dp_offload) {
> + return;
> + }
> +
> + LIST_FOR_EACH (offload, dpif_list_node, &dp_offload->offload_providers) {
> + int err;
> +
> + if (!offload->class->port_del) {
> + continue;
> + }
> +
> + err = offload->class->port_del(offload, port_no);
> + if (err) {
> + VLOG_ERR("Failed deleting port_no %d from dpif-offload provider "
> + "%s, error %s", port_no, dpif_offload_name(offload),
> + ovs_strerror(err));
> + }
> + }
> +}
> +
> +void
> +dpif_offload_port_set_config(struct dpif *dpif, odp_port_t port_no,
> + const struct smap *cfg)
> +{
> + struct dp_offload *dp_offload = dpif_offload_get_dp_offload(dpif);
> + struct dpif_offload *offload;
> +
> + if (!dp_offload) {
> + return;
> + }
> + LIST_FOR_EACH (offload, dpif_list_node, &dp_offload->offload_providers) {
> + if (offload->class->port_set_config) {
> + offload->class->port_set_config(offload, port_no, cfg);
> + }
> + }
> +}
> +
> void
> dpif_offload_dump_start(struct dpif_offload_dump *dump,
> const struct dpif *dpif)
> @@ -550,3 +641,171 @@ dpif_offload_set_global_cfg(const struct smap
> *other_cfg)
> }
> }
> }
> +
> +
> +struct dpif_offload_port_mgr *
> +dpif_offload_port_mgr_init(void)
> +{
> + struct dpif_offload_port_mgr *mgr = xmalloc(sizeof *mgr);
> +
> + ovs_mutex_init(&mgr->cmap_mod_lock);
> +
> + cmap_init(&mgr->odp_port_to_port);
> + cmap_init(&mgr->netdev_to_port);
> + cmap_init(&mgr->ifindex_to_port);
> +
> + return mgr;
> +}
> +
> +void dpif_offload_port_mgr_uninit(struct dpif_offload_port_mgr *mgr)
> +{
> + if (!mgr) {
> + return;
> + }
> +
> + ovs_assert(cmap_count(&mgr->odp_port_to_port) == 0);
> + ovs_assert(cmap_count(&mgr->netdev_to_port) == 0);
> + ovs_assert(cmap_count(&mgr->ifindex_to_port) == 0);
> +
> + cmap_destroy(&mgr->odp_port_to_port);
> + cmap_destroy(&mgr->netdev_to_port);
> + cmap_destroy(&mgr->ifindex_to_port);
> + free(mgr);
> +}
> +
> +struct dpif_offload_port_mgr_port *
> +dpif_offload_port_mgr_find_by_ifindex(struct dpif_offload_port_mgr *mgr,
> + int ifindex)
> +{
> + struct dpif_offload_port_mgr_port *port;
> +
> + if (ifindex < 0) {
> + return NULL;
> + }
> +
> + CMAP_FOR_EACH_WITH_HASH (port, ifindex_node, hash_int(ifindex, 0),
> + &mgr->ifindex_to_port) {
> + if (port->ifindex == ifindex) {
> + return port;
> + }
> + }
> + return NULL;
> +}
> +
> +struct dpif_offload_port_mgr_port *
> +dpif_offload_port_mgr_find_by_netdev(struct dpif_offload_port_mgr *mgr,
> + struct netdev *netdev)
> +{
> + struct dpif_offload_port_mgr_port *port;
> +
> + if (!netdev) {
> + return NULL;
> + }
> +
> + CMAP_FOR_EACH_WITH_HASH (port, netdev_node, hash_pointer(netdev, 0),
> + &mgr->netdev_to_port) {
> + if (port->netdev == netdev) {
> + return port;
> + }
> + }
> + return NULL;
> +}
> +
> +struct dpif_offload_port_mgr_port *
> +dpif_offload_port_mgr_find_by_odp_port(struct dpif_offload_port_mgr *mgr,
> + odp_port_t port_no)
> +{
> + struct dpif_offload_port_mgr_port *port;
> +
> + CMAP_FOR_EACH_WITH_HASH (port, odp_port_node,
> + hash_int(odp_to_u32(port_no), 0),
> + &mgr->odp_port_to_port) {
> + if (port->port_no == port_no) {
> + return port;
> + }
> + }
> + return NULL;
> +}
> +
> +struct dpif_offload_port_mgr_port *
> +dpif_offload_port_mgr_remove(struct dpif_offload_port_mgr *mgr,
> + odp_port_t port_no, bool keep_netdev_ref)
> +{
> + struct dpif_offload_port_mgr_port *port;
> +
> + ovs_mutex_lock(&mgr->cmap_mod_lock);
> +
> + port = dpif_offload_port_mgr_find_by_odp_port(mgr, port_no);
> +
> + if (port) {
> + cmap_remove(&mgr->odp_port_to_port, &port->odp_port_node,
> + hash_int(odp_to_u32(port_no), 0));
> + cmap_remove(&mgr->netdev_to_port, &port->netdev_node,
> + hash_pointer(port->netdev, 0));
> +
> + if (port->ifindex >= 0) {
> + cmap_remove(&mgr->ifindex_to_port, &port->ifindex_node,
> + hash_int(port->ifindex, 0));
> + }
> + if (!keep_netdev_ref) {
> + netdev_close(port->netdev);
> + }
> + }
> +
> + ovs_mutex_unlock(&mgr->cmap_mod_lock);
> + return port;
> +}
> +
> +bool
> +dpif_offload_port_mgr_add(struct dpif_offload_port_mgr *mgr,
> + struct dpif_offload_port_mgr_port *port,
> + struct netdev *netdev, odp_port_t port_no,
> + bool need_ifindex)
> +{
> + ovs_assert(netdev);
> +
> + memset(port, 0, sizeof *port);
> + port->port_no = port_no;
> + port->ifindex = need_ifindex ? netdev_get_ifindex(netdev) : -1;
> +
> + ovs_mutex_lock(&mgr->cmap_mod_lock);
> +
> + if (dpif_offload_port_mgr_find_by_odp_port(mgr, port_no)
> + || dpif_offload_port_mgr_find_by_ifindex(mgr, port->ifindex)
> + || dpif_offload_port_mgr_find_by_netdev(mgr, netdev)) {
> +
> + ovs_mutex_unlock(&mgr->cmap_mod_lock);
> + return false;
> + }
> +
> + port->netdev = netdev_ref(netdev);
> +
> + cmap_insert(&mgr->odp_port_to_port, &port->odp_port_node,
> + hash_int(odp_to_u32(port_no), 0));
> +
> + cmap_insert(&mgr->netdev_to_port, &port->netdev_node,
> + hash_pointer(netdev, 0));
> +
> + if (port->ifindex >= 0) {
> + cmap_insert(&mgr->ifindex_to_port, &port->ifindex_node,
> + hash_int(port->ifindex, 0));
> + }
> +
> + ovs_mutex_unlock(&mgr->cmap_mod_lock);
> + return true;
> +}
> +
> +void
> +dpif_offload_port_mgr_traverse_ports(
> + struct dpif_offload_port_mgr *mgr,
> + bool (*cb)(struct dpif_offload_port_mgr_port *, void *),
> + void *aux)
> +{
> + struct dpif_offload_port_mgr_port *port;
> +
> + DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, mgr) {
> + if (cb(port, aux)) {
> + break;
> + }
> + }
> +}
> diff --git a/lib/dpif.c b/lib/dpif.c
> index 4d4b5127b..20bf578d6 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -359,10 +359,11 @@ do_open(const char *name, const char *type, bool
> create, struct dpif **dpifp)
> const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif));
> struct dpif_port_dump port_dump;
> struct dpif_port dpif_port;
> + bool new_offload_provider;
>
> ovs_assert(dpif->dpif_class == registered_class->dpif_class);
>
> - dpif_offload_attach_providers(dpif);
> + new_offload_provider = dpif_offload_attach_providers(dpif) != EEXIST;
>
> DPIF_PORT_FOR_EACH(&dpif_port, &port_dump, dpif) {
> struct netdev *netdev;
> @@ -377,6 +378,9 @@ do_open(const char *name, const char *type, bool create,
> struct dpif **dpifp)
> if (!err) {
> netdev_set_dpif_type(netdev, dpif_type_str);
> netdev_ports_insert(netdev, &dpif_port);
> + if (new_offload_provider) {
> + dpif_offload_port_add(dpif, netdev, dpif_port.port_no);
> + }
> netdev_close(netdev);
> } else {
> VLOG_WARN("could not open netdev %s type %s: %s",
> @@ -621,6 +625,8 @@ dpif_port_add(struct dpif *dpif, struct netdev *netdev,
> odp_port_t *port_nop)
> dpif_port.name = CONST_CAST(char *, netdev_name);
> dpif_port.port_no = port_no;
> netdev_ports_insert(netdev, &dpif_port);
> +
> + dpif_offload_port_add(dpif, netdev, port_no);
> }
> } else {
> VLOG_WARN_RL(&error_rl, "%s: failed to add %s as port: %s",
> @@ -652,6 +658,8 @@ dpif_port_del(struct dpif *dpif, odp_port_t port_no, bool
> local_delete)
> }
> }
>
> + dpif_offload_port_del(dpif, port_no);
> +
> netdev_ports_remove(port_no, dpif_normalize_type(dpif_type(dpif)));
> return error;
> }
> @@ -703,6 +711,7 @@ dpif_port_set_config(struct dpif *dpif, odp_port_t
> port_no,
> if (error) {
> log_operation(dpif, "port_set_config", error);
> }
> + dpif_offload_port_set_config(dpif, port_no, cfg);
> }
>
> return error;
> diff --git a/lib/dummy.h b/lib/dummy.h
> index b16ce0bbb..f0eb30ee2 100644
> --- a/lib/dummy.h
> +++ b/lib/dummy.h
> @@ -19,6 +19,8 @@
>
> #include <stdbool.h>
>
> +struct netdev_class;
> +
> /* Degree of dummy support.
> *
> * Beyond enabling support for dummies, it can be useful to replace some
> kinds
> @@ -38,5 +40,6 @@ void dpif_dummy_register(enum dummy_level);
> void netdev_dummy_register(enum dummy_level);
> void timeval_dummy_register(void);
> void ofpact_dummy_enable(void);
> +bool is_dummy_netdev_class(const struct netdev_class *);
>
> #endif /* dummy.h */
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index d4e504c4e..e1c1182b0 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -6493,7 +6493,7 @@ out:
> }
>
> bool
> -netdev_dpdk_flow_api_supported(struct netdev *netdev)
> +netdev_dpdk_flow_api_supported(struct netdev *netdev, bool check_only)
> {
> struct netdev_dpdk *dev;
> bool ret = false;
> @@ -6512,7 +6512,7 @@ netdev_dpdk_flow_api_supported(struct netdev *netdev)
> dev = netdev_dpdk_cast(netdev);
> ovs_mutex_lock(&dev->mutex);
> if (dev->type == DPDK_DEV_ETH) {
> - if (dev->requested_rx_steer_flags) {
> + if (dev->requested_rx_steer_flags && !check_only) {
> VLOG_WARN("%s: rx-steering is mutually exclusive with
> hw-offload,"
> " falling back to default rss mode",
> netdev_get_name(netdev));
> diff --git a/lib/netdev-dpdk.h b/lib/netdev-dpdk.h
> index 86df7a1e8..e6779d478 100644
> --- a/lib/netdev-dpdk.h
> +++ b/lib/netdev-dpdk.h
> @@ -32,7 +32,7 @@ struct netdev;
> void netdev_dpdk_register(const struct smap *);
> void free_dpdk_buf(struct dp_packet *);
>
> -bool netdev_dpdk_flow_api_supported(struct netdev *);
> +bool netdev_dpdk_flow_api_supported(struct netdev *, bool check_only);
>
> int
> netdev_dpdk_rte_flow_destroy(struct netdev *netdev,
> diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
> index 0270ab228..611538004 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -213,8 +213,8 @@ static void dummy_packet_stream_close(struct
> dummy_packet_stream *);
> static void pkt_list_delete(struct ovs_list *);
> static void addr_list_delete(struct ovs_list *);
>
> -static bool
> -is_dummy_class(const struct netdev_class *class)
> +bool
> +is_dummy_netdev_class(const struct netdev_class *class)
> {
> return class->construct == netdev_dummy_construct;
> }
> @@ -222,14 +222,14 @@ is_dummy_class(const struct netdev_class *class)
> static struct netdev_dummy *
> netdev_dummy_cast(const struct netdev *netdev)
> {
> - ovs_assert(is_dummy_class(netdev_get_class(netdev)));
> + ovs_assert(is_dummy_netdev_class(netdev_get_class(netdev)));
> return CONTAINER_OF(netdev, struct netdev_dummy, up);
> }
>
> static struct netdev_rxq_dummy *
> netdev_rxq_dummy_cast(const struct netdev_rxq *rx)
> {
> - ovs_assert(is_dummy_class(netdev_get_class(rx->netdev)));
> + ovs_assert(is_dummy_netdev_class(netdev_get_class(rx->netdev)));
> return CONTAINER_OF(rx, struct netdev_rxq_dummy, up);
> }
>
> @@ -1929,7 +1929,7 @@ static const struct netdev_class dummy_pmd_class = {
> static int
> netdev_dummy_offloads_init_flow_api(struct netdev *netdev)
> {
> - return is_dummy_class(netdev->netdev_class) ? 0 : EOPNOTSUPP;
> + return is_dummy_netdev_class(netdev->netdev_class) ? 0 : EOPNOTSUPP;
> }
>
> static const struct netdev_flow_api netdev_offload_dummy = {
> @@ -2101,7 +2101,7 @@ netdev_dummy_receive(struct unixctl_conn *conn,
> int i, k = 1, rx_qid = 0;
>
> netdev = netdev_from_name(argv[k++]);
> - if (!netdev || !is_dummy_class(netdev->netdev_class)) {
> + if (!netdev || !is_dummy_netdev_class(netdev->netdev_class)) {
> unixctl_command_reply_error(conn, "no such dummy netdev");
> goto exit_netdev;
> }
> @@ -2192,7 +2192,7 @@ netdev_dummy_set_admin_state(struct unixctl_conn *conn,
> int argc,
>
> if (argc > 2) {
> struct netdev *netdev = netdev_from_name(argv[1]);
> - if (netdev && is_dummy_class(netdev->netdev_class)) {
> + if (netdev && is_dummy_netdev_class(netdev->netdev_class)) {
> struct netdev_dummy *dummy_dev = netdev_dummy_cast(netdev);
>
> ovs_mutex_lock(&dummy_dev->mutex);
> @@ -2254,7 +2254,7 @@ netdev_dummy_conn_state(struct unixctl_conn *conn, int
> argc,
> const char *dev_name = argv[1];
> struct netdev *netdev = netdev_from_name(dev_name);
>
> - if (netdev && is_dummy_class(netdev->netdev_class)) {
> + if (netdev && is_dummy_netdev_class(netdev->netdev_class)) {
> struct netdev_dummy *dummy_dev = netdev_dummy_cast(netdev);
>
> ovs_mutex_lock(&dummy_dev->mutex);
> @@ -2289,7 +2289,7 @@ netdev_dummy_ip4addr(struct unixctl_conn *conn, int
> argc OVS_UNUSED,
> {
> struct netdev *netdev = netdev_from_name(argv[1]);
>
> - if (netdev && is_dummy_class(netdev->netdev_class)) {
> + if (netdev && is_dummy_netdev_class(netdev->netdev_class)) {
> struct in_addr ip, mask;
> struct in6_addr ip6;
> uint32_t plen;
> @@ -2323,7 +2323,7 @@ netdev_dummy_ip6addr(struct unixctl_conn *conn, int
> argc OVS_UNUSED,
> {
> struct netdev *netdev = netdev_from_name(argv[1]);
>
> - if (netdev && is_dummy_class(netdev->netdev_class)) {
> + if (netdev && is_dummy_netdev_class(netdev->netdev_class)) {
> struct in6_addr ip6;
> char *error;
> uint32_t plen;
> diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
> index ca1982ccd..c4f97be70 100644
> --- a/lib/netdev-offload-dpdk.c
> +++ b/lib/netdev-offload-dpdk.c
> @@ -2500,7 +2500,7 @@ netdev_offload_dpdk_init_flow_api(struct netdev *netdev)
> return EOPNOTSUPP;
> }
>
> - if (netdev_dpdk_flow_api_supported(netdev)) {
> + if (netdev_dpdk_flow_api_supported(netdev, false)) {
> ret = offload_data_init(netdev);
> }
>
> @@ -2510,7 +2510,7 @@ netdev_offload_dpdk_init_flow_api(struct netdev *netdev)
> static void
> netdev_offload_dpdk_uninit_flow_api(struct netdev *netdev)
> {
> - if (netdev_dpdk_flow_api_supported(netdev)) {
> + if (netdev_dpdk_flow_api_supported(netdev, true)) {
> offload_data_destroy(netdev);
> }
> }
> diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
> index 33a68c49c..87bc95180 100644
> --- a/lib/netdev-provider.h
> +++ b/lib/netdev-provider.h
> @@ -100,6 +100,7 @@ struct netdev {
> struct ovs_list saved_flags_list; /* Contains "struct
> netdev_saved_flags". */
>
> /* Functions to control flow offloading. */
> + OVSRCU_TYPE(const struct dpif_offload *) dpif_offload;
> OVSRCU_TYPE(const struct netdev_flow_api *) flow_api;
> const char *dpif_type; /* Type of dpif this netdev belongs to.
> */
> struct netdev_hw_info hw_info; /* Offload-capable netdev info. */
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev