xref: /openbmc/linux/net/ethtool/stats.c (revision f09ea6fb)
1*f09ea6fbSJakub Kicinski // SPDX-License-Identifier: GPL-2.0-only
2*f09ea6fbSJakub Kicinski 
3*f09ea6fbSJakub Kicinski #include "netlink.h"
4*f09ea6fbSJakub Kicinski #include "common.h"
5*f09ea6fbSJakub Kicinski #include "bitset.h"
6*f09ea6fbSJakub Kicinski 
7*f09ea6fbSJakub Kicinski struct stats_req_info {
8*f09ea6fbSJakub Kicinski 	struct ethnl_req_info		base;
9*f09ea6fbSJakub Kicinski 	DECLARE_BITMAP(stat_mask, __ETHTOOL_STATS_CNT);
10*f09ea6fbSJakub Kicinski };
11*f09ea6fbSJakub Kicinski 
12*f09ea6fbSJakub Kicinski #define STATS_REQINFO(__req_base) \
13*f09ea6fbSJakub Kicinski 	container_of(__req_base, struct stats_req_info, base)
14*f09ea6fbSJakub Kicinski 
15*f09ea6fbSJakub Kicinski struct stats_reply_data {
16*f09ea6fbSJakub Kicinski 	struct ethnl_reply_data		base;
17*f09ea6fbSJakub Kicinski 	struct ethtool_eth_phy_stats	phy_stats;
18*f09ea6fbSJakub Kicinski };
19*f09ea6fbSJakub Kicinski 
20*f09ea6fbSJakub Kicinski #define STATS_REPDATA(__reply_base) \
21*f09ea6fbSJakub Kicinski 	container_of(__reply_base, struct stats_reply_data, base)
22*f09ea6fbSJakub Kicinski 
23*f09ea6fbSJakub Kicinski const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = {
24*f09ea6fbSJakub Kicinski 	[ETHTOOL_STATS_ETH_PHY]			= "eth-phy",
25*f09ea6fbSJakub Kicinski };
26*f09ea6fbSJakub Kicinski 
27*f09ea6fbSJakub Kicinski const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN] = {
28*f09ea6fbSJakub Kicinski 	[ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR]	= "SymbolErrorDuringCarrier",
29*f09ea6fbSJakub Kicinski };
30*f09ea6fbSJakub Kicinski 
31*f09ea6fbSJakub Kicinski const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = {
32*f09ea6fbSJakub Kicinski 	[ETHTOOL_A_STATS_HEADER]	=
33*f09ea6fbSJakub Kicinski 		NLA_POLICY_NESTED(ethnl_header_policy),
34*f09ea6fbSJakub Kicinski 	[ETHTOOL_A_STATS_GROUPS]	= { .type = NLA_NESTED },
35*f09ea6fbSJakub Kicinski };
36*f09ea6fbSJakub Kicinski 
37*f09ea6fbSJakub Kicinski static int stats_parse_request(struct ethnl_req_info *req_base,
38*f09ea6fbSJakub Kicinski 			       struct nlattr **tb,
39*f09ea6fbSJakub Kicinski 			       struct netlink_ext_ack *extack)
40*f09ea6fbSJakub Kicinski {
41*f09ea6fbSJakub Kicinski 	struct stats_req_info *req_info = STATS_REQINFO(req_base);
42*f09ea6fbSJakub Kicinski 	bool mod = false;
43*f09ea6fbSJakub Kicinski 	int err;
44*f09ea6fbSJakub Kicinski 
45*f09ea6fbSJakub Kicinski 	err = ethnl_update_bitset(req_info->stat_mask, __ETHTOOL_STATS_CNT,
46*f09ea6fbSJakub Kicinski 				  tb[ETHTOOL_A_STATS_GROUPS], stats_std_names,
47*f09ea6fbSJakub Kicinski 				  extack, &mod);
48*f09ea6fbSJakub Kicinski 	if (err)
49*f09ea6fbSJakub Kicinski 		return err;
50*f09ea6fbSJakub Kicinski 
51*f09ea6fbSJakub Kicinski 	if (!mod) {
52*f09ea6fbSJakub Kicinski 		NL_SET_ERR_MSG(extack, "no stats requested");
53*f09ea6fbSJakub Kicinski 		return -EINVAL;
54*f09ea6fbSJakub Kicinski 	}
55*f09ea6fbSJakub Kicinski 
56*f09ea6fbSJakub Kicinski 	return 0;
57*f09ea6fbSJakub Kicinski }
58*f09ea6fbSJakub Kicinski 
59*f09ea6fbSJakub Kicinski static int stats_prepare_data(const struct ethnl_req_info *req_base,
60*f09ea6fbSJakub Kicinski 			      struct ethnl_reply_data *reply_base,
61*f09ea6fbSJakub Kicinski 			      struct genl_info *info)
62*f09ea6fbSJakub Kicinski {
63*f09ea6fbSJakub Kicinski 	const struct stats_req_info *req_info = STATS_REQINFO(req_base);
64*f09ea6fbSJakub Kicinski 	struct stats_reply_data *data = STATS_REPDATA(reply_base);
65*f09ea6fbSJakub Kicinski 	struct net_device *dev = reply_base->dev;
66*f09ea6fbSJakub Kicinski 	int ret;
67*f09ea6fbSJakub Kicinski 
68*f09ea6fbSJakub Kicinski 	ret = ethnl_ops_begin(dev);
69*f09ea6fbSJakub Kicinski 	if (ret < 0)
70*f09ea6fbSJakub Kicinski 		return ret;
71*f09ea6fbSJakub Kicinski 
72*f09ea6fbSJakub Kicinski 	memset(&data->phy_stats, 0xff, sizeof(data->phy_stats));
73*f09ea6fbSJakub Kicinski 
74*f09ea6fbSJakub Kicinski 	if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
75*f09ea6fbSJakub Kicinski 	    dev->ethtool_ops->get_eth_phy_stats)
76*f09ea6fbSJakub Kicinski 		dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats);
77*f09ea6fbSJakub Kicinski 
78*f09ea6fbSJakub Kicinski 	ethnl_ops_complete(dev);
79*f09ea6fbSJakub Kicinski 	return 0;
80*f09ea6fbSJakub Kicinski }
81*f09ea6fbSJakub Kicinski 
82*f09ea6fbSJakub Kicinski static int stats_reply_size(const struct ethnl_req_info *req_base,
83*f09ea6fbSJakub Kicinski 			    const struct ethnl_reply_data *reply_base)
84*f09ea6fbSJakub Kicinski {
85*f09ea6fbSJakub Kicinski 	const struct stats_req_info *req_info = STATS_REQINFO(req_base);
86*f09ea6fbSJakub Kicinski 	unsigned int n_grps = 0, n_stats = 0;
87*f09ea6fbSJakub Kicinski 	int len = 0;
88*f09ea6fbSJakub Kicinski 
89*f09ea6fbSJakub Kicinski 	if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) {
90*f09ea6fbSJakub Kicinski 		n_stats += sizeof(struct ethtool_eth_phy_stats) / sizeof(u64);
91*f09ea6fbSJakub Kicinski 		n_grps++;
92*f09ea6fbSJakub Kicinski 	}
93*f09ea6fbSJakub Kicinski 
94*f09ea6fbSJakub Kicinski 	len += n_grps * (nla_total_size(0) + /* _A_STATS_GRP */
95*f09ea6fbSJakub Kicinski 			 nla_total_size(4) + /* _A_STATS_GRP_ID */
96*f09ea6fbSJakub Kicinski 			 nla_total_size(4)); /* _A_STATS_GRP_SS_ID */
97*f09ea6fbSJakub Kicinski 	len += n_stats * (nla_total_size(0) + /* _A_STATS_GRP_STAT */
98*f09ea6fbSJakub Kicinski 			  nla_total_size_64bit(sizeof(u64)));
99*f09ea6fbSJakub Kicinski 
100*f09ea6fbSJakub Kicinski 	return len;
101*f09ea6fbSJakub Kicinski }
102*f09ea6fbSJakub Kicinski 
103*f09ea6fbSJakub Kicinski static int stat_put(struct sk_buff *skb, u16 attrtype, u64 val)
104*f09ea6fbSJakub Kicinski {
105*f09ea6fbSJakub Kicinski 	struct nlattr *nest;
106*f09ea6fbSJakub Kicinski 	int ret;
107*f09ea6fbSJakub Kicinski 
108*f09ea6fbSJakub Kicinski 	if (val == ETHTOOL_STAT_NOT_SET)
109*f09ea6fbSJakub Kicinski 		return 0;
110*f09ea6fbSJakub Kicinski 
111*f09ea6fbSJakub Kicinski 	/* We want to start stats attr types from 0, so we don't have a type
112*f09ea6fbSJakub Kicinski 	 * for pad inside ETHTOOL_A_STATS_GRP_STAT. Pad things on the outside
113*f09ea6fbSJakub Kicinski 	 * of ETHTOOL_A_STATS_GRP_STAT. Since we're one nest away from the
114*f09ea6fbSJakub Kicinski 	 * actual attr we're 4B off - nla_need_padding_for_64bit() & co.
115*f09ea6fbSJakub Kicinski 	 * can't be used.
116*f09ea6fbSJakub Kicinski 	 */
117*f09ea6fbSJakub Kicinski #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
118*f09ea6fbSJakub Kicinski 	if (!IS_ALIGNED((unsigned long)skb_tail_pointer(skb), 8))
119*f09ea6fbSJakub Kicinski 		if (!nla_reserve(skb, ETHTOOL_A_STATS_GRP_PAD, 0))
120*f09ea6fbSJakub Kicinski 			return -EMSGSIZE;
121*f09ea6fbSJakub Kicinski #endif
122*f09ea6fbSJakub Kicinski 
123*f09ea6fbSJakub Kicinski 	nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP_STAT);
124*f09ea6fbSJakub Kicinski 	if (!nest)
125*f09ea6fbSJakub Kicinski 		return -EMSGSIZE;
126*f09ea6fbSJakub Kicinski 
127*f09ea6fbSJakub Kicinski 	ret = nla_put_u64_64bit(skb, attrtype, val, -1 /* not used */);
128*f09ea6fbSJakub Kicinski 	if (ret) {
129*f09ea6fbSJakub Kicinski 		nla_nest_cancel(skb, nest);
130*f09ea6fbSJakub Kicinski 		return ret;
131*f09ea6fbSJakub Kicinski 	}
132*f09ea6fbSJakub Kicinski 
133*f09ea6fbSJakub Kicinski 	nla_nest_end(skb, nest);
134*f09ea6fbSJakub Kicinski 	return 0;
135*f09ea6fbSJakub Kicinski }
136*f09ea6fbSJakub Kicinski 
137*f09ea6fbSJakub Kicinski static int stats_put_phy_stats(struct sk_buff *skb,
138*f09ea6fbSJakub Kicinski 			       const struct stats_reply_data *data)
139*f09ea6fbSJakub Kicinski {
140*f09ea6fbSJakub Kicinski 	if (stat_put(skb, ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR,
141*f09ea6fbSJakub Kicinski 		     data->phy_stats.SymbolErrorDuringCarrier))
142*f09ea6fbSJakub Kicinski 		return -EMSGSIZE;
143*f09ea6fbSJakub Kicinski 	return 0;
144*f09ea6fbSJakub Kicinski }
145*f09ea6fbSJakub Kicinski 
146*f09ea6fbSJakub Kicinski static int stats_put_stats(struct sk_buff *skb,
147*f09ea6fbSJakub Kicinski 			   const struct stats_reply_data *data,
148*f09ea6fbSJakub Kicinski 			   u32 id, u32 ss_id,
149*f09ea6fbSJakub Kicinski 			   int (*cb)(struct sk_buff *skb,
150*f09ea6fbSJakub Kicinski 				     const struct stats_reply_data *data))
151*f09ea6fbSJakub Kicinski {
152*f09ea6fbSJakub Kicinski 	struct nlattr *nest;
153*f09ea6fbSJakub Kicinski 
154*f09ea6fbSJakub Kicinski 	nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP);
155*f09ea6fbSJakub Kicinski 	if (!nest)
156*f09ea6fbSJakub Kicinski 		return -EMSGSIZE;
157*f09ea6fbSJakub Kicinski 
158*f09ea6fbSJakub Kicinski 	if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_ID, id) ||
159*f09ea6fbSJakub Kicinski 	    nla_put_u32(skb, ETHTOOL_A_STATS_GRP_SS_ID, ss_id))
160*f09ea6fbSJakub Kicinski 		goto err_cancel;
161*f09ea6fbSJakub Kicinski 
162*f09ea6fbSJakub Kicinski 	if (cb(skb, data))
163*f09ea6fbSJakub Kicinski 		goto err_cancel;
164*f09ea6fbSJakub Kicinski 
165*f09ea6fbSJakub Kicinski 	nla_nest_end(skb, nest);
166*f09ea6fbSJakub Kicinski 	return 0;
167*f09ea6fbSJakub Kicinski 
168*f09ea6fbSJakub Kicinski err_cancel:
169*f09ea6fbSJakub Kicinski 	nla_nest_cancel(skb, nest);
170*f09ea6fbSJakub Kicinski 	return -EMSGSIZE;
171*f09ea6fbSJakub Kicinski }
172*f09ea6fbSJakub Kicinski 
173*f09ea6fbSJakub Kicinski static int stats_fill_reply(struct sk_buff *skb,
174*f09ea6fbSJakub Kicinski 			    const struct ethnl_req_info *req_base,
175*f09ea6fbSJakub Kicinski 			    const struct ethnl_reply_data *reply_base)
176*f09ea6fbSJakub Kicinski {
177*f09ea6fbSJakub Kicinski 	const struct stats_req_info *req_info = STATS_REQINFO(req_base);
178*f09ea6fbSJakub Kicinski 	const struct stats_reply_data *data = STATS_REPDATA(reply_base);
179*f09ea6fbSJakub Kicinski 	int ret = 0;
180*f09ea6fbSJakub Kicinski 
181*f09ea6fbSJakub Kicinski 	if (!ret && test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask))
182*f09ea6fbSJakub Kicinski 		ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_PHY,
183*f09ea6fbSJakub Kicinski 				      ETH_SS_STATS_ETH_PHY,
184*f09ea6fbSJakub Kicinski 				      stats_put_phy_stats);
185*f09ea6fbSJakub Kicinski 
186*f09ea6fbSJakub Kicinski 	return ret;
187*f09ea6fbSJakub Kicinski }
188*f09ea6fbSJakub Kicinski 
189*f09ea6fbSJakub Kicinski const struct ethnl_request_ops ethnl_stats_request_ops = {
190*f09ea6fbSJakub Kicinski 	.request_cmd		= ETHTOOL_MSG_STATS_GET,
191*f09ea6fbSJakub Kicinski 	.reply_cmd		= ETHTOOL_MSG_STATS_GET_REPLY,
192*f09ea6fbSJakub Kicinski 	.hdr_attr		= ETHTOOL_A_STATS_HEADER,
193*f09ea6fbSJakub Kicinski 	.req_info_size		= sizeof(struct stats_req_info),
194*f09ea6fbSJakub Kicinski 	.reply_data_size	= sizeof(struct stats_reply_data),
195*f09ea6fbSJakub Kicinski 
196*f09ea6fbSJakub Kicinski 	.parse_request		= stats_parse_request,
197*f09ea6fbSJakub Kicinski 	.prepare_data		= stats_prepare_data,
198*f09ea6fbSJakub Kicinski 	.reply_size		= stats_reply_size,
199*f09ea6fbSJakub Kicinski 	.fill_reply		= stats_fill_reply,
200*f09ea6fbSJakub Kicinski };
201