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