Subject: [PATCH ethtool-next v2 v2] JSON output support for Netlink implementation of --show-coalesce option

From: Maxim Georgiev <[email-masked]>
Date: Wed, 14 Dec 2022 20:56:51 -0700

Add --json support for Netlink implementation of --show-coalesce option
No changes for non-JSON output for this featire.

Example output without --json:
[ethtool-git]$ sudo ./ethtool --show-coalesce enp9s0u2u1u2
Coalesce parameters for enp9s0u2u1u2:
Adaptive RX: n/a  TX: n/a
stats-block-usecs:	n/a
sample-interval:	n/a
pkt-rate-low:		n/a
pkt-rate-high:		n/a

rx-usecs:	15000
rx-frames:	n/a
rx-usecs-irq:	n/a
rx-frames-irq:	n/a

tx-usecs:	0
tx-frames:	n/a
tx-usecs-irq:	n/a
tx-frames-irq:	n/a

rx-usecs-low:	n/a
rx-frame-low:	n/a
tx-usecs-low:	n/a
tx-frame-low:	n/a

rx-usecs-high:	n/a
rx-frame-high:	n/a
tx-usecs-high:	n/a
tx-frame-high:	n/a

CQE mode RX: n/a  TX: n/a

Same output with --json:
[ethtool-git]$ sudo ./ethtool --json --show-coalesce enp9s0u2u1u2
[ {
        "ifname": "enp9s0u2u1u2",
        "rx-usecs": 15000,
        "tx-usecs": 0
    } ]

Suggested-by: Jakub Kicinski <[email-masked]>
Signed-off-by: Maxim Georgiev <[email-masked]>
---
Changes in v2:
 - Moved the patch to ethtool-next branch
 - Eliminated ':' in JSON key names
 - Replaced the last 'putchar('\n')' call left in coalesce_reply_cb() with show_cr()

 ethtool.c          |  1 +
 netlink/channels.c | 18 +++++-----
 netlink/coalesce.c | 88 ++++++++++++++++++++++++++++++----------------
 netlink/netlink.h  | 24 ++++++++++---
 netlink/rings.c    | 21 +++++------
 5 files changed, 98 insertions(+), 54 deletions(-)

diff --git a/ethtool.c b/ethtool.c
index 3207e49..3b8412c 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5694,6 +5694,7 @@ static const struct option args[] = {
 	},
 	{
 		.opts	= "-c|--show-coalesce",
+		.json	= true,
 		.func	= do_gcoalesce,
 		.nlfunc	= nl_gcoalesce,
 		.help	= "Show coalesce options"
diff --git a/netlink/channels.c b/netlink/channels.c
index 894c74b..5cae227 100644
--- a/netlink/channels.c
+++ b/netlink/channels.c
@@ -37,15 +37,17 @@ int channels_reply_cb(const struct nlmsghdr *nlhdr, void *data)
 		putchar('\n');
 	printf("Channel parameters for %s:\n", nlctx->devname);
 	printf("Pre-set maximums:\n");
-	show_u32(tb[ETHTOOL_A_CHANNELS_RX_MAX], "RX:\t\t");
-	show_u32(tb[ETHTOOL_A_CHANNELS_TX_MAX], "TX:\t\t");
-	show_u32(tb[ETHTOOL_A_CHANNELS_OTHER_MAX], "Other:\t\t");
-	show_u32(tb[ETHTOOL_A_CHANNELS_COMBINED_MAX], "Combined:\t");
+	show_u32("rx-max", "RX:\t\t", tb[ETHTOOL_A_CHANNELS_RX_MAX]);
+	show_u32("tx-max", "TX:\t\t", tb[ETHTOOL_A_CHANNELS_TX_MAX]);
+	show_u32("other-max", "Other:\t\t", tb[ETHTOOL_A_CHANNELS_OTHER_MAX]);
+	show_u32("combined-max", "Combined:\t",
+		 tb[ETHTOOL_A_CHANNELS_COMBINED_MAX]);
 	printf("Current hardware settings:\n");
-	show_u32(tb[ETHTOOL_A_CHANNELS_RX_COUNT], "RX:\t\t");
-	show_u32(tb[ETHTOOL_A_CHANNELS_TX_COUNT], "TX:\t\t");
-	show_u32(tb[ETHTOOL_A_CHANNELS_OTHER_COUNT], "Other:\t\t");
-	show_u32(tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT], "Combined:\t");
+	show_u32("rx", "RX:\t\t", tb[ETHTOOL_A_CHANNELS_RX_COUNT]);
+	show_u32("tx", "TX:\t\t", tb[ETHTOOL_A_CHANNELS_TX_COUNT]);
+	show_u32("other", "Other:\t\t", tb[ETHTOOL_A_CHANNELS_OTHER_COUNT]);
+	show_u32("combined", "Combined:\t",
+		 tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT]);
 
 	return MNL_CB_OK;
 }
diff --git a/netlink/coalesce.c b/netlink/coalesce.c
index 15037c2..3cc35ba 100644
--- a/netlink/coalesce.c
+++ b/netlink/coalesce.c
@@ -33,43 +33,64 @@ int coalesce_reply_cb(const struct nlmsghdr *nlhdr, void *data)
 	if (!dev_ok(nlctx))
 		return err_ret;
 
+	open_json_object(NULL);
+
 	if (silent)
-		putchar('\n');
-	printf("Coalesce parameters for %s:\n", nlctx->devname);
+		show_cr();
+	print_string(PRINT_ANY, "ifname", "Coalesce parameters for %s:\n",
+		     nlctx->devname);
 	show_bool("rx", "Adaptive RX: %s  ",
 		  tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX]);
 	show_bool("tx", "TX: %s\n", tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX]);
-	show_u32(tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS],
-		 "stats-block-usecs: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL],
-		 "sample-interval: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], "pkt-rate-low: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], "pkt-rate-high: ");
-	putchar('\n');
-	show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS], "rx-usecs: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], "rx-frames: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], "rx-usecs-irq: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], "rx-frames-irq: ");
-	putchar('\n');
-	show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS], "tx-usecs: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], "tx-frames: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], "tx-usecs-irq: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], "tx-frames-irq: ");
-	putchar('\n');
-	show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], "rx-usecs-low: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], "rx-frame-low: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], "tx-usecs-low: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], "tx-frame-low: ");
-	putchar('\n');
-	show_u32(tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], "rx-usecs-high: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], "rx-frame-high: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], "tx-usecs-high: ");
-	show_u32(tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], "tx-frame-high: ");
-	putchar('\n');
+	show_u32("stats-block-usecs", "stats-block-usecs:\t",
+		 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS]);
+	show_u32("sample-interval", "sample-interval:\t",
+		 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL]);
+	show_u32("pkt-rate-low", "pkt-rate-low:\t\t",
+		 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW]);
+	show_u32("pkt-rate-high", "pkt-rate-high:\t\t",
+		 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH]);
+	show_cr();
+	show_u32("rx-usecs", "rx-usecs:\t", tb[ETHTOOL_A_COALESCE_RX_USECS]);
+	show_u32("rx-frames", "rx-frames:\t",
+		 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES]);
+	show_u32("rx-usecs-irq", "rx-usecs-irq:\t",
+		 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ]);
+	show_u32("rx-frames-irq", "rx-frames-irq:\t",
+		 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ]);
+	show_cr();
+	show_u32("tx-usecs", "tx-usecs:\t", tb[ETHTOOL_A_COALESCE_TX_USECS]);
+	show_u32("tx-frames", "tx-frames:\t",
+		 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES]);
+	show_u32("tx-usecs-irq", "tx-usecs-irq:\t",
+		 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ]);
+	show_u32("tx-frames-irq", "tx-frames-irq:\t",
+		 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ]);
+	show_cr();
+	show_u32("rx-usecs-low", "rx-usecs-low:\t",
+		 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW]);
+	show_u32("rx-frame-low", "rx-frame-low:\t",
+		 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW]);
+	show_u32("tx-usecs-low", "tx-usecs-low:\t",
+		 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW]);
+	show_u32("tx-frame-low", "tx-frame-low:\t",
+		 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW]);
+	show_cr();
+	show_u32("rx-usecs-high", "rx-usecs-high:\t",
+		 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH]);
+	show_u32("rx-frame-high", "rx-frame-high:\t",
+		 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH]);
+	show_u32("tx-usecs-high", "tx-usecs-high:\t",
+		 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH]);
+	show_u32("tx-frame-high", "tx-frame-high:\t",
+		 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH]);
+	show_cr();
 	show_bool("rx", "CQE mode RX: %s  ",
 		  tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX]);
 	show_bool("tx", "TX: %s\n", tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX]);
-	putchar('\n');
+	show_cr();
+
+	close_json_object();
 
 	return MNL_CB_OK;
 }
@@ -92,7 +113,12 @@ int nl_gcoalesce(struct cmd_context *ctx)
 				      ETHTOOL_A_COALESCE_HEADER, 0);
 	if (ret < 0)
 		return ret;
-	return nlsock_send_get_request(nlsk, coalesce_reply_cb);
+
+	new_json_obj(ctx->json);
+	ret = nlsock_send_get_request(nlsk, coalesce_reply_cb);
+	delete_json_obj();
+	return ret;
+
 }
 
 /* COALESCE_SET */
diff --git a/netlink/netlink.h b/netlink/netlink.h
index f43c1bf..3240fca 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -100,12 +100,20 @@ int dump_link_modes(struct nl_context *nlctx, const struct nlattr *bitset,
 		    const char *between, const char *after,
 		    const char *if_none);
 
-static inline void show_u32(const struct nlattr *attr, const char *label)
+static inline void show_u32(const char *key,
+			    const char *fmt,
+			    const struct nlattr *attr)
 {
-	if (attr)
-		printf("%s%u\n", label, mnl_attr_get_u32(attr));
-	else
-		printf("%sn/a\n", label);
+	if (is_json_context()) {
+		if (attr)
+			print_uint(PRINT_JSON, key, NULL,
+				   mnl_attr_get_u32(attr));
+	} else {
+		if (attr)
+			printf("%s%u\n", fmt, mnl_attr_get_u32(attr));
+		else
+			printf("%sn/a\n", fmt);
+	}
 }
 
 static inline const char *u8_to_bool(const uint8_t *val)
@@ -132,6 +140,12 @@ static inline void show_bool(const char *key, const char *fmt,
 	show_bool_val(key, fmt, attr ? mnl_attr_get_payload(attr) : NULL);
 }
 
+static inline void show_cr(void)
+{
+	if (!is_json_context())
+		putchar('\n');
+}
+
 /* misc */
 
 static inline void copy_devname(char *dst, const char *src)
diff --git a/netlink/rings.c b/netlink/rings.c
index 6284035..5996d5a 100644
--- a/netlink/rings.c
+++ b/netlink/rings.c
@@ -38,17 +38,18 @@ int rings_reply_cb(const struct nlmsghdr *nlhdr, void *data)
 		putchar('\n');
 	printf("Ring parameters for %s:\n", nlctx->devname);
 	printf("Pre-set maximums:\n");
-	show_u32(tb[ETHTOOL_A_RINGS_RX_MAX], "RX:\t\t");
-	show_u32(tb[ETHTOOL_A_RINGS_RX_MINI_MAX], "RX Mini:\t");
-	show_u32(tb[ETHTOOL_A_RINGS_RX_JUMBO_MAX], "RX Jumbo:\t");
-	show_u32(tb[ETHTOOL_A_RINGS_TX_MAX], "TX:\t\t");
+	show_u32("rx-max", "RX:\t\t", tb[ETHTOOL_A_RINGS_RX_MAX]);
+	show_u32("rx-mini-max", "RX Mini:\t", tb[ETHTOOL_A_RINGS_RX_MINI_MAX]);
+	show_u32("rx-jumbo-max", "RX Jumbo:\t",
+		 tb[ETHTOOL_A_RINGS_RX_JUMBO_MAX]);
+	show_u32("tx-max", "TX:\t\t", tb[ETHTOOL_A_RINGS_TX_MAX]);
 	printf("Current hardware settings:\n");
-	show_u32(tb[ETHTOOL_A_RINGS_RX], "RX:\t\t");
-	show_u32(tb[ETHTOOL_A_RINGS_RX_MINI], "RX Mini:\t");
-	show_u32(tb[ETHTOOL_A_RINGS_RX_JUMBO], "RX Jumbo:\t");
-	show_u32(tb[ETHTOOL_A_RINGS_TX], "TX:\t\t");
-	show_u32(tb[ETHTOOL_A_RINGS_RX_BUF_LEN], "RX Buf Len:\t\t");
-	show_u32(tb[ETHTOOL_A_RINGS_CQE_SIZE], "CQE Size:\t\t");
+	show_u32("rx", "RX:\t\t", tb[ETHTOOL_A_RINGS_RX]);
+	show_u32("rx-mini", "RX Mini:\t", tb[ETHTOOL_A_RINGS_RX_MINI]);
+	show_u32("rx-jumbo", "RX Jumbo:\t", tb[ETHTOOL_A_RINGS_RX_JUMBO]);
+	show_u32("tx", "TX:\t\t", tb[ETHTOOL_A_RINGS_TX]);
+	show_u32("rx-buf-len", "RX Buf Len:\t", tb[ETHTOOL_A_RINGS_RX_BUF_LEN]);
+	show_u32("cqe-size", "CQE Size:\t", tb[ETHTOOL_A_RINGS_CQE_SIZE]);
 	show_bool("tx-push", "TX Push:\t%s\n", tb[ETHTOOL_A_RINGS_TX_PUSH]);
 
 	tcp_hds = tb[ETHTOOL_A_RINGS_TCP_DATA_SPLIT] ?
-- 
2.38.1