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