On 02/12/2025 16:05, Eelco Chaudron wrote:
Move the existing API for querying global datapath-specific
offload statistics from dpif to dpif-offload.

Note that this patch will temporarily remove the global dpif
offload stats feature for dpdk offload, but it will be
reintroduced in the next patch.

Why? we can add the new implementation first, and cleanup later.

If you think it becomes too large commit, we can add the new code as a dead code first, then do the transition and finally cleanup.


Signed-off-by: Eelco Chaudron <[email protected]>
---
  include/openvswitch/netdev.h |  1 +
  lib/dpctl.c                  | 42 +++++++++++++---
  lib/dpif-netdev.c            | 96 ------------------------------------
  lib/dpif-netlink.c           |  1 -
  lib/dpif-offload-dummy.c     | 55 ++++++++++++++-------
  lib/dpif-offload-provider.h  |  8 +++
  lib/dpif-offload.c           | 42 ++++++++++++++++
  lib/dpif-offload.h           | 12 +++++
  lib/dpif-provider.h          |  7 ---
  lib/dpif.c                   |  8 ---
  lib/dpif.h                   |  8 ---
  lib/netdev.c                 |  2 +
  tests/ofproto-dpif.at        | 16 ++++++
  13 files changed, 152 insertions(+), 146 deletions(-)

diff --git a/include/openvswitch/netdev.h b/include/openvswitch/netdev.h
index 8212ffb00..01161bdba 100644
--- a/include/openvswitch/netdev.h
+++ b/include/openvswitch/netdev.h
@@ -101,6 +101,7 @@ struct netdev_custom_counter {
/* Structure representation of custom statistics */
  struct netdev_custom_stats {
+    char *label; /* Optional label/description for this set of counters. */
      uint16_t size;
      struct netdev_custom_counter *counters;
  };
diff --git a/lib/dpctl.c b/lib/dpctl.c
index 1411c5a31..074e73922 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -33,6 +33,7 @@
  #include "dirs.h"
  #include "dpctl.h"
  #include "dpif.h"
+#include "dpif-offload.h"
  #include "dpif-provider.h"
  #include "openvswitch/dynamic-string.h"
  #include "flow.h"
@@ -1597,34 +1598,59 @@ dpctl_del_flows(int argc, const char *argv[], struct 
dpctl_params *dpctl_p)
      return error;
  }
+static int
+compare_custom_stats(const void *a, const void *b)
+{
+    const struct netdev_custom_stats *sa = a;
+    const struct netdev_custom_stats *sb = b;
+
+    return sa->label && sb->label ? strcmp(sa->label, sb->label)
+                                  : (sa->label != NULL) - (sb->label != NULL);
+}
+
  static int
  dpctl_offload_stats_show(int argc, const char *argv[],
                           struct dpctl_params *dpctl_p)
  {
-    struct netdev_custom_stats stats;
+    struct netdev_custom_stats *stats_array;
      struct dpif *dpif;
+    size_t n_stats;
      int error;
-    size_t i;
error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
      if (error) {
          return error;
      }
- memset(&stats, 0, sizeof(stats));
-    error = dpif_offload_stats_get(dpif, &stats);
+    error = dpif_offload_stats_get(dpif, &stats_array, &n_stats);
      if (error) {
          dpctl_error(dpctl_p, error, "retrieving offload statistics");
          goto close_dpif;
      }
+ if (stats_array && n_stats) {
+        qsort(stats_array, n_stats, sizeof *stats_array, compare_custom_stats);
+    }
+
      dpctl_print(dpctl_p, "HW Offload stats:\n");
-    for (i = 0; i < stats.size; i++) {
-        dpctl_print(dpctl_p, "   %s: %6" PRIu64 "\n",
-                    stats.counters[i].name, stats.counters[i].value);
+    for (size_t i = 0; i < n_stats; i++) {
+        struct netdev_custom_stats *stats = &stats_array[i];
+
+        if (!stats) {
+            continue;
+        }
+
+        if (stats->label) {
+            dpctl_print(dpctl_p, "  %s:\n", stats->label);
+        }
+        for (size_t j = 0; j < stats->size; j++) {
+            dpctl_print(dpctl_p, "    %s: %6" PRIu64 "\n",
+                        stats->counters[j].name, stats->counters[j].value);
+        }
+        netdev_free_custom_stats_counters(stats);
      }
- netdev_free_custom_stats_counters(&stats);
+    free(stats_array);
close_dpif:
      dpif_close(dpif);
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 586b589a3..2aeef471f 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -4735,101 +4735,6 @@ dpif_netdev_operate(struct dpif *dpif, struct dpif_op 
**ops, size_t n_ops)
      }
  }
-static int
-dpif_netdev_offload_stats_get(struct dpif *dpif,
-                              struct netdev_custom_stats *stats)
-{
-    enum {
-        DP_NETDEV_HW_OFFLOADS_STATS_ENQUEUED,
-        DP_NETDEV_HW_OFFLOADS_STATS_INSERTED,
-        DP_NETDEV_HW_OFFLOADS_STATS_LAT_CMA_MEAN,
-        DP_NETDEV_HW_OFFLOADS_STATS_LAT_CMA_STDDEV,
-        DP_NETDEV_HW_OFFLOADS_STATS_LAT_EMA_MEAN,
-        DP_NETDEV_HW_OFFLOADS_STATS_LAT_EMA_STDDEV,
-    };
-    struct {
-        const char *name;
-        uint64_t total;
-    } hwol_stats[] = {
-        [DP_NETDEV_HW_OFFLOADS_STATS_ENQUEUED] =
-            { "                Enqueued offloads", 0 },
-        [DP_NETDEV_HW_OFFLOADS_STATS_INSERTED] =
-            { "                Inserted offloads", 0 },
-        [DP_NETDEV_HW_OFFLOADS_STATS_LAT_CMA_MEAN] =
-            { "  Cumulative Average latency (us)", 0 },
-        [DP_NETDEV_HW_OFFLOADS_STATS_LAT_CMA_STDDEV] =
-            { "   Cumulative Latency stddev (us)", 0 },
-        [DP_NETDEV_HW_OFFLOADS_STATS_LAT_EMA_MEAN] =
-            { " Exponential Average latency (us)", 0 },
-        [DP_NETDEV_HW_OFFLOADS_STATS_LAT_EMA_STDDEV] =
-            { "  Exponential Latency stddev (us)", 0 },
-    };
-
-    unsigned int nb_thread;
-    unsigned int tid;
-    size_t i;
-
-    if (!dpif_offload_is_offload_enabled()) {
-        return EINVAL;
-    }
-
-    nb_thread = dpdk_offload_thread_nb();
-    if (!nb_thread) {
-        return EINVAL;
-    }
-
-    /* nb_thread counters for the overall total as well. */
-    stats->size = ARRAY_SIZE(hwol_stats) * (nb_thread + 1);
-    stats->counters = xcalloc(stats->size, sizeof *stats->counters);
-
-    for (tid = 0; tid < nb_thread; tid++) {
-        uint64_t counts[ARRAY_SIZE(hwol_stats)];
-        size_t idx = ((tid + 1) * ARRAY_SIZE(hwol_stats));
-
-        memset(counts, 0, sizeof counts);
-        if (dp_offload_threads != NULL) {
-            atomic_read_relaxed(&dp_offload_threads[tid].enqueued_item,
-                                &counts[DP_NETDEV_HW_OFFLOADS_STATS_ENQUEUED]);
-
-            counts[DP_NETDEV_HW_OFFLOADS_STATS_LAT_CMA_MEAN] =
-                mov_avg_cma(&dp_offload_threads[tid].cma);
-            counts[DP_NETDEV_HW_OFFLOADS_STATS_LAT_CMA_STDDEV] =
-                mov_avg_cma_std_dev(&dp_offload_threads[tid].cma);
-
-            counts[DP_NETDEV_HW_OFFLOADS_STATS_LAT_EMA_MEAN] =
-                mov_avg_ema(&dp_offload_threads[tid].ema);
-            counts[DP_NETDEV_HW_OFFLOADS_STATS_LAT_EMA_STDDEV] =
-                mov_avg_ema_std_dev(&dp_offload_threads[tid].ema);
-        }
-
-        for (i = 0; i < ARRAY_SIZE(hwol_stats); i++) {
-            snprintf(stats->counters[idx + i].name,
-                     sizeof(stats->counters[idx + i].name),
-                     "  [%3u] %s", tid, hwol_stats[i].name);
-            stats->counters[idx + i].value = counts[i];
-            hwol_stats[i].total += counts[i];
-        }
-    }
-
-    /* Do an average of the average for the aggregate. */
-    hwol_stats[DP_NETDEV_HW_OFFLOADS_STATS_LAT_CMA_MEAN].total /= nb_thread;
-    hwol_stats[DP_NETDEV_HW_OFFLOADS_STATS_LAT_CMA_STDDEV].total /= nb_thread;
-    hwol_stats[DP_NETDEV_HW_OFFLOADS_STATS_LAT_EMA_MEAN].total /= nb_thread;
-    hwol_stats[DP_NETDEV_HW_OFFLOADS_STATS_LAT_EMA_STDDEV].total /= nb_thread;
-
-    /* Get the total offload count. */
-    hwol_stats[DP_NETDEV_HW_OFFLOADS_STATS_INSERTED].total =
-        dpif_offload_flow_get_n_offloaded(dpif);
-
-    for (i = 0; i < ARRAY_SIZE(hwol_stats); i++) {
-        snprintf(stats->counters[i].name, sizeof(stats->counters[i].name),
-                 "  Total %s", hwol_stats[i].name);
-        stats->counters[i].value = hwol_stats[i].total;
-    }
-
-    return 0;
-}
-
  /* Enable or Disable PMD auto load balancing. */
  static void
  set_pmd_auto_lb(struct dp_netdev *dp, bool state, bool always_log)
@@ -9990,7 +9895,6 @@ const struct dpif_class dpif_netdev_class = {
      dpif_netdev_flow_dump_thread_destroy,
      dpif_netdev_flow_dump_next,
      dpif_netdev_operate,
-    dpif_netdev_offload_stats_get,
      NULL,                       /* recv_set */
      NULL,                       /* handlers_set */
      dpif_netdev_number_handlers_required,
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index d384e8f09..d76e2ccfb 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -4071,7 +4071,6 @@ const struct dpif_class dpif_netlink_class = {
      dpif_netlink_flow_dump_thread_destroy,
      dpif_netlink_flow_dump_next,
      dpif_netlink_operate,
-    NULL,                       /* offload_stats_get */
      dpif_netlink_recv_set,
      dpif_netlink_handlers_set,
      dpif_netlink_number_handlers_required,
diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
index 3a0d51295..ce849e3dd 100644
--- a/lib/dpif-offload-dummy.c
+++ b/lib/dpif-offload-dummy.c
@@ -272,6 +272,24 @@ dpif_offload_dummy_get_debug(const struct dpif_offload 
*offload, struct ds *ds,
      }
  }
+static int
+dpif_offload_dummy_get_global_stats(const struct dpif_offload *offload_,
+                                    struct netdev_custom_stats *stats)
+{
+    struct dpif_offload_dummy *offload = dpif_offload_dummy_cast(offload_);
+
+    /* Add a single counter telling how many ports we are servicing. */
+    stats->label = xstrdup(dpif_offload_name(offload_));
+    stats->size = 1;
+    stats->counters = xmalloc(sizeof(struct netdev_custom_counter) * 1);
+    stats->counters[0].value = dpif_offload_port_mgr_port_count(
+        offload->port_mgr);
+    ovs_strzcpy(stats->counters[0].name, "Offloaded port count",
+                sizeof stats->counters[0].name);
+
+    return 0;
+}
+
  static bool
  dpif_offload_dummy_can_offload(struct dpif_offload *dpif_offload OVS_UNUSED,
                                 struct netdev *netdev)
@@ -279,24 +297,25 @@ dpif_offload_dummy_can_offload(struct dpif_offload 
*dpif_offload OVS_UNUSED,
      return is_dummy_netdev_class(netdev->netdev_class) ? true : false;
  }
-#define DEFINE_DPIF_DUMMY_CLASS(NAME, TYPE_STR) \
-    struct dpif_offload_class NAME = {                          \
-        .type = TYPE_STR,                                       \
-        .impl_type = DPIF_OFFLOAD_IMPL_HW_ONLY,                 \
-        .supported_dpif_types = (const char *const[]) {         \
-            "dummy",                                            \
-            NULL},                                              \
-        .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,                \
-        .port_dump_start = dpif_offload_dummy_port_dump_start,  \
-        .port_dump_next = dpif_offload_dummy_port_dump_next,    \
-        .port_dump_done = dpif_offload_dummy_port_dump_done,    \
-        .get_netdev = dpif_offload_dummy_get_netdev,            \
+#define DEFINE_DPIF_DUMMY_CLASS(NAME, TYPE_STR)                  \
+    struct dpif_offload_class NAME = {                           \
+        .type = TYPE_STR,                                        \
+        .impl_type = DPIF_OFFLOAD_IMPL_HW_ONLY,                  \
+        .supported_dpif_types = (const char *const[]) {          \
+            "dummy",                                             \
+            NULL},                                               \
+        .open = dpif_offload_dummy_open,                         \
+        .close = dpif_offload_dummy_close,                       \
+        .set_config = dpif_offload_dummy_set_config,             \
+        .get_debug = dpif_offload_dummy_get_debug,               \
+        .get_global_stats = dpif_offload_dummy_get_global_stats, \
+        .can_offload = dpif_offload_dummy_can_offload,           \
+        .port_add = dpif_offload_dummy_port_add,                 \
+        .port_del = dpif_offload_dummy_port_del,                 \
+        .port_dump_start = dpif_offload_dummy_port_dump_start,   \
+        .port_dump_next = dpif_offload_dummy_port_dump_next,     \
+        .port_dump_done = dpif_offload_dummy_port_dump_done,     \
+        .get_netdev = dpif_offload_dummy_get_netdev,             \
      }
DEFINE_DPIF_DUMMY_CLASS(dpif_offload_dummy_class, "dummy");
diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h
index 29189d4b1..1e63e1199 100644
--- a/lib/dpif-offload-provider.h
+++ b/lib/dpif-offload-provider.h
@@ -132,6 +132,14 @@ struct dpif_offload_class {
      void (*get_debug)(const struct dpif_offload *offload, struct ds *ds,
                        struct json *json);
+ /* Get hardware offload activity counters from the data plane.
+     * These counters are not interface offload statistics, but rather a status
+     * report of hardware offload management: how many offloads are currently
+     * waiting, inserted, etc.  If this function returns an error, the 'stats'
+     * structure should not be touched, that is, it remains uninitialized. */
+    int (*get_global_stats)(const struct dpif_offload *offload,
+                            struct netdev_custom_stats *stats);
+
      /* 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.c b/lib/dpif-offload.c
index 198b36b0c..8a42d5f13 100644
--- a/lib/dpif-offload.c
+++ b/lib/dpif-offload.c
@@ -435,6 +435,48 @@ dpif_offload_get_debug(const struct dpif_offload *offload, 
struct ds *ds,
      return true;
  }
+int dpif_offload_stats_get(struct dpif *dpif,
+                           struct netdev_custom_stats **stats_,
+                           size_t *n_stats)
+{
+    struct dp_offload *dp_offload = dpif_offload_get_dp_offload(dpif);
+    struct netdev_custom_stats *stats;
+    struct dpif_offload *offload;
+    size_t n_providers;
+    int error = 0;
+
+    *n_stats = 0;
+    if (!dp_offload || !dpif_offload_is_offload_enabled()) {
+        *stats_ = NULL;
+        return 0;
+    }
+
+    n_providers = ovs_list_size(&dp_offload->offload_providers);
+    stats = xcalloc(n_providers, sizeof(struct netdev_custom_stats));
+
+    LIST_FOR_EACH (offload, dpif_list_node, &dp_offload->offload_providers) {
+        if (!offload->class->get_global_stats) {
+            continue;
+        }
+
+        error = offload->class->get_global_stats(offload, &stats[(*n_stats)]);
+        if (error) {
+            for (int i = 0; i < *n_stats; i++) {
+                netdev_free_custom_stats_counters(&stats[i]);
+            }
+            *n_stats = 0;
+            free(stats);
+            stats = NULL;
+            break;
+        }
+
+        (*n_stats)++;
+    }
+
+    *stats_ = stats;
+    return error;
+}
+
  bool
  dpif_offload_is_offload_enabled(void)
  {
diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h
index 213897ca2..2ca790568 100644
--- a/lib/dpif-offload.h
+++ b/lib/dpif-offload.h
@@ -134,6 +134,18 @@ int dpif_offload_port_dump_done(struct 
dpif_offload_port_dump *);
            : (dpif_offload_port_dump_done(DUMP), false));           \
          )
+/* Queries the datapath for hardware offload stats.
+ *
+ * On success, '*stats' will point to a heap-allocated array of
+ * 'netdev_custom_stats' structures, and '*n_stats' will be set to the
+ * number of statistics returned.
+ *
+ * The caller is responsible for freeing the memory using
+ * 'netdev_free_custom_stats_counters()' on each 'stats' object, and
+ * call free() on 'stats'. */
+int dpif_offload_stats_get(struct dpif *, struct netdev_custom_stats **stats,
+                           size_t *n_stats);
+
  
  /* Netdev specific function, which can be used in the fast path. */
  bool dpif_offload_netdev_same_offload(const struct netdev *,
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index e1c91caff..18878fd8e 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -366,13 +366,6 @@ struct dpif_class {
       * dpif_op. */
      void (*operate)(struct dpif *dpif, struct dpif_op **ops, size_t n_ops);
- /* Get hardware-offloads activity counters from a dataplane.
-     * Those counters are not offload statistics (which are accessible through
-     * netdev statistics), but a status of hardware offload management:
-     * how many offloads are currently waiting, inserted, etc. */
-    int (*offload_stats_get)(struct dpif *dpif,
-                             struct netdev_custom_stats *stats);
-
      /* Enables or disables receiving packets with dpif_recv() for 'dpif'.
       * Turning packet receive off and then back on is allowed to change 
Netlink
       * PID assignments (see ->port_get_pid()).  The client is responsible for
diff --git a/lib/dpif.c b/lib/dpif.c
index 089043ad4..bda11a41b 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1470,14 +1470,6 @@ dpif_operate(struct dpif *dpif, struct dpif_op **ops, 
size_t n_ops,
      }
  }
-int dpif_offload_stats_get(struct dpif *dpif,
-                           struct netdev_custom_stats *stats)
-{
-    return (dpif->dpif_class->offload_stats_get
-            ? dpif->dpif_class->offload_stats_get(dpif, stats)
-            : EOPNOTSUPP);
-}
-
  /* Returns a string that represents 'type', for use in log messages. */
  const char *
  dpif_upcall_type_to_string(enum dpif_upcall_type type)
diff --git a/lib/dpif.h b/lib/dpif.h
index c1b0daa3e..3e6a34a25 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -793,14 +793,6 @@ struct dpif_op {
  void dpif_operate(struct dpif *, struct dpif_op **ops, size_t n_ops,
                    enum dpif_offload_type);
-/* Queries the datapath for hardware offloads stats.
- *
- * Statistics are written in 'stats' following the 'netdev_custom_stats'
- * format. They are allocated on the heap and must be freed by the caller,
- * using 'netdev_free_custom_stats_counters'.
- */
-int dpif_offload_stats_get(struct dpif *dpif,
-                           struct netdev_custom_stats *stats);
  
  /* Upcalls. */
diff --git a/lib/netdev.c b/lib/netdev.c
index 6b0ea8e11..ee5d68c99 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -2440,6 +2440,8 @@ netdev_free_custom_stats_counters(struct 
netdev_custom_stats *custom_stats)
              custom_stats->counters = NULL;
              custom_stats->size = 0;
          }
+        free(custom_stats->label);
+        custom_stats->label = NULL;
      }
  }
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 10596a968..1099c6f71 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -10229,6 +10229,22 @@ AT_CHECK([ovs-appctl --format json --pretty 
dpif/offload/show], [0], [dnl
  OVS_VSWITCHD_STOP
  AT_CLEANUP
+AT_SETUP([ofproto-dpif - offload - ovs-appctl dpctl/offload-stats-show])
+AT_KEYWORDS([dpif-offload])
+OVS_VSWITCHD_START([add-br br1 -- set bridge br1 datapath-type=dummy], [], [],
+  [], [-- set Open_vSwitch . other_config:hw-offload=true])
+
+AT_CHECK([ovs-appctl dpctl/offload-stats-show], [0], [dnl
+HW Offload stats:
+  dummy[[dummy@ovs-dummy]]:
+    Offloaded port count:      3
+  dummy_x[[dummy@ovs-dummy]]:
+    Offloaded port count:      0
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
  dnl ----------------------------------------------------------------------
  AT_BANNER([ofproto-dpif -- megaflows])
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to