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