diff mbox series

[v2,ethtool,2/5] netlink: add support for MAC Merge layer

Message ID 20230124142056.3778131-3-vladimir.oltean@nxp.com (mailing list archive)
State Superseded, archived
Delegated to: Michal Kubecek
Headers show
Series MAC Merge layer support | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Vladimir Oltean Jan. 24, 2023, 2:20 p.m. UTC
$ ethtool --include-statistics --show-mm eno0
MAC Merge layer state for eno0:
pMAC enabled: on
TX enabled: on
TX active: on
TX minimum fragment size: 60
RX minimum fragment size: 60
Verify enabled: off
Verify time: 127 ms
Max verify time: 127 ms
Verification status: SUCCEEDED
Statistics:
  MACMergeFrameAssErrorCount: 0
  MACMergeFrameSmdErrorCount: 0
  MACMergeFrameAssOkCount: 0
  MACMergeFragCountRx: 0
  MACMergeFragCountTx: 0
  MACMergeHoldCount: 0

$ ethtool --include-statistics --json --show-mm eno0
[ {
        "ifname": "eno0",
        "pmac-enabled": true,
        "tx-enabled": true,
        "tx-active": true,
        "tx-min-frag-size": 60,
        "rx-min-frag-size": 60,
        "verify-enabled": true,
        "verify-time": 127,
        "max-verify-time": 127,
        "verify-status": "SUCCEEDED",
        "statistics": {
            "MACMergeFrameAssErrorCount": 0,
            "MACMergeFrameSmdErrorCount": 0,
            "MACMergeFrameAssOkCount": 0,
            "MACMergeFragCountRx": 0,
            "MACMergeFragCountTx": 0,
            "MACMergeHoldCount": 0
        }
    } ]

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
v1->v2:
- rebase on top of PLCA changes
- ETHTOOL_A_MM_ADD_FRAG_SIZE became ETHTOOL_A_MM_TX_MIN_FRAG_SIZE
- ETHTOOL_A_MM_RX_MIN_FRAG_SIZE was introduced
- ETHTOOL_A_MM_SUPPORTED disappeared
- fix help text for --show-mm
- use the newly introduced show_u32() instead of hand-rolling own
  implementation - show_u32_json()

 Makefile.am            |   2 +-
 ethtool.c              |  16 +++
 netlink/desc-ethtool.c |  29 +++++
 netlink/extapi.h       |   4 +
 netlink/mm.c           | 270 +++++++++++++++++++++++++++++++++++++++++
 netlink/parser.c       |   6 +-
 netlink/parser.h       |   4 +
 7 files changed, 327 insertions(+), 4 deletions(-)
 create mode 100644 netlink/mm.c
diff mbox series

Patch

diff --git a/Makefile.am b/Makefile.am
index 4d3442441265..ee734fa1e97c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -37,7 +37,7 @@  ethtool_SOURCES += \
 		  netlink/features.c netlink/privflags.c netlink/rings.c \
 		  netlink/channels.c netlink/coalesce.c netlink/pause.c \
 		  netlink/eee.c netlink/tsinfo.c netlink/fec.c \
-		  netlink/stats.c \
+		  netlink/stats.c netlink/mm.c \
 		  netlink/desc-ethtool.c netlink/desc-genlctrl.c \
 		  netlink/module-eeprom.c netlink/module.c \
 		  netlink/desc-rtnl.c netlink/cable_test.c netlink/tunnels.c \
diff --git a/ethtool.c b/ethtool.c
index 76a81cf55c3c..9846419f0ebf 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -6117,6 +6117,22 @@  static const struct option args[] = {
 		.nlfunc	= nl_plca_get_status,
 		.help	= "Get PLCA status information",
 	},
+	{
+		.opts	= "--show-mm",
+		.json	= true,
+		.nlfunc	= nl_get_mm,
+		.help	= "Show MAC merge layer state",
+	},
+	{
+		.opts	= "--set-mm",
+		.nlfunc	= nl_set_mm,
+		.help	= "Set MAC merge layer parameters",
+			  "		[ verify-enabled on|off ]\n"
+			  "		[ verify-time N ]\n"
+			  "		[ tx-enabled on|off ]\n"
+			  "		[ pmac-enabled on|off ]\n"
+			  "		[ tx-min-frag-size 60-252 ]\n"
+	},
 	{
 		.opts	= "-h|--help",
 		.no_dev	= true,
diff --git a/netlink/desc-ethtool.c b/netlink/desc-ethtool.c
index b3ac64d1e593..5ca81e22703a 100644
--- a/netlink/desc-ethtool.c
+++ b/netlink/desc-ethtool.c
@@ -442,6 +442,31 @@  static const struct pretty_nla_desc __pse_desc[] = {
 	NLATTR_DESC_U32_ENUM(ETHTOOL_A_PODL_PSE_PW_D_STATUS, pse_pw_d_status),
 };
 
+static const struct pretty_nla_desc __mm_stats_desc[] = {
+	NLATTR_DESC_BINARY(ETHTOOL_A_MM_STAT_PAD),
+	NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS),
+	NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_SMD_ERRORS),
+	NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_REASSEMBLY_OK),
+	NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_RX_FRAG_COUNT),
+	NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_TX_FRAG_COUNT),
+	NLATTR_DESC_U64(ETHTOOL_A_MM_STAT_HOLD_COUNT),
+};
+
+static const struct pretty_nla_desc __mm_desc[] = {
+	NLATTR_DESC_INVALID(ETHTOOL_A_MM_UNSPEC),
+	NLATTR_DESC_NESTED(ETHTOOL_A_MM_HEADER, header),
+	NLATTR_DESC_U8(ETHTOOL_A_MM_VERIFY_STATUS),
+	NLATTR_DESC_U8(ETHTOOL_A_MM_VERIFY_ENABLED),
+	NLATTR_DESC_U32(ETHTOOL_A_MM_VERIFY_TIME),
+	NLATTR_DESC_U32(ETHTOOL_A_MM_MAX_VERIFY_TIME),
+	NLATTR_DESC_U8(ETHTOOL_A_MM_TX_ENABLED),
+	NLATTR_DESC_U8(ETHTOOL_A_MM_TX_ACTIVE),
+	NLATTR_DESC_U8(ETHTOOL_A_MM_PMAC_ENABLED),
+	NLATTR_DESC_U32(ETHTOOL_A_MM_TX_MIN_FRAG_SIZE),
+	NLATTR_DESC_U32(ETHTOOL_A_MM_RX_MIN_FRAG_SIZE),
+	NLATTR_DESC_NESTED(ETHTOOL_A_MM_STATS, mm_stats),
+};
+
 const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
 	NLMSG_DESC_INVALID(ETHTOOL_MSG_USER_NONE),
 	NLMSG_DESC(ETHTOOL_MSG_STRSET_GET, strset),
@@ -481,6 +506,8 @@  const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
 	NLMSG_DESC(ETHTOOL_MSG_MODULE_SET, module),
 	NLMSG_DESC(ETHTOOL_MSG_PSE_GET, pse),
 	NLMSG_DESC(ETHTOOL_MSG_PSE_SET, pse),
+	NLMSG_DESC(ETHTOOL_MSG_MM_GET, mm),
+	NLMSG_DESC(ETHTOOL_MSG_MM_SET, mm),
 };
 
 const unsigned int ethnl_umsg_n_desc = ARRAY_SIZE(ethnl_umsg_desc);
@@ -524,6 +551,8 @@  const struct pretty_nlmsg_desc ethnl_kmsg_desc[] = {
 	NLMSG_DESC(ETHTOOL_MSG_MODULE_GET_REPLY, module),
 	NLMSG_DESC(ETHTOOL_MSG_MODULE_NTF, module),
 	NLMSG_DESC(ETHTOOL_MSG_PSE_GET_REPLY, pse),
+	NLMSG_DESC(ETHTOOL_MSG_MM_GET_REPLY, mm),
+	NLMSG_DESC(ETHTOOL_MSG_MM_NTF, mm),
 };
 
 const unsigned int ethnl_kmsg_n_desc = ARRAY_SIZE(ethnl_kmsg_desc);
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 0add156e644a..af0b92cc8117 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -50,6 +50,8 @@  int nl_getmodule(struct cmd_context *ctx);
 int nl_plca_get_cfg(struct cmd_context *ctx);
 int nl_plca_set_cfg(struct cmd_context *ctx);
 int nl_plca_get_status(struct cmd_context *ctx);
+int nl_get_mm(struct cmd_context *ctx);
+int nl_set_mm(struct cmd_context *ctx);
 
 void nl_monitor_usage(void);
 
@@ -120,6 +122,8 @@  nl_get_eeprom_page(struct cmd_context *ctx __maybe_unused,
 #define nl_get_plca_cfg		NULL
 #define nl_set_plca_cfg		NULL
 #define nl_get_plca_status	NULL
+#define nl_get_mm		NULL
+#define nl_set_mm		NULL
 
 #endif /* ETHTOOL_ENABLE_NETLINK */
 
diff --git a/netlink/mm.c b/netlink/mm.c
new file mode 100644
index 000000000000..42f309476ed7
--- /dev/null
+++ b/netlink/mm.c
@@ -0,0 +1,270 @@ 
+/*
+ * mm.c - netlink implementation of MAC merge layer settings
+ *
+ * Implementation of "ethtool --show-mm <dev>" and "ethtool --set-mm <dev> ..."
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "bitset.h"
+#include "parser.h"
+
+/* MM_GET */
+
+static const char *
+mm_verify_state_to_string(enum ethtool_mm_verify_status state)
+{
+	switch (state) {
+	case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
+		return "INITIAL";
+	case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
+		return "VERIFYING";
+	case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
+		return "SUCCEEDED";
+	case ETHTOOL_MM_VERIFY_STATUS_FAILED:
+		return "FAILED";
+	case ETHTOOL_MM_VERIFY_STATUS_DISABLED:
+		return "DISABLED";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static int show_mm_stats(const struct nlattr *nest)
+{
+	const struct nlattr *tb[ETHTOOL_A_MM_STAT_MAX + 1] = {};
+	DECLARE_ATTR_TB_INFO(tb);
+	static const struct {
+		unsigned int attr;
+		char *name;
+	} stats[] = {
+		{ ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS, "MACMergeFrameAssErrorCount" },
+		{ ETHTOOL_A_MM_STAT_SMD_ERRORS, "MACMergeFrameSmdErrorCount" },
+		{ ETHTOOL_A_MM_STAT_REASSEMBLY_OK, "MACMergeFrameAssOkCount" },
+		{ ETHTOOL_A_MM_STAT_RX_FRAG_COUNT, "MACMergeFragCountRx" },
+		{ ETHTOOL_A_MM_STAT_TX_FRAG_COUNT, "MACMergeFragCountTx" },
+		{ ETHTOOL_A_MM_STAT_HOLD_COUNT, "MACMergeHoldCount" },
+	};
+	bool header = false;
+	unsigned int i;
+	size_t n;
+	int ret;
+
+	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
+	if (ret < 0)
+		return ret;
+
+	open_json_object("statistics");
+	for (i = 0; i < ARRAY_SIZE(stats); i++) {
+		char fmt[64];
+
+		if (!tb[stats[i].attr])
+			continue;
+
+		if (!header && !is_json_context()) {
+			printf("Statistics:\n");
+			header = true;
+		}
+
+		if (mnl_attr_validate(tb[stats[i].attr], MNL_TYPE_U64)) {
+			fprintf(stderr, "malformed netlink message (statistic)\n");
+			goto err_close_stats;
+		}
+
+		n = snprintf(fmt, sizeof(fmt), "  %s: %%" PRIu64 "\n",
+			     stats[i].name);
+		if (n >= sizeof(fmt)) {
+			fprintf(stderr, "internal error - malformed label\n");
+			continue;
+		}
+
+		print_u64(PRINT_ANY, stats[i].name, fmt,
+			  mnl_attr_get_u64(tb[stats[i].attr]));
+	}
+	close_json_object();
+
+	return 0;
+
+err_close_stats:
+	close_json_object();
+	return -1;
+}
+
+int mm_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+	const struct nlattr *tb[ETHTOOL_A_MM_MAX + 1] = {};
+	struct nl_context *nlctx = data;
+	DECLARE_ATTR_TB_INFO(tb);
+	bool silent;
+	int err_ret;
+	int ret;
+
+	silent = nlctx->is_dump || nlctx->is_monitor;
+	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+	if (ret < 0)
+		return err_ret;
+	nlctx->devname = get_dev_name(tb[ETHTOOL_A_MM_HEADER]);
+	if (!dev_ok(nlctx))
+		return err_ret;
+
+	if (silent)
+		print_nl();
+
+	open_json_object(NULL);
+
+	print_string(PRINT_ANY, "ifname", "MAC Merge layer state for %s:\n",
+		     nlctx->devname);
+
+	show_bool("pmac-enabled", "pMAC enabled: %s\n",
+		  tb[ETHTOOL_A_MM_PMAC_ENABLED]);
+	show_bool("tx-enabled", "TX enabled: %s\n",
+		  tb[ETHTOOL_A_MM_TX_ENABLED]);
+	show_bool("tx-active", "TX active: %s\n", tb[ETHTOOL_A_MM_TX_ACTIVE]);
+	show_u32("tx-min-frag-size", "TX minimum fragment size: %u\n",
+		 tb[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE]);
+	show_u32("rx-min-frag-size", "RX minimum fragment size: %u\n",
+		 tb[ETHTOOL_A_MM_RX_MIN_FRAG_SIZE]);
+	show_bool("verify-enabled", "Verify enabled: %s\n",
+		  tb[ETHTOOL_A_MM_VERIFY_ENABLED]);
+	show_u32("verify-time", "Verify time: %u ms\n",
+		 tb[ETHTOOL_A_MM_VERIFY_TIME]);
+	show_u32("max-verify-time", "Max verify time: %u ms\n",
+		 tb[ETHTOOL_A_MM_MAX_VERIFY_TIME]);
+
+	if (tb[ETHTOOL_A_MM_VERIFY_STATUS]) {
+		u8 val = mnl_attr_get_u8(tb[ETHTOOL_A_MM_VERIFY_STATUS]);
+
+		print_string(PRINT_ANY, "verify-status", "Verification status: %s\n",
+			     mm_verify_state_to_string(val));
+	}
+
+	if (tb[ETHTOOL_A_MM_STATS]) {
+		ret = show_mm_stats(tb[ETHTOOL_A_MM_STATS]);
+		if (ret) {
+			fprintf(stderr, "Failed to print stats: %d\n", ret);
+			goto err;
+		}
+	}
+
+	if (!silent)
+		print_nl();
+
+	close_json_object();
+
+	return MNL_CB_OK;
+
+err:
+	close_json_object();
+	return err_ret;
+}
+
+int nl_get_mm(struct cmd_context *ctx)
+{
+	struct nl_context *nlctx = ctx->nlctx;
+	struct nl_socket *nlsk = nlctx->ethnl_socket;
+	u32 flags;
+	int ret;
+
+	if (netlink_cmd_check(ctx, ETHTOOL_MSG_MM_GET, true))
+		return -EOPNOTSUPP;
+	if (ctx->argc > 0) {
+		fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+			*ctx->argp);
+		return 1;
+	}
+
+	flags = get_stats_flag(nlctx, ETHTOOL_MSG_MM_GET, ETHTOOL_A_MM_HEADER);
+	ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_MM_GET,
+				      ETHTOOL_A_MM_HEADER, flags);
+	if (ret)
+		return ret;
+
+	new_json_obj(ctx->json);
+	ret = nlsock_send_get_request(nlsk, mm_reply_cb);
+	delete_json_obj();
+	return ret;
+}
+
+/* MM_SET */
+
+static const struct param_parser mm_set_params[] = {
+	{
+		.arg		= "verify-enabled",
+		.type		= ETHTOOL_A_MM_VERIFY_ENABLED,
+		.handler	= nl_parse_u8bool,
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "verify-time",
+		.type		= ETHTOOL_A_MM_VERIFY_TIME,
+		.handler	= nl_parse_direct_u32,
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "tx-enabled",
+		.type		= ETHTOOL_A_MM_TX_ENABLED,
+		.handler	= nl_parse_u8bool,
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "pmac-enabled",
+		.type		= ETHTOOL_A_MM_PMAC_ENABLED,
+		.handler	= nl_parse_u8bool,
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "tx-min-frag-size",
+		.type		= ETHTOOL_A_MM_TX_MIN_FRAG_SIZE,
+		.handler	= nl_parse_direct_u32,
+		.min_argc	= 1,
+	},
+	{}
+};
+
+int nl_set_mm(struct cmd_context *ctx)
+{
+	struct nl_context *nlctx = ctx->nlctx;
+	struct nl_msg_buff *msgbuff;
+	struct nl_socket *nlsk;
+	int ret;
+
+	if (netlink_cmd_check(ctx, ETHTOOL_MSG_MM_SET, false))
+		return -EOPNOTSUPP;
+
+	nlctx->cmd = "--set-mm";
+	nlctx->argp = ctx->argp;
+	nlctx->argc = ctx->argc;
+	nlctx->devname = ctx->devname;
+	nlsk = nlctx->ethnl_socket;
+	msgbuff = &nlsk->msgbuff;
+
+	ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_MM_SET,
+		       NLM_F_REQUEST | NLM_F_ACK);
+	if (ret)
+		return ret;
+
+	if (ethnla_fill_header(msgbuff, ETHTOOL_A_MM_HEADER,
+			       ctx->devname, 0))
+		return -EMSGSIZE;
+
+	ret = nl_parser(nlctx, mm_set_params, NULL, PARSER_GROUP_NONE, NULL);
+	if (ret)
+		return ret;
+
+	ret = nlsock_sendmsg(nlsk, NULL);
+	if (ret < 0)
+		return ret;
+
+	ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+	if (ret)
+		return nlctx->exit_code;
+
+	return 0;
+}
diff --git a/netlink/parser.c b/netlink/parser.c
index f982f229a040..d6090915ec48 100644
--- a/netlink/parser.c
+++ b/netlink/parser.c
@@ -37,7 +37,7 @@  static void parser_err_min_argc(struct nl_context *nlctx, unsigned int min_argc)
 			nlctx->cmd, nlctx->param, min_argc);
 }
 
-static void parser_err_invalid_value(struct nl_context *nlctx, const char *val)
+void parser_err_invalid_value(struct nl_context *nlctx, const char *val)
 {
 	fprintf(stderr, "ethtool (%s): invalid value '%s' for parameter '%s'\n",
 		nlctx->cmd, val, nlctx->param);
@@ -136,8 +136,8 @@  static int lookup_u32(const char *arg, uint32_t *result,
 	return -EINVAL;
 }
 
-static int lookup_u8(const char *arg, uint8_t *result,
-		     const struct lookup_entry_u8 *tbl)
+int lookup_u8(const char *arg, uint8_t *result,
+	      const struct lookup_entry_u8 *tbl)
 {
 	if (!arg)
 		return -EINVAL;
diff --git a/netlink/parser.h b/netlink/parser.h
index 8a4e8afa410b..a3c73138b9f2 100644
--- a/netlink/parser.h
+++ b/netlink/parser.h
@@ -99,6 +99,10 @@  struct char_bitset_parser_data {
 
 int parse_u32(const char *arg, uint32_t *result);
 
+int lookup_u8(const char *arg, uint8_t *result,
+	      const struct lookup_entry_u8 *tbl);
+void parser_err_invalid_value(struct nl_context *nlctx, const char *val);
+
 /* parser handlers to use as param_parser::handler */
 
 /* NLA_FLAG represented by on | off */