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