This commit introduces the initial framework and APIs to support the hardware offload dpif provider.
Signed-off-by: Eelco Chaudron <echau...@redhat.com> --- lib/automake.mk | 4 + lib/dpif-offload-dummy.c | 52 +++++ lib/dpif-offload-provider.h | 97 +++++++++ lib/dpif-offload.c | 382 ++++++++++++++++++++++++++++++++++++ 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, 698 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 diff --git a/lib/automake.mk b/lib/automake.mk index 78d6e6516..314102ecc 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -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 \ diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c new file mode 100644 index 000000000..04f4b5227 --- /dev/null +++ b/lib/dpif-offload-dummy.c @@ -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"); diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h new file mode 100644 index 000000000..3b7d936dd --- /dev/null +++ b/lib/dpif-offload-provider.h @@ -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", "rte_flow", + * "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 */ diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c new file mode 100644 index 000000000..35bc18649 --- /dev/null +++ b/lib/dpif-offload.c @@ -0,0 +1,382 @@ +/* + * 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 = dpif_offload_get_dp_offload(dump->dpif); + + if (!dp_offload || !offload || !dump || dump->error) { + 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; +} diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h new file mode 100644 index 000000000..66de104e8 --- /dev/null +++ b/lib/dpif-offload.h @@ -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 */ diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h index 520e21e68..e99807782 100644 --- a/lib/dpif-provider.h +++ b/lib/dpif-provider.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; diff --git a/lib/dpif.c b/lib/dpif.c index 070fc0131..b77cfa0e5 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -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. diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index ed9e44ce2..dba1dddde 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -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" @@ -6702,6 +6703,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) { @@ -7059,6 +7125,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, diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index a0cd4a5ce..79d9d683e 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -10071,6 +10071,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]) -- 2.50.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev