1*2b30f829SVladimir Oltean // SPDX-License-Identifier: GPL-2.0-only 2*2b30f829SVladimir Oltean /* 3*2b30f829SVladimir Oltean * Copyright 2022-2023 NXP 4*2b30f829SVladimir Oltean */ 5*2b30f829SVladimir Oltean #include "common.h" 6*2b30f829SVladimir Oltean #include "netlink.h" 7*2b30f829SVladimir Oltean 8*2b30f829SVladimir Oltean struct mm_req_info { 9*2b30f829SVladimir Oltean struct ethnl_req_info base; 10*2b30f829SVladimir Oltean }; 11*2b30f829SVladimir Oltean 12*2b30f829SVladimir Oltean struct mm_reply_data { 13*2b30f829SVladimir Oltean struct ethnl_reply_data base; 14*2b30f829SVladimir Oltean struct ethtool_mm_state state; 15*2b30f829SVladimir Oltean struct ethtool_mm_stats stats; 16*2b30f829SVladimir Oltean }; 17*2b30f829SVladimir Oltean 18*2b30f829SVladimir Oltean #define MM_REPDATA(__reply_base) \ 19*2b30f829SVladimir Oltean container_of(__reply_base, struct mm_reply_data, base) 20*2b30f829SVladimir Oltean 21*2b30f829SVladimir Oltean #define ETHTOOL_MM_STAT_CNT \ 22*2b30f829SVladimir Oltean (__ETHTOOL_A_MM_STAT_CNT - (ETHTOOL_A_MM_STAT_PAD + 1)) 23*2b30f829SVladimir Oltean 24*2b30f829SVladimir Oltean const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1] = { 25*2b30f829SVladimir Oltean [ETHTOOL_A_MM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_stats), 26*2b30f829SVladimir Oltean }; 27*2b30f829SVladimir Oltean 28*2b30f829SVladimir Oltean static int mm_prepare_data(const struct ethnl_req_info *req_base, 29*2b30f829SVladimir Oltean struct ethnl_reply_data *reply_base, 30*2b30f829SVladimir Oltean struct genl_info *info) 31*2b30f829SVladimir Oltean { 32*2b30f829SVladimir Oltean struct mm_reply_data *data = MM_REPDATA(reply_base); 33*2b30f829SVladimir Oltean struct net_device *dev = reply_base->dev; 34*2b30f829SVladimir Oltean const struct ethtool_ops *ops; 35*2b30f829SVladimir Oltean int ret; 36*2b30f829SVladimir Oltean 37*2b30f829SVladimir Oltean ops = dev->ethtool_ops; 38*2b30f829SVladimir Oltean 39*2b30f829SVladimir Oltean if (!ops->get_mm) 40*2b30f829SVladimir Oltean return -EOPNOTSUPP; 41*2b30f829SVladimir Oltean 42*2b30f829SVladimir Oltean ethtool_stats_init((u64 *)&data->stats, 43*2b30f829SVladimir Oltean sizeof(data->stats) / sizeof(u64)); 44*2b30f829SVladimir Oltean 45*2b30f829SVladimir Oltean ret = ethnl_ops_begin(dev); 46*2b30f829SVladimir Oltean if (ret < 0) 47*2b30f829SVladimir Oltean return ret; 48*2b30f829SVladimir Oltean 49*2b30f829SVladimir Oltean ret = ops->get_mm(dev, &data->state); 50*2b30f829SVladimir Oltean if (ret) 51*2b30f829SVladimir Oltean goto out_complete; 52*2b30f829SVladimir Oltean 53*2b30f829SVladimir Oltean if (ops->get_mm_stats && (req_base->flags & ETHTOOL_FLAG_STATS)) 54*2b30f829SVladimir Oltean ops->get_mm_stats(dev, &data->stats); 55*2b30f829SVladimir Oltean 56*2b30f829SVladimir Oltean out_complete: 57*2b30f829SVladimir Oltean ethnl_ops_complete(dev); 58*2b30f829SVladimir Oltean 59*2b30f829SVladimir Oltean return 0; 60*2b30f829SVladimir Oltean } 61*2b30f829SVladimir Oltean 62*2b30f829SVladimir Oltean static int mm_reply_size(const struct ethnl_req_info *req_base, 63*2b30f829SVladimir Oltean const struct ethnl_reply_data *reply_base) 64*2b30f829SVladimir Oltean { 65*2b30f829SVladimir Oltean int len = 0; 66*2b30f829SVladimir Oltean 67*2b30f829SVladimir Oltean len += nla_total_size(sizeof(u8)); /* _MM_PMAC_ENABLED */ 68*2b30f829SVladimir Oltean len += nla_total_size(sizeof(u8)); /* _MM_TX_ENABLED */ 69*2b30f829SVladimir Oltean len += nla_total_size(sizeof(u8)); /* _MM_TX_ACTIVE */ 70*2b30f829SVladimir Oltean len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_ENABLED */ 71*2b30f829SVladimir Oltean len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_STATUS */ 72*2b30f829SVladimir Oltean len += nla_total_size(sizeof(u32)); /* _MM_VERIFY_TIME */ 73*2b30f829SVladimir Oltean len += nla_total_size(sizeof(u32)); /* _MM_MAX_VERIFY_TIME */ 74*2b30f829SVladimir Oltean len += nla_total_size(sizeof(u32)); /* _MM_TX_MIN_FRAG_SIZE */ 75*2b30f829SVladimir Oltean len += nla_total_size(sizeof(u32)); /* _MM_RX_MIN_FRAG_SIZE */ 76*2b30f829SVladimir Oltean 77*2b30f829SVladimir Oltean if (req_base->flags & ETHTOOL_FLAG_STATS) 78*2b30f829SVladimir Oltean len += nla_total_size(0) + /* _MM_STATS */ 79*2b30f829SVladimir Oltean nla_total_size_64bit(sizeof(u64)) * ETHTOOL_MM_STAT_CNT; 80*2b30f829SVladimir Oltean 81*2b30f829SVladimir Oltean return len; 82*2b30f829SVladimir Oltean } 83*2b30f829SVladimir Oltean 84*2b30f829SVladimir Oltean static int mm_put_stat(struct sk_buff *skb, u64 val, u16 attrtype) 85*2b30f829SVladimir Oltean { 86*2b30f829SVladimir Oltean if (val == ETHTOOL_STAT_NOT_SET) 87*2b30f829SVladimir Oltean return 0; 88*2b30f829SVladimir Oltean if (nla_put_u64_64bit(skb, attrtype, val, ETHTOOL_A_MM_STAT_PAD)) 89*2b30f829SVladimir Oltean return -EMSGSIZE; 90*2b30f829SVladimir Oltean return 0; 91*2b30f829SVladimir Oltean } 92*2b30f829SVladimir Oltean 93*2b30f829SVladimir Oltean static int mm_put_stats(struct sk_buff *skb, 94*2b30f829SVladimir Oltean const struct ethtool_mm_stats *stats) 95*2b30f829SVladimir Oltean { 96*2b30f829SVladimir Oltean struct nlattr *nest; 97*2b30f829SVladimir Oltean 98*2b30f829SVladimir Oltean nest = nla_nest_start(skb, ETHTOOL_A_MM_STATS); 99*2b30f829SVladimir Oltean if (!nest) 100*2b30f829SVladimir Oltean return -EMSGSIZE; 101*2b30f829SVladimir Oltean 102*2b30f829SVladimir Oltean if (mm_put_stat(skb, stats->MACMergeFrameAssErrorCount, 103*2b30f829SVladimir Oltean ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS) || 104*2b30f829SVladimir Oltean mm_put_stat(skb, stats->MACMergeFrameSmdErrorCount, 105*2b30f829SVladimir Oltean ETHTOOL_A_MM_STAT_SMD_ERRORS) || 106*2b30f829SVladimir Oltean mm_put_stat(skb, stats->MACMergeFrameAssOkCount, 107*2b30f829SVladimir Oltean ETHTOOL_A_MM_STAT_REASSEMBLY_OK) || 108*2b30f829SVladimir Oltean mm_put_stat(skb, stats->MACMergeFragCountRx, 109*2b30f829SVladimir Oltean ETHTOOL_A_MM_STAT_RX_FRAG_COUNT) || 110*2b30f829SVladimir Oltean mm_put_stat(skb, stats->MACMergeFragCountTx, 111*2b30f829SVladimir Oltean ETHTOOL_A_MM_STAT_TX_FRAG_COUNT) || 112*2b30f829SVladimir Oltean mm_put_stat(skb, stats->MACMergeHoldCount, 113*2b30f829SVladimir Oltean ETHTOOL_A_MM_STAT_HOLD_COUNT)) 114*2b30f829SVladimir Oltean goto err_cancel; 115*2b30f829SVladimir Oltean 116*2b30f829SVladimir Oltean nla_nest_end(skb, nest); 117*2b30f829SVladimir Oltean return 0; 118*2b30f829SVladimir Oltean 119*2b30f829SVladimir Oltean err_cancel: 120*2b30f829SVladimir Oltean nla_nest_cancel(skb, nest); 121*2b30f829SVladimir Oltean return -EMSGSIZE; 122*2b30f829SVladimir Oltean } 123*2b30f829SVladimir Oltean 124*2b30f829SVladimir Oltean static int mm_fill_reply(struct sk_buff *skb, 125*2b30f829SVladimir Oltean const struct ethnl_req_info *req_base, 126*2b30f829SVladimir Oltean const struct ethnl_reply_data *reply_base) 127*2b30f829SVladimir Oltean { 128*2b30f829SVladimir Oltean const struct mm_reply_data *data = MM_REPDATA(reply_base); 129*2b30f829SVladimir Oltean const struct ethtool_mm_state *state = &data->state; 130*2b30f829SVladimir Oltean 131*2b30f829SVladimir Oltean if (nla_put_u8(skb, ETHTOOL_A_MM_TX_ENABLED, state->tx_enabled) || 132*2b30f829SVladimir Oltean nla_put_u8(skb, ETHTOOL_A_MM_TX_ACTIVE, state->tx_active) || 133*2b30f829SVladimir Oltean nla_put_u8(skb, ETHTOOL_A_MM_PMAC_ENABLED, state->pmac_enabled) || 134*2b30f829SVladimir Oltean nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_ENABLED, state->verify_enabled) || 135*2b30f829SVladimir Oltean nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_STATUS, state->verify_status) || 136*2b30f829SVladimir Oltean nla_put_u32(skb, ETHTOOL_A_MM_VERIFY_TIME, state->verify_time) || 137*2b30f829SVladimir Oltean nla_put_u32(skb, ETHTOOL_A_MM_MAX_VERIFY_TIME, state->max_verify_time) || 138*2b30f829SVladimir Oltean nla_put_u32(skb, ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, state->tx_min_frag_size) || 139*2b30f829SVladimir Oltean nla_put_u32(skb, ETHTOOL_A_MM_RX_MIN_FRAG_SIZE, state->rx_min_frag_size)) 140*2b30f829SVladimir Oltean return -EMSGSIZE; 141*2b30f829SVladimir Oltean 142*2b30f829SVladimir Oltean if (req_base->flags & ETHTOOL_FLAG_STATS && 143*2b30f829SVladimir Oltean mm_put_stats(skb, &data->stats)) 144*2b30f829SVladimir Oltean return -EMSGSIZE; 145*2b30f829SVladimir Oltean 146*2b30f829SVladimir Oltean return 0; 147*2b30f829SVladimir Oltean } 148*2b30f829SVladimir Oltean 149*2b30f829SVladimir Oltean const struct ethnl_request_ops ethnl_mm_request_ops = { 150*2b30f829SVladimir Oltean .request_cmd = ETHTOOL_MSG_MM_GET, 151*2b30f829SVladimir Oltean .reply_cmd = ETHTOOL_MSG_MM_GET_REPLY, 152*2b30f829SVladimir Oltean .hdr_attr = ETHTOOL_A_MM_HEADER, 153*2b30f829SVladimir Oltean .req_info_size = sizeof(struct mm_req_info), 154*2b30f829SVladimir Oltean .reply_data_size = sizeof(struct mm_reply_data), 155*2b30f829SVladimir Oltean 156*2b30f829SVladimir Oltean .prepare_data = mm_prepare_data, 157*2b30f829SVladimir Oltean .reply_size = mm_reply_size, 158*2b30f829SVladimir Oltean .fill_reply = mm_fill_reply, 159*2b30f829SVladimir Oltean }; 160*2b30f829SVladimir Oltean 161*2b30f829SVladimir Oltean const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1] = { 162*2b30f829SVladimir Oltean [ETHTOOL_A_MM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 163*2b30f829SVladimir Oltean [ETHTOOL_A_MM_VERIFY_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1), 164*2b30f829SVladimir Oltean [ETHTOOL_A_MM_VERIFY_TIME] = NLA_POLICY_RANGE(NLA_U32, 1, 128), 165*2b30f829SVladimir Oltean [ETHTOOL_A_MM_TX_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1), 166*2b30f829SVladimir Oltean [ETHTOOL_A_MM_PMAC_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1), 167*2b30f829SVladimir Oltean [ETHTOOL_A_MM_TX_MIN_FRAG_SIZE] = NLA_POLICY_RANGE(NLA_U32, 60, 252), 168*2b30f829SVladimir Oltean }; 169*2b30f829SVladimir Oltean 170*2b30f829SVladimir Oltean static void mm_state_to_cfg(const struct ethtool_mm_state *state, 171*2b30f829SVladimir Oltean struct ethtool_mm_cfg *cfg) 172*2b30f829SVladimir Oltean { 173*2b30f829SVladimir Oltean /* We could also compare state->verify_status against 174*2b30f829SVladimir Oltean * ETHTOOL_MM_VERIFY_STATUS_DISABLED, but state->verify_enabled 175*2b30f829SVladimir Oltean * is more like an administrative state which should be seen in 176*2b30f829SVladimir Oltean * ETHTOOL_MSG_MM_GET replies. For example, a port with verification 177*2b30f829SVladimir Oltean * disabled might be in the ETHTOOL_MM_VERIFY_STATUS_INITIAL 178*2b30f829SVladimir Oltean * if it's down. 179*2b30f829SVladimir Oltean */ 180*2b30f829SVladimir Oltean cfg->verify_enabled = state->verify_enabled; 181*2b30f829SVladimir Oltean cfg->verify_time = state->verify_time; 182*2b30f829SVladimir Oltean cfg->tx_enabled = state->tx_enabled; 183*2b30f829SVladimir Oltean cfg->pmac_enabled = state->pmac_enabled; 184*2b30f829SVladimir Oltean cfg->tx_min_frag_size = state->tx_min_frag_size; 185*2b30f829SVladimir Oltean } 186*2b30f829SVladimir Oltean 187*2b30f829SVladimir Oltean int ethnl_set_mm(struct sk_buff *skb, struct genl_info *info) 188*2b30f829SVladimir Oltean { 189*2b30f829SVladimir Oltean struct netlink_ext_ack *extack = info->extack; 190*2b30f829SVladimir Oltean struct ethnl_req_info req_info = {}; 191*2b30f829SVladimir Oltean struct ethtool_mm_state state = {}; 192*2b30f829SVladimir Oltean struct nlattr **tb = info->attrs; 193*2b30f829SVladimir Oltean struct ethtool_mm_cfg cfg = {}; 194*2b30f829SVladimir Oltean const struct ethtool_ops *ops; 195*2b30f829SVladimir Oltean struct net_device *dev; 196*2b30f829SVladimir Oltean bool mod = false; 197*2b30f829SVladimir Oltean int ret; 198*2b30f829SVladimir Oltean 199*2b30f829SVladimir Oltean ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_MM_HEADER], 200*2b30f829SVladimir Oltean genl_info_net(info), extack, true); 201*2b30f829SVladimir Oltean if (ret) 202*2b30f829SVladimir Oltean return ret; 203*2b30f829SVladimir Oltean 204*2b30f829SVladimir Oltean dev = req_info.dev; 205*2b30f829SVladimir Oltean ops = dev->ethtool_ops; 206*2b30f829SVladimir Oltean 207*2b30f829SVladimir Oltean if (!ops->get_mm || !ops->set_mm) { 208*2b30f829SVladimir Oltean ret = -EOPNOTSUPP; 209*2b30f829SVladimir Oltean goto out_dev_put; 210*2b30f829SVladimir Oltean } 211*2b30f829SVladimir Oltean 212*2b30f829SVladimir Oltean rtnl_lock(); 213*2b30f829SVladimir Oltean ret = ethnl_ops_begin(dev); 214*2b30f829SVladimir Oltean if (ret < 0) 215*2b30f829SVladimir Oltean goto out_rtnl_unlock; 216*2b30f829SVladimir Oltean 217*2b30f829SVladimir Oltean ret = ops->get_mm(dev, &state); 218*2b30f829SVladimir Oltean if (ret) 219*2b30f829SVladimir Oltean goto out_complete; 220*2b30f829SVladimir Oltean 221*2b30f829SVladimir Oltean mm_state_to_cfg(&state, &cfg); 222*2b30f829SVladimir Oltean 223*2b30f829SVladimir Oltean ethnl_update_bool(&cfg.verify_enabled, tb[ETHTOOL_A_MM_VERIFY_ENABLED], 224*2b30f829SVladimir Oltean &mod); 225*2b30f829SVladimir Oltean ethnl_update_u32(&cfg.verify_time, tb[ETHTOOL_A_MM_VERIFY_TIME], &mod); 226*2b30f829SVladimir Oltean ethnl_update_bool(&cfg.tx_enabled, tb[ETHTOOL_A_MM_TX_ENABLED], &mod); 227*2b30f829SVladimir Oltean ethnl_update_bool(&cfg.pmac_enabled, tb[ETHTOOL_A_MM_PMAC_ENABLED], 228*2b30f829SVladimir Oltean &mod); 229*2b30f829SVladimir Oltean ethnl_update_u32(&cfg.tx_min_frag_size, 230*2b30f829SVladimir Oltean tb[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE], &mod); 231*2b30f829SVladimir Oltean 232*2b30f829SVladimir Oltean if (!mod) 233*2b30f829SVladimir Oltean goto out_complete; 234*2b30f829SVladimir Oltean 235*2b30f829SVladimir Oltean if (cfg.verify_time > state.max_verify_time) { 236*2b30f829SVladimir Oltean NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MM_VERIFY_TIME], 237*2b30f829SVladimir Oltean "verifyTime exceeds device maximum"); 238*2b30f829SVladimir Oltean ret = -ERANGE; 239*2b30f829SVladimir Oltean goto out_complete; 240*2b30f829SVladimir Oltean } 241*2b30f829SVladimir Oltean 242*2b30f829SVladimir Oltean ret = ops->set_mm(dev, &cfg, extack); 243*2b30f829SVladimir Oltean if (ret) 244*2b30f829SVladimir Oltean goto out_complete; 245*2b30f829SVladimir Oltean 246*2b30f829SVladimir Oltean ethtool_notify(dev, ETHTOOL_MSG_MM_NTF, NULL); 247*2b30f829SVladimir Oltean 248*2b30f829SVladimir Oltean out_complete: 249*2b30f829SVladimir Oltean ethnl_ops_complete(dev); 250*2b30f829SVladimir Oltean out_rtnl_unlock: 251*2b30f829SVladimir Oltean rtnl_unlock(); 252*2b30f829SVladimir Oltean out_dev_put: 253*2b30f829SVladimir Oltean ethnl_parse_header_dev_put(&req_info); 254*2b30f829SVladimir Oltean return ret; 255*2b30f829SVladimir Oltean } 256