>-----Original Message-----
>From: dev <[email protected]> On Behalf Of Eelco Chaudron
>Sent: Tuesday, 2 December 2025 16:05
>To: [email protected]
>Subject: [ovs-dev] [PATCH v2 07/41] dpif-offload: Add port registration and
>management APIs.
>
>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.
>
>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().
>---
> lib/dpif-offload-dpdk.c | 17 +++
> lib/dpif-offload-dummy.c | 152 +++++++++++++++++++-
> lib/dpif-offload-provider.h | 75 +++++++++-
> lib/dpif-offload-tc.c | 20 +++
> lib/dpif-offload.c | 268 +++++++++++++++++++++++++++++++++++-
> lib/dpif.c | 12 +-
> 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, 553 insertions(+), 25 deletions(-)
>
>diff --git a/lib/dpif-offload-dpdk.c b/lib/dpif-offload-dpdk.c index
>6ac4af8d8..76e0ba15f 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"
>@@ -100,6 +102,20 @@ dpif_offload_dpdk_set_config(struct dpif_offload
>*offload_,
> }
> }
>
>+static bool
>+dpif_offload_dpdk_can_offload(struct dpif_offload *offload OVS_UNUSED,
I see the offload parameter is not used also at the final commit. Do we still
want to keep it?
>+ struct netdev *netdev) {
>+ if (netdev_vport_is_vport_class(netdev->netdev_class)
>+ && strcmp(netdev_get_dpif_type(netdev), "netdev")) {
Indentation.
>+ 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[]) { @@ -108,6 +124,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
>1d7b72b00..827165ba2 100644
>--- a/lib/dpif-offload-dummy.c
>+++ b/lib/dpif-offload-dummy.c
>@@ -15,27 +15,167 @@
> */
>
> #include <config.h>
>+#include <errno.h>
>
> #include "dpif.h"
> #include "dpif-offload-provider.h"
> #include "dpif-offload.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 *dpif_offload OVS_UNUSED,
No need for this argument.
>+ 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 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(dpif_offload, port);
>+ }
>+ netdev_close(port->netdev);
>+ ovsrcu_postpone(free, 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(struct dpif_offload));
>+ struct dpif_offload_dummy *offload_dummy;
>+
>+ offload_dummy = xmalloc(sizeof(struct dpif_offload_dummy));
>
>- dpif_offload_init(offload, offload_class, dpif);
>- *dpif_offload = offload;
>+ dpif_offload_init(&offload_dummy->offload, offload_class, dpif);
>+ offload_dummy->port_mgr = dpif_offload_port_mgr_init();
>+ offload_dummy->once_enable = (struct ovsthread_once)
>+ OVSTHREAD_ONCE_INITIALIZER;
>+
>+ *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. */
>+ 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) ? true : false;
No need for the ?:
> }
>
> #define DEFINE_DPIF_DUMMY_CLASS(NAME, TYPE_STR) \
>@@ -46,6 +186,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
>09a7dfc2c..496959ea3 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"
>@@ -76,8 +77,8 @@ struct dpif_offload_class {
> /* Attempts to open the offload provider for the specified dpif.
> * If successful, stores a pointer to the new dpif offload in
> * 'dpif_offload **', which must be of class 'dpif_offload_class'.
>- * On failure, there are no requirements for what is stored in
>- * 'dpif_offload **'. */
>+ * On failure (indicated by a negative return value), there are no
>+ * requirements for what is stored in 'dpif_offload **'. */
This comment change can be on the first commit introduced it.
> int (*open)(const struct dpif_offload_class *,
> struct dpif *, struct dpif_offload **);
>
>@@ -91,6 +92,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
>+ * 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
>+ * as above in 'port_deleted' applies here.*/
>+ void (*port_set_config)(struct dpif_offload *, odp_port_t port_no,
>+ const struct smap *cfg);
> };
>
>
>@@ -100,9 +126,54 @@ 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);
>+
>+
> /* 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 c09530daa..2c9081438 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 "util.h"
> #include "tc.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;
>@@ -80,6 +86,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[]) { @@ -88,4 +107,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 4c89ae443..43258dd55
>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]);
> }
>@@ -172,7 +174,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;
?
> }
> }
>
>@@ -199,7 +201,7 @@ dpif_offload_attach_dp_offload(struct dpif *dpif, {
> ovsrcu_set(&dpif->dp_offload, dp_offload);
> ovs_refcount_ref(&dp_offload->ref_cnt);
>- return 0;
>+ return EEXIST;
> }
>
> static int
>@@ -293,6 +295,9 @@ 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) { @@ -414,6 +419,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) @@ -547,3 +638,174 @@
>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)
>+ {
"{" should be in the same line above.
>+ 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;
>+
>+ CMAP_FOR_EACH (port, odp_port_node, &mgr->odp_port_to_port) {
>+ if (cb(port, aux)) {
>+ break;
>+ }
>+ }
>+}
>diff --git a/lib/dpif.c b/lib/dpif.c
>index 4d4b5127b..fb889322a 100644
>--- a/lib/dpif.c
>+++ b/lib/dpif.c
>@@ -359,10 +359,12 @@ 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
>+ ? false : true;
new_offload_provider = dpif_offload_attach_providers(dpif) != EEXIST
>
> DPIF_PORT_FOR_EACH(&dpif_port, &port_dump, dpif) {
> struct netdev *netdev;
>@@ -377,6 +379,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
>+626,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
>+659,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 +712,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 ff5a75a36..fbd60d377
>100644
>--- a/lib/netdev-dpdk.c
>+++ b/lib/netdev-dpdk.c
>@@ -6516,7 +6516,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;
>@@ -6535,7 +6535,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 b72820fcc..0e5239eeb 100644
>--- a/lib/netdev-dummy.c
>+++ b/lib/netdev-dummy.c
>@@ -203,8 +203,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; } @@ -212,14
>+212,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); }
>
>@@ -1850,7 +1850,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 = { @@ -2022,7
>+2022,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;
> }
>@@ -2113,7 +2113,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);
>@@ -2175,7 +2175,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);
>@@ -2210,7 +2210,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;
>@@ -2244,7 +2244,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