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 */ 571241e329SSubbaraya Sundeep nla_total_size(sizeof(u8)) + /* _RINGS_TCP_DATA_SPLIT */ 58*4dc84c06SJie Wang nla_total_size(sizeof(u32) + /* _RINGS_CQE_SIZE */ 59*4dc84c06SJie Wang nla_total_size(sizeof(u8))); /* _RINGS_TX_PUSH */ 60e4a1717bSMichal Kubecek } 61e4a1717bSMichal Kubecek 62e4a1717bSMichal Kubecek static int rings_fill_reply(struct sk_buff *skb, 63e4a1717bSMichal Kubecek const struct ethnl_req_info *req_base, 64e4a1717bSMichal Kubecek const struct ethnl_reply_data *reply_base) 65e4a1717bSMichal Kubecek { 66e4a1717bSMichal Kubecek const struct rings_reply_data *data = RINGS_REPDATA(reply_base); 679690ae60SJakub Kicinski const struct kernel_ethtool_ringparam *kr = &data->kernel_ringparam; 68e4a1717bSMichal Kubecek const struct ethtool_ringparam *ringparam = &data->ringparam; 69e4a1717bSMichal Kubecek 709690ae60SJakub Kicinski WARN_ON(kr->tcp_data_split > ETHTOOL_TCP_DATA_SPLIT_ENABLED); 719690ae60SJakub Kicinski 72e4a1717bSMichal Kubecek if ((ringparam->rx_max_pending && 73e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MAX, 74e4a1717bSMichal Kubecek ringparam->rx_max_pending) || 75e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_RX, 76e4a1717bSMichal Kubecek ringparam->rx_pending))) || 77e4a1717bSMichal Kubecek (ringparam->rx_mini_max_pending && 78e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI_MAX, 79e4a1717bSMichal Kubecek ringparam->rx_mini_max_pending) || 80e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI, 81e4a1717bSMichal Kubecek ringparam->rx_mini_pending))) || 82e4a1717bSMichal Kubecek (ringparam->rx_jumbo_max_pending && 83e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO_MAX, 84e4a1717bSMichal Kubecek ringparam->rx_jumbo_max_pending) || 85e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO, 86e4a1717bSMichal Kubecek ringparam->rx_jumbo_pending))) || 87e4a1717bSMichal Kubecek (ringparam->tx_max_pending && 88e4a1717bSMichal Kubecek (nla_put_u32(skb, ETHTOOL_A_RINGS_TX_MAX, 89e4a1717bSMichal Kubecek ringparam->tx_max_pending) || 90e4a1717bSMichal Kubecek nla_put_u32(skb, ETHTOOL_A_RINGS_TX, 910b70c256SHao Chen ringparam->tx_pending))) || 929690ae60SJakub Kicinski (kr->rx_buf_len && 939690ae60SJakub Kicinski (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_BUF_LEN, kr->rx_buf_len))) || 949690ae60SJakub Kicinski (kr->tcp_data_split && 959690ae60SJakub Kicinski (nla_put_u8(skb, ETHTOOL_A_RINGS_TCP_DATA_SPLIT, 961241e329SSubbaraya Sundeep kr->tcp_data_split))) || 971241e329SSubbaraya Sundeep (kr->cqe_size && 98*4dc84c06SJie Wang (nla_put_u32(skb, ETHTOOL_A_RINGS_CQE_SIZE, kr->cqe_size))) || 99*4dc84c06SJie Wang nla_put_u8(skb, ETHTOOL_A_RINGS_TX_PUSH, !!kr->tx_push)) 100e4a1717bSMichal Kubecek return -EMSGSIZE; 101e4a1717bSMichal Kubecek 102e4a1717bSMichal Kubecek return 0; 103e4a1717bSMichal Kubecek } 104e4a1717bSMichal Kubecek 105e4a1717bSMichal Kubecek const struct ethnl_request_ops ethnl_rings_request_ops = { 106e4a1717bSMichal Kubecek .request_cmd = ETHTOOL_MSG_RINGS_GET, 107e4a1717bSMichal Kubecek .reply_cmd = ETHTOOL_MSG_RINGS_GET_REPLY, 108e4a1717bSMichal Kubecek .hdr_attr = ETHTOOL_A_RINGS_HEADER, 109e4a1717bSMichal Kubecek .req_info_size = sizeof(struct rings_req_info), 110e4a1717bSMichal Kubecek .reply_data_size = sizeof(struct rings_reply_data), 111e4a1717bSMichal Kubecek 112e4a1717bSMichal Kubecek .prepare_data = rings_prepare_data, 113e4a1717bSMichal Kubecek .reply_size = rings_reply_size, 114e4a1717bSMichal Kubecek .fill_reply = rings_fill_reply, 115e4a1717bSMichal Kubecek }; 1162fc2929eSMichal Kubecek 1172fc2929eSMichal Kubecek /* RINGS_SET */ 1182fc2929eSMichal Kubecek 119ff419afaSJakub Kicinski const struct nla_policy ethnl_rings_set_policy[] = { 120329d9c33SJakub Kicinski [ETHTOOL_A_RINGS_HEADER] = 121329d9c33SJakub Kicinski NLA_POLICY_NESTED(ethnl_header_policy), 1222fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_RX] = { .type = NLA_U32 }, 1232fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_RX_MINI] = { .type = NLA_U32 }, 1242fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_RX_JUMBO] = { .type = NLA_U32 }, 1252fc2929eSMichal Kubecek [ETHTOOL_A_RINGS_TX] = { .type = NLA_U32 }, 1260b70c256SHao Chen [ETHTOOL_A_RINGS_RX_BUF_LEN] = NLA_POLICY_MIN(NLA_U32, 1), 1271241e329SSubbaraya Sundeep [ETHTOOL_A_RINGS_CQE_SIZE] = NLA_POLICY_MIN(NLA_U32, 1), 128*4dc84c06SJie Wang [ETHTOOL_A_RINGS_TX_PUSH] = NLA_POLICY_MAX(NLA_U8, 1), 1292fc2929eSMichal Kubecek }; 1302fc2929eSMichal Kubecek 1312fc2929eSMichal Kubecek int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info) 1322fc2929eSMichal Kubecek { 1330b70c256SHao Chen struct kernel_ethtool_ringparam kernel_ringparam = {}; 1342fc2929eSMichal Kubecek struct ethtool_ringparam ringparam = {}; 1352fc2929eSMichal Kubecek struct ethnl_req_info req_info = {}; 1365028588bSJakub Kicinski struct nlattr **tb = info->attrs; 1372fc2929eSMichal Kubecek const struct nlattr *err_attr; 1382fc2929eSMichal Kubecek const struct ethtool_ops *ops; 1392fc2929eSMichal Kubecek struct net_device *dev; 1402fc2929eSMichal Kubecek bool mod = false; 1412fc2929eSMichal Kubecek int ret; 1422fc2929eSMichal Kubecek 1432fc2929eSMichal Kubecek ret = ethnl_parse_header_dev_get(&req_info, 1442fc2929eSMichal Kubecek tb[ETHTOOL_A_RINGS_HEADER], 1452fc2929eSMichal Kubecek genl_info_net(info), info->extack, 1462fc2929eSMichal Kubecek true); 1472fc2929eSMichal Kubecek if (ret < 0) 1482fc2929eSMichal Kubecek return ret; 1492fc2929eSMichal Kubecek dev = req_info.dev; 1502fc2929eSMichal Kubecek ops = dev->ethtool_ops; 1512fc2929eSMichal Kubecek ret = -EOPNOTSUPP; 1522fc2929eSMichal Kubecek if (!ops->get_ringparam || !ops->set_ringparam) 1532fc2929eSMichal Kubecek goto out_dev; 1542fc2929eSMichal Kubecek 155*4dc84c06SJie Wang if (tb[ETHTOOL_A_RINGS_TX_PUSH] && 156*4dc84c06SJie Wang !(ops->supported_ring_params & ETHTOOL_RING_USE_TX_PUSH)) { 157*4dc84c06SJie Wang ret = -EOPNOTSUPP; 158*4dc84c06SJie Wang NL_SET_ERR_MSG_ATTR(info->extack, 159*4dc84c06SJie Wang tb[ETHTOOL_A_RINGS_TX_PUSH], 160*4dc84c06SJie Wang "setting tx push not supported"); 161*4dc84c06SJie Wang goto out_dev; 162*4dc84c06SJie Wang } 163*4dc84c06SJie Wang 1642fc2929eSMichal Kubecek rtnl_lock(); 1652fc2929eSMichal Kubecek ret = ethnl_ops_begin(dev); 1662fc2929eSMichal Kubecek if (ret < 0) 1672fc2929eSMichal Kubecek goto out_rtnl; 16874624944SHao Chen ops->get_ringparam(dev, &ringparam, &kernel_ringparam, info->extack); 1692fc2929eSMichal Kubecek 1702fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.rx_pending, tb[ETHTOOL_A_RINGS_RX], &mod); 1712fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.rx_mini_pending, 1722fc2929eSMichal Kubecek tb[ETHTOOL_A_RINGS_RX_MINI], &mod); 1732fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.rx_jumbo_pending, 1742fc2929eSMichal Kubecek tb[ETHTOOL_A_RINGS_RX_JUMBO], &mod); 1752fc2929eSMichal Kubecek ethnl_update_u32(&ringparam.tx_pending, tb[ETHTOOL_A_RINGS_TX], &mod); 1760b70c256SHao Chen ethnl_update_u32(&kernel_ringparam.rx_buf_len, 1770b70c256SHao Chen tb[ETHTOOL_A_RINGS_RX_BUF_LEN], &mod); 1781241e329SSubbaraya Sundeep ethnl_update_u32(&kernel_ringparam.cqe_size, 1791241e329SSubbaraya Sundeep tb[ETHTOOL_A_RINGS_CQE_SIZE], &mod); 180*4dc84c06SJie Wang ethnl_update_u8(&kernel_ringparam.tx_push, 181*4dc84c06SJie Wang tb[ETHTOOL_A_RINGS_TX_PUSH], &mod); 1822fc2929eSMichal Kubecek ret = 0; 1832fc2929eSMichal Kubecek if (!mod) 1842fc2929eSMichal Kubecek goto out_ops; 1852fc2929eSMichal Kubecek 1862fc2929eSMichal Kubecek /* ensure new ring parameters are within limits */ 1872fc2929eSMichal Kubecek if (ringparam.rx_pending > ringparam.rx_max_pending) 1882fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_RX]; 1892fc2929eSMichal Kubecek else if (ringparam.rx_mini_pending > ringparam.rx_mini_max_pending) 1902fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_RX_MINI]; 1912fc2929eSMichal Kubecek else if (ringparam.rx_jumbo_pending > ringparam.rx_jumbo_max_pending) 1922fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_RX_JUMBO]; 1932fc2929eSMichal Kubecek else if (ringparam.tx_pending > ringparam.tx_max_pending) 1942fc2929eSMichal Kubecek err_attr = tb[ETHTOOL_A_RINGS_TX]; 1952fc2929eSMichal Kubecek else 1962fc2929eSMichal Kubecek err_attr = NULL; 1972fc2929eSMichal Kubecek if (err_attr) { 1982fc2929eSMichal Kubecek ret = -EINVAL; 1992fc2929eSMichal Kubecek NL_SET_ERR_MSG_ATTR(info->extack, err_attr, 2005ec82c49SColin Ian King "requested ring size exceeds maximum"); 2012fc2929eSMichal Kubecek goto out_ops; 2022fc2929eSMichal Kubecek } 2032fc2929eSMichal Kubecek 2040b70c256SHao Chen if (kernel_ringparam.rx_buf_len != 0 && 2050b70c256SHao Chen !(ops->supported_ring_params & ETHTOOL_RING_USE_RX_BUF_LEN)) { 2060b70c256SHao Chen ret = -EOPNOTSUPP; 2070b70c256SHao Chen NL_SET_ERR_MSG_ATTR(info->extack, 2080b70c256SHao Chen tb[ETHTOOL_A_RINGS_RX_BUF_LEN], 2090b70c256SHao Chen "setting rx buf len not supported"); 2100b70c256SHao Chen goto out_ops; 2110b70c256SHao Chen } 2120b70c256SHao Chen 2131241e329SSubbaraya Sundeep if (kernel_ringparam.cqe_size && 2141241e329SSubbaraya Sundeep !(ops->supported_ring_params & ETHTOOL_RING_USE_CQE_SIZE)) { 2151241e329SSubbaraya Sundeep ret = -EOPNOTSUPP; 2161241e329SSubbaraya Sundeep NL_SET_ERR_MSG_ATTR(info->extack, 2171241e329SSubbaraya Sundeep tb[ETHTOOL_A_RINGS_CQE_SIZE], 2181241e329SSubbaraya Sundeep "setting cqe size not supported"); 2191241e329SSubbaraya Sundeep goto out_ops; 2201241e329SSubbaraya Sundeep } 2211241e329SSubbaraya Sundeep 22274624944SHao Chen ret = dev->ethtool_ops->set_ringparam(dev, &ringparam, 22374624944SHao Chen &kernel_ringparam, info->extack); 224bc9d1c99SMichal Kubecek if (ret < 0) 225bc9d1c99SMichal Kubecek goto out_ops; 226bc9d1c99SMichal Kubecek ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF, NULL); 2272fc2929eSMichal Kubecek 2282fc2929eSMichal Kubecek out_ops: 2292fc2929eSMichal Kubecek ethnl_ops_complete(dev); 2302fc2929eSMichal Kubecek out_rtnl: 2312fc2929eSMichal Kubecek rtnl_unlock(); 2322fc2929eSMichal Kubecek out_dev: 23334ac17ecSEric Dumazet ethnl_parse_header_dev_put(&req_info); 2342fc2929eSMichal Kubecek return ret; 2352fc2929eSMichal Kubecek } 236