/* * tsinfo.c - netlink implementation of timestamping commands * * Implementation of "ethtool -T " */ #include #include #include #include #include "../internal.h" #include "../common.h" #include "netlink.h" #include "bitset.h" /* TSINFO_GET */ static int tsinfo_show_stats(const struct nlattr *nest) { const struct nlattr *tb[ETHTOOL_A_TS_STAT_MAX + 1] = {}; DECLARE_ATTR_TB_INFO(tb); static const struct { unsigned int attr; char *name; } stats[] = { { ETHTOOL_A_TS_STAT_TX_PKTS, "tx_pkts" }, { ETHTOOL_A_TS_STAT_TX_LOST, "tx_lost" }, { ETHTOOL_A_TS_STAT_TX_ERR, "tx_err" }, }; bool header = false; unsigned int i; __u64 val; 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_U32)) { val = mnl_attr_get_u32(tb[stats[i].attr]); } else if (!mnl_attr_validate(tb[stats[i].attr], MNL_TYPE_U64)) { val = mnl_attr_get_u64(tb[stats[i].attr]); } else { fprintf(stderr, "malformed netlink message (statistic)\n"); goto err_close_stats; } snprintf(fmt, sizeof(fmt), " %s: %%" PRIu64 "\n", stats[i].name); print_u64(PRINT_ANY, stats[i].name, fmt, val); } close_json_object(); return 0; err_close_stats: close_json_object(); return -1; } static void tsinfo_dump_cb(unsigned int idx, const char *name, bool val, void *data __maybe_unused) { if (!val) return; if (name) printf("\t%s\n", name); else printf("\tbit%u\n", idx); } static int tsinfo_dump_list(struct nl_context *nlctx, const struct nlattr *attr, const char *label, const char *if_empty, unsigned int stringset_id) { const struct stringset *strings = NULL; int ret; printf("%s:", label); ret = 0; if (!attr || bitset_is_empty(attr, false, &ret)) { printf("%s\n", if_empty); return ret; } putchar('\n'); if (ret < 0) return ret; if (bitset_is_compact(attr)) { ret = netlink_init_ethnl2_socket(nlctx); if (ret < 0) return ret; strings = global_stringset(stringset_id, nlctx->ethnl2_socket); } return walk_bitset(attr, strings, tsinfo_dump_cb, NULL); } int tsinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data) { const struct nlattr *tb[ETHTOOL_A_TSINFO_MAX + 1] = {}; DECLARE_ATTR_TB_INFO(tb); struct nl_context *nlctx = data; bool silent; int err_ret; int ret; silent = nlctx->is_dump; 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_TSINFO_HEADER]); if (!dev_ok(nlctx)) return err_ret; if (silent) putchar('\n'); printf("Time stamping parameters for %s:\n", nlctx->devname); ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TIMESTAMPING], "Capabilities", "", ETH_SS_SOF_TIMESTAMPING); if (ret < 0) return err_ret; printf("PTP Hardware Clock: "); if (tb[ETHTOOL_A_TSINFO_PHC_INDEX]) printf("%d\n", mnl_attr_get_u32(tb[ETHTOOL_A_TSINFO_PHC_INDEX])); else printf("none\n"); ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TX_TYPES], "Hardware Transmit Timestamp Modes", " none", ETH_SS_TS_TX_TYPES); if (ret < 0) return err_ret; ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_RX_FILTERS], "Hardware Receive Filter Modes", " none", ETH_SS_TS_RX_FILTERS); if (ret < 0) return err_ret; if (tb[ETHTOOL_A_TSINFO_STATS]) { ret = tsinfo_show_stats(tb[ETHTOOL_A_TSINFO_STATS]); if (ret < 0) return err_ret; } return MNL_CB_OK; } int nl_tsinfo(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_TSINFO_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_TSINFO_GET, ETHTOOL_A_TSINFO_HEADER); ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TSINFO_GET, ETHTOOL_A_TSINFO_HEADER, flags); if (ret < 0) return ret; return nlsock_send_get_request(nlsk, tsinfo_reply_cb); }