xref: /openbmc/linux/net/ethtool/stats.c (revision f946270d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include "netlink.h"
4 #include "common.h"
5 #include "bitset.h"
6 
7 struct stats_req_info {
8 	struct ethnl_req_info		base;
9 	DECLARE_BITMAP(stat_mask, __ETHTOOL_STATS_CNT);
10 	enum ethtool_mac_stats_src	src;
11 };
12 
13 #define STATS_REQINFO(__req_base) \
14 	container_of(__req_base, struct stats_req_info, base)
15 
16 struct stats_reply_data {
17 	struct ethnl_reply_data		base;
18 	struct_group(stats,
19 		struct ethtool_eth_phy_stats	phy_stats;
20 		struct ethtool_eth_mac_stats	mac_stats;
21 		struct ethtool_eth_ctrl_stats	ctrl_stats;
22 		struct ethtool_rmon_stats	rmon_stats;
23 	);
24 	const struct ethtool_rmon_hist_range	*rmon_ranges;
25 };
26 
27 #define STATS_REPDATA(__reply_base) \
28 	container_of(__reply_base, struct stats_reply_data, base)
29 
30 const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = {
31 	[ETHTOOL_STATS_ETH_PHY]			= "eth-phy",
32 	[ETHTOOL_STATS_ETH_MAC]			= "eth-mac",
33 	[ETHTOOL_STATS_ETH_CTRL]		= "eth-ctrl",
34 	[ETHTOOL_STATS_RMON]			= "rmon",
35 };
36 
37 const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN] = {
38 	[ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR]	= "SymbolErrorDuringCarrier",
39 };
40 
41 const char stats_eth_mac_names[__ETHTOOL_A_STATS_ETH_MAC_CNT][ETH_GSTRING_LEN] = {
42 	[ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT]	= "FramesTransmittedOK",
43 	[ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL]	= "SingleCollisionFrames",
44 	[ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL]	= "MultipleCollisionFrames",
45 	[ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT]	= "FramesReceivedOK",
46 	[ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR]	= "FrameCheckSequenceErrors",
47 	[ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR]	= "AlignmentErrors",
48 	[ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES]	= "OctetsTransmittedOK",
49 	[ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER]	= "FramesWithDeferredXmissions",
50 	[ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL]	= "LateCollisions",
51 	[ETHTOOL_A_STATS_ETH_MAC_11_XS_COL]	= "FramesAbortedDueToXSColls",
52 	[ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR]	= "FramesLostDueToIntMACXmitError",
53 	[ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR]	= "CarrierSenseErrors",
54 	[ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES]	= "OctetsReceivedOK",
55 	[ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR]	= "FramesLostDueToIntMACRcvError",
56 	[ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST]	= "MulticastFramesXmittedOK",
57 	[ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST]	= "BroadcastFramesXmittedOK",
58 	[ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER]	= "FramesWithExcessiveDeferral",
59 	[ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST]	= "MulticastFramesReceivedOK",
60 	[ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST]	= "BroadcastFramesReceivedOK",
61 	[ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR]	= "InRangeLengthErrors",
62 	[ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN]	= "OutOfRangeLengthField",
63 	[ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR]	= "FrameTooLongErrors",
64 };
65 
66 const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH_GSTRING_LEN] = {
67 	[ETHTOOL_A_STATS_ETH_CTRL_3_TX]		= "MACControlFramesTransmitted",
68 	[ETHTOOL_A_STATS_ETH_CTRL_4_RX]		= "MACControlFramesReceived",
69 	[ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP]	= "UnsupportedOpcodesReceived",
70 };
71 
72 const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN] = {
73 	[ETHTOOL_A_STATS_RMON_UNDERSIZE]	= "etherStatsUndersizePkts",
74 	[ETHTOOL_A_STATS_RMON_OVERSIZE]		= "etherStatsOversizePkts",
75 	[ETHTOOL_A_STATS_RMON_FRAG]		= "etherStatsFragments",
76 	[ETHTOOL_A_STATS_RMON_JABBER]		= "etherStatsJabbers",
77 };
78 
79 const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_SRC + 1] = {
80 	[ETHTOOL_A_STATS_HEADER]	=
81 		NLA_POLICY_NESTED(ethnl_header_policy),
82 	[ETHTOOL_A_STATS_GROUPS]	= { .type = NLA_NESTED },
83 	[ETHTOOL_A_STATS_SRC]		=
84 		NLA_POLICY_MAX(NLA_U32, ETHTOOL_MAC_STATS_SRC_PMAC),
85 };
86 
stats_parse_request(struct ethnl_req_info * req_base,struct nlattr ** tb,struct netlink_ext_ack * extack)87 static int stats_parse_request(struct ethnl_req_info *req_base,
88 			       struct nlattr **tb,
89 			       struct netlink_ext_ack *extack)
90 {
91 	enum ethtool_mac_stats_src src = ETHTOOL_MAC_STATS_SRC_AGGREGATE;
92 	struct stats_req_info *req_info = STATS_REQINFO(req_base);
93 	bool mod = false;
94 	int err;
95 
96 	err = ethnl_update_bitset(req_info->stat_mask, __ETHTOOL_STATS_CNT,
97 				  tb[ETHTOOL_A_STATS_GROUPS], stats_std_names,
98 				  extack, &mod);
99 	if (err)
100 		return err;
101 
102 	if (!mod) {
103 		NL_SET_ERR_MSG(extack, "no stats requested");
104 		return -EINVAL;
105 	}
106 
107 	if (tb[ETHTOOL_A_STATS_SRC])
108 		src = nla_get_u32(tb[ETHTOOL_A_STATS_SRC]);
109 
110 	req_info->src = src;
111 
112 	return 0;
113 }
114 
stats_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)115 static int stats_prepare_data(const struct ethnl_req_info *req_base,
116 			      struct ethnl_reply_data *reply_base,
117 			      const struct genl_info *info)
118 {
119 	const struct stats_req_info *req_info = STATS_REQINFO(req_base);
120 	struct stats_reply_data *data = STATS_REPDATA(reply_base);
121 	enum ethtool_mac_stats_src src = req_info->src;
122 	struct net_device *dev = reply_base->dev;
123 	int ret;
124 
125 	ret = ethnl_ops_begin(dev);
126 	if (ret < 0)
127 		return ret;
128 
129 	if ((src == ETHTOOL_MAC_STATS_SRC_EMAC ||
130 	     src == ETHTOOL_MAC_STATS_SRC_PMAC) &&
131 	    !__ethtool_dev_mm_supported(dev)) {
132 		NL_SET_ERR_MSG_MOD(info->extack,
133 				   "Device does not support MAC merge layer");
134 		ethnl_ops_complete(dev);
135 		return -EOPNOTSUPP;
136 	}
137 
138 	/* Mark all stats as unset (see ETHTOOL_STAT_NOT_SET) to prevent them
139 	 * from being reported to user space in case driver did not set them.
140 	 */
141 	memset(&data->stats, 0xff, sizeof(data->stats));
142 
143 	data->phy_stats.src = src;
144 	data->mac_stats.src = src;
145 	data->ctrl_stats.src = src;
146 	data->rmon_stats.src = src;
147 
148 	if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
149 	    dev->ethtool_ops->get_eth_phy_stats)
150 		dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats);
151 	if (test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask) &&
152 	    dev->ethtool_ops->get_eth_mac_stats)
153 		dev->ethtool_ops->get_eth_mac_stats(dev, &data->mac_stats);
154 	if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask) &&
155 	    dev->ethtool_ops->get_eth_ctrl_stats)
156 		dev->ethtool_ops->get_eth_ctrl_stats(dev, &data->ctrl_stats);
157 	if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask) &&
158 	    dev->ethtool_ops->get_rmon_stats)
159 		dev->ethtool_ops->get_rmon_stats(dev, &data->rmon_stats,
160 						 &data->rmon_ranges);
161 
162 	ethnl_ops_complete(dev);
163 	return 0;
164 }
165 
stats_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)166 static int stats_reply_size(const struct ethnl_req_info *req_base,
167 			    const struct ethnl_reply_data *reply_base)
168 {
169 	const struct stats_req_info *req_info = STATS_REQINFO(req_base);
170 	unsigned int n_grps = 0, n_stats = 0;
171 	int len = 0;
172 
173 	len += nla_total_size(sizeof(u32)); /* _STATS_SRC */
174 
175 	if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) {
176 		n_stats += sizeof(struct ethtool_eth_phy_stats) / sizeof(u64);
177 		n_grps++;
178 	}
179 	if (test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask)) {
180 		n_stats += sizeof(struct ethtool_eth_mac_stats) / sizeof(u64);
181 		n_grps++;
182 	}
183 	if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask)) {
184 		n_stats += sizeof(struct ethtool_eth_ctrl_stats) / sizeof(u64);
185 		n_grps++;
186 	}
187 	if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) {
188 		n_stats += sizeof(struct ethtool_rmon_stats) / sizeof(u64);
189 		n_grps++;
190 		/* Above includes the space for _A_STATS_GRP_HIST_VALs */
191 
192 		len += (nla_total_size(0) +	/* _A_STATS_GRP_HIST */
193 			nla_total_size(4) +	/* _A_STATS_GRP_HIST_BKT_LOW */
194 			nla_total_size(4)) *	/* _A_STATS_GRP_HIST_BKT_HI */
195 			ETHTOOL_RMON_HIST_MAX * 2;
196 	}
197 
198 	len += n_grps * (nla_total_size(0) + /* _A_STATS_GRP */
199 			 nla_total_size(4) + /* _A_STATS_GRP_ID */
200 			 nla_total_size(4)); /* _A_STATS_GRP_SS_ID */
201 	len += n_stats * (nla_total_size(0) + /* _A_STATS_GRP_STAT */
202 			  nla_total_size_64bit(sizeof(u64)));
203 
204 	return len;
205 }
206 
stat_put(struct sk_buff * skb,u16 attrtype,u64 val)207 static int stat_put(struct sk_buff *skb, u16 attrtype, u64 val)
208 {
209 	struct nlattr *nest;
210 	int ret;
211 
212 	if (val == ETHTOOL_STAT_NOT_SET)
213 		return 0;
214 
215 	/* We want to start stats attr types from 0, so we don't have a type
216 	 * for pad inside ETHTOOL_A_STATS_GRP_STAT. Pad things on the outside
217 	 * of ETHTOOL_A_STATS_GRP_STAT. Since we're one nest away from the
218 	 * actual attr we're 4B off - nla_need_padding_for_64bit() & co.
219 	 * can't be used.
220 	 */
221 #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
222 	if (!IS_ALIGNED((unsigned long)skb_tail_pointer(skb), 8))
223 		if (!nla_reserve(skb, ETHTOOL_A_STATS_GRP_PAD, 0))
224 			return -EMSGSIZE;
225 #endif
226 
227 	nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP_STAT);
228 	if (!nest)
229 		return -EMSGSIZE;
230 
231 	ret = nla_put_u64_64bit(skb, attrtype, val, -1 /* not used */);
232 	if (ret) {
233 		nla_nest_cancel(skb, nest);
234 		return ret;
235 	}
236 
237 	nla_nest_end(skb, nest);
238 	return 0;
239 }
240 
stats_put_phy_stats(struct sk_buff * skb,const struct stats_reply_data * data)241 static int stats_put_phy_stats(struct sk_buff *skb,
242 			       const struct stats_reply_data *data)
243 {
244 	if (stat_put(skb, ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR,
245 		     data->phy_stats.SymbolErrorDuringCarrier))
246 		return -EMSGSIZE;
247 	return 0;
248 }
249 
stats_put_mac_stats(struct sk_buff * skb,const struct stats_reply_data * data)250 static int stats_put_mac_stats(struct sk_buff *skb,
251 			       const struct stats_reply_data *data)
252 {
253 	if (stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT,
254 		     data->mac_stats.FramesTransmittedOK) ||
255 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL,
256 		     data->mac_stats.SingleCollisionFrames) ||
257 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL,
258 		     data->mac_stats.MultipleCollisionFrames) ||
259 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT,
260 		     data->mac_stats.FramesReceivedOK) ||
261 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR,
262 		     data->mac_stats.FrameCheckSequenceErrors) ||
263 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR,
264 		     data->mac_stats.AlignmentErrors) ||
265 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES,
266 		     data->mac_stats.OctetsTransmittedOK) ||
267 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER,
268 		     data->mac_stats.FramesWithDeferredXmissions) ||
269 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL,
270 		     data->mac_stats.LateCollisions) ||
271 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_11_XS_COL,
272 		     data->mac_stats.FramesAbortedDueToXSColls) ||
273 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR,
274 		     data->mac_stats.FramesLostDueToIntMACXmitError) ||
275 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR,
276 		     data->mac_stats.CarrierSenseErrors) ||
277 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES,
278 		     data->mac_stats.OctetsReceivedOK) ||
279 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR,
280 		     data->mac_stats.FramesLostDueToIntMACRcvError) ||
281 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST,
282 		     data->mac_stats.MulticastFramesXmittedOK) ||
283 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST,
284 		     data->mac_stats.BroadcastFramesXmittedOK) ||
285 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER,
286 		     data->mac_stats.FramesWithExcessiveDeferral) ||
287 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST,
288 		     data->mac_stats.MulticastFramesReceivedOK) ||
289 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST,
290 		     data->mac_stats.BroadcastFramesReceivedOK) ||
291 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR,
292 		     data->mac_stats.InRangeLengthErrors) ||
293 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN,
294 		     data->mac_stats.OutOfRangeLengthField) ||
295 	    stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR,
296 		     data->mac_stats.FrameTooLongErrors))
297 		return -EMSGSIZE;
298 	return 0;
299 }
300 
stats_put_ctrl_stats(struct sk_buff * skb,const struct stats_reply_data * data)301 static int stats_put_ctrl_stats(struct sk_buff *skb,
302 				const struct stats_reply_data *data)
303 {
304 	if (stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_3_TX,
305 		     data->ctrl_stats.MACControlFramesTransmitted) ||
306 	    stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_4_RX,
307 		     data->ctrl_stats.MACControlFramesReceived) ||
308 	    stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP,
309 		     data->ctrl_stats.UnsupportedOpcodesReceived))
310 		return -EMSGSIZE;
311 	return 0;
312 }
313 
stats_put_rmon_hist(struct sk_buff * skb,u32 attr,const u64 * hist,const struct ethtool_rmon_hist_range * ranges)314 static int stats_put_rmon_hist(struct sk_buff *skb, u32 attr, const u64 *hist,
315 			       const struct ethtool_rmon_hist_range *ranges)
316 {
317 	struct nlattr *nest;
318 	int i;
319 
320 	if (!ranges)
321 		return 0;
322 
323 	for (i = 0; i <	ETHTOOL_RMON_HIST_MAX; i++) {
324 		if (!ranges[i].low && !ranges[i].high)
325 			break;
326 		if (hist[i] == ETHTOOL_STAT_NOT_SET)
327 			continue;
328 
329 		nest = nla_nest_start(skb, attr);
330 		if (!nest)
331 			return -EMSGSIZE;
332 
333 		if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_LOW,
334 				ranges[i].low) ||
335 		    nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_HI,
336 				ranges[i].high) ||
337 		    nla_put_u64_64bit(skb, ETHTOOL_A_STATS_GRP_HIST_VAL,
338 				      hist[i], ETHTOOL_A_STATS_GRP_PAD))
339 			goto err_cancel_hist;
340 
341 		nla_nest_end(skb, nest);
342 	}
343 
344 	return 0;
345 
346 err_cancel_hist:
347 	nla_nest_cancel(skb, nest);
348 	return -EMSGSIZE;
349 }
350 
stats_put_rmon_stats(struct sk_buff * skb,const struct stats_reply_data * data)351 static int stats_put_rmon_stats(struct sk_buff *skb,
352 				const struct stats_reply_data *data)
353 {
354 	if (stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_RX,
355 				data->rmon_stats.hist, data->rmon_ranges) ||
356 	    stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_TX,
357 				data->rmon_stats.hist_tx, data->rmon_ranges))
358 		return -EMSGSIZE;
359 
360 	if (stat_put(skb, ETHTOOL_A_STATS_RMON_UNDERSIZE,
361 		     data->rmon_stats.undersize_pkts) ||
362 	    stat_put(skb, ETHTOOL_A_STATS_RMON_OVERSIZE,
363 		     data->rmon_stats.oversize_pkts) ||
364 	    stat_put(skb, ETHTOOL_A_STATS_RMON_FRAG,
365 		     data->rmon_stats.fragments) ||
366 	    stat_put(skb, ETHTOOL_A_STATS_RMON_JABBER,
367 		     data->rmon_stats.jabbers))
368 		return -EMSGSIZE;
369 
370 	return 0;
371 }
372 
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))373 static int stats_put_stats(struct sk_buff *skb,
374 			   const struct stats_reply_data *data,
375 			   u32 id, u32 ss_id,
376 			   int (*cb)(struct sk_buff *skb,
377 				     const struct stats_reply_data *data))
378 {
379 	struct nlattr *nest;
380 
381 	nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP);
382 	if (!nest)
383 		return -EMSGSIZE;
384 
385 	if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_ID, id) ||
386 	    nla_put_u32(skb, ETHTOOL_A_STATS_GRP_SS_ID, ss_id))
387 		goto err_cancel;
388 
389 	if (cb(skb, data))
390 		goto err_cancel;
391 
392 	nla_nest_end(skb, nest);
393 	return 0;
394 
395 err_cancel:
396 	nla_nest_cancel(skb, nest);
397 	return -EMSGSIZE;
398 }
399 
stats_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)400 static int stats_fill_reply(struct sk_buff *skb,
401 			    const struct ethnl_req_info *req_base,
402 			    const struct ethnl_reply_data *reply_base)
403 {
404 	const struct stats_req_info *req_info = STATS_REQINFO(req_base);
405 	const struct stats_reply_data *data = STATS_REPDATA(reply_base);
406 	int ret = 0;
407 
408 	if (nla_put_u32(skb, ETHTOOL_A_STATS_SRC, req_info->src))
409 		return -EMSGSIZE;
410 
411 	if (!ret && test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask))
412 		ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_PHY,
413 				      ETH_SS_STATS_ETH_PHY,
414 				      stats_put_phy_stats);
415 	if (!ret && test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask))
416 		ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_MAC,
417 				      ETH_SS_STATS_ETH_MAC,
418 				      stats_put_mac_stats);
419 	if (!ret && test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask))
420 		ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_CTRL,
421 				      ETH_SS_STATS_ETH_CTRL,
422 				      stats_put_ctrl_stats);
423 	if (!ret && test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask))
424 		ret = stats_put_stats(skb, data, ETHTOOL_STATS_RMON,
425 				      ETH_SS_STATS_RMON, stats_put_rmon_stats);
426 
427 	return ret;
428 }
429 
430 const struct ethnl_request_ops ethnl_stats_request_ops = {
431 	.request_cmd		= ETHTOOL_MSG_STATS_GET,
432 	.reply_cmd		= ETHTOOL_MSG_STATS_GET_REPLY,
433 	.hdr_attr		= ETHTOOL_A_STATS_HEADER,
434 	.req_info_size		= sizeof(struct stats_req_info),
435 	.reply_data_size	= sizeof(struct stats_reply_data),
436 
437 	.parse_request		= stats_parse_request,
438 	.prepare_data		= stats_prepare_data,
439 	.reply_size		= stats_reply_size,
440 	.fill_reply		= stats_fill_reply,
441 };
442 
ethtool_stats_sum(u64 a,u64 b)443 static u64 ethtool_stats_sum(u64 a, u64 b)
444 {
445 	if (a == ETHTOOL_STAT_NOT_SET)
446 		return b;
447 	if (b == ETHTOOL_STAT_NOT_SET)
448 		return a;
449 	return a + b;
450 }
451 
452 /* Avoid modifying the aggregation procedure every time a new counter is added
453  * by treating the structures as an array of u64 statistics.
454  */
ethtool_aggregate_stats(void * aggr_stats,const void * emac_stats,const void * pmac_stats,size_t stats_size,size_t stats_offset)455 static void ethtool_aggregate_stats(void *aggr_stats, const void *emac_stats,
456 				    const void *pmac_stats, size_t stats_size,
457 				    size_t stats_offset)
458 {
459 	size_t num_stats = stats_size / sizeof(u64);
460 	const u64 *s1 = emac_stats + stats_offset;
461 	const u64 *s2 = pmac_stats + stats_offset;
462 	u64 *s = aggr_stats + stats_offset;
463 	int i;
464 
465 	for (i = 0; i < num_stats; i++)
466 		s[i] = ethtool_stats_sum(s1[i], s2[i]);
467 }
468 
ethtool_aggregate_mac_stats(struct net_device * dev,struct ethtool_eth_mac_stats * mac_stats)469 void ethtool_aggregate_mac_stats(struct net_device *dev,
470 				 struct ethtool_eth_mac_stats *mac_stats)
471 {
472 	const struct ethtool_ops *ops = dev->ethtool_ops;
473 	struct ethtool_eth_mac_stats pmac, emac;
474 
475 	memset(&emac, 0xff, sizeof(emac));
476 	memset(&pmac, 0xff, sizeof(pmac));
477 	emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
478 	pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
479 
480 	ops->get_eth_mac_stats(dev, &emac);
481 	ops->get_eth_mac_stats(dev, &pmac);
482 
483 	ethtool_aggregate_stats(mac_stats, &emac, &pmac,
484 				sizeof(mac_stats->stats),
485 				offsetof(struct ethtool_eth_mac_stats, stats));
486 }
487 EXPORT_SYMBOL(ethtool_aggregate_mac_stats);
488 
ethtool_aggregate_phy_stats(struct net_device * dev,struct ethtool_eth_phy_stats * phy_stats)489 void ethtool_aggregate_phy_stats(struct net_device *dev,
490 				 struct ethtool_eth_phy_stats *phy_stats)
491 {
492 	const struct ethtool_ops *ops = dev->ethtool_ops;
493 	struct ethtool_eth_phy_stats pmac, emac;
494 
495 	memset(&emac, 0xff, sizeof(emac));
496 	memset(&pmac, 0xff, sizeof(pmac));
497 	emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
498 	pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
499 
500 	ops->get_eth_phy_stats(dev, &emac);
501 	ops->get_eth_phy_stats(dev, &pmac);
502 
503 	ethtool_aggregate_stats(phy_stats, &emac, &pmac,
504 				sizeof(phy_stats->stats),
505 				offsetof(struct ethtool_eth_phy_stats, stats));
506 }
507 EXPORT_SYMBOL(ethtool_aggregate_phy_stats);
508 
ethtool_aggregate_ctrl_stats(struct net_device * dev,struct ethtool_eth_ctrl_stats * ctrl_stats)509 void ethtool_aggregate_ctrl_stats(struct net_device *dev,
510 				  struct ethtool_eth_ctrl_stats *ctrl_stats)
511 {
512 	const struct ethtool_ops *ops = dev->ethtool_ops;
513 	struct ethtool_eth_ctrl_stats pmac, emac;
514 
515 	memset(&emac, 0xff, sizeof(emac));
516 	memset(&pmac, 0xff, sizeof(pmac));
517 	emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
518 	pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
519 
520 	ops->get_eth_ctrl_stats(dev, &emac);
521 	ops->get_eth_ctrl_stats(dev, &pmac);
522 
523 	ethtool_aggregate_stats(ctrl_stats, &emac, &pmac,
524 				sizeof(ctrl_stats->stats),
525 				offsetof(struct ethtool_eth_ctrl_stats, stats));
526 }
527 EXPORT_SYMBOL(ethtool_aggregate_ctrl_stats);
528 
ethtool_aggregate_pause_stats(struct net_device * dev,struct ethtool_pause_stats * pause_stats)529 void ethtool_aggregate_pause_stats(struct net_device *dev,
530 				   struct ethtool_pause_stats *pause_stats)
531 {
532 	const struct ethtool_ops *ops = dev->ethtool_ops;
533 	struct ethtool_pause_stats pmac, emac;
534 
535 	memset(&emac, 0xff, sizeof(emac));
536 	memset(&pmac, 0xff, sizeof(pmac));
537 	emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
538 	pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
539 
540 	ops->get_pause_stats(dev, &emac);
541 	ops->get_pause_stats(dev, &pmac);
542 
543 	ethtool_aggregate_stats(pause_stats, &emac, &pmac,
544 				sizeof(pause_stats->stats),
545 				offsetof(struct ethtool_pause_stats, stats));
546 }
547 EXPORT_SYMBOL(ethtool_aggregate_pause_stats);
548 
ethtool_aggregate_rmon_stats(struct net_device * dev,struct ethtool_rmon_stats * rmon_stats)549 void ethtool_aggregate_rmon_stats(struct net_device *dev,
550 				  struct ethtool_rmon_stats *rmon_stats)
551 {
552 	const struct ethtool_ops *ops = dev->ethtool_ops;
553 	const struct ethtool_rmon_hist_range *dummy;
554 	struct ethtool_rmon_stats pmac, emac;
555 
556 	memset(&emac, 0xff, sizeof(emac));
557 	memset(&pmac, 0xff, sizeof(pmac));
558 	emac.src = ETHTOOL_MAC_STATS_SRC_EMAC;
559 	pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC;
560 
561 	ops->get_rmon_stats(dev, &emac, &dummy);
562 	ops->get_rmon_stats(dev, &pmac, &dummy);
563 
564 	ethtool_aggregate_stats(rmon_stats, &emac, &pmac,
565 				sizeof(rmon_stats->stats),
566 				offsetof(struct ethtool_rmon_stats, stats));
567 }
568 EXPORT_SYMBOL(ethtool_aggregate_rmon_stats);
569