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 | |
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(bitmap: req_info->stat_mask, nbits: __ETHTOOL_STATS_CNT, |
97 | attr: tb[ETHTOOL_A_STATS_GROUPS], names: stats_std_names, |
98 | extack, mod: &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(nla: tb[ETHTOOL_A_STATS_SRC]); |
109 | |
110 | req_info->src = src; |
111 | |
112 | return 0; |
113 | } |
114 | |
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 | |
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(payload: 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(payload: 0) + /* _A_STATS_GRP_HIST */ |
193 | nla_total_size(payload: 4) + /* _A_STATS_GRP_HIST_BKT_LOW */ |
194 | nla_total_size(payload: 4)) * /* _A_STATS_GRP_HIST_BKT_HI */ |
195 | ETHTOOL_RMON_HIST_MAX * 2; |
196 | } |
197 | |
198 | len += n_grps * (nla_total_size(payload: 0) + /* _A_STATS_GRP */ |
199 | nla_total_size(payload: 4) + /* _A_STATS_GRP_ID */ |
200 | nla_total_size(payload: 4)); /* _A_STATS_GRP_SS_ID */ |
201 | len += n_stats * (nla_total_size(payload: 0) + /* _A_STATS_GRP_STAT */ |
202 | nla_total_size_64bit(payload: sizeof(u64))); |
203 | |
204 | return len; |
205 | } |
206 | |
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, attrtype: ETHTOOL_A_STATS_GRP_STAT); |
228 | if (!nest) |
229 | return -EMSGSIZE; |
230 | |
231 | ret = nla_put_u64_64bit(skb, attrtype, value: val, padattr: -1 /* not used */); |
232 | if (ret) { |
233 | nla_nest_cancel(skb, start: nest); |
234 | return ret; |
235 | } |
236 | |
237 | nla_nest_end(skb, start: nest); |
238 | return 0; |
239 | } |
240 | |
241 | static int stats_put_phy_stats(struct sk_buff *skb, |
242 | const struct stats_reply_data *data) |
243 | { |
244 | if (stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR, |
245 | val: data->phy_stats.SymbolErrorDuringCarrier)) |
246 | return -EMSGSIZE; |
247 | return 0; |
248 | } |
249 | |
250 | static int stats_put_mac_stats(struct sk_buff *skb, |
251 | const struct stats_reply_data *data) |
252 | { |
253 | if (stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT, |
254 | val: data->mac_stats.FramesTransmittedOK) || |
255 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL, |
256 | val: data->mac_stats.SingleCollisionFrames) || |
257 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL, |
258 | val: data->mac_stats.MultipleCollisionFrames) || |
259 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT, |
260 | val: data->mac_stats.FramesReceivedOK) || |
261 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR, |
262 | val: data->mac_stats.FrameCheckSequenceErrors) || |
263 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR, |
264 | val: data->mac_stats.AlignmentErrors) || |
265 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES, |
266 | val: data->mac_stats.OctetsTransmittedOK) || |
267 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER, |
268 | val: data->mac_stats.FramesWithDeferredXmissions) || |
269 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL, |
270 | val: data->mac_stats.LateCollisions) || |
271 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_11_XS_COL, |
272 | val: data->mac_stats.FramesAbortedDueToXSColls) || |
273 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR, |
274 | val: data->mac_stats.FramesLostDueToIntMACXmitError) || |
275 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR, |
276 | val: data->mac_stats.CarrierSenseErrors) || |
277 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES, |
278 | val: data->mac_stats.OctetsReceivedOK) || |
279 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR, |
280 | val: data->mac_stats.FramesLostDueToIntMACRcvError) || |
281 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST, |
282 | val: data->mac_stats.MulticastFramesXmittedOK) || |
283 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST, |
284 | val: data->mac_stats.BroadcastFramesXmittedOK) || |
285 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER, |
286 | val: data->mac_stats.FramesWithExcessiveDeferral) || |
287 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST, |
288 | val: data->mac_stats.MulticastFramesReceivedOK) || |
289 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST, |
290 | val: data->mac_stats.BroadcastFramesReceivedOK) || |
291 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR, |
292 | val: data->mac_stats.InRangeLengthErrors) || |
293 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN, |
294 | val: data->mac_stats.OutOfRangeLengthField) || |
295 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR, |
296 | val: data->mac_stats.FrameTooLongErrors)) |
297 | return -EMSGSIZE; |
298 | return 0; |
299 | } |
300 | |
301 | static int stats_put_ctrl_stats(struct sk_buff *skb, |
302 | const struct stats_reply_data *data) |
303 | { |
304 | if (stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_CTRL_3_TX, |
305 | val: data->ctrl_stats.MACControlFramesTransmitted) || |
306 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_CTRL_4_RX, |
307 | val: data->ctrl_stats.MACControlFramesReceived) || |
308 | stat_put(skb, attrtype: ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP, |
309 | val: data->ctrl_stats.UnsupportedOpcodesReceived)) |
310 | return -EMSGSIZE; |
311 | return 0; |
312 | } |
313 | |
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, attrtype: attr); |
330 | if (!nest) |
331 | return -EMSGSIZE; |
332 | |
333 | if (nla_put_u32(skb, attrtype: ETHTOOL_A_STATS_GRP_HIST_BKT_LOW, |
334 | value: ranges[i].low) || |
335 | nla_put_u32(skb, attrtype: ETHTOOL_A_STATS_GRP_HIST_BKT_HI, |
336 | value: ranges[i].high) || |
337 | nla_put_u64_64bit(skb, attrtype: ETHTOOL_A_STATS_GRP_HIST_VAL, |
338 | value: hist[i], padattr: ETHTOOL_A_STATS_GRP_PAD)) |
339 | goto err_cancel_hist; |
340 | |
341 | nla_nest_end(skb, start: nest); |
342 | } |
343 | |
344 | return 0; |
345 | |
346 | err_cancel_hist: |
347 | nla_nest_cancel(skb, start: nest); |
348 | return -EMSGSIZE; |
349 | } |
350 | |
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, attr: ETHTOOL_A_STATS_GRP_HIST_RX, |
355 | hist: data->rmon_stats.hist, ranges: data->rmon_ranges) || |
356 | stats_put_rmon_hist(skb, attr: ETHTOOL_A_STATS_GRP_HIST_TX, |
357 | hist: data->rmon_stats.hist_tx, ranges: data->rmon_ranges)) |
358 | return -EMSGSIZE; |
359 | |
360 | if (stat_put(skb, attrtype: ETHTOOL_A_STATS_RMON_UNDERSIZE, |
361 | val: data->rmon_stats.undersize_pkts) || |
362 | stat_put(skb, attrtype: ETHTOOL_A_STATS_RMON_OVERSIZE, |
363 | val: data->rmon_stats.oversize_pkts) || |
364 | stat_put(skb, attrtype: ETHTOOL_A_STATS_RMON_FRAG, |
365 | val: data->rmon_stats.fragments) || |
366 | stat_put(skb, attrtype: ETHTOOL_A_STATS_RMON_JABBER, |
367 | val: data->rmon_stats.jabbers)) |
368 | return -EMSGSIZE; |
369 | |
370 | return 0; |
371 | } |
372 | |
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, attrtype: ETHTOOL_A_STATS_GRP); |
382 | if (!nest) |
383 | return -EMSGSIZE; |
384 | |
385 | if (nla_put_u32(skb, attrtype: ETHTOOL_A_STATS_GRP_ID, value: id) || |
386 | nla_put_u32(skb, attrtype: ETHTOOL_A_STATS_GRP_SS_ID, value: ss_id)) |
387 | goto err_cancel; |
388 | |
389 | if (cb(skb, data)) |
390 | goto err_cancel; |
391 | |
392 | nla_nest_end(skb, start: nest); |
393 | return 0; |
394 | |
395 | err_cancel: |
396 | nla_nest_cancel(skb, start: nest); |
397 | return -EMSGSIZE; |
398 | } |
399 | |
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, attrtype: ETHTOOL_A_STATS_SRC, value: 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, id: ETHTOOL_STATS_ETH_PHY, |
413 | ss_id: ETH_SS_STATS_ETH_PHY, |
414 | cb: stats_put_phy_stats); |
415 | if (!ret && test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask)) |
416 | ret = stats_put_stats(skb, data, id: ETHTOOL_STATS_ETH_MAC, |
417 | ss_id: ETH_SS_STATS_ETH_MAC, |
418 | cb: stats_put_mac_stats); |
419 | if (!ret && test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask)) |
420 | ret = stats_put_stats(skb, data, id: ETHTOOL_STATS_ETH_CTRL, |
421 | ss_id: ETH_SS_STATS_ETH_CTRL, |
422 | cb: stats_put_ctrl_stats); |
423 | if (!ret && test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) |
424 | ret = stats_put_stats(skb, data, id: ETHTOOL_STATS_RMON, |
425 | ss_id: ETH_SS_STATS_RMON, cb: 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 | |
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 | */ |
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(a: s1[i], b: s2[i]); |
467 | } |
468 | |
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(aggr_stats: mac_stats, emac_stats: &emac, pmac_stats: &pmac, |
484 | stats_size: sizeof(mac_stats->stats), |
485 | offsetof(struct ethtool_eth_mac_stats, stats)); |
486 | } |
487 | EXPORT_SYMBOL(ethtool_aggregate_mac_stats); |
488 | |
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(aggr_stats: phy_stats, emac_stats: &emac, pmac_stats: &pmac, |
504 | stats_size: sizeof(phy_stats->stats), |
505 | offsetof(struct ethtool_eth_phy_stats, stats)); |
506 | } |
507 | EXPORT_SYMBOL(ethtool_aggregate_phy_stats); |
508 | |
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(aggr_stats: ctrl_stats, emac_stats: &emac, pmac_stats: &pmac, |
524 | stats_size: sizeof(ctrl_stats->stats), |
525 | offsetof(struct ethtool_eth_ctrl_stats, stats)); |
526 | } |
527 | EXPORT_SYMBOL(ethtool_aggregate_ctrl_stats); |
528 | |
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(aggr_stats: pause_stats, emac_stats: &emac, pmac_stats: &pmac, |
544 | stats_size: sizeof(pause_stats->stats), |
545 | offsetof(struct ethtool_pause_stats, stats)); |
546 | } |
547 | EXPORT_SYMBOL(ethtool_aggregate_pause_stats); |
548 | |
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(aggr_stats: rmon_stats, emac_stats: &emac, pmac_stats: &pmac, |
565 | stats_size: sizeof(rmon_stats->stats), |
566 | offsetof(struct ethtool_rmon_stats, stats)); |
567 | } |
568 | EXPORT_SYMBOL(ethtool_aggregate_rmon_stats); |
569 |
Definitions
- stats_req_info
- stats_reply_data
- stats_std_names
- stats_eth_phy_names
- stats_eth_mac_names
- stats_eth_ctrl_names
- stats_rmon_names
- ethnl_stats_get_policy
- stats_parse_request
- stats_prepare_data
- stats_reply_size
- stat_put
- stats_put_phy_stats
- stats_put_mac_stats
- stats_put_ctrl_stats
- stats_put_rmon_hist
- stats_put_rmon_stats
- stats_put_stats
- stats_fill_reply
- ethnl_stats_request_ops
- ethtool_stats_sum
- ethtool_aggregate_stats
- ethtool_aggregate_mac_stats
- ethtool_aggregate_phy_stats
- ethtool_aggregate_ctrl_stats
- ethtool_aggregate_pause_stats
Improve your Profiling and Debugging skills
Find out more