@@ -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,
+ 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[]) {
@@ -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
@@ -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");
@@ -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 **'. */
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,
@@ -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,
};
@@ -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]);
}
@@ -165,7 +167,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;
}
}
@@ -192,7 +194,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
@@ -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;
+ }
+ }
+}
@@ -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;
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;
@@ -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 */
@@ -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));
@@ -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,
@@ -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;
@@ -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);
}
}
@@ -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. */
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 <echaudro@redhat.com> --- 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 | 264 +++++++++++++++++++++++++++++++++++- 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, 549 insertions(+), 25 deletions(-)