This patch adds a new API that allows offload providers to expose free-form debug information. The information can be used for troubleshooting the offload providers state.
The new API is integrated into the existing 'ovs-appctl dpif/offload/show' command, which now displays this debug output when available. Support for this API has been implemented for all currently supported offload providers. Acked-by: Eli Britstein <[email protected]> Signed-off-by: Eelco Chaudron <[email protected]> --- v3 changes: - Fixed some newline issues. --- include/openvswitch/json.h | 1 + lib/dpif-offload-dpdk.c | 37 +++++++++++ lib/dpif-offload-dummy.c | 40 ++++++++++++ lib/dpif-offload-provider.h | 9 +++ lib/dpif-offload-tc.c | 40 ++++++++++++ lib/dpif-offload.c | 12 ++++ lib/dpif-offload.h | 2 + lib/json.c | 7 +++ ofproto/ofproto-dpif.c | 14 ++++- tests/ofproto-dpif.at | 104 +++++++++++++++++++++++++++++-- tests/system-dpdk.at | 13 +++- tests/system-offloads-traffic.at | 31 +++++++-- 12 files changed, 296 insertions(+), 14 deletions(-) diff --git a/include/openvswitch/json.h b/include/openvswitch/json.h index 134890553..ea2c4da04 100644 --- a/include/openvswitch/json.h +++ b/include/openvswitch/json.h @@ -123,6 +123,7 @@ struct json *json_array_create_3(struct json *, struct json *, struct json *); bool json_array_contains_string(const struct json *, const char *); struct json *json_object_create(void); +bool json_object_is_empty(struct json *); void json_object_put(struct json *, const char *name, struct json *value); void json_object_put_nocopy(struct json *, char *name, struct json *value); void json_object_put_string(struct json *, diff --git a/lib/dpif-offload-dpdk.c b/lib/dpif-offload-dpdk.c index 6ab304f2f..508d8b9c5 100644 --- a/lib/dpif-offload-dpdk.c +++ b/lib/dpif-offload-dpdk.c @@ -23,6 +23,7 @@ #include "netdev-vport.h" #include "util.h" +#include "openvswitch/json.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(dpif_offload_dpdk); @@ -173,6 +174,41 @@ dpif_offload_dpdk_set_config(struct dpif_offload *offload_, } } +static void +dpif_offload_dpdk_get_debug(const struct dpif_offload *offload_, struct ds *ds, + struct json *json) +{ + struct dpif_offload_dpdk *offload = dpif_offload_dpdk_cast(offload_); + + if (json) { + struct json *json_ports = json_object_create(); + struct dpif_offload_port_mgr_port *port; + + DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload->port_mgr) { + struct json *json_port = json_object_create(); + + json_object_put(json_port, "port_no", + json_integer_create(odp_to_u32(port->port_no))); + + json_object_put(json_ports, netdev_get_name(port->netdev), + json_port); + } + + if (!json_object_is_empty(json_ports)) { + json_object_put(json, "ports", json_ports); + } else { + json_destroy(json_ports); + } + } else if (ds) { + struct dpif_offload_port_mgr_port *port; + + DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload->port_mgr) { + ds_put_format(ds, " - %s: port_no: %u\n", + netdev_get_name(port->netdev), port->port_no); + } + } +} + static bool dpif_offload_dpdk_can_offload(struct dpif_offload *offload OVS_UNUSED, struct netdev *netdev) @@ -193,6 +229,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, + .get_debug = dpif_offload_dpdk_get_debug, .can_offload = dpif_offload_dpdk_can_offload, .port_add = dpif_offload_dpdk_port_add, .port_del = dpif_offload_dpdk_port_del, diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c index a133b0eaf..d4b91a1a4 100644 --- a/lib/dpif-offload-dummy.c +++ b/lib/dpif-offload-dummy.c @@ -24,6 +24,8 @@ #include "netdev-provider.h" #include "util.h" +#include "openvswitch/json.h" + struct dpif_offload_dummy { struct dpif_offload offload; struct dpif_offload_port_mgr *port_mgr; @@ -157,6 +159,43 @@ dpif_offload_dummy_set_config(struct dpif_offload *dpif_offload, } } +static void +dpif_offload_dummy_get_debug(const struct dpif_offload *offload, struct ds *ds, + struct json *json) +{ + struct dpif_offload_dummy *offload_dummy; + + offload_dummy = dpif_offload_dummy_cast(offload); + + if (json) { + struct json *json_ports = json_object_create(); + struct dpif_offload_port_mgr_port *port; + + DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload_dummy->port_mgr) { + struct json *json_port = json_object_create(); + + json_object_put(json_port, "port_no", + json_integer_create(odp_to_u32(port->port_no))); + + json_object_put(json_ports, netdev_get_name(port->netdev), + json_port); + } + + if (!json_object_is_empty(json_ports)) { + json_object_put(json, "ports", json_ports); + } else { + json_destroy(json_ports); + } + } else if (ds) { + struct dpif_offload_port_mgr_port *port; + + DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload_dummy->port_mgr) { + ds_put_format(ds, " - %s: port_no: %u\n", + netdev_get_name(port->netdev), port->port_no); + } + } +} + static bool dpif_offload_dummy_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED, struct netdev *netdev) @@ -172,6 +211,7 @@ dpif_offload_dummy_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED, .open = dpif_offload_dummy_open, \ .close = dpif_offload_dummy_close, \ .set_config = dpif_offload_dummy_set_config, \ + .get_debug = dpif_offload_dummy_get_debug, \ .can_offload = dpif_offload_dummy_can_offload, \ .port_add = dpif_offload_dummy_port_add, \ .port_del = dpif_offload_dummy_port_del, \ diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h index 3cbd36856..4ee752e3c 100644 --- a/lib/dpif-offload-provider.h +++ b/lib/dpif-offload-provider.h @@ -91,6 +91,15 @@ struct dpif_offload_class { void (*set_config)(struct dpif_offload *, const struct smap *other_config); + /* Retrieve debug information from the offload provider in either string + * (ds) or JSON format. If both formats are requested, the provider may + * choose which one to return. Note that the actual format is unspecified, + * it's up to the provider to decide what to return. If 'ds' is supplied, + * it should be initialized, and might already contain data. The caller is + * responsible for freeing any returned 'ds' or 'json' pointers. */ + void (*get_debug)(const struct dpif_offload *offload, struct ds *ds, + struct json *json); + /* 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'. */ diff --git a/lib/dpif-offload-tc.c b/lib/dpif-offload-tc.c index c52214e07..7db04814a 100644 --- a/lib/dpif-offload-tc.c +++ b/lib/dpif-offload-tc.c @@ -24,6 +24,7 @@ #include "tc.h" #include "util.h" +#include "openvswitch/json.h" #include "openvswitch/vlog.h" VLOG_DEFINE_THIS_MODULE(dpif_offload_tc); @@ -158,6 +159,44 @@ dpif_offload_tc_set_config(struct dpif_offload *offload, } } +static void +dpif_offload_tc_get_debug(const struct dpif_offload *offload, struct ds *ds, + struct json *json) +{ + struct dpif_offload_tc *offload_tc = dpif_offload_tc_cast(offload); + + if (json) { + struct json *json_ports = json_object_create(); + struct dpif_offload_port_mgr_port *port; + + DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload_tc->port_mgr) { + struct json *json_port = json_object_create(); + + json_object_put(json_port, "port_no", + json_integer_create(odp_to_u32(port->port_no))); + json_object_put(json_port, "ifindex", + json_integer_create(port->ifindex)); + + json_object_put(json_ports, netdev_get_name(port->netdev), + json_port); + } + + if (!json_object_is_empty(json_ports)) { + json_object_put(json, "ports", json_ports); + } else { + json_destroy(json_ports); + } + } else if (ds) { + struct dpif_offload_port_mgr_port *port; + + DPIF_OFFLOAD_PORT_MGR_PORT_FOR_EACH (port, offload_tc->port_mgr) { + ds_put_format(ds, " - %s: port_no: %u, ifindex: %d\n", + netdev_get_name(port->netdev), + port->port_no, port->ifindex); + } + } +} + static bool dpif_offload_tc_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED, struct netdev *netdev) @@ -177,6 +216,7 @@ 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, + .get_debug = dpif_offload_tc_get_debug, .can_offload = dpif_offload_tc_can_offload, .port_add = dpif_offload_tc_port_add, .port_del = dpif_offload_tc_port_del, diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c index 873d457e5..c38e07283 100644 --- a/lib/dpif-offload.c +++ b/lib/dpif-offload.c @@ -401,6 +401,18 @@ dpif_offload_type(const struct dpif_offload *offload) return offload->class->type; } +bool +dpif_offload_get_debug(const struct dpif_offload *offload, struct ds *ds, + struct json *json) +{ + if (!offload->class->get_debug) { + return false; + } + + offload->class->get_debug(offload, ds, json); + return true; +} + bool dpif_offload_enabled(void) { diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h index 9e6f4f3c3..14e0418bd 100644 --- a/lib/dpif-offload.h +++ b/lib/dpif-offload.h @@ -44,6 +44,8 @@ int dpif_attach_offload_providers(struct dpif *); void dpif_detach_offload_providers(struct dpif *); const char *dpif_offload_name(const struct dpif_offload *); const char *dpif_offload_type(const struct dpif_offload *); +bool dpif_offload_get_debug(const struct dpif_offload *, struct ds *, + struct json *); void dpif_offload_dump_start(struct dpif_offload_dump *, const struct dpif *); bool dpif_offload_dump_next(struct dpif_offload_dump *, struct dpif_offload **); diff --git a/lib/json.c b/lib/json.c index 23622ab36..23000f195 100644 --- a/lib/json.c +++ b/lib/json.c @@ -397,6 +397,13 @@ json_object_create(void) return json; } +bool +json_object_is_empty(struct json *json) +{ + return json && json->type == JSON_OBJECT + && shash_is_empty(json->object); +} + struct json * json_integer_create(long long int integer) { diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index fdc23d632..dd318de86 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -6719,6 +6719,7 @@ dpif_offload_show_backer_text(const struct dpif_backer *backer, struct ds *ds) DPIF_OFFLOAD_FOR_EACH (offload, &dump, backer->dpif) { ds_put_format(ds, " %s\n", dpif_offload_type(offload)); + dpif_offload_get_debug(offload, ds, NULL); } } @@ -6726,16 +6727,25 @@ static struct json * dpif_offload_show_backer_json(struct json *backers, const struct dpif_backer *backer) { - struct json *json_providers = json_array_create_empty(); + struct json *json_providers = json_object_create(); + struct json *json_priority = json_array_create_empty(); struct json *json_backer = json_object_create(); struct dpif_offload_dump dump; struct dpif_offload *offload; DPIF_OFFLOAD_FOR_EACH (offload, &dump, backer->dpif) { - json_array_add(json_providers, + struct json *debug_data = json_object_create(); + + json_array_add(json_priority, json_string_create(dpif_offload_type(offload))); + + dpif_offload_get_debug(offload, NULL, debug_data); + + json_object_put(json_providers, dpif_offload_type(offload), + debug_data); } + json_object_put(json_backer, "priority", json_priority); json_object_put(json_backer, "providers", json_providers); json_object_put(backers, dpif_name(backer->dpif), json_backer); return json_backer; diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index e60643701..15d5b7a55 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -20,6 +20,28 @@ check_dpflow_stats () { echo "n_flows=$flows n_buckets=$buckets" } +sort_dpif_offload_show () { + awk ' + /^ -/ { dashlines[[++n]] = $0; next } + { print } + END { + # asort(dashlines) is a GNU extension, so we need to do it + # manually here as ofproto-dpif is also executed on FreeBSD. + for (i = 1; i <= n; i++) { + for (j = i + 1; j <= n; j++) { + if (dashlines[[i]] > dashlines[[j]]) { + tmp = dashlines[[i]] + dashlines[[i]] = dashlines[[j]] + dashlines[[j]] = tmp + } + } + } + + for (i=1; i<=n; i++) print dashlines[[i]] + } + ' +} + m4_divert_pop([PREPARE_TESTS]) @@ -10108,20 +10130,34 @@ 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 +AT_CHECK([ovs-appctl dpif/offload/show | sort_dpif_offload_show], [0], [dnl Globally enabled: false Datapaths: dummy@ovs-dummy: dummy dummy_x + - br0: port_no: 100 + - br1: port_no: 101 + - ovs-dummy: port_no: 0 ]) AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl { "dummy@ovs-dummy": { - "providers": [[ + "priority": [[ "dummy", - "dummy_x"]]}, + "dummy_x"]], + "providers": { + "dummy": { + "ports": { + "br0": { + "port_no": 100}, + "br1": { + "port_no": 101}, + "ovs-dummy": { + "port_no": 0}}}, + "dummy_x": { + }}}, "enabled": false} ]) @@ -10146,20 +10182,76 @@ AT_KEYWORDS([dpif-offload]) OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy], [], [], [], [-- set Open_vSwitch . other_config:hw-offload-priority=dummy_x,dummy]) -AT_CHECK([ovs-appctl dpif/offload/show], [0], [dnl +AT_CHECK([ovs-appctl dpif/offload/show | sort_dpif_offload_show], [0], [dnl Globally enabled: false Datapaths: dummy@ovs-dummy: dummy_x dummy + - br0: port_no: 100 + - br1: port_no: 101 + - ovs-dummy: port_no: 0 ]) AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl { "dummy@ovs-dummy": { - "providers": [[ + "priority": [[ "dummy_x", - "dummy"]]}, + "dummy"]], + "providers": { + "dummy": { + }, + "dummy_x": { + "ports": { + "br0": { + "port_no": 100}, + "br1": { + "port_no": 101}, + "ovs-dummy": { + "port_no": 0}}}}}, + "enabled": false} +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif - offload - port priority order]) +AT_KEYWORDS([dpif-offload]) +OVS_VSWITCHD_START([add-port br0 p1 -- \ + set Interface p1 type=dummy ofport_request=1 -- \ + set port p1 other_config:hw-offload-priority=dummy_x,dummy -- \ + add-port br0 p2 -- \ + set Interface p2 type=dummy ofport_request=2 -- \ + set port p2 other_config:hw-offload-priority=none -- \ + add-port br0 p3 -- \ + set Interface p3 type=dummy ofport_request=3 -- \ + set port p3 other_config:hw-offload-priority=dummy_x -- \ + add-port br0 p4 -- \ + set Interface p4 type=dummy ofport_request=4 -- \ + set port p4 other_config:hw-offload-priority=dummy]) + +AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl +{ + "dummy@ovs-dummy": { + "priority": [[ + "dummy", + "dummy_x"]], + "providers": { + "dummy": { + "ports": { + "br0": { + "port_no": 100}, + "ovs-dummy": { + "port_no": 0}, + "p4": { + "port_no": 4}}}, + "dummy_x": { + "ports": { + "p1": { + "port_no": 1}, + "p3": { + "port_no": 3}}}}}, "enabled": false} ]) diff --git a/tests/system-dpdk.at b/tests/system-dpdk.at index c1910b756..17d3d2595 100644 --- a/tests/system-dpdk.at +++ b/tests/system-dpdk.at @@ -948,20 +948,29 @@ AT_KEYWORDS([dpdk dpif-offload]) OVS_DPDK_PRE_CHECK() OVS_DPDK_START([--no-pci]) AT_CHECK([ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev]) +AT_CHECK([ovs-vsctl add-port br0 p1 \ + -- set Interface p1 type=dpdk options:dpdk-devargs=net_null0,no-rx=1], + [], [stdout], [stderr]) AT_CHECK([ovs-appctl dpif/offload/show], [0], [dnl Globally enabled: false Datapaths: netdev@ovs-netdev: dpdk + - p1: port_no: 2 ]) AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl { "enabled": false, "netdev@ovs-netdev": { - "providers": [[ - "dpdk"]]}} + "priority": [[ + "dpdk"]], + "providers": { + "dpdk": { + "ports": { + "p1": { + "port_no": 2}}}}}} ]) OVS_DPDK_STOP_VSWITCHD diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at index 92d3523dc..c9b5ee6c4 100644 --- a/tests/system-offloads-traffic.at +++ b/tests/system-offloads-traffic.at @@ -1172,19 +1172,42 @@ AT_KEYWORDS([dpif-offload]) OVS_TRAFFIC_VSWITCHD_START([], [], [-- set Open_vSwitch . other_config:hw-offload=true]) -AT_CHECK([ovs-appctl dpif/offload/show], [0], [dnl +sort_dpif_offload_show () { + awk ' + /^ -/ { dashlines[[++n]] = $0; next } + { print } + END { + asort(dashlines) + for (i=1; i<=n; i++) print dashlines[[i]] + } + ' | sed -E 's/ifindex: [[0-9]]+/ifindex: 0/g' +} + +AT_CHECK([ovs-appctl dpif/offload/show | sort_dpif_offload_show], [0], [dnl Globally enabled: true Datapaths: system@ovs-system: tc + - br0: port_no: 1, ifindex: 0 + - ovs-system: port_no: 0, ifindex: 0 ]) -AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl +AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show \ + | sed -E 's/"ifindex": [[0-9]]+/"ifindex": 0/g'], [0], [dnl { "enabled": true, "system@ovs-system": { - "providers": [[ - "tc"]]}} + "priority": [[ + "tc"]], + "providers": { + "tc": { + "ports": { + "br0": { + "ifindex": 0, + "port_no": 1}, + "ovs-system": { + "ifindex": 0, + "port_no": 0}}}}}} ]) OVS_TRAFFIC_VSWITCHD_STOP -- 2.52.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
