When --format json is passed to ovs-appctl, upcall/show returns a JSON
object keyed by datapath name. Each entry contains a "flows" sub-object
(with "average", "current", "limit", "maximum", and "offloaded" counts),
"dump-duration-ms", "ufid-enabled", and a "revalidators" object indexed
by revalidator ID. Revalidators that are not fully initialized yet
(ID 0) are skipped, otherwise they would overwrite each other.
The implementation is split into separate text and JSON helper functions.
Example output:
{"ovs-system": {"dump-duration-ms": 1,
"flows": {"average": 0, "current": 0,
"limit": 10000, "maximum": 0,
"offloaded": 0},
"revalidators": {"4": {"keys": 0}},
"ufid-enabled": true}}
Signed-off-by: Timothy Redaelli <[email protected]>
---
NEWS | 3 +-
ofproto/ofproto-dpif-upcall.c | 101 +++++++++++++++++++++++++++++-----
tests/ofproto-dpif.at | 46 ++++++++++++++++
3 files changed, 135 insertions(+), 15 deletions(-)
diff --git a/NEWS b/NEWS
index 0153fa0cb..660d80356 100644
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,8 @@
Post-v4.0.0
--------------------
- ovs-appctl:
- * Added JSON output support (--format json) for 'fdb/stats-show'.
+ * Added JSON output support (--format json) for 'fdb/stats-show',
+ 'upcall/show'.
v4.0.0 - 17 Aug 2026
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 8e4897202..c877a0c24 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -28,6 +28,7 @@
#include "openvswitch/dynamic-string.h"
#include "fail-open.h"
#include "guarded-list.h"
+#include "openvswitch/json.h"
#include "latch.h"
#include "openvswitch/list.h"
#include "netlink.h"
@@ -3160,11 +3161,71 @@ dp_purge_cb(void *aux, unsigned pmd_id)
udpif_resume_revalidators(udpif);
}
+static struct json *
+upcall_unixctl_show_json(void)
+{
+ struct json *json_udpifs = json_object_create();
+ struct udpif *udpif;
+
+ LIST_FOR_EACH (udpif, list_node, &all_udpifs) {
+ struct json *json_revalidators = json_object_create();
+ struct json *json_flows = json_object_create();
+ struct json *json_udpif = json_object_create();
+ uint64_t n_offloaded_flows;
+ unsigned int flow_limit;
+ bool ufid_enabled;
+ size_t i;
+
+ atomic_read_relaxed(&udpif->flow_limit, &flow_limit);
+ ufid_enabled = udpif_use_ufid(udpif);
+ n_offloaded_flows = dpif_offload_flow_count(udpif->dpif);
+
+ json_object_put(json_flows, "average",
+ json_integer_create(udpif->avg_n_flows));
+ json_object_put(json_flows, "current",
+ json_integer_create(udpif_get_n_flows(udpif)));
+ json_object_put(json_flows, "limit",
+ json_integer_create(flow_limit));
+ json_object_put(json_flows, "maximum",
+ json_integer_create(udpif->max_n_flows));
+ json_object_put(json_flows, "offloaded",
+ json_integer_create(n_offloaded_flows));
+ json_object_put(json_udpif, "dump-duration-ms",
+ json_integer_create(udpif->dump_duration));
+ json_object_put(json_udpif, "flows", json_flows);
+
+ for (i = 0; i < udpif->n_revalidators; i++) {
+ struct revalidator *revalidator = &udpif->revalidators[i];
+ struct json *json_rv;
+ int j, elements = 0;
+ char *key;
+
+ /* Revalidators that are not fully initialized yet have ID 0.
+ * Skip them, otherwise they would overwrite each other. */
+ if (!revalidator->id) {
+ continue;
+ }
+
+ json_rv = json_object_create();
+ key = xasprintf("%u", revalidator->id);
+ for (j = i; j < N_UMAPS; j += udpif->n_revalidators) {
+ elements += cmap_count(&udpif->ukeys[j].cmap);
+ }
+ json_object_put(json_rv, "keys", json_integer_create(elements));
+ json_object_put_nocopy(json_revalidators, key, json_rv);
+ }
+ json_object_put(json_udpif, "revalidators", json_revalidators);
+ json_object_put(json_udpif, "ufid-enabled",
+ json_boolean_create(ufid_enabled));
+
+ json_object_put(json_udpifs, dpif_name(udpif->dpif), json_udpif);
+ }
+ return json_udpifs;
+}
+
static void
-upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+upcall_unixctl_show_text(struct ds *ds)
{
- struct ds ds = DS_EMPTY_INITIALIZER;
uint64_t n_offloaded_flows;
struct udpif *udpif;
@@ -3176,23 +3237,23 @@ upcall_unixctl_show(struct unixctl_conn *conn, int argc
OVS_UNUSED,
atomic_read_relaxed(&udpif->flow_limit, &flow_limit);
ufid_enabled = udpif_use_ufid(udpif);
- ds_put_format(&ds, "%s:\n", dpif_name(udpif->dpif));
- ds_put_format(&ds, " flows : (current %lu)"
+ ds_put_format(ds, "%s:\n", dpif_name(udpif->dpif));
+ ds_put_format(ds, " flows : (current %lu)"
" (avg %u) (max %u) (limit %u)\n", udpif_get_n_flows(udpif),
udpif->avg_n_flows, udpif->max_n_flows, flow_limit);
n_offloaded_flows = dpif_offload_flow_count(udpif->dpif);
if (n_offloaded_flows) {
- ds_put_format(&ds, " offloaded flows : %"PRIu64"\n",
+ ds_put_format(ds, " offloaded flows : %"PRIu64"\n",
n_offloaded_flows);
}
- ds_put_format(&ds, " dump duration : %lldms\n", udpif->dump_duration);
- ds_put_format(&ds, " ufid enabled : ");
+ ds_put_format(ds, " dump duration : %lldms\n", udpif->dump_duration);
+ ds_put_format(ds, " ufid enabled : ");
if (ufid_enabled) {
- ds_put_format(&ds, "true\n");
+ ds_put_format(ds, "true\n");
} else {
- ds_put_format(&ds, "false\n");
+ ds_put_format(ds, "false\n");
}
- ds_put_char(&ds, '\n');
+ ds_put_char(ds, '\n');
for (i = 0; i < udpif->n_revalidators; i++) {
struct revalidator *revalidator = &udpif->revalidators[i];
@@ -3201,12 +3262,24 @@ upcall_unixctl_show(struct unixctl_conn *conn, int argc
OVS_UNUSED,
for (j = i; j < N_UMAPS; j += udpif->n_revalidators) {
elements += cmap_count(&udpif->ukeys[j].cmap);
}
- ds_put_format(&ds, " %u: (keys %d)\n", revalidator->id, elements);
+ ds_put_format(ds, " %u: (keys %d)\n", revalidator->id, elements);
}
}
+}
- unixctl_command_reply(conn, ds_cstr(&ds));
- ds_destroy(&ds);
+static void
+upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
+{
+ if (unixctl_command_get_output_format(conn) == UNIXCTL_OUTPUT_FMT_JSON) {
+ unixctl_command_reply_json(conn, upcall_unixctl_show_json());
+ } else {
+ struct ds ds = DS_EMPTY_INITIALIZER;
+
+ upcall_unixctl_show_text(&ds);
+ unixctl_command_reply(conn, ds_cstr(&ds));
+ ds_destroy(&ds);
+ }
}
/* Disable using the megaflows.
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 778f83a8c..7bb429fd9 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -51,6 +51,52 @@ AT_CHECK([ovs-appctl revalidator/wait])
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - upcall/show JSON output])
+OVS_VSWITCHD_START([], [], [=override])
+
+dnl Use one revalidator thread for deterministic output.
+AT_CHECK([ovs-vsctl set open_vswitch . other_config:n-revalidator-threads=1])
+OVS_WAIT_UNTIL([ovs-appctl upcall/show | grep -c '(keys' | grep -q '^1$'])
+
+dnl Add a second datapath to check the output with multiple datapaths.
+dnl Wait for both revalidators to be initialized, i.e. to have a non-zero ID.
+AT_CHECK([ovs-vsctl add-br br1 -- set bridge br1 datapath-type=netdev])
+OVS_WAIT_UNTIL([test $(ovs-appctl upcall/show | dnl
+ grep -c '^ [[1-9]][[0-9]]*: (keys') = 2])
+
+AT_CHECK([ovs-appctl --format json --pretty upcall/show | dnl
+ sed -e 's/: [[0-9]][[0-9]]*/: <cleared>/g' dnl
+ -e 's/"[[0-9]][[0-9]]*": {/"<revalidator>": {/g'], [0], [dnl
+{
+ "dummy@ovs-dummy": {
+ "dump-duration-ms": <cleared>,
+ "flows": {
+ "average": <cleared>,
+ "current": <cleared>,
+ "limit": <cleared>,
+ "maximum": <cleared>,
+ "offloaded": <cleared>},
+ "revalidators": {
+ "<revalidator>": {
+ "keys": <cleared>}},
+ "ufid-enabled": true},
+ "netdev@ovs-netdev": {
+ "dump-duration-ms": <cleared>,
+ "flows": {
+ "average": <cleared>,
+ "current": <cleared>,
+ "limit": <cleared>,
+ "maximum": <cleared>,
+ "offloaded": <cleared>},
+ "revalidators": {
+ "<revalidator>": {
+ "keys": <cleared>}},
+ "ufid-enabled": true}}
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - lldp revalidator event(REV_RECONFIGURE)])
OVS_VSWITCHD_START(
[add-port br0 p1 -- set interface p1 ofport_request=1 type=dummy]
--
2.56.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev