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

Reply via email to