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 */ 569690ae60SJakub Kicinski nla_total_size(sizeof(u32)) + /* _RINGS_RX_BUF_LEN */ 57*1241e329SSubbaraya Sundeep nla_total_size(sizeof(u8)) + /* _RINGS_TCP_DATA_SPLIT */ 58*1241e329SSubbaraya Sundeep nla_total_size(sizeof(u32)); /* _RINGS_CQE_SIZE */ 59e4a1717bSMichal Kubecek } 60e4a1717bSMichal Kubecek 61e4a1717bSMichal Kubecek static int rings_fill_reply(struct sk_buff *skb, 62e4a1717bSMichal Kubecek const struct ethnl_req_info *req_base, 63e4a1717bSMichal Kubecek const struct ethnl_reply_data *reply_base) 64e4a1717bSMichal Kubecek { 65e4a1717bSMichal Kubecek const struct rings_reply_data *data = RINGS_REPDATA(reply_base); 669690ae60SJakub Kicinski const struct kernel_ethtool_ringparam *kr = &data->kernel_ringparam; 67e4a1717bSMichal Kubecek const struct ethtool_ringparam *ringparam = &data->ringparam; 68e4a1717bSMichal Kubecek 699690ae60SJakub Kicinski WARN_ON(kr->tcp_data_split > ETHTOOL_TCP_DATA_SPLIT_ENABLED); 709690ae60SJakub Kicinski 71e4a1717bSMichal Kubecek if ((ringparam->rx_max_pending && 72e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MAX, 73e4a1717bSMichal Kubecek ringparam->rx_max_pending) || 74e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_RX, 75e4a1717bSMichal Kubecek ringparam->rx_pending))) || 76e4a1717bSMichal Kubecek (ringparam->rx_mini_max_pending && 77e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI_MAX, 78e4a1717bSMichal Kubecek ringparam->rx_mini_max_pending) || 79e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI, 80e4a1717bSMichal Kubecek ringparam->rx_mini_pending))) || 81e4a1717bSMichal Kubecek (ringparam->rx_jumbo_max_pending && 82e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO_MAX, 83e4a1717bSMichal Kubecek ringparam->rx_jumbo_max_pending) || 84e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO, 85e4a1717bSMichal Kubecek ringparam->rx_jumbo_pending))) || 86e4a1717bSMichal Kubecek (ringparam->tx_max_pending && 87e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_TX_MAX, 88e4a1717bSMichal Kubecek ringparam->tx_max_pending) || 89e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_TX, 900b70c256SHao Chen ringparam->tx_pending))) || 919690ae60SJakub Kicinski (kr->rx_buf_len && 929690ae60SJakub Kicinski (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_BUF_LEN, kr->rx_buf_len))) || 939690ae60SJakub Kicinski (kr->tcp_data_split && 949690ae60SJakub Kicinski (nla_put_u8(skb, ETHTOOL_A_RINGS_TCP_DATA_SPLIT, 95*1241e329SSubbaraya Sundeep kr->tcp_data_split))) || 96*1241e329SSubbaraya Sundeep (kr->cqe_size && 97*1241e329SSubbaraya Sundeep (nla_put_u32(skb, ETHTOOL_A_RINGS_CQE_SIZE, kr->cqe_size)))) 98e4a1717bSMichal Kubecek return -EMSGSIZE; 99e4a1717bSMichal Kubecek 100e4a1717bSMichal Kubecek return 0; 101e4a1717bSMichal Kubecek } 102e4a1717bSMichal Kubecek 103e4a1717bSMichal Kubecek const struct ethnl_request_ops ethnl_rings_request_ops = { 104e4a1717bSMichal Kubecek .request_cmd = ETHTOOL_MSG_RINGS_GET, 105e4a1717bSMichal Kubecek .reply_cmd = ETHTOOL_MSG_RINGS_GET_REPLY, 106e4a1717bSMichal Kubecek .hdr_attr = ETHTOOL_A_RINGS_HEADER, 107e4a1717bSMichal Kubecek .req_info_size = sizeof(struct rings_req_info), 108e4a1717bSMichal Kubecek .reply_data_size = sizeof(struct rings_reply_data), 109e4a1717bSMichal Kubecek 110e4a1717bSMichal Kubecek .prepare_data = rings_prepare_data, 111e4a1717bSMichal Kubecek .reply_size = rings_reply_size, 112e4a1717bSMichal Kubecek .fill_reply = rings_fill_reply, 113e4a1717bSMichal Kubecek }; 1142fc2929eSMichal Kubecek 1152fc2929eSMichal Kubecek /* RINGS_SET */ 1162fc2929eSMichal Kubecek 117ff419afaSJakub Kicinski const struct nla_policy ethnl_rings_set_policy[] = { 118329d9c33SJakub Kicinski [ETHTOOL_A_RINGS_HEADER] = 119329d9c33SJakub Kicinski NLA_POLICY_NESTED(ethnl_header_policy), 1202fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_RX] = { .type = NLA_U32 }, 1212fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_RX_MINI] = { .type = NLA_U32 }, 1222fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_RX_JUMBO] = { .type = NLA_U32 }, 1232fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_TX] = { .type = NLA_U32 }, 1240b70c256SHao Chen [ETHTOOL_A_RINGS_RX_BUF_LEN] = NLA_POLICY_MIN(NLA_U32, 1), 125*1241e329SSubbaraya Sundeep [ETHTOOL_A_RINGS_CQE_SIZE] = NLA_POLICY_MIN(NLA_U32, 1), 1262fc2929eSMichal Kubecek }; 1272fc2929eSMichal Kubecek 1282fc2929eSMichal Kubecek int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info) 1292fc2929eSMichal Kubecek { 1300b70c256SHao Chen struct kernel_ethtool_ringparam kernel_ringparam = {}; 1312fc2929eSMichal Kubecek struct ethtool_ringparam ringparam = {}; 1322fc2929eSMichal Kubecek struct ethnl_req_info req_info = {}; 1335028588bSJakub Kicinski struct nlattr **tb = info->attrs; 1342fc2929eSMichal Kubecek const struct nlattr *err_attr; 1352fc2929eSMichal Kubecek const struct ethtool_ops *ops; 1362fc2929eSMichal Kubecek struct net_device *dev; 1372fc2929eSMichal Kubecek bool mod = false; 1382fc2929eSMichal Kubecek int ret; 1392fc2929eSMichal Kubecek 1402fc2929eSMichal Kubecek ret = ethnl_parse_header_dev_get(&req_info, 1412fc2929eSMichal Kubecek tb[ETHTOOL_A_RINGS_HEADER], 1422fc2929eSMichal Kubecek genl_info_net(info), info->extack, 1432fc2929eSMichal Kubecek true); 1442fc2929eSMichal Kubecek if (ret < 0) 1452fc2929eSMichal Kubecek return ret; 1462fc2929eSMichal Kubecek dev = req_info.dev; 1472fc2929eSMichal Kubecek ops = dev->ethtool_ops; 1482fc2929eSMichal Kubecek ret = -EOPNOTSUPP; 1492fc2929eSMichal Kubecek if (!ops->get_ringparam || !ops->set_ringparam) 1502fc2929eSMichal Kubecek goto out_dev; 1512fc2929eSMichal Kubecek 1522fc2929eSMichal Kubecek rtnl_lock(); 1532fc2929eSMichal Kubecek ret = ethnl_ops_begin(dev); 1542fc2929eSMichal Kubecek if (ret < 0) 1552fc2929eSMichal Kubecek goto out_rtnl; 15674624944SHao Chen ops->get_ringparam(dev, &ringparam, &kernel_ringparam, info->extack); 1572fc2929eSMichal Kubecek 1582fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.rx_pending, tb[ETHTOOL_A_RINGS_RX], &mod); 1592fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.rx_mini_pending, 1602fc2929eSMichal Kubecek tb[ETHTOOL_A_RINGS_RX_MINI], &mod); 1612fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.rx_jumbo_pending, 1622fc2929eSMichal Kubecek tb[ETHTOOL_A_RINGS_RX_JUMBO], &mod); 1632fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.tx_pending, tb[ETHTOOL_A_RINGS_TX], &mod); 1640b70c256SHao Chen ethnl_update_u32(&kernel_ringparam.rx_buf_len, 1650b70c256SHao Chen tb[ETHTOOL_A_RINGS_RX_BUF_LEN], &mod); 166*1241e329SSubbaraya Sundeep ethnl_update_u32(&kernel_ringparam.cqe_size, 167*1241e329SSubbaraya Sundeep tb[ETHTOOL_A_RINGS_CQE_SIZE], &mod); 1682fc2929eSMichal Kubecek ret = 0; 1692fc2929eSMichal Kubecek if (!mod) 1702fc2929eSMichal Kubecek goto out_ops; 1712fc2929eSMichal Kubecek 1722fc2929eSMichal Kubecek /* ensure new ring parameters are within limits */ 1732fc2929eSMichal Kubecek if (ringparam.rx_pending > ringparam.rx_max_pending) 1742fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_RX]; 1752fc2929eSMichal Kubecek else if (ringparam.rx_mini_pending > ringparam.rx_mini_max_pending) 1762fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_RX_MINI]; 1772fc2929eSMichal Kubecek else if (ringparam.rx_jumbo_pending > ringparam.rx_jumbo_max_pending) 1782fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_RX_JUMBO]; 1792fc2929eSMichal Kubecek else if (ringparam.tx_pending > ringparam.tx_max_pending) 1802fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_TX]; 1812fc2929eSMichal Kubecek else 1822fc2929eSMichal Kubecek err_attr = NULL; 1832fc2929eSMichal Kubecek if (err_attr) { 1842fc2929eSMichal Kubecek ret = -EINVAL; 1852fc2929eSMichal Kubecek NL_SET_ERR_MSG_ATTR(info->extack, err_attr, 1865ec82c49SColin Ian King "requested ring size exceeds maximum"); 1872fc2929eSMichal Kubecek goto out_ops; 1882fc2929eSMichal Kubecek } 1892fc2929eSMichal Kubecek 1900b70c256SHao Chen if (kernel_ringparam.rx_buf_len != 0 && 1910b70c256SHao Chen !(ops->supported_ring_params & ETHTOOL_RING_USE_RX_BUF_LEN)) { 1920b70c256SHao Chen ret = -EOPNOTSUPP; 1930b70c256SHao Chen NL_SET_ERR_MSG_ATTR(info->extack, 1940b70c256SHao Chen tb[ETHTOOL_A_RINGS_RX_BUF_LEN], 1950b70c256SHao Chen "setting rx buf len not supported"); 1960b70c256SHao Chen goto out_ops; 1970b70c256SHao Chen } 1980b70c256SHao Chen 199*1241e329SSubbaraya Sundeep if (kernel_ringparam.cqe_size && 200*1241e329SSubbaraya Sundeep !(ops->supported_ring_params & ETHTOOL_RING_USE_CQE_SIZE)) { 201*1241e329SSubbaraya Sundeep ret = -EOPNOTSUPP; 202*1241e329SSubbaraya Sundeep NL_SET_ERR_MSG_ATTR(info->extack, 203*1241e329SSubbaraya Sundeep tb[ETHTOOL_A_RINGS_CQE_SIZE], 204*1241e329SSubbaraya Sundeep "setting cqe size not supported"); 205*1241e329SSubbaraya Sundeep goto out_ops; 206*1241e329SSubbaraya Sundeep } 207*1241e329SSubbaraya Sundeep 20874624944SHao Chen ret = dev->ethtool_ops->set_ringparam(dev, &ringparam, 20974624944SHao Chen &kernel_ringparam, info->extack); 210bc9d1c99SMichal Kubecek if (ret < 0) 211bc9d1c99SMichal Kubecek goto out_ops; 212bc9d1c99SMichal Kubecek ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF, NULL); 2132fc2929eSMichal Kubecek 2142fc2929eSMichal Kubecek out_ops: 2152fc2929eSMichal Kubecek ethnl_ops_complete(dev); 2162fc2929eSMichal Kubecek out_rtnl: 2172fc2929eSMichal Kubecek rtnl_unlock(); 2182fc2929eSMichal Kubecek out_dev: 21934ac17ecSEric Dumazet ethnl_parse_header_dev_put(&req_info); 2202fc2929eSMichal Kubecek return ret; 2212fc2929eSMichal Kubecek } 222