@@ -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 \
@@ -6096,6 +6096,20 @@ static const struct option args[] = {
.help = "Set transceiver module settings",
.xhelp = " [ power-mode-policy high|auto ]\n"
},
+ {
+ .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-disable on|off ]\n"
+ " [ enabled on|off ]\n"
+ " [ add-frag-size 0|1|2|3 ]\n"
+ },
{
.opts = "-h|--help",
.no_dev = true,
@@ -81,7 +81,7 @@ void open_json_array(const char *key, const char *str)
if (key)
jsonw_name(_jw, key);
jsonw_start_array(_jw);
- } else {
+ } else if (str) {
printf("%s", str);
}
}
@@ -93,7 +93,7 @@ void close_json_array(const char *delim)
{
if (is_json_context())
jsonw_end_array(_jw);
- else
+ else if (delim)
printf("%s", delim);
}
@@ -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_SUPPORTED),
+ 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_ADD_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);
@@ -47,6 +47,8 @@ int nl_gmodule(struct cmd_context *ctx);
int nl_smodule(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
int nl_getmodule(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);
@@ -114,6 +116,8 @@ nl_get_eeprom_page(struct cmd_context *ctx __maybe_unused,
#define nl_getmodule NULL
#define nl_gmodule NULL
#define nl_smodule NULL
+#define nl_get_mm NULL
+#define nl_set_mm NULL
#endif /* ETHTOOL_ENABLE_NETLINK */
new file mode 100644
@@ -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("supported", "MAC merge supported: %s\n",
+ tb[ETHTOOL_A_MM_SUPPORTED]);
+ 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_json("add-frag-size", "addFragSize: %u\n",
+ tb[ETHTOOL_A_MM_ADD_FRAG_SIZE]);
+ show_bool("verify-enabled", "Verify enabled: %s\n",
+ tb[ETHTOOL_A_MM_VERIFY_ENABLED]);
+ show_u32_json("verify-time", "Verify time: %u ms\n",
+ tb[ETHTOOL_A_MM_VERIFY_TIME]);
+ show_u32_json("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 = "add-frag-size",
+ .type = ETHTOOL_A_MM_ADD_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;
+}
@@ -100,6 +100,40 @@ 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_u8_val(const char *key, const char *fmt, uint8_t *val)
+{
+ if (is_json_context()) {
+ if (val)
+ print_uint(PRINT_JSON, key, NULL, *val);
+ } else {
+ if (val)
+ print_uint(PRINT_FP, NULL, fmt, *val);
+ }
+}
+
+static inline void show_u8(const char *key, const char *fmt,
+ const struct nlattr *attr)
+{
+ show_u8_val(key, fmt, attr ? mnl_attr_get_payload(attr) : NULL);
+}
+
+static inline void show_u32_val(const char *key, const char *fmt, uint32_t *val)
+{
+ if (is_json_context()) {
+ if (val)
+ print_uint(PRINT_JSON, key, NULL, *val);
+ } else {
+ if (val)
+ print_uint(PRINT_FP, NULL, fmt, *val);
+ }
+}
+
+static inline void show_u32_json(const char *key, const char *fmt,
+ const struct nlattr *attr)
+{
+ show_u32_val(key, fmt, attr ? mnl_attr_get_payload(attr) : NULL);
+}
+
static inline void show_u32(const char *key,
const char *fmt,
const struct nlattr *attr)
@@ -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;
@@ -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 */
$ ethtool --include-statistics --show-mm eno0 MAC merge layer state for eno0: MAC merge supported: on pMAC enabled: on TX enabled: on TX active: on addFragSize: 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", "supported": true, "pmac-enabled": true, "tx-enabled": true, "tx-active": true, "add-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> --- Makefile.am | 2 +- ethtool.c | 14 +++ json_print.c | 4 +- netlink/desc-ethtool.c | 29 +++++ netlink/extapi.h | 4 + netlink/mm.c | 270 +++++++++++++++++++++++++++++++++++++++++ netlink/netlink.h | 34 ++++++ netlink/parser.c | 6 +- netlink/parser.h | 4 + 9 files changed, 361 insertions(+), 6 deletions(-) create mode 100644 netlink/mm.c