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]>
---
 lib/dpif-offload-dummy.c    | 152 ++++++++++++++++++++-
 lib/dpif-offload-provider.h |  71 ++++++++++
 lib/dpif-offload-rte_flow.c |  17 +++
 lib/dpif-offload-tc.c       |  20 +++
 lib/dpif-offload.c          | 260 +++++++++++++++++++++++++++++++++++-
 lib/dpif.c                  |   5 +
 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, 539 insertions(+), 20 deletions(-)

diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
index 04f4b5227..d4997891c 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,
+    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;
 }
 
 #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 ae92a6f74..ce309a672 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"
@@ -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_rte_flow_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-rte_flow.c b/lib/dpif-offload-rte_flow.c
index 3cfe0f3d3..0041b5613 100644
--- a/lib/dpif-offload-rte_flow.c
+++ b/lib/dpif-offload-rte_flow.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"
@@ -102,6 +104,20 @@ dpif_offload_rte_set_config(struct dpif_offload *offload,
     }
 }
 
+static bool
+dpif_offload_rte_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), "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_rte_flow_class = {
     .type = "rte_flow",
     .supported_dpif_types = (const char *const[]) {
@@ -110,6 +126,7 @@ struct dpif_offload_class dpif_offload_rte_flow_class = {
     .open = dpif_offload_rte_open,
     .close = dpif_offload_rte_close,
     .set_config = dpif_offload_rte_set_config,
+    .can_offload = dpif_offload_rte_can_offload,
 };
 
 /* XXX: Temporary functions below, which will be removed once fully
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 12fb4e7b8..e8d96aef9 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"
@@ -142,7 +143,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]);
     }
@@ -407,6 +409,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)
@@ -539,3 +627,173 @@ 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->netdev = netdev_ref(netdev);
+    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, port->netdev)) {
+
+        ovs_mutex_unlock(&mgr->cmap_mod_lock);
+        return false;
+    }
+
+    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 a173d643a..54fce1e08 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -621,6 +621,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 +654,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 +707,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 2d15909bc..32298e707 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -6499,7 +6499,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;
@@ -6518,7 +6518,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 16f863284..947bd9fa7 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -2501,7 +2501,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);
     }
 
@@ -2511,7 +2511,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 5ae379469..26c63839a 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. */
-- 
2.50.1

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

Reply via email to