Message ID | 20200915235259.457050-6-kuba@kernel.org |
---|---|
State | Superseded |
Headers | show |
Series | [ethtool-next,1/5] update UAPI header copies | expand |
On Tue, Sep 15, 2020 at 04:52:59PM -0700, Jakub Kicinski wrote: > Add support for requesting pause frame stats from the kernel. > > # ./ethtool -I -a eth0 > Pause parameters for eth0: > Autonegotiate: on > RX: on > TX: on > Statistics: > tx_pause_frames: 1 > rx_pause_frames: 1 > > # ./ethtool -I --json -a eth0 > [ { > "ifname": "eth0", > "autonegotiate": true, > "rx": true, > "tx": true, > "statistics": { > "tx_pause_frames": 1, > "rx_pause_frames": 1 > } > } ] > > Signed-off-by: Jakub Kicinski <kuba@kernel.org> > --- > netlink/pause.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 65 insertions(+), 1 deletion(-) > > diff --git a/netlink/pause.c b/netlink/pause.c > index 30ecdccb15eb..f9dec9fe887a 100644 > --- a/netlink/pause.c > +++ b/netlink/pause.c > @@ -5,6 +5,7 @@ > */ > > #include <errno.h> > +#include <inttypes.h> > #include <string.h> > #include <stdio.h> > > @@ -110,6 +111,62 @@ static int show_pause_autoneg_status(struct nl_context *nlctx) > return ret; > } > > +static int show_pause_stats(const struct nlattr *nest) > +{ > + const struct nlattr *tb[ETHTOOL_A_PAUSE_STAT_MAX + 1] = {}; > + DECLARE_ATTR_TB_INFO(tb); > + static const struct { > + unsigned int attr; > + char *name; > + } stats[] = { > + { ETHTOOL_A_PAUSE_STAT_TX_FRAMES, "tx_pause_frames" }, > + { ETHTOOL_A_PAUSE_STAT_RX_FRAMES, "rx_pause_frames" }, > + }; > + 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[32]; > + > + 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: %%" PRId64 "\n", > + stats[i].name); The stats are unsigned so the format should be PRIu64 here. > + if (n >= sizeof(fmt)) { > + fprintf(stderr, "internal error - malformed label\n"); > + goto err_close_stats; > + } > + > + 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 pause_reply_cb(const struct nlmsghdr *nlhdr, void *data) > { > const struct nlattr *tb[ETHTOOL_A_PAUSE_MAX + 1] = {}; > @@ -147,6 +204,11 @@ int pause_reply_cb(const struct nlmsghdr *nlhdr, void *data) > if (ret < 0) > goto err_close_dev; > } > + if (tb[ETHTOOL_A_PAUSE_STATS]) { > + ret = show_pause_stats(tb[ETHTOOL_A_PAUSE_STATS]); > + if (ret < 0) > + goto err_close_dev; > + } > if (!silent) > print_nl(); > > @@ -163,6 +225,7 @@ int nl_gpause(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_PAUSE_GET, true)) > @@ -173,8 +236,9 @@ int nl_gpause(struct cmd_context *ctx) > return 1; > } > > + flags = nlctx->ctx->show_stats ? ETHTOOL_FLAG_STATS : 0; > ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PAUSE_GET, > - ETHTOOL_A_PAUSE_HEADER, 0); > + ETHTOOL_A_PAUSE_HEADER, flags); > if (ret < 0) > return ret; > When the stats are supported by kernel but not provided by a device, the request will succeed and usual output without stats will be shown. However, when stats are requested on a pre-5.10 kernel not recognizing ETHTOOL_FLAG_STATS, the request will fail: mike@lion:~/work/git/ethtool> ./ethtool --debug 0x10 -I -a eth0 netlink error: unrecognized request flags netlink error: Operation not supported offending message and attribute: ETHTOOL_MSG_PAUSE_GET ETHTOOL_A_PAUSE_HEADER ETHTOOL_A_HEADER_DEV_NAME = "eth0" ===> ETHTOOL_A_HEADER_FLAGS = 0x00000004 We should probably repeat the request with flags=0 in this case but that would require keeping the offset of ETHTOOL_A_HEADER_FLAGS attribute and checking for -EOPNOTSUPP with this offset in nlsock_process_ack(). Michal
On Thu, 24 Sep 2020 00:45:10 +0200 Michal Kubecek wrote: > > + 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: %%" PRId64 "\n", > > + stats[i].name); > > The stats are unsigned so the format should be PRIu64 here. Good catch. > > @@ -173,8 +236,9 @@ int nl_gpause(struct cmd_context *ctx) > > return 1; > > } > > > > + flags = nlctx->ctx->show_stats ? ETHTOOL_FLAG_STATS : 0; > > ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PAUSE_GET, > > - ETHTOOL_A_PAUSE_HEADER, 0); > > + ETHTOOL_A_PAUSE_HEADER, flags); > > if (ret < 0) > > return ret; > > > > When the stats are supported by kernel but not provided by a device, > the request will succeed and usual output without stats will be shown. > However, when stats are requested on a pre-5.10 kernel not recognizing > ETHTOOL_FLAG_STATS, the request will fail: > > mike@lion:~/work/git/ethtool> ./ethtool --debug 0x10 -I -a eth0 > netlink error: unrecognized request flags > netlink error: Operation not supported > offending message and attribute: > ETHTOOL_MSG_PAUSE_GET > ETHTOOL_A_PAUSE_HEADER > ETHTOOL_A_HEADER_DEV_NAME = "eth0" > ===> ETHTOOL_A_HEADER_FLAGS = 0x00000004 > > We should probably repeat the request with flags=0 in this case but that > would require keeping the offset of ETHTOOL_A_HEADER_FLAGS attribute and > checking for -EOPNOTSUPP with this offset in nlsock_process_ack(). Makes sense, will do.
diff --git a/netlink/pause.c b/netlink/pause.c index 30ecdccb15eb..f9dec9fe887a 100644 --- a/netlink/pause.c +++ b/netlink/pause.c @@ -5,6 +5,7 @@ */ #include <errno.h> +#include <inttypes.h> #include <string.h> #include <stdio.h> @@ -110,6 +111,62 @@ static int show_pause_autoneg_status(struct nl_context *nlctx) return ret; } +static int show_pause_stats(const struct nlattr *nest) +{ + const struct nlattr *tb[ETHTOOL_A_PAUSE_STAT_MAX + 1] = {}; + DECLARE_ATTR_TB_INFO(tb); + static const struct { + unsigned int attr; + char *name; + } stats[] = { + { ETHTOOL_A_PAUSE_STAT_TX_FRAMES, "tx_pause_frames" }, + { ETHTOOL_A_PAUSE_STAT_RX_FRAMES, "rx_pause_frames" }, + }; + 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[32]; + + 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: %%" PRId64 "\n", + stats[i].name); + if (n >= sizeof(fmt)) { + fprintf(stderr, "internal error - malformed label\n"); + goto err_close_stats; + } + + 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 pause_reply_cb(const struct nlmsghdr *nlhdr, void *data) { const struct nlattr *tb[ETHTOOL_A_PAUSE_MAX + 1] = {}; @@ -147,6 +204,11 @@ int pause_reply_cb(const struct nlmsghdr *nlhdr, void *data) if (ret < 0) goto err_close_dev; } + if (tb[ETHTOOL_A_PAUSE_STATS]) { + ret = show_pause_stats(tb[ETHTOOL_A_PAUSE_STATS]); + if (ret < 0) + goto err_close_dev; + } if (!silent) print_nl(); @@ -163,6 +225,7 @@ int nl_gpause(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_PAUSE_GET, true)) @@ -173,8 +236,9 @@ int nl_gpause(struct cmd_context *ctx) return 1; } + flags = nlctx->ctx->show_stats ? ETHTOOL_FLAG_STATS : 0; ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PAUSE_GET, - ETHTOOL_A_PAUSE_HEADER, 0); + ETHTOOL_A_PAUSE_HEADER, flags); if (ret < 0) return ret;
Add support for requesting pause frame stats from the kernel. # ./ethtool -I -a eth0 Pause parameters for eth0: Autonegotiate: on RX: on TX: on Statistics: tx_pause_frames: 1 rx_pause_frames: 1 # ./ethtool -I --json -a eth0 [ { "ifname": "eth0", "autonegotiate": true, "rx": true, "tx": true, "statistics": { "tx_pause_frames": 1, "rx_pause_frames": 1 } } ] Signed-off-by: Jakub Kicinski <kuba@kernel.org> --- netlink/pause.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-)