From patchwork Wed Apr 14 20:23:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Kicinski X-Patchwork-Id: 421532 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A4D1EC433ED for ; Wed, 14 Apr 2021 20:24:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 869686044F for ; Wed, 14 Apr 2021 20:24:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350044AbhDNUY5 (ORCPT ); Wed, 14 Apr 2021 16:24:57 -0400 Received: from mail.kernel.org ([198.145.29.99]:49308 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1348617AbhDNUXu (ORCPT ); Wed, 14 Apr 2021 16:23:50 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 00B8361177; Wed, 14 Apr 2021 20:23:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1618431809; bh=kGhJclpsgxQPTXBZhdlBlJuKmjSTKksx4UOyUrAzjYY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F+j7RoADAAiGgVRDUIkjIzlnJM4MIJ9zZqMStX71VlOqzj76kmw7ExD+s5Zd5AyXG l94v48fWbSK0eVYhuWo9hE9V3sQfgOzqZftDZBeVlMQpt3svIzVx2hQdcPhkkW7/kV qL0QCJuZJcpNjZvFCUOO91L60gEoqcz0pBX01pNxYfPu74nEukFIH8gnClO2KRO1nV UKW9xY0CkKrvcFL0pcEVN6hHRmrRCaowfOdlHsKVY4ko20UfRhsSn2tYcSSy2N9AS+ ybcFwuWEGHsFW9NiX2TVNHCVh87lXUuktvDA+eSWE7DuiBMGo36k+YBFUitGmDSFzT vJ4qrn3leFYiA== From: Jakub Kicinski To: netdev@vger.kernel.org Cc: davem@davemloft.net, andrew@lunn.ch, mkubecek@suse.cz, idosch@nvidia.com, Jakub Kicinski Subject: [RFC net-next 1/6] docs: networking: extend the statistics documentation Date: Wed, 14 Apr 2021 13:23:20 -0700 Message-Id: <20210414202325.2225774-2-kuba@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210414202325.2225774-1-kuba@kernel.org> References: <20210414202325.2225774-1-kuba@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Make the lack of expectations for switching NICs explicit, describe the new stats. Signed-off-by: Jakub Kicinski --- Documentation/networking/statistics.rst | 44 +++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/statistics.rst b/Documentation/networking/statistics.rst index b748fe44ee02..5aaa66bba971 100644 --- a/Documentation/networking/statistics.rst +++ b/Documentation/networking/statistics.rst @@ -44,8 +44,27 @@ If `-s` is specified once the detailed errors won't be shown. Protocol-specific statistics ---------------------------- -Some of the interfaces used for configuring devices are also able -to report related statistics. For example ethtool interface used +Protocol-specific statistics are exposed via relevant interfaces, +the same interfaces used to configure them. + +ethtool +~~~~~~~ + +Ethtool exposes common low-level statistics. +All the standard statistics are expected to be maintained +by the device, not the driver (as opposed to driver-defined stats +described in the next section which mix software and hardware stats). +For devices which contain unmanaged +switches (e.g. legacy SR-IOV or multi-host NICs) the events counted +may not pertain exclusively to the packets destined to +the local host interface. In other words the events may +be counted at the network port (MAC/PHY blocks) without separation +for different host side (PCIe) devices. Such ambiguity must not +be present when internal switch is managed by Linux (so called +switchdev mode for NICs). + +Standard ethtool statistics can be accessed via the interfaces used +for configuration. For example ethtool interface used to configure pause frames can report corresponding hardware counters:: $ ethtool --include-statistics -a eth0 @@ -57,6 +76,27 @@ to report related statistics. For example ethtool interface used tx_pause_frames: 1 rx_pause_frames: 1 +General Ethernet statistics not associated with any particular +functionality are exposed via ``ethtool -S $ifc`` by specifying +the ``--groups`` parameter:: + + $ ethtool -S eth0 --groups eth-phy eth-mac eth-ctrl rmon + Stats for eth0: + eth-phy-SymbolErrorDuringCarrier: 0 + eth-mac-FramesTransmittedOK: 1 + eth-mac-FrameTooLongErrors: 1 + eth-ctrl-MACControlFramesTransmitted: 1 + eth-ctrl-MACControlFramesReceived: 0 + eth-ctrl-UnsupportedOpcodesReceived: 1 + rmon-etherStatsUndersizePkts: 1 + rmon-etherStatsJabbers: 0 + rmon-rx-etherStatsPkts64Octets: 1 + rmon-rx-etherStatsPkts65to127Octets: 0 + rmon-rx-etherStatsPkts128to255Octets: 0 + rmon-tx-etherStatsPkts64Octets: 2 + rmon-tx-etherStatsPkts65to127Octets: 3 + rmon-tx-etherStatsPkts128to255Octets: 0 + Driver-defined statistics ------------------------- From patchwork Wed Apr 14 20:23:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Kicinski X-Patchwork-Id: 421533 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8EF6EC433B4 for ; Wed, 14 Apr 2021 20:24:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6E14E61177 for ; Wed, 14 Apr 2021 20:24:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349773AbhDNUYr (ORCPT ); Wed, 14 Apr 2021 16:24:47 -0400 Received: from mail.kernel.org ([198.145.29.99]:49330 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349684AbhDNUXw (ORCPT ); Wed, 14 Apr 2021 16:23:52 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id B8030611CC; Wed, 14 Apr 2021 20:23:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1618431810; bh=OYt77F5KOVIW/3yiH6usOqylE7bkzlY4jW/97q2yK0k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=KcCQDWq2RGXmU8AKENA10x2qiVWnkg5Drp/TR4Aw7Wn1Kj5C6gFadVceCi+o28U7b jOuAluoQD8jC1h6lcNwN4ypFcoelRV/rkIQGoCLztb/Z1ONnmYXT5BGPQWvWl4IqFm bxWM1t5Qb7SARb6lWs/WhCVmq2WcdYeOs9zfFg4P8ITrTVodIqc3yem0lV0ivPAurr XUj2CosufJybo4bwpKb0YEGhioEo354Gq1cv7C2nzbqDqSN/EwaT3GB9zBvn4TafnS Uh0DTT/OJd/Z4LbwarIU1kmwgykG6zWo6yhbNTh5Qs8fcye85G13n3/q2LtAv3IIiO kh6wO0hB+KpxA== From: Jakub Kicinski To: netdev@vger.kernel.org Cc: davem@davemloft.net, andrew@lunn.ch, mkubecek@suse.cz, idosch@nvidia.com, Jakub Kicinski Subject: [RFC net-next 3/6] ethtool: add a new command for reading standard stats Date: Wed, 14 Apr 2021 13:23:22 -0700 Message-Id: <20210414202325.2225774-4-kuba@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210414202325.2225774-1-kuba@kernel.org> References: <20210414202325.2225774-1-kuba@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add an interface for reading standard stats, including stats which don't have a corresponding control interface. Start with IEEE 802.3 PHY stats. There seems to be only one stat to expose there. Define API to not require user space changes when new stats or groups are added. Groups are based on bitset, stats have a string set associated. Place the stats directly in the group nest. This halves the space, and while we could go for more encapsulations it seems unlikely to be needed in practice. Signed-off-by: Jakub Kicinski --- include/linux/ethtool.h | 10 ++ include/uapi/linux/ethtool.h | 4 + include/uapi/linux/ethtool_netlink.h | 44 +++++++ net/ethtool/Makefile | 2 +- net/ethtool/netlink.c | 10 ++ net/ethtool/netlink.h | 5 + net/ethtool/stats.c | 166 +++++++++++++++++++++++++++ net/ethtool/strset.c | 10 ++ 8 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 net/ethtool/stats.c diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 112a85b57f1f..2d5455eedbf4 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -250,6 +250,13 @@ static inline void ethtool_stats_init(u64 *stats, unsigned int n) stats[n] = ETHTOOL_STAT_NOT_SET; } +/* Basic IEEE 802.3 PHY statistics (30.3.2.1.*), not otherwise exposed + * via a more targeted API. + */ +struct ethtool_eth_phy_stats { + u64 SymbolErrorDuringCarrier; +}; + /** * struct ethtool_pause_stats - statistics for IEEE 802.3x pause frames * @tx_pause_frames: transmitted pause frame count. Reported to user space @@ -487,6 +494,7 @@ struct ethtool_module_eeprom { * @get_module_eeprom_by_page: Get a region of plug-in module EEPROM data from * specified page. Returns a negative error code or the amount of bytes * read. + * @get_eth_phy_stats: Query some of the IEEE 802.3 PHY statistics. * * All operations are optional (i.e. the function pointer may be set * to %NULL) and callers must take this into account. Callers must @@ -597,6 +605,8 @@ struct ethtool_ops { int (*get_module_eeprom_by_page)(struct net_device *dev, const struct ethtool_module_eeprom *page, struct netlink_ext_ack *extack); + void (*get_eth_phy_stats)(struct net_device *dev, + struct ethtool_eth_phy_stats *phy_stats); }; int ethtool_check_ops(const struct ethtool_ops *ops); diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index f91e079e3108..190ae6e03918 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -669,6 +669,8 @@ enum ethtool_link_ext_substate_cable_issue { * @ETH_SS_TS_TX_TYPES: timestamping Tx types * @ETH_SS_TS_RX_FILTERS: timestamping Rx filters * @ETH_SS_UDP_TUNNEL_TYPES: UDP tunnel types + * @ETH_SS_STATS_STD: standardized stats + * @ETH_SS_STATS_ETH_PHY: names of IEEE 802.3 PHY statistics * * @ETH_SS_COUNT: number of defined string sets */ @@ -689,6 +691,8 @@ enum ethtool_stringset { ETH_SS_TS_TX_TYPES, ETH_SS_TS_RX_FILTERS, ETH_SS_UDP_TUNNEL_TYPES, + ETH_SS_STATS_STD, + ETH_SS_STATS_ETH_PHY, /* add new constants above here */ ETH_SS_COUNT diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 3a2b31ccbc5b..e6a473a3a5f1 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -45,6 +45,7 @@ enum { ETHTOOL_MSG_FEC_GET, ETHTOOL_MSG_FEC_SET, ETHTOOL_MSG_MODULE_EEPROM_GET, + ETHTOOL_MSG_STATS_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -86,6 +87,7 @@ enum { ETHTOOL_MSG_FEC_GET_REPLY, ETHTOOL_MSG_FEC_NTF, ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY, + ETHTOOL_MSG_STATS_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -679,6 +681,48 @@ enum { ETHTOOL_A_MODULE_EEPROM_MAX = (__ETHTOOL_A_MODULE_EEPROM_CNT - 1) }; +/* STATS */ + +enum { + ETHTOOL_A_STATS_UNSPEC, + ETHTOOL_A_STATS_PAD, + ETHTOOL_A_STATS_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_STATS_GROUPS, /* bitset */ + + ETHTOOL_A_STATS_GRP, /* nest - _A_STATS_GRP_* */ + + /* add new constants above here */ + __ETHTOOL_A_STATS_CNT, + ETHTOOL_A_STATS_MAX = (__ETHTOOL_A_STATS_CNT - 1) +}; + +enum { + ETHTOOL_STATS_ETH_PHY, + + /* add new constants above here */ + __ETHTOOL_STATS_CNT +}; + +enum { + ETHTOOL_A_STATS_GRP_UNSPEC, + ETHTOOL_A_STATS_GRP_PAD, + + ETHTOOL_A_STATS_GRP_ID, /* u32 */ + ETHTOOL_A_STATS_GRP_SS_ID, /* u32 */ + + /* add new constants above here */ + ETHTOOL_A_STATS_GRP_FIRST_ATTR = 64, +}; + +enum { + /* 30.3.2.1.5 aSymbolErrorDuringCarrier */ + ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR = ETHTOOL_A_STATS_GRP_FIRST_ATTR, + + /* add new constants above here */ + __ETHTOOL_A_STATS_ETH_PHY_CNT, + ETHTOOL_A_STATS_ETH_PHY_MAX = (__ETHTOOL_A_STATS_ETH_PHY_CNT - 1) +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 83842685fd8c..723c9a8a8cdf 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ linkstate.o debug.o wol.o features.o privflags.o rings.o \ channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \ - tunnels.o fec.o eeprom.o + tunnels.o fec.o eeprom.o stats.o diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 5f5d7c4b3d4a..290012d0d11d 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -247,6 +247,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_FEC_GET] = ðnl_fec_request_ops, [ETHTOOL_MSG_TSINFO_GET] = ðnl_tsinfo_request_ops, [ETHTOOL_MSG_MODULE_EEPROM_GET] = ðnl_module_eeprom_request_ops, + [ETHTOOL_MSG_STATS_GET] = ðnl_stats_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -942,6 +943,15 @@ static const struct genl_ops ethtool_genl_ops[] = { .policy = ethnl_module_eeprom_get_policy, .maxattr = ARRAY_SIZE(ethnl_module_eeprom_get_policy) - 1, }, + { + .cmd = ETHTOOL_MSG_STATS_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + .policy = ethnl_stats_get_policy, + .maxattr = ARRAY_SIZE(ethnl_stats_get_policy) - 1, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 3f93b3c41c31..79631792313e 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -346,6 +346,7 @@ extern const struct ethnl_request_ops ethnl_eee_request_ops; extern const struct ethnl_request_ops ethnl_tsinfo_request_ops; extern const struct ethnl_request_ops ethnl_fec_request_ops; extern const struct ethnl_request_ops ethnl_module_eeprom_request_ops; +extern const struct ethnl_request_ops ethnl_stats_request_ops; extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1]; extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1]; @@ -380,6 +381,7 @@ extern const struct nla_policy ethnl_tunnel_info_get_policy[ETHTOOL_A_TUNNEL_INF extern const struct nla_policy ethnl_fec_get_policy[ETHTOOL_A_FEC_HEADER + 1]; extern const struct nla_policy ethnl_fec_set_policy[ETHTOOL_A_FEC_AUTO + 1]; extern const struct nla_policy ethnl_module_eeprom_get_policy[ETHTOOL_A_MODULE_EEPROM_DATA + 1]; +extern const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1]; int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); @@ -399,4 +401,7 @@ int ethnl_tunnel_info_start(struct netlink_callback *cb); int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb); int ethnl_set_fec(struct sk_buff *skb, struct genl_info *info); +extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; +extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN]; + #endif /* _NET_ETHTOOL_NETLINK_H */ diff --git a/net/ethtool/stats.c b/net/ethtool/stats.c new file mode 100644 index 000000000000..68bf6a7614fe --- /dev/null +++ b/net/ethtool/stats.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" +#include "bitset.h" + +struct stats_req_info { + struct ethnl_req_info base; + DECLARE_BITMAP(stat_mask, __ETHTOOL_STATS_CNT); +}; + +#define STATS_REQINFO(__req_base) \ + container_of(__req_base, struct stats_req_info, base) + +struct stats_reply_data { + struct ethnl_reply_data base; + struct ethtool_eth_phy_stats phy_stats; +}; + +#define STATS_REPDATA(__reply_base) \ + container_of(__reply_base, struct stats_reply_data, base) + +const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = { + [ETHTOOL_STATS_ETH_PHY] = "eth-phy", +}; + +const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN] = { + [ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR] = "SymbolErrorDuringCarrier", +}; + +const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = { + [ETHTOOL_A_STATS_HEADER] = + NLA_POLICY_NESTED(ethnl_header_policy), + [ETHTOOL_A_STATS_GROUPS] = { .type = NLA_NESTED }, +}; + +static int stats_parse_request(struct ethnl_req_info *req_base, + struct nlattr **tb, + struct netlink_ext_ack *extack) +{ + struct stats_req_info *req_info = STATS_REQINFO(req_base); + bool mod = false; + int err; + + err = ethnl_update_bitset(req_info->stat_mask, __ETHTOOL_STATS_CNT, + tb[ETHTOOL_A_STATS_GROUPS], stats_std_names, + extack, &mod); + if (err) + return err; + + if (!mod) { + NL_SET_ERR_MSG(extack, "no stats requested"); + return -EINVAL; + } + + return 0; +} + +static int stats_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + struct genl_info *info) +{ + const struct stats_req_info *req_info = STATS_REQINFO(req_base); + struct stats_reply_data *data = STATS_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + int ret; + + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; + + memset(&data->phy_stats, 0xff, sizeof(data->phy_stats)); + + if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) && + dev->ethtool_ops->get_eth_phy_stats) + dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats); + + ethnl_ops_complete(dev); + return 0; +} + +static int stats_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct stats_req_info *req_info = STATS_REQINFO(req_base); + int len = 0; + + if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) + len += nla_total_size(0) + + sizeof(struct ethtool_eth_phy_stats) / sizeof(u64) * + nla_total_size_64bit(sizeof(u64)); + + return len; +} + +static int stat_put(struct sk_buff *skb, u16 attrtype, u64 val) +{ + if (val == ETHTOOL_STAT_NOT_SET) + return 0; + return nla_put_u64_64bit(skb, attrtype, val, ETHTOOL_A_STATS_GRP_PAD); +} + +static int stats_put_phy_stats(struct sk_buff *skb, + const struct stats_reply_data *data) +{ + if (stat_put(skb, ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR, + data->phy_stats.SymbolErrorDuringCarrier)) + return -EMSGSIZE; + return 0; +} + +static int stats_put_stats(struct sk_buff *skb, + const struct stats_reply_data *data, + u32 id, u32 ss_id, + int (*cb)(struct sk_buff *skb, + const struct stats_reply_data *data)) +{ + struct nlattr *nest; + + nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP); + if (!nest) + return -EMSGSIZE; + + if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_ID, id) || + nla_put_u32(skb, ETHTOOL_A_STATS_GRP_SS_ID, ss_id)) + goto err_cancel; + + if (cb(skb, data)) + goto err_cancel; + + nla_nest_end(skb, nest); + return 0; + +err_cancel: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static int stats_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct stats_req_info *req_info = STATS_REQINFO(req_base); + const struct stats_reply_data *data = STATS_REPDATA(reply_base); + int ret = 0; + + if (!ret && test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) + ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_PHY, + ETH_SS_STATS_ETH_PHY, + stats_put_phy_stats); + + return ret; +} + +const struct ethnl_request_ops ethnl_stats_request_ops = { + .request_cmd = ETHTOOL_MSG_STATS_GET, + .reply_cmd = ETHTOOL_MSG_STATS_GET_REPLY, + .hdr_attr = ETHTOOL_A_STATS_HEADER, + .req_info_size = sizeof(struct stats_req_info), + .reply_data_size = sizeof(struct stats_reply_data), + + .parse_request = stats_parse_request, + .prepare_data = stats_prepare_data, + .reply_size = stats_reply_size, + .fill_reply = stats_fill_reply, +}; diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c index c3a5489964cd..5f3c73587ff4 100644 --- a/net/ethtool/strset.c +++ b/net/ethtool/strset.c @@ -80,6 +80,16 @@ static const struct strset_info info_template[] = { .count = __ETHTOOL_UDP_TUNNEL_TYPE_CNT, .strings = udp_tunnel_type_names, }, + [ETH_SS_STATS_STD] = { + .per_dev = false, + .count = __ETHTOOL_STATS_CNT, + .strings = stats_std_names, + }, + [ETH_SS_STATS_ETH_PHY] = { + .per_dev = false, + .count = __ETHTOOL_A_STATS_ETH_PHY_CNT, + .strings = stats_eth_phy_names, + }, }; struct strset_req_info { From patchwork Wed Apr 14 20:23:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Kicinski X-Patchwork-Id: 421534 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, INCLUDES_CR_TRAILER, INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6B500C433ED for ; Wed, 14 Apr 2021 20:24:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 49DDB61158 for ; Wed, 14 Apr 2021 20:24:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349369AbhDNUYb (ORCPT ); Wed, 14 Apr 2021 16:24:31 -0400 Received: from mail.kernel.org ([198.145.29.99]:49374 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1349716AbhDNUXy (ORCPT ); Wed, 14 Apr 2021 16:23:54 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id EB5E96124B; Wed, 14 Apr 2021 20:23:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1618431811; bh=1MGgFaQy/moIhtFx6XGcVWLaWfWApTWv2HuMkcVPJtw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uRuPbu6L+/J3yAhym/cFsawJzIdbvibT+4HFVPUwsyqU6Zk2ar66sKMLJ/EGKh4r1 gERP3R/wnvh5b8elLdY1qsl2IYKH6h/ABes+KDfgX8/oYN67BRCqCkjPQVd+PS5hZz 8RqewX/jC75Tf/RPyGvE/QLDhScA84eYgSol+Pf2L+B44vh4HBTu7QdAQy+mQ50JsG ZIwExPB9rZmVOXF47Ej77kKNCT2cQwmk/O9bwp+r05074O1HOu7Z+e0pzD2M4EoAER GEmIhHLLPzyhImcSpPVBSNfdIwVoTgHHLWcxCtjegiMYxyt8h1PJvLsEKHpMx4ignH dEWONZGXB56Qg== From: Jakub Kicinski To: netdev@vger.kernel.org Cc: davem@davemloft.net, andrew@lunn.ch, mkubecek@suse.cz, idosch@nvidia.com, Jakub Kicinski Subject: [RFC net-next 6/6] ethtool: add interface to read RMON stats Date: Wed, 14 Apr 2021 13:23:25 -0700 Message-Id: <20210414202325.2225774-7-kuba@kernel.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210414202325.2225774-1-kuba@kernel.org> References: <20210414202325.2225774-1-kuba@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Most devices maintain RMON (RFC 2819) stats - particularly the "histogram" of packets received by size. Unlike other RFCs which duplicate IEEE stats, the short/oversized frame counters in RMON don't seem to match IEEE stats 1-to-1 either, so expose those, too. Do not expose basic packet, CRC errors etc - those are already otherwise covered. Because standard defines packet ranges only up to 1518, and everything above that should theoretically be "oversized" - devices often create their own ranges. Going beyond what the RFC defines - expose the "histogram" in the Tx direction (assume for now that the ranges will be the same). Signed-off-by: Jakub Kicinski --- include/linux/ethtool.h | 43 +++++++++++++++ include/uapi/linux/ethtool.h | 2 + include/uapi/linux/ethtool_netlink.h | 23 ++++++++ net/ethtool/netlink.h | 1 + net/ethtool/stats.c | 82 ++++++++++++++++++++++++++++ net/ethtool/strset.c | 5 ++ 6 files changed, 156 insertions(+) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 22bab13c5729..f633c561eeef 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -371,6 +371,44 @@ struct ethtool_module_eeprom { __u8 *data; }; +/** + * struct ethtool_rmon_hist_range - byte range for histogram statistics + * @low: low bound of the bucket (inclusive) + * @high: high bound of the bucket (inclusive) + */ +struct ethtool_rmon_hist_range { + u16 low; + u16 high; +}; + +#define ETHTOOL_RMON_HIST_MAX 10 + +/** + * struct ethtool_rmon_stats - selected RMON (RFC 2819) statistics + * @undersize_pkts: Equivalent to `etherStatsUndersizePkts` from the RFC. + * @oversize_pkts: Equivalent to `etherStatsOversizePkts` from the RFC. + * @fragments: Equivalent to `etherStatsFragments` from the RFC. + * @jabbers: Equivalent to `etherStatsJabbers` from the RFC. + * @hist: Packet counter for packet length buckets (e.g. + * `etherStatsPkts128to255Octets` from the RFC). + * @hist_tx: Tx counters in similar form to @hist, not defined in the RFC. + * + * Selection of RMON (RFC 2819) statistics which are not exposed via different + * APIs, primarily the packet-length-based counters. + * Unfortunately different designs choose different buckets beyond + * the 1024B mark (jumbo frame teritory), so the definition of the bucket + * ranges is left to the driver. + */ +struct ethtool_rmon_stats { + u64 undersize_pkts; + u64 oversize_pkts; + u64 fragments; + u64 jabbers; + + u64 hist[ETHTOOL_RMON_HIST_MAX]; + u64 hist_tx[ETHTOOL_RMON_HIST_MAX]; +}; + /** * struct ethtool_ops - optional netdev operations * @cap_link_lanes_supported: indicates if the driver supports lanes @@ -533,6 +571,8 @@ struct ethtool_module_eeprom { * read. * @get_eth_phy_stats: Query some of the IEEE 802.3 PHY statistics. * @get_eth_mac_stats: Query some of the IEEE 802.3 MAC statistics. + * @get_rmon_stats: Query some of the RMON (RFC 2819) statistics. + * Set %ranges to a pointer to zero-terminated array of byte ranges. * * All operations are optional (i.e. the function pointer may be set * to %NULL) and callers must take this into account. Callers must @@ -649,6 +689,9 @@ struct ethtool_ops { struct ethtool_eth_mac_stats *mac_stats); void (*get_eth_ctrl_stats)(struct net_device *dev, struct ethtool_eth_ctrl_stats *ctrl_stats); + void (*get_rmon_stats)(struct net_device *dev, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges); }; int ethtool_check_ops(const struct ethtool_ops *ops); diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 9cb8df89d4f2..cfef6b08169a 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -673,6 +673,7 @@ enum ethtool_link_ext_substate_cable_issue { * @ETH_SS_STATS_ETH_PHY: names of IEEE 802.3 PHY statistics * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics + * @ETH_SS_STATS_RMON: names of RMON statistics * * @ETH_SS_COUNT: number of defined string sets */ @@ -697,6 +698,7 @@ enum ethtool_stringset { ETH_SS_STATS_ETH_PHY, ETH_SS_STATS_ETH_MAC, ETH_SS_STATS_ETH_CTRL, + ETH_SS_STATS_RMON, /* add new constants above here */ ETH_SS_COUNT diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 7c061dfc3b67..d38733d83bfb 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -700,6 +700,7 @@ enum { ETHTOOL_STATS_ETH_PHY, ETHTOOL_STATS_ETH_MAC, ETHTOOL_STATS_ETH_CTRL, + ETHTOOL_STATS_RMON, /* add new constants above here */ __ETHTOOL_STATS_CNT @@ -790,6 +791,28 @@ enum { ETHTOOL_A_STATS_ETH_CTRL_MAX = (__ETHTOOL_A_STATS_ETH_CTRL_CNT - 1) }; +enum { + ETHTOOL_A_STATS_RMON_HIST = ETHTOOL_A_STATS_GRP_FIRST_ATTR, + ETHTOOL_A_STATS_RMON_HIST_BKT_LOW, /* u32 */ + ETHTOOL_A_STATS_RMON_HIST_BKT_HI, /* u32 */ + ETHTOOL_A_STATS_RMON_HIST_VAL, + + ETHTOOL_A_STATS_RMON_HIST_TX, + + /* etherStatsUndersizePkts */ + ETHTOOL_A_STATS_RMON_UNDERSIZE, + /* etherStatsOversizePkts */ + ETHTOOL_A_STATS_RMON_OVERSIZE, + /* etherStatsFragments */ + ETHTOOL_A_STATS_RMON_FRAG, + /* etherStatsJabbers */ + ETHTOOL_A_STATS_RMON_JABBER, + + /* add new constants above here */ + __ETHTOOL_A_STATS_RMON_CNT, + ETHTOOL_A_STATS_RMON_MAX = (__ETHTOOL_A_STATS_RMON_CNT - 1) +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index bd96eb57c07c..8abcbc10796c 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -405,5 +405,6 @@ extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN]; extern const char stats_eth_mac_names[__ETHTOOL_A_STATS_ETH_MAC_CNT][ETH_GSTRING_LEN]; extern const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH_GSTRING_LEN]; +extern const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN]; #endif /* _NET_ETHTOOL_NETLINK_H */ diff --git a/net/ethtool/stats.c b/net/ethtool/stats.c index 5feb6aba26c8..2ce1326299d9 100644 --- a/net/ethtool/stats.c +++ b/net/ethtool/stats.c @@ -17,6 +17,8 @@ struct stats_reply_data { struct ethtool_eth_phy_stats phy_stats; struct ethtool_eth_mac_stats mac_stats; struct ethtool_eth_ctrl_stats ctrl_stats; + struct ethtool_rmon_stats rmon_stats; + const struct ethtool_rmon_hist_range *rmon_ranges; }; #define STATS_REPDATA(__reply_base) \ @@ -26,6 +28,7 @@ const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = { [ETHTOOL_STATS_ETH_PHY] = "eth-phy", [ETHTOOL_STATS_ETH_MAC] = "eth-mac", [ETHTOOL_STATS_ETH_CTRL] = "eth-ctrl", + [ETHTOOL_STATS_RMON] = "rmon", }; const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN] = { @@ -63,6 +66,13 @@ const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH_GSTRING_LEN] [ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP] = "UnsupportedOpcodesReceived", }; +const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN] = { + [ETHTOOL_A_STATS_RMON_UNDERSIZE] = "etherStatsUndersizePkts", + [ETHTOOL_A_STATS_RMON_OVERSIZE] = "etherStatsOversizePkts", + [ETHTOOL_A_STATS_RMON_FRAG] = "etherStatsFragments", + [ETHTOOL_A_STATS_RMON_JABBER] = "etherStatsJabbers", +}; + const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = { [ETHTOOL_A_STATS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), @@ -107,6 +117,7 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base, memset(&data->phy_stats, 0xff, sizeof(data->phy_stats)); memset(&data->mac_stats, 0xff, sizeof(data->mac_stats)); memset(&data->ctrl_stats, 0xff, sizeof(data->mac_stats)); + memset(&data->rmon_stats, 0xff, sizeof(data->rmon_stats)); if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) && dev->ethtool_ops->get_eth_phy_stats) @@ -117,6 +128,10 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base, if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask) && dev->ethtool_ops->get_eth_ctrl_stats) dev->ethtool_ops->get_eth_ctrl_stats(dev, &data->ctrl_stats); + if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask) && + dev->ethtool_ops->get_rmon_stats) + dev->ethtool_ops->get_rmon_stats(dev, &data->rmon_stats, + &data->rmon_ranges); ethnl_ops_complete(dev); return 0; @@ -140,6 +155,17 @@ static int stats_reply_size(const struct ethnl_req_info *req_base, len += nla_total_size(0) + sizeof(struct ethtool_eth_ctrl_stats) / sizeof(u64) * nla_total_size_64bit(sizeof(u64)); + if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) { + len += nla_total_size(0) + + sizeof(struct ethtool_rmon_stats) / sizeof(u64) * + nla_total_size_64bit(sizeof(u64)); + /* Above includes the space for _A_STATS_RMON_HIST_VALs */ + + len += (nla_total_size(0) + /* _A_STATS_RMON_HIST */ + nla_total_size(4) + /* _A_STATS_RMON_HIST_BKT_LOW */ + nla_total_size(4)) * /* _A_STATS_RMON_HIST_BKT_HI */ + ETHTOOL_RMON_HIST_MAX * 2; + } return len; } @@ -224,6 +250,59 @@ static int stats_put_ctrl_stats(struct sk_buff *skb, return 0; } +static int stats_put_rmon_hist(struct sk_buff *skb, u32 attr, const u64 *hist, + const struct ethtool_rmon_hist_range *ranges) +{ + struct nlattr *nest; + int i; + + for (i = 0; i < ETHTOOL_RMON_HIST_MAX; i++) { + if (!ranges[i].low && !ranges[i].high) + break; + if (hist[i] == ETHTOOL_STAT_NOT_SET) + continue; + + nest = nla_nest_start(skb, attr); + + if (nla_put_u32(skb, ETHTOOL_A_STATS_RMON_HIST_BKT_LOW, + ranges[i].low) || + nla_put_u32(skb, ETHTOOL_A_STATS_RMON_HIST_BKT_HI, + ranges[i].high) || + stat_put(skb, ETHTOOL_A_STATS_RMON_HIST_VAL, hist[i])) + goto err_cancel_hist; + + nla_nest_end(skb, nest); + } + + return 0; + +err_cancel_hist: + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static int stats_put_rmon_stats(struct sk_buff *skb, + const struct stats_reply_data *data) +{ + if (stats_put_rmon_hist(skb, ETHTOOL_A_STATS_RMON_HIST, + data->rmon_stats.hist, data->rmon_ranges) || + stats_put_rmon_hist(skb, ETHTOOL_A_STATS_RMON_HIST_TX, + data->rmon_stats.hist_tx, data->rmon_ranges)) + return -EMSGSIZE; + + if (stat_put(skb, ETHTOOL_A_STATS_RMON_UNDERSIZE, + data->rmon_stats.undersize_pkts) || + stat_put(skb, ETHTOOL_A_STATS_RMON_OVERSIZE, + data->rmon_stats.oversize_pkts) || + stat_put(skb, ETHTOOL_A_STATS_RMON_FRAG, + data->rmon_stats.fragments) || + stat_put(skb, ETHTOOL_A_STATS_RMON_JABBER, + data->rmon_stats.jabbers)) + return -EMSGSIZE; + + return 0; +} + static int stats_put_stats(struct sk_buff *skb, const struct stats_reply_data *data, u32 id, u32 ss_id, @@ -271,6 +350,9 @@ static int stats_fill_reply(struct sk_buff *skb, ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_CTRL, ETH_SS_STATS_ETH_CTRL, stats_put_ctrl_stats); + if (!ret && test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) + ret = stats_put_stats(skb, data, ETHTOOL_STATS_RMON, + ETH_SS_STATS_RMON, stats_put_rmon_stats); return ret; } diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c index a33c603a7a02..b3029fff715d 100644 --- a/net/ethtool/strset.c +++ b/net/ethtool/strset.c @@ -100,6 +100,11 @@ static const struct strset_info info_template[] = { .count = __ETHTOOL_A_STATS_ETH_CTRL_CNT, .strings = stats_eth_ctrl_names, }, + [ETH_SS_STATS_RMON] = { + .per_dev = false, + .count = __ETHTOOL_A_STATS_RMON_CNT, + .strings = stats_rmon_names, + }, }; struct strset_req_info {