@@ -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 *,
@@ -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);
@@ -186,6 +187,57 @@ dpif_offload_dpdk_set_config(struct dpif_offload *offload_,
}
}
+static bool
+dpif_offload_dpdk_get_port_debug_ds(struct dpif_offload_port_mgr_port *port,
+ void *aux)
+{
+ struct ds *ds = aux;
+
+ ds_put_format(ds, " - %s: port_no: %u\n",
+ netdev_get_name(port->netdev), port->port_no);
+
+ return false;
+}
+
+static bool
+dpif_offload_dpdk_get_port_debug_json(struct dpif_offload_port_mgr_port *port,
+ void *aux)
+{
+ struct json *json_port = json_object_create();
+ struct json *json = aux;
+
+ json_object_put(json_port, "port_no",
+ json_integer_create(odp_to_u32(port->port_no)));
+
+ json_object_put(json, netdev_get_name(port->netdev), json_port);
+ return false;
+}
+
+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();
+
+ dpif_offload_port_mgr_traverse_ports(
+ offload->port_mgr, dpif_offload_dpdk_get_port_debug_json,
+ json_ports);
+
+ if (!json_object_is_empty(json_ports)) {
+ json_object_put(json, "ports", json_ports);
+ } else {
+ json_destroy(json_ports);
+ }
+
+ } else if (ds) {
+ dpif_offload_port_mgr_traverse_ports(
+ offload->port_mgr, dpif_offload_dpdk_get_port_debug_ds, ds);
+ }
+}
+
static bool
dpif_offload_dpdk_can_offload(struct dpif_offload *offload OVS_UNUSED,
struct netdev *netdev)
@@ -208,6 +260,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,
@@ -24,6 +24,7 @@
#include "netdev-provider.h"
#include "util.h"
+#include "openvswitch/json.h"
struct dpif_offload_dummy {
struct dpif_offload offload;
@@ -171,6 +172,59 @@ dpif_offload_dummy_set_config(struct dpif_offload *dpif_offload,
}
}
+static bool
+dpif_offload_dummy_get_port_debug_ds(struct dpif_offload_port_mgr_port *port,
+ void *aux)
+{
+ struct ds *ds = aux;
+
+ ds_put_format(ds, " - %s: port_no: %u\n", netdev_get_name(port->netdev),
+ port->port_no);
+
+ return false;
+}
+
+static bool
+dpif_offload_dummy_get_port_debug_json(struct dpif_offload_port_mgr_port *port,
+ void *aux)
+{
+ struct json *json_port = json_object_create();
+ struct json *json = aux;
+
+ json_object_put(json_port, "port_no",
+ json_integer_create(odp_to_u32(port->port_no)));
+
+ json_object_put(json, netdev_get_name(port->netdev), json_port);
+ return false;
+}
+
+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();
+
+ dpif_offload_port_mgr_traverse_ports(
+ offload_dummy->port_mgr, dpif_offload_dummy_get_port_debug_json,
+ json_ports);
+
+ if (!json_object_is_empty(json_ports)) {
+ json_object_put(json, "ports", json_ports);
+ } else {
+ json_destroy(json_ports);
+ }
+
+ } else if (ds) {
+ dpif_offload_port_mgr_traverse_ports(
+ offload_dummy->port_mgr, dpif_offload_dummy_get_port_debug_ds, ds);
+ }
+}
+
static bool
dpif_offload_dummy_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED,
struct netdev *netdev)
@@ -187,6 +241,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, \
@@ -93,6 +93,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'. */
@@ -24,6 +24,7 @@
#include "util.h"
#include "tc.h"
+#include "openvswitch/json.h"
#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(dpif_offload_tc);
@@ -169,6 +170,59 @@ dpif_offload_tc_set_config(struct dpif_offload *offload,
}
}
+static bool
+dpif_offload_tc_get_port_debug_ds(struct dpif_offload_port_mgr_port *port,
+ void *aux)
+{
+ struct ds *ds = aux;
+
+ ds_put_format(ds, " - %s: port_no: %u, ifindex: %d\n",
+ netdev_get_name(port->netdev), port->port_no, port->ifindex);
+
+ return false;
+}
+
+static bool
+dpif_offload_tc_get_port_debug_json(struct dpif_offload_port_mgr_port *port,
+ void *aux)
+{
+ struct json *json_port = json_object_create();
+ struct json *json = aux;
+
+ 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, netdev_get_name(port->netdev), json_port);
+ return false;
+}
+
+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();
+
+ dpif_offload_port_mgr_traverse_ports(
+ offload_tc->port_mgr, dpif_offload_tc_get_port_debug_json,
+ json_ports);
+
+ if (!json_object_is_empty(json_ports)) {
+ json_object_put(json, "ports", json_ports);
+ } else {
+ json_destroy(json_ports);
+ }
+
+ } else if (ds) {
+ dpif_offload_port_mgr_traverse_ports(offload_tc->port_mgr,
+ dpif_offload_tc_get_port_debug_ds,
+ ds);
+ }
+}
+
static bool
dpif_offload_tc_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED,
struct netdev *netdev)
@@ -190,6 +244,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,
@@ -396,6 +396,18 @@ dpif_offload_class_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_is_offload_enabled(void)
{
@@ -44,6 +44,8 @@ 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 *);
+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 **);
@@ -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)
{
@@ -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_class_type(offload));
+ dpif_offload_get_debug(offload, ds, NULL);
}
}
@@ -6733,15 +6734,26 @@ dpif_offload_show_backer_json(struct json *backers,
/* 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 provider to "providers" object using its name as key. */
+ struct json *json_providers = json_object_create();
+
+ /* Add provider to "priority" array using its name as key. */
+ struct json *json_priority = 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,
+ struct json *debug_data = json_object_create();
+
+ json_array_add(json_priority,
json_string_create(dpif_offload_class_type(offload)));
+
+ dpif_offload_get_debug(offload, NULL, debug_data);
+
+ json_object_put(json_providers, dpif_offload_class_type(offload),
+ debug_data);
}
+ json_object_put(json_backer, "priority", json_priority);
json_object_put(json_backer, "providers", json_providers);
return json_backer;
}
@@ -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])
@@ -10079,20 +10101,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}
])
@@ -10117,26 +10153,82 @@ 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_TRAFFIC_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}
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
dnl ----------------------------------------------------------------------
AT_BANNER([ofproto-dpif -- megaflows])
@@ -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
@@ -1172,19 +1172,44 @@ 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 () {
+ dnl Note: We do not use an m4 macro as it does not like the $0, or escaped
+ dnl variants and loops until it runs out of memory.
+ 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
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. Signed-off-by: Eelco Chaudron <echaudro@redhat.com> --- include/openvswitch/json.h | 1 + lib/dpif-offload-dpdk.c | 53 ++++++++++++++++ lib/dpif-offload-dummy.c | 55 ++++++++++++++++ lib/dpif-offload-provider.h | 9 +++ lib/dpif-offload-tc.c | 55 ++++++++++++++++ lib/dpif-offload.c | 12 ++++ lib/dpif-offload.h | 2 + lib/json.c | 7 +++ ofproto/ofproto-dpif.c | 18 +++++- tests/ofproto-dpif.at | 104 +++++++++++++++++++++++++++++-- tests/system-dpdk.at | 13 +++- tests/system-offloads-traffic.at | 33 ++++++++-- 12 files changed, 347 insertions(+), 15 deletions(-)