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 <echau...@redhat.com>
---
 include/openvswitch/json.h       |  1 +
 lib/dpif-offload-dummy.c         | 56 +++++++++++++++++++
 lib/dpif-offload-provider.h      |  9 +++
 lib/dpif-offload-rte_flow.c      | 53 ++++++++++++++++++
 lib/dpif-offload-tc.c            | 55 ++++++++++++++++++
 lib/dpif-offload.c               | 11 ++++
 lib/dpif-offload.h               |  2 +
 lib/json.c                       |  7 +++
 ofproto/ofproto-dpif.c           | 18 +++++-
 tests/ofproto-dpif.at            | 96 ++++++++++++++++++++++++++++++--
 tests/system-dpdk.at             | 13 ++++-
 tests/system-offloads-traffic.at | 29 ++++++++--
 12 files changed, 335 insertions(+), 15 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-dummy.c b/lib/dpif-offload-dummy.c
index d4997891c..1bdb9d043 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;
@@ -171,6 +173,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 +242,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 ce309a672..4dfdbeeae 100644
--- a/lib/dpif-offload-provider.h
+++ b/lib/dpif-offload-provider.h
@@ -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'. */
diff --git a/lib/dpif-offload-rte_flow.c b/lib/dpif-offload-rte_flow.c
index 01a7a0730..e4c44c57a 100644
--- a/lib/dpif-offload-rte_flow.c
+++ b/lib/dpif-offload-rte_flow.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_rte_flow);
@@ -187,6 +188,57 @@ dpif_offload_rte_set_config(struct dpif_offload *offload,
     }
 }
 
+static bool
+dpif_offload_rte_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_rte_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_rte_get_debug(const struct dpif_offload *offload, struct ds *ds,
+                           struct json *json)
+{
+    struct dpif_offload_rte_flow *offload_rte = dpif_offload_rte_cast(offload);
+
+    if (json) {
+        struct json *json_ports = json_object_create();
+
+        dpif_offload_port_mgr_traverse_ports(
+            offload_rte->port_mgr, dpif_offload_rte_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_rte->port_mgr, dpif_offload_rte_get_port_debug_ds, ds);
+    }
+}
+
 static bool
 dpif_offload_rte_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED,
                              struct netdev *netdev)
@@ -209,6 +261,7 @@ struct dpif_offload_class dpif_offload_rte_flow_class = {
     .open = dpif_offload_rte_open,
     .close = dpif_offload_rte_close,
     .set_config = dpif_offload_rte_set_config,
+    .get_debug = dpif_offload_rte_get_debug,
     .can_offload = dpif_offload_rte_can_offload,
     .port_add = dpif_offload_rte_port_add,
     .port_del = dpif_offload_rte_port_del,
diff --git a/lib/dpif-offload-tc.c b/lib/dpif-offload-tc.c
index c3090829b..66d52851a 100644
--- a/lib/dpif-offload-tc.c
+++ b/lib/dpif-offload-tc.c
@@ -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,
diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c
index 98bce266a..f18754f60 100644
--- a/lib/dpif-offload.c
+++ b/lib/dpif-offload.c
@@ -396,6 +396,17 @@ 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)
 {
diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h
index 2720d2b5f..dcca10b4b 100644
--- a/lib/dpif-offload.h
+++ b/lib/dpif-offload.h
@@ -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 **);
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 29d6fa59d..62528fcc6 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -6713,6 +6713,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);
     }
 }
 
@@ -6727,15 +6728,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;
 }
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 8711295be..775b7b823 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])
 
 
@@ -10075,20 +10097,31 @@ 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
 ])
 
 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}}},
+      "dummy_x": {
+        }}},
   "enabled": false}
 ])
 
@@ -10113,26 +10146,77 @@ 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
 ])
 
 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}}}}},
   "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},
+          "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])
 
diff --git a/tests/system-dpdk.at b/tests/system-dpdk.at
index 336cfe696..9985da6be 100644
--- a/tests/system-dpdk.at
+++ b/tests/system-dpdk.at
@@ -876,20 +876,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:
   rte_flow
+  - p1: port_no: 2
 ])
 
 AT_CHECK([ovs-appctl --format json --pretty dpif/offload/show], [0], [dnl
 {
   "enabled": false,
   "netdev@ovs-netdev": {
-    "providers": [[
-      "rte_flow"]]}}
+    "priority": [[
+      "rte_flow"]],
+    "providers": {
+      "rte_flow": {
+        "ports": {
+          "p1": {
+            "port_no": 2}}}}}}
 ])
 
 OVS_DPDK_STOP_VSWITCHD
diff --git a/tests/system-offloads-traffic.at b/tests/system-offloads-traffic.at
index 471c8fdb8..98403c449 100644
--- a/tests/system-offloads-traffic.at
+++ b/tests/system-offloads-traffic.at
@@ -1172,19 +1172,40 @@ 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
 ])
 
-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_TRAFFIC_VSWITCHD_STOP
-- 
2.50.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to