1e4a1717bSMichal Kubecek // SPDX-License-Identifier: GPL-2.0-only 2e4a1717bSMichal Kubecek 3e4a1717bSMichal Kubecek #include "netlink.h" 4e4a1717bSMichal Kubecek #include "common.h" 5e4a1717bSMichal Kubecek 6e4a1717bSMichal Kubecek struct rings_req_info { 7e4a1717bSMichal Kubecek struct ethnl_req_info base; 8e4a1717bSMichal Kubecek }; 9e4a1717bSMichal Kubecek 10e4a1717bSMichal Kubecek struct rings_reply_data { 11e4a1717bSMichal Kubecek struct ethnl_reply_data base; 12e4a1717bSMichal Kubecek struct ethtool_ringparam ringparam; 130b70c256SHao Chen struct kernel_ethtool_ringparam kernel_ringparam; 14e4a1717bSMichal Kubecek }; 15e4a1717bSMichal Kubecek 16e4a1717bSMichal Kubecek #define RINGS_REPDATA(__reply_base) \ 17e4a1717bSMichal Kubecek container_of(__reply_base, struct rings_reply_data, base) 18e4a1717bSMichal Kubecek 19ff419afaSJakub Kicinski const struct nla_policy ethnl_rings_get_policy[] = { 20329d9c33SJakub Kicinski [ETHTOOL_A_RINGS_HEADER] = 21329d9c33SJakub Kicinski NLA_POLICY_NESTED(ethnl_header_policy), 22e4a1717bSMichal Kubecek }; 23e4a1717bSMichal Kubecek 24e4a1717bSMichal Kubecek static int rings_prepare_data(const struct ethnl_req_info *req_base, 25e4a1717bSMichal Kubecek struct ethnl_reply_data *reply_base, 26e4a1717bSMichal Kubecek struct genl_info *info) 27e4a1717bSMichal Kubecek { 28e4a1717bSMichal Kubecek struct rings_reply_data *data = RINGS_REPDATA(reply_base); 2974624944SHao Chen struct netlink_ext_ack *extack = info ? info->extack : NULL; 30e4a1717bSMichal Kubecek struct net_device *dev = reply_base->dev; 31e4a1717bSMichal Kubecek int ret; 32e4a1717bSMichal Kubecek 33e4a1717bSMichal Kubecek if (!dev->ethtool_ops->get_ringparam) 34e4a1717bSMichal Kubecek return -EOPNOTSUPP; 35e4a1717bSMichal Kubecek ret = ethnl_ops_begin(dev); 36e4a1717bSMichal Kubecek if (ret < 0) 37e4a1717bSMichal Kubecek return ret; 3874624944SHao Chen dev->ethtool_ops->get_ringparam(dev, &data->ringparam, 3974624944SHao Chen &data->kernel_ringparam, extack); 40e4a1717bSMichal Kubecek ethnl_ops_complete(dev); 41e4a1717bSMichal Kubecek 42e4a1717bSMichal Kubecek return 0; 43e4a1717bSMichal Kubecek } 44e4a1717bSMichal Kubecek 45e4a1717bSMichal Kubecek static int rings_reply_size(const struct ethnl_req_info *req_base, 46e4a1717bSMichal Kubecek const struct ethnl_reply_data *reply_base) 47e4a1717bSMichal Kubecek { 48e4a1717bSMichal Kubecek return nla_total_size(sizeof(u32)) + /* _RINGS_RX_MAX */ 49e4a1717bSMichal Kubecek nla_total_size(sizeof(u32)) + /* _RINGS_RX_MINI_MAX */ 50e4a1717bSMichal Kubecek nla_total_size(sizeof(u32)) + /* _RINGS_RX_JUMBO_MAX */ 51e4a1717bSMichal Kubecek nla_total_size(sizeof(u32)) + /* _RINGS_TX_MAX */ 52e4a1717bSMichal Kubecek nla_total_size(sizeof(u32)) + /* _RINGS_RX */ 53e4a1717bSMichal Kubecek nla_total_size(sizeof(u32)) + /* _RINGS_RX_MINI */ 54e4a1717bSMichal Kubecek nla_total_size(sizeof(u32)) + /* _RINGS_RX_JUMBO */ 550b70c256SHao Chen nla_total_size(sizeof(u32)) + /* _RINGS_TX */ 56*9690ae60SJakub Kicinski nla_total_size(sizeof(u32)) + /* _RINGS_RX_BUF_LEN */ 57*9690ae60SJakub Kicinski nla_total_size(sizeof(u8)); /* _RINGS_TCP_DATA_SPLIT */ 58e4a1717bSMichal Kubecek } 59e4a1717bSMichal Kubecek 60e4a1717bSMichal Kubecek static int rings_fill_reply(struct sk_buff *skb, 61e4a1717bSMichal Kubecek const struct ethnl_req_info *req_base, 62e4a1717bSMichal Kubecek const struct ethnl_reply_data *reply_base) 63e4a1717bSMichal Kubecek { 64e4a1717bSMichal Kubecek const struct rings_reply_data *data = RINGS_REPDATA(reply_base); 65*9690ae60SJakub Kicinski const struct kernel_ethtool_ringparam *kr = &data->kernel_ringparam; 66e4a1717bSMichal Kubecek const struct ethtool_ringparam *ringparam = &data->ringparam; 67e4a1717bSMichal Kubecek 68*9690ae60SJakub Kicinski WARN_ON(kr->tcp_data_split > ETHTOOL_TCP_DATA_SPLIT_ENABLED); 69*9690ae60SJakub Kicinski 70e4a1717bSMichal Kubecek if ((ringparam->rx_max_pending && 71e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MAX, 72e4a1717bSMichal Kubecek ringparam->rx_max_pending) || 73e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_RX, 74e4a1717bSMichal Kubecek ringparam->rx_pending))) || 75e4a1717bSMichal Kubecek (ringparam->rx_mini_max_pending && 76e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI_MAX, 77e4a1717bSMichal Kubecek ringparam->rx_mini_max_pending) || 78e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI, 79e4a1717bSMichal Kubecek ringparam->rx_mini_pending))) || 80e4a1717bSMichal Kubecek (ringparam->rx_jumbo_max_pending && 81e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO_MAX, 82e4a1717bSMichal Kubecek ringparam->rx_jumbo_max_pending) || 83e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO, 84e4a1717bSMichal Kubecek ringparam->rx_jumbo_pending))) || 85e4a1717bSMichal Kubecek (ringparam->tx_max_pending && 86e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_TX_MAX, 87e4a1717bSMichal Kubecek ringparam->tx_max_pending) || 88e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_TX, 890b70c256SHao Chen ringparam->tx_pending))) || 90*9690ae60SJakub Kicinski (kr->rx_buf_len && 91*9690ae60SJakub Kicinski (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_BUF_LEN, kr->rx_buf_len))) || 92*9690ae60SJakub Kicinski (kr->tcp_data_split && 93*9690ae60SJakub Kicinski (nla_put_u8(skb, ETHTOOL_A_RINGS_TCP_DATA_SPLIT, 94*9690ae60SJakub Kicinski kr->tcp_data_split)))) 95e4a1717bSMichal Kubecek return -EMSGSIZE; 96e4a1717bSMichal Kubecek 97e4a1717bSMichal Kubecek return 0; 98e4a1717bSMichal Kubecek } 99e4a1717bSMichal Kubecek 100e4a1717bSMichal Kubecek const struct ethnl_request_ops ethnl_rings_request_ops = { 101e4a1717bSMichal Kubecek .request_cmd = ETHTOOL_MSG_RINGS_GET, 102e4a1717bSMichal Kubecek .reply_cmd = ETHTOOL_MSG_RINGS_GET_REPLY, 103e4a1717bSMichal Kubecek .hdr_attr = ETHTOOL_A_RINGS_HEADER, 104e4a1717bSMichal Kubecek .req_info_size = sizeof(struct rings_req_info), 105e4a1717bSMichal Kubecek .reply_data_size = sizeof(struct rings_reply_data), 106e4a1717bSMichal Kubecek 107e4a1717bSMichal Kubecek .prepare_data = rings_prepare_data, 108e4a1717bSMichal Kubecek .reply_size = rings_reply_size, 109e4a1717bSMichal Kubecek .fill_reply = rings_fill_reply, 110e4a1717bSMichal Kubecek }; 1112fc2929eSMichal Kubecek 1122fc2929eSMichal Kubecek /* RINGS_SET */ 1132fc2929eSMichal Kubecek 114ff419afaSJakub Kicinski const struct nla_policy ethnl_rings_set_policy[] = { 115329d9c33SJakub Kicinski [ETHTOOL_A_RINGS_HEADER] = 116329d9c33SJakub Kicinski NLA_POLICY_NESTED(ethnl_header_policy), 1172fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_RX] = { .type = NLA_U32 }, 1182fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_RX_MINI] = { .type = NLA_U32 }, 1192fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_RX_JUMBO] = { .type = NLA_U32 }, 1202fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_TX] = { .type = NLA_U32 }, 1210b70c256SHao Chen [ETHTOOL_A_RINGS_RX_BUF_LEN] = NLA_POLICY_MIN(NLA_U32, 1), 1222fc2929eSMichal Kubecek }; 1232fc2929eSMichal Kubecek 1242fc2929eSMichal Kubecek int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info) 1252fc2929eSMichal Kubecek { 1260b70c256SHao Chen struct kernel_ethtool_ringparam kernel_ringparam = {}; 1272fc2929eSMichal Kubecek struct ethtool_ringparam ringparam = {}; 1282fc2929eSMichal Kubecek struct ethnl_req_info req_info = {}; 1295028588bSJakub Kicinski struct nlattr **tb = info->attrs; 1302fc2929eSMichal Kubecek const struct nlattr *err_attr; 1312fc2929eSMichal Kubecek const struct ethtool_ops *ops; 1322fc2929eSMichal Kubecek struct net_device *dev; 1332fc2929eSMichal Kubecek bool mod = false; 1342fc2929eSMichal Kubecek int ret; 1352fc2929eSMichal Kubecek 1362fc2929eSMichal Kubecek ret = ethnl_parse_header_dev_get(&req_info, 1372fc2929eSMichal Kubecek tb[ETHTOOL_A_RINGS_HEADER], 1382fc2929eSMichal Kubecek genl_info_net(info), info->extack, 1392fc2929eSMichal Kubecek true); 1402fc2929eSMichal Kubecek if (ret < 0) 1412fc2929eSMichal Kubecek return ret; 1422fc2929eSMichal Kubecek dev = req_info.dev; 1432fc2929eSMichal Kubecek ops = dev->ethtool_ops; 1442fc2929eSMichal Kubecek ret = -EOPNOTSUPP; 1452fc2929eSMichal Kubecek if (!ops->get_ringparam || !ops->set_ringparam) 1462fc2929eSMichal Kubecek goto out_dev; 1472fc2929eSMichal Kubecek 1482fc2929eSMichal Kubecek rtnl_lock(); 1492fc2929eSMichal Kubecek ret = ethnl_ops_begin(dev); 1502fc2929eSMichal Kubecek if (ret < 0) 1512fc2929eSMichal Kubecek goto out_rtnl; 15274624944SHao Chen ops->get_ringparam(dev, &ringparam, &kernel_ringparam, info->extack); 1532fc2929eSMichal Kubecek 1542fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.rx_pending, tb[ETHTOOL_A_RINGS_RX], &mod); 1552fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.rx_mini_pending, 1562fc2929eSMichal Kubecek tb[ETHTOOL_A_RINGS_RX_MINI], &mod); 1572fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.rx_jumbo_pending, 1582fc2929eSMichal Kubecek tb[ETHTOOL_A_RINGS_RX_JUMBO], &mod); 1592fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.tx_pending, tb[ETHTOOL_A_RINGS_TX], &mod); 1600b70c256SHao Chen ethnl_update_u32(&kernel_ringparam.rx_buf_len, 1610b70c256SHao Chen tb[ETHTOOL_A_RINGS_RX_BUF_LEN], &mod); 1622fc2929eSMichal Kubecek ret = 0; 1632fc2929eSMichal Kubecek if (!mod) 1642fc2929eSMichal Kubecek goto out_ops; 1652fc2929eSMichal Kubecek 1662fc2929eSMichal Kubecek /* ensure new ring parameters are within limits */ 1672fc2929eSMichal Kubecek if (ringparam.rx_pending > ringparam.rx_max_pending) 1682fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_RX]; 1692fc2929eSMichal Kubecek else if (ringparam.rx_mini_pending > ringparam.rx_mini_max_pending) 1702fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_RX_MINI]; 1712fc2929eSMichal Kubecek else if (ringparam.rx_jumbo_pending > ringparam.rx_jumbo_max_pending) 1722fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_RX_JUMBO]; 1732fc2929eSMichal Kubecek else if (ringparam.tx_pending > ringparam.tx_max_pending) 1742fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_TX]; 1752fc2929eSMichal Kubecek else 1762fc2929eSMichal Kubecek err_attr = NULL; 1772fc2929eSMichal Kubecek if (err_attr) { 1782fc2929eSMichal Kubecek ret = -EINVAL; 1792fc2929eSMichal Kubecek NL_SET_ERR_MSG_ATTR(info->extack, err_attr, 1805ec82c49SColin Ian King "requested ring size exceeds maximum"); 1812fc2929eSMichal Kubecek goto out_ops; 1822fc2929eSMichal Kubecek } 1832fc2929eSMichal Kubecek 1840b70c256SHao Chen if (kernel_ringparam.rx_buf_len != 0 && 1850b70c256SHao Chen !(ops->supported_ring_params & ETHTOOL_RING_USE_RX_BUF_LEN)) { 1860b70c256SHao Chen ret = -EOPNOTSUPP; 1870b70c256SHao Chen NL_SET_ERR_MSG_ATTR(info->extack, 1880b70c256SHao Chen tb[ETHTOOL_A_RINGS_RX_BUF_LEN], 1890b70c256SHao Chen "setting rx buf len not supported"); 1900b70c256SHao Chen goto out_ops; 1910b70c256SHao Chen } 1920b70c256SHao Chen 19374624944SHao Chen ret = dev->ethtool_ops->set_ringparam(dev, &ringparam, 19474624944SHao Chen &kernel_ringparam, info->extack); 195bc9d1c99SMichal Kubecek if (ret < 0) 196bc9d1c99SMichal Kubecek goto out_ops; 197bc9d1c99SMichal Kubecek ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF, NULL); 1982fc2929eSMichal Kubecek 1992fc2929eSMichal Kubecek out_ops: 2002fc2929eSMichal Kubecek ethnl_ops_complete(dev); 2012fc2929eSMichal Kubecek out_rtnl: 2022fc2929eSMichal Kubecek rtnl_unlock(); 2032fc2929eSMichal Kubecek out_dev: 20434ac17ecSEric Dumazet ethnl_parse_header_dev_put(&req_info); 2052fc2929eSMichal Kubecek return ret; 2062fc2929eSMichal Kubecek } 207