@@ -143,6 +143,10 @@ lib_libopenvswitch_la_SOURCES = \
lib/dpif-netdev-private.h \
lib/dpif-netdev-perf.c \
lib/dpif-netdev-perf.h \
+ lib/dpif-offload.c \
+ lib/dpif-offload.h \
+ lib/dpif-offload-dummy.c \
+ lib/dpif-offload-provider.h \
lib/dpif-provider.h \
lib/dpif.c \
lib/dpif.h \
new file mode 100644
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ #include <config.h>
+
+#include "dpif.h"
+#include "dpif-offload-provider.h"
+#include "dpif-offload.h"
+#include "util.h"
+
+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));
+
+ dpif_offload_init(offload, offload_class, dpif);
+ *dpif_offload = offload;
+ return 0;
+}
+
+static void
+dpif_offload_dummy_close(struct dpif_offload *dpif_offload)
+{
+ free(dpif_offload);
+}
+
+#define DEFINE_DPIF_DUMMY_CLASS(NAME, TYPE_STR) \
+ struct dpif_offload_class NAME = { \
+ .type = TYPE_STR, \
+ .supported_dpif_types = (const char *const[]) { \
+ "dummy", \
+ NULL}, \
+ .open = dpif_offload_dummy_open, \
+ .close = dpif_offload_dummy_close, \
+ }
+
+DEFINE_DPIF_DUMMY_CLASS(dpif_offload_dummy_class, "dummy");
+DEFINE_DPIF_DUMMY_CLASS(dpif_offload_dummy_x_class, "dummy_x");
new file mode 100644
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DPIF_OFFLOAD_PROVIDER_H
+#define DPIF_OFFLOAD_PROVIDER_H
+
+#include "ovs-thread.h"
+#include "openvswitch/list.h"
+
+/* The DPIF Offload Provider introduces an abstraction layer for hardware
+ * offload functionality implemented at the netdevice level. It sits above
+ * the netdevice layer within the DPIF (Datapath Interface) framework,
+ * providing a standardized API for offloading packet processing tasks to
+ * hardware-accelerated datapaths.
+ *
+ * By decoupling hardware-specific implementations from the core DPIF layer,
+ * this abstraction enables greater flexibility, maintainability, and support
+ * for multiple hardware offload mechanisms without directly modifying DPIF
+ * internals. */
+
+/* DPIF Offload specific structure pointed to in struct dpif. */
+struct dp_offload {
+ char *dpif_name; /* Name of the associated dpif. */
+
+ struct ovs_list offload_providers; /* Note that offload providers will
+ * only be added at dpif creation time
+ * and removed during destruction.
+ * No intermediate additions or
+ * deletions are allowed; hence no
+ * locking of the list is required. */
+
+ struct ovs_mutex offload_mutex; /* Mutex to protect all below. */
+ struct ovs_refcount ref_cnt;
+};
+
+/* This structure should be treated as opaque by dpif offload implementations.
+ */
+struct dpif_offload {
+ const struct dpif_offload_class *class;
+ struct ovs_list dpif_list_node;
+ char *name;
+};
+
+
+struct dpif_offload_class {
+ /* Type of DPIF offload provider in this class, e.g., "tc", "dpdk",
+ * "dummy", etc. */
+ const char *type;
+
+ /* List of DPIF implementation types supported by the offload provider.
+ * This is implemented as a pointer to a null-terminated list of const
+ * type strings. For more details on these type strings, see the
+ * 'struct dpif_class' definition. */
+ const char *const *supported_dpif_types;
+
+ /* Called when the dpif offload provider class is registered. Note that
+ * this is the global initialization, not the per dpif one. */
+ int (*init)(void);
+
+ /* 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 **'. */
+ int (*open)(const struct dpif_offload_class *,
+ struct dpif *, struct dpif_offload **);
+
+ /* Closes 'dpif_offload' and frees associated memory and resources.
+ * This includes freeing the 'dpif_offload' structure allocated by
+ * open() above. If your implementation accesses this provider using
+ * RCU pointers, it's responsible for handling deferred deallocation. */
+ void (*close)(struct dpif_offload *);
+};
+
+
+extern struct dpif_offload_class dpif_offload_dummy_class;
+extern struct dpif_offload_class dpif_offload_dummy_x_class;
+
+
+/* Global function, called by the dpif layer. */
+void dp_offload_initialize(void);
+
+
+#endif /* DPIF_OFFLOAD_PROVIDER_H */
new file mode 100644
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <errno.h>
+
+#include "dpif-offload.h"
+#include "dpif-offload-provider.h"
+#include "dpif-provider.h"
+#include "unixctl.h"
+#include "util.h"
+#include "openvswitch/dynamic-string.h"
+#include "openvswitch/shash.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(dpif_offload);
+
+static struct ovs_mutex dpif_offload_mutex = OVS_MUTEX_INITIALIZER;
+static struct shash dpif_offload_classes \
+ OVS_GUARDED_BY(dpif_offload_mutex) = \
+ SHASH_INITIALIZER(&dpif_offload_classes);
+static struct shash dpif_offload_providers \
+ OVS_GUARDED_BY(dpif_offload_mutex) = \
+ SHASH_INITIALIZER(&dpif_offload_providers);
+
+static const struct dpif_offload_class *base_dpif_offload_classes[] = {
+ &dpif_offload_dummy_class,
+ &dpif_offload_dummy_x_class,
+};
+
+static int
+dpif_offload_register_provider__(const struct dpif_offload_class *class)
+ OVS_REQUIRES(dpif_offload_mutex)
+{
+ int error;
+
+ if (shash_find(&dpif_offload_classes, class->type)) {
+ VLOG_WARN("attempted to register duplicate dpif offload class: %s",
+ class->type);
+ return EEXIST;
+ }
+
+ if (!class->supported_dpif_types) {
+ VLOG_WARN("attempted to register a dpif offload class without any "
+ "supported dpifs: %s", class->type);
+ return EINVAL;
+ }
+
+ error = class->init ? class->init() : 0;
+ if (error) {
+ VLOG_WARN("failed to initialize %s dpif offload class: %s",
+ class->type, ovs_strerror(error));
+ return error;
+ }
+
+ shash_add(&dpif_offload_classes, class->type, class);
+ return 0;
+}
+
+static int
+dpif_offload_register_provider(const struct dpif_offload_class *class)
+{
+ int error;
+
+ ovs_mutex_lock(&dpif_offload_mutex);
+ error = dpif_offload_register_provider__(class);
+ ovs_mutex_unlock(&dpif_offload_mutex);
+
+ return error;
+}
+
+static void
+dpif_offload_show_classes(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+ const struct shash_node **list;
+ struct ds ds;
+
+ ds_init(&ds);
+ ovs_mutex_lock(&dpif_offload_mutex);
+
+ list = shash_sort(&dpif_offload_classes);
+ for (size_t i = 0; i < shash_count(&dpif_offload_classes); i++) {
+ const struct dpif_offload_class *class = list[i]->data;
+
+ if (i == 0) {
+ ds_put_cstr(&ds, "Offload Class Supported dpif class(es)\n");
+ ds_put_cstr(&ds, "---------------- ------------------------\n");
+ }
+
+ ds_put_format(&ds, "%-16s ", list[i]->name);
+
+ for (size_t j = 0; class->supported_dpif_types[j] != NULL; j++) {
+ ds_put_format(&ds, "%*s%s\n", j == 0 ? 0 : 18, "",
+ class->supported_dpif_types[j]);
+ }
+ }
+
+ ovs_mutex_unlock(&dpif_offload_mutex);
+ free(list);
+
+ unixctl_command_reply(conn, ds_cstr(&ds));
+ ds_destroy(&ds);
+}
+
+void
+dp_offload_initialize(void)
+{
+ unixctl_command_register("dpif/offload/classes", NULL, 0, 0,
+ dpif_offload_show_classes, NULL);
+
+ 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);
+
+ dpif_offload_register_provider(base_dpif_offload_classes[i]);
+ }
+}
+
+static struct dp_offload*
+dpif_offload_get_dp_offload(const struct dpif *dpif)
+{
+ return ovsrcu_get(struct dp_offload *, &dpif->dp_offload);
+}
+
+static int
+dpif_offload_attach_provider_to_dp_offload__(struct dp_offload *dp_offload,
+ struct dpif_offload *offload)
+{
+ struct ovs_list *providers_list = &dp_offload->offload_providers;
+ struct dpif_offload *offload_entry;
+
+ LIST_FOR_EACH (offload_entry, dpif_list_node, providers_list) {
+ if (offload_entry == offload || !strcmp(offload->name,
+ offload_entry->name)) {
+ return EEXIST;
+ }
+ }
+
+ ovs_list_push_back(providers_list, &offload->dpif_list_node);
+ return 0;
+}
+
+static int
+dpif_offload_attach_provider_to_dp_offload(struct dp_offload *dp_offload,
+ struct dpif_offload *offload)
+{
+ int error;
+
+ ovs_assert(dp_offload);
+
+ error = dpif_offload_attach_provider_to_dp_offload__(dp_offload, offload);
+ return error;
+}
+
+static int
+dpif_offload_attach_dp_offload(struct dpif *dpif,
+ struct dp_offload *dp_offload)
+ OVS_REQUIRES(dpif_offload_mutex)
+{
+ ovsrcu_set(&dpif->dp_offload, dp_offload);
+ ovs_refcount_ref(&dp_offload->ref_cnt);
+ return 0;
+}
+
+static int
+dpif_offload_attach_providers_(struct dpif *dpif)
+ OVS_REQUIRES(dpif_offload_mutex)
+{
+ const char *dpif_type_str = dpif_normalize_type(dpif_type(dpif));
+ struct dp_offload *dp_offload;
+ struct shash_node *node;
+
+ /* Allocate and attach dp_offload to dpif. */
+ dp_offload = xmalloc(sizeof *dp_offload);
+ dp_offload->dpif_name = xstrdup(dpif_name(dpif));
+ ovs_mutex_init_recursive(&dp_offload->offload_mutex);
+ ovs_refcount_init(&dp_offload->ref_cnt);
+ ovs_list_init(&dp_offload->offload_providers);
+ shash_add(&dpif_offload_providers, dp_offload->dpif_name, dp_offload);
+
+ /* Attach all the providers supporting this dpif type. */
+ SHASH_FOR_EACH (node, &dpif_offload_classes) {
+ const struct dpif_offload_class *class = node->data;
+ for (size_t i = 0; class->supported_dpif_types[i] != NULL; i++) {
+ if (!strcmp(class->supported_dpif_types[i], dpif_type_str)) {
+ struct dpif_offload *offload;
+ int error;
+
+ error = class->open(class, dpif, &offload);
+ if (!error) {
+
+ error = dpif_offload_attach_provider_to_dp_offload(
+ dp_offload, offload);
+ if (error) {
+ VLOG_WARN("failed to add dpif offload provider "
+ "%s to %s: %s",
+ class->type, dpif_name(dpif),
+ ovs_strerror(error));
+ class->close(offload);
+ }
+ } else {
+ VLOG_WARN("failed to initialize dpif offload provider "
+ "%s for %s: %s",
+ class->type, dpif_name(dpif),
+ ovs_strerror(error));
+ }
+ break;
+ }
+ }
+ }
+
+ /* Attach dp_offload to dpif. */
+ ovsrcu_set(&dpif->dp_offload, dp_offload);
+
+ return 0;
+}
+
+int
+dpif_offload_attach_providers(struct dpif *dpif)
+{
+ struct dp_offload *dp_offload;
+ int rc;
+
+ ovs_mutex_lock(&dpif_offload_mutex);
+
+ dp_offload = shash_find_data(&dpif_offload_providers, dpif_name(dpif));
+ if (dp_offload) {
+ rc = dpif_offload_attach_dp_offload(dpif, dp_offload);
+ } else {
+ rc = dpif_offload_attach_providers_(dpif);
+ }
+
+ ovs_mutex_unlock(&dpif_offload_mutex);
+ return rc;
+}
+
+static void
+dpif_offload_free_dp_offload_rcu(struct dp_offload *dp_offload)
+{
+ struct dpif_offload *offload_entry;
+
+ /* We need to use the safe variant here as we removed the entry, and the
+ * close API will free() it. */
+ LIST_FOR_EACH_SAFE (offload_entry, dpif_list_node,
+ &dp_offload->offload_providers)
+ {
+ char *name = offload_entry->name;
+
+ ovs_list_remove(&offload_entry->dpif_list_node);
+ offload_entry->class->close(offload_entry);
+ ovsrcu_postpone(free, name);
+ }
+
+ /* Free remaining resources. */
+ ovs_mutex_destroy(&dp_offload->offload_mutex);
+ free(dp_offload->dpif_name);
+ free(dp_offload);
+}
+
+void
+dpif_offload_detach_providers(struct dpif *dpif)
+{
+ struct dp_offload *dp_offload = dpif_offload_get_dp_offload(dpif);
+
+ if (dp_offload) {
+ /* Take dpif_offload_mutex so that, if dp_offload->ref_cnt falls to
+ * zero, we can't get a new reference to 'dp_offload' through the
+ * 'dpif_offload_providers' shash. */
+ ovs_mutex_lock(&dpif_offload_mutex);
+ if (ovs_refcount_unref_relaxed(&dp_offload->ref_cnt) == 1) {
+ shash_find_and_delete(&dpif_offload_providers,
+ dp_offload->dpif_name);
+ ovsrcu_postpone(dpif_offload_free_dp_offload_rcu, dp_offload);
+ }
+ ovs_mutex_unlock(&dpif_offload_mutex);
+ ovsrcu_set(&dpif->dp_offload, NULL);
+ }
+}
+
+
+void
+dpif_offload_init(struct dpif_offload *offload,
+ const struct dpif_offload_class *class,
+ struct dpif *dpif)
+{
+ ovs_assert(offload && class && dpif);
+
+ offload->class = class;
+ offload->name = xasprintf("%s[%s]", class->type, dpif_name(dpif));
+}
+
+const char *
+dpif_offload_name(const struct dpif_offload *offload)
+{
+ return offload->name;
+}
+
+const char *
+dpif_offload_class_type(const struct dpif_offload *offload)
+{
+ return offload->class->type;
+}
+
+void
+dpif_offload_dump_start(struct dpif_offload_dump *dump,
+ const struct dpif *dpif)
+{
+ memset(dump, 0, sizeof *dump);
+ dump->dpif = dpif;
+}
+
+bool
+dpif_offload_dump_next(struct dpif_offload_dump *dump,
+ struct dpif_offload **offload)
+{
+ struct dp_offload *dp_offload;
+
+ if (!offload || !dump || dump->error) {
+ return false;
+ }
+
+ dp_offload = dpif_offload_get_dp_offload(dump->dpif);
+ if (!dp_offload) {
+ return false;
+ }
+
+ if (dump->state) {
+ /* In theory, list entries should not be removed. However, in case
+ * someone calls this during destruction and the node has disappeared,
+ * we will return EIDRM (Identifier removed). */
+ struct dpif_offload *offload_entry = NULL;
+
+ LIST_FOR_EACH (offload_entry, dpif_list_node,
+ &dp_offload->offload_providers) {
+ if (offload_entry == dump->state) {
+ if (ovs_list_back(&dp_offload->offload_providers)
+ == &offload_entry->dpif_list_node) {
+ dump->error = EOF;
+ } else {
+ *offload = CONTAINER_OF(
+ offload_entry->dpif_list_node.next,
+ struct dpif_offload, dpif_list_node);
+
+ dump->state = *offload;
+ }
+ break;
+ }
+ }
+
+ if (!offload_entry) {
+ dump->error = EIDRM;
+ }
+ } else {
+ /* Get the first entry in the list. */
+ if (!ovs_list_is_empty(&dp_offload->offload_providers)) {
+ *offload = CONTAINER_OF(
+ ovs_list_front(&dp_offload->offload_providers),
+ struct dpif_offload, dpif_list_node);
+
+ dump->state = *offload;
+ } else {
+ dump->error = EOF;
+ }
+ }
+
+ return !dump->error;
+}
+
+int
+dpif_offload_dump_done(struct dpif_offload_dump *dump)
+{
+ return dump->error == EOF ? 0 : dump->error;
+}
new file mode 100644
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2025 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ #ifndef DPIF_OFFLOAD_H
+ #define DPIF_OFFLOAD_H
+
+ #include "dpif.h"
+
+/* Forward declarations of private structures. */
+struct dpif_offload_class;
+struct dpif_offload;
+
+/* Structure used by the dpif_offload_dump_* functions. */
+struct dpif_offload_dump {
+ const struct dpif *dpif;
+ int error;
+ void *state;
+};
+
+
+/* Per dpif specific functions. */
+void dpif_offload_init(struct dpif_offload *,
+ const struct dpif_offload_class *, struct dpif *);
+int dpif_offload_attach_providers(struct dpif *);
+void dpif_offload_detach_providers(struct dpif *);
+const char *dpif_offload_name(const struct dpif_offload *);
+const char *dpif_offload_class_type(const struct dpif_offload *);
+void dpif_offload_dump_start(struct dpif_offload_dump *, const struct dpif *);
+bool dpif_offload_dump_next(struct dpif_offload_dump *,
+ struct dpif_offload **);
+int dpif_offload_dump_done(struct dpif_offload_dump *);
+
+/* Iterates through each DPIF_OFFLOAD in DPIF, using DUMP as state.
+ *
+ * Arguments all have pointer type.
+ *
+ * If you break out of the loop, then you need to free the dump structure by
+ * hand using dpif_offload_dump_done(). */
+#define DPIF_OFFLOAD_FOR_EACH(DPIF_OFFLOAD, DUMP, DPIF) \
+ for (dpif_offload_dump_start(DUMP, DPIF); \
+ (dpif_offload_dump_next(DUMP, &DPIF_OFFLOAD) \
+ ? true \
+ : (dpif_offload_dump_done(DUMP), false)); \
+ )
+
+#endif /* DPIF_OFFLOAD_H */
@@ -23,6 +23,7 @@
* ports that they contain may be fixed or dynamic. */
#include "openflow/openflow.h"
+#include "ovs-thread.h"
#include "dpif.h"
#include "util.h"
@@ -30,6 +31,9 @@
extern "C" {
#endif
+/* Forward declarations of private structures. */
+struct dp_offload;
+
/* Open vSwitch datapath interface.
*
* This structure should be treated as opaque by dpif implementations. */
@@ -40,6 +44,9 @@ struct dpif {
uint8_t netflow_engine_type;
uint8_t netflow_engine_id;
long long int current_ms;
+
+ /* dpif offload provider specific variables. */
+ OVSRCU_TYPE(struct dp_offload *) dp_offload;
};
struct dpif_ipf_status;
@@ -27,6 +27,8 @@
#include "dp-packet.h"
#include "dpctl.h"
#include "dpif-netdev.h"
+#include "dpif-offload.h"
+#include "dpif-offload-provider.h"
#include "flow.h"
#include "netdev-offload.h"
#include "netdev-provider.h"
@@ -125,6 +127,7 @@ dp_initialize(void)
tnl_port_map_init();
tnl_neigh_cache_init();
route_table_init();
+ dp_offload_initialize();
for (i = 0; i < ARRAY_SIZE(base_dpif_classes); i++) {
dp_register_provider(base_dpif_classes[i]);
@@ -359,6 +362,8 @@ do_open(const char *name, const char *type, bool create, struct dpif **dpifp)
ovs_assert(dpif->dpif_class == registered_class->dpif_class);
+ dpif_offload_attach_providers(dpif);
+
DPIF_PORT_FOR_EACH(&dpif_port, &port_dump, dpif) {
struct netdev *netdev;
int err;
@@ -459,6 +464,7 @@ dpif_close(struct dpif *dpif)
if (rc->refcount == 1) {
dpif_remove_netdev_ports(dpif);
}
+ dpif_offload_detach_providers(dpif);
dpif_uninit(dpif, true);
dp_class_unref(rc);
}
@@ -1714,6 +1720,7 @@ dpif_init(struct dpif *dpif, const struct dpif_class *dpif_class,
dpif->full_name = xasprintf("%s@%s", dpif_class->type, name);
dpif->netflow_engine_type = netflow_engine_type;
dpif->netflow_engine_id = netflow_engine_id;
+ ovsrcu_set(&dpif->dp_offload, NULL);
}
/* Undoes the results of initialization.
@@ -25,6 +25,7 @@
#include "coverage.h"
#include "cfm.h"
#include "ct-dpif.h"
+#include "dpif-offload.h"
#include "fail-open.h"
#include "guarded-list.h"
#include "hmapx.h"
@@ -6708,6 +6709,71 @@ done:
return changed;
}
+static void
+dpif_offload_show_backer_text(const struct dpif_backer *backer, struct ds *ds)
+{
+ struct dpif_offload_dump dump;
+ struct dpif_offload *offload;
+
+ ds_put_format(ds, "%s:\n", dpif_name(backer->dpif));
+
+ DPIF_OFFLOAD_FOR_EACH (offload, &dump, backer->dpif) {
+ ds_put_format(ds, " %s\n", dpif_offload_class_type(offload));
+ }
+}
+
+static struct json *
+dpif_offload_show_backer_json(struct json *backers,
+ const struct dpif_backer *backer)
+{
+ struct json *json_backer = json_object_create();
+ struct dpif_offload_dump dump;
+ struct dpif_offload *offload;
+
+ /* Add datapath as new JSON object using its name as key. */
+ json_object_put(backers, dpif_name(backer->dpif), json_backer);
+
+ /* Add provider to "providers" array using its name as key. */
+ struct json *json_providers = json_array_create_empty();
+
+ /* Add offload provides as new JSON objects using its type as key. */
+ DPIF_OFFLOAD_FOR_EACH (offload, &dump, backer->dpif) {
+ json_array_add(json_providers,
+ json_string_create(dpif_offload_class_type(offload)));
+ }
+
+ json_object_put(json_backer, "providers", json_providers);
+ return json_backer;
+}
+
+
+static void
+ofproto_unixctl_dpif_offload_show(struct unixctl_conn *conn,
+ int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED,
+ void *aux OVS_UNUSED) {
+ if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
+ struct json *backers = json_object_create();
+ const struct shash_node *backer;
+
+ SHASH_FOR_EACH (backer, &all_dpif_backers) {
+ dpif_offload_show_backer_json(backers, backer->data);
+ }
+ unixctl_command_reply_json(conn, backers);
+ } else {
+ const struct shash_node **backers = shash_sort(&all_dpif_backers);
+ struct ds ds = DS_EMPTY_INITIALIZER;
+
+ for (int i = 0; i < shash_count(&all_dpif_backers); i++) {
+ dpif_offload_show_backer_text(backers[i]->data, &ds);
+ }
+ free(backers);
+
+ unixctl_command_reply(conn, ds_cstr(&ds));
+ ds_destroy(&ds);
+ }
+}
+
static struct json *
dpif_show_backer_json(struct json *backers, const struct dpif_backer *backer)
{
@@ -7065,6 +7131,9 @@ ofproto_unixctl_init(void)
ofproto_unixctl_mcast_snooping_show, NULL);
unixctl_command_register("dpif/dump-dps", "", 0, 0,
ofproto_unixctl_dpif_dump_dps, NULL);
+ unixctl_command_register("dpif/offload/show", "", 0, 0,
+ ofproto_unixctl_dpif_offload_show,
+ NULL);
unixctl_command_register("dpif/show", "", 0, 0, ofproto_unixctl_dpif_show,
NULL);
unixctl_command_register("dpif/show-dp-features", "bridge", 1, 1,
@@ -10075,6 +10075,27 @@ AT_CHECK([ovs-appctl ofproto/trace br0 in_port=p0,tcp --ct-next 'trk,est' | dnl
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - offload - ovs-appctl dpif/offload/])
+AT_KEYWORDS([dpif-offload])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy])
+
+AT_CHECK([ovs-appctl dpif/offload/show], [0], [dnl
+dummy@ovs-dummy:
+ dummy_x
+ dummy
+])
+
+AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl
+{
+ "dummy@ovs-dummy": {
+ "providers": [[
+ "dummy_x",
+ "dummy"]]}}
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
dnl ----------------------------------------------------------------------
AT_BANNER([ofproto-dpif -- megaflows])
This commit introduces the initial framework and APIs to support the hardware offload dpif provider. Signed-off-by: Eelco Chaudron <echaudro@redhat.com> --- lib/automake.mk | 4 + lib/dpif-offload-dummy.c | 52 +++++ lib/dpif-offload-provider.h | 97 +++++++++ lib/dpif-offload.c | 387 ++++++++++++++++++++++++++++++++++++ lib/dpif-offload.h | 59 ++++++ lib/dpif-provider.h | 7 + lib/dpif.c | 7 + ofproto/ofproto-dpif.c | 69 +++++++ tests/ofproto-dpif.at | 21 ++ 9 files changed, 703 insertions(+) create mode 100644 lib/dpif-offload-dummy.c create mode 100644 lib/dpif-offload-provider.h create mode 100644 lib/dpif-offload.c create mode 100644 lib/dpif-offload.h