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 }; 11 12 #define STATS_REQINFO(__req_base) \ 13 container_of(__req_base, struct stats_req_info, base) 14 15 struct stats_reply_data { 16 struct ethnl_reply_data base; 17 struct ethtool_eth_phy_stats phy_stats; 18 }; 19 20 #define STATS_REPDATA(__reply_base) \ 21 container_of(__reply_base, struct stats_reply_data, base) 22 23 const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = { 24 [ETHTOOL_STATS_ETH_PHY] = "eth-phy", 25 }; 26 27 const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN] = { 28 [ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR] = "SymbolErrorDuringCarrier", 29 }; 30 31 const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = { 32 [ETHTOOL_A_STATS_HEADER] = 33 NLA_POLICY_NESTED(ethnl_header_policy), 34 [ETHTOOL_A_STATS_GROUPS] = { .type = NLA_NESTED }, 35 }; 36 37 static int stats_parse_request(struct ethnl_req_info *req_base, 38 struct nlattr **tb, 39 struct netlink_ext_ack *extack) 40 { 41 struct stats_req_info *req_info = STATS_REQINFO(req_base); 42 bool mod = false; 43 int err; 44 45 err = ethnl_update_bitset(req_info->stat_mask, __ETHTOOL_STATS_CNT, 46 tb[ETHTOOL_A_STATS_GROUPS], stats_std_names, 47 extack, &mod); 48 if (err) 49 return err; 50 51 if (!mod) { 52 NL_SET_ERR_MSG(extack, "no stats requested"); 53 return -EINVAL; 54 } 55 56 return 0; 57 } 58 59 static int stats_prepare_data(const struct ethnl_req_info *req_base, 60 struct ethnl_reply_data *reply_base, 61 struct genl_info *info) 62 { 63 const struct stats_req_info *req_info = STATS_REQINFO(req_base); 64 struct stats_reply_data *data = STATS_REPDATA(reply_base); 65 struct net_device *dev = reply_base->dev; 66 int ret; 67 68 ret = ethnl_ops_begin(dev); 69 if (ret < 0) 70 return ret; 71 72 memset(&data->phy_stats, 0xff, sizeof(data->phy_stats)); 73 74 if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) && 75 dev->ethtool_ops->get_eth_phy_stats) 76 dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats); 77 78 ethnl_ops_complete(dev); 79 return 0; 80 } 81 82 static int stats_reply_size(const struct ethnl_req_info *req_base, 83 const struct ethnl_reply_data *reply_base) 84 { 85 const struct stats_req_info *req_info = STATS_REQINFO(req_base); 86 unsigned int n_grps = 0, n_stats = 0; 87 int len = 0; 88 89 if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) { 90 n_stats += sizeof(struct ethtool_eth_phy_stats) / sizeof(u64); 91 n_grps++; 92 } 93 94 len += n_grps * (nla_total_size(0) + /* _A_STATS_GRP */ 95 nla_total_size(4) + /* _A_STATS_GRP_ID */ 96 nla_total_size(4)); /* _A_STATS_GRP_SS_ID */ 97 len += n_stats * (nla_total_size(0) + /* _A_STATS_GRP_STAT */ 98 nla_total_size_64bit(sizeof(u64))); 99 100 return len; 101 } 102 103 static int stat_put(struct sk_buff *skb, u16 attrtype, u64 val) 104 { 105 struct nlattr *nest; 106 int ret; 107 108 if (val == ETHTOOL_STAT_NOT_SET) 109 return 0; 110 111 /* We want to start stats attr types from 0, so we don't have a type 112 * for pad inside ETHTOOL_A_STATS_GRP_STAT. Pad things on the outside 113 * of ETHTOOL_A_STATS_GRP_STAT. Since we're one nest away from the 114 * actual attr we're 4B off - nla_need_padding_for_64bit() & co. 115 * can't be used. 116 */ 117 #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 118 if (!IS_ALIGNED((unsigned long)skb_tail_pointer(skb), 8)) 119 if (!nla_reserve(skb, ETHTOOL_A_STATS_GRP_PAD, 0)) 120 return -EMSGSIZE; 121 #endif 122 123 nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP_STAT); 124 if (!nest) 125 return -EMSGSIZE; 126 127 ret = nla_put_u64_64bit(skb, attrtype, val, -1 /* not used */); 128 if (ret) { 129 nla_nest_cancel(skb, nest); 130 return ret; 131 } 132 133 nla_nest_end(skb, nest); 134 return 0; 135 } 136 137 static int stats_put_phy_stats(struct sk_buff *skb, 138 const struct stats_reply_data *data) 139 { 140 if (stat_put(skb, ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR, 141 data->phy_stats.SymbolErrorDuringCarrier)) 142 return -EMSGSIZE; 143 return 0; 144 } 145 146 static int stats_put_stats(struct sk_buff *skb, 147 const struct stats_reply_data *data, 148 u32 id, u32 ss_id, 149 int (*cb)(struct sk_buff *skb, 150 const struct stats_reply_data *data)) 151 { 152 struct nlattr *nest; 153 154 nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP); 155 if (!nest) 156 return -EMSGSIZE; 157 158 if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_ID, id) || 159 nla_put_u32(skb, ETHTOOL_A_STATS_GRP_SS_ID, ss_id)) 160 goto err_cancel; 161 162 if (cb(skb, data)) 163 goto err_cancel; 164 165 nla_nest_end(skb, nest); 166 return 0; 167 168 err_cancel: 169 nla_nest_cancel(skb, nest); 170 return -EMSGSIZE; 171 } 172 173 static int stats_fill_reply(struct sk_buff *skb, 174 const struct ethnl_req_info *req_base, 175 const struct ethnl_reply_data *reply_base) 176 { 177 const struct stats_req_info *req_info = STATS_REQINFO(req_base); 178 const struct stats_reply_data *data = STATS_REPDATA(reply_base); 179 int ret = 0; 180 181 if (!ret && test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) 182 ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_PHY, 183 ETH_SS_STATS_ETH_PHY, 184 stats_put_phy_stats); 185 186 return ret; 187 } 188 189 const struct ethnl_request_ops ethnl_stats_request_ops = { 190 .request_cmd = ETHTOOL_MSG_STATS_GET, 191 .reply_cmd = ETHTOOL_MSG_STATS_GET_REPLY, 192 .hdr_attr = ETHTOOL_A_STATS_HEADER, 193 .req_info_size = sizeof(struct stats_req_info), 194 .reply_data_size = sizeof(struct stats_reply_data), 195 196 .parse_request = stats_parse_request, 197 .prepare_data = stats_prepare_data, 198 .reply_size = stats_reply_size, 199 .fill_reply = stats_fill_reply, 200 }; 201