162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include "netlink.h" 462306a36Sopenharmony_ci#include "common.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_cistruct rings_req_info { 762306a36Sopenharmony_ci struct ethnl_req_info base; 862306a36Sopenharmony_ci}; 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistruct rings_reply_data { 1162306a36Sopenharmony_ci struct ethnl_reply_data base; 1262306a36Sopenharmony_ci struct ethtool_ringparam ringparam; 1362306a36Sopenharmony_ci struct kernel_ethtool_ringparam kernel_ringparam; 1462306a36Sopenharmony_ci u32 supported_ring_params; 1562306a36Sopenharmony_ci}; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define RINGS_REPDATA(__reply_base) \ 1862306a36Sopenharmony_ci container_of(__reply_base, struct rings_reply_data, base) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciconst struct nla_policy ethnl_rings_get_policy[] = { 2162306a36Sopenharmony_ci [ETHTOOL_A_RINGS_HEADER] = 2262306a36Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int rings_prepare_data(const struct ethnl_req_info *req_base, 2662306a36Sopenharmony_ci struct ethnl_reply_data *reply_base, 2762306a36Sopenharmony_ci const struct genl_info *info) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct rings_reply_data *data = RINGS_REPDATA(reply_base); 3062306a36Sopenharmony_ci struct net_device *dev = reply_base->dev; 3162306a36Sopenharmony_ci int ret; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (!dev->ethtool_ops->get_ringparam) 3462306a36Sopenharmony_ci return -EOPNOTSUPP; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci data->supported_ring_params = dev->ethtool_ops->supported_ring_params; 3762306a36Sopenharmony_ci ret = ethnl_ops_begin(dev); 3862306a36Sopenharmony_ci if (ret < 0) 3962306a36Sopenharmony_ci return ret; 4062306a36Sopenharmony_ci dev->ethtool_ops->get_ringparam(dev, &data->ringparam, 4162306a36Sopenharmony_ci &data->kernel_ringparam, info->extack); 4262306a36Sopenharmony_ci ethnl_ops_complete(dev); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int rings_reply_size(const struct ethnl_req_info *req_base, 4862306a36Sopenharmony_ci const struct ethnl_reply_data *reply_base) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci return nla_total_size(sizeof(u32)) + /* _RINGS_RX_MAX */ 5162306a36Sopenharmony_ci nla_total_size(sizeof(u32)) + /* _RINGS_RX_MINI_MAX */ 5262306a36Sopenharmony_ci nla_total_size(sizeof(u32)) + /* _RINGS_RX_JUMBO_MAX */ 5362306a36Sopenharmony_ci nla_total_size(sizeof(u32)) + /* _RINGS_TX_MAX */ 5462306a36Sopenharmony_ci nla_total_size(sizeof(u32)) + /* _RINGS_RX */ 5562306a36Sopenharmony_ci nla_total_size(sizeof(u32)) + /* _RINGS_RX_MINI */ 5662306a36Sopenharmony_ci nla_total_size(sizeof(u32)) + /* _RINGS_RX_JUMBO */ 5762306a36Sopenharmony_ci nla_total_size(sizeof(u32)) + /* _RINGS_TX */ 5862306a36Sopenharmony_ci nla_total_size(sizeof(u32)) + /* _RINGS_RX_BUF_LEN */ 5962306a36Sopenharmony_ci nla_total_size(sizeof(u8)) + /* _RINGS_TCP_DATA_SPLIT */ 6062306a36Sopenharmony_ci nla_total_size(sizeof(u32) + /* _RINGS_CQE_SIZE */ 6162306a36Sopenharmony_ci nla_total_size(sizeof(u8)) + /* _RINGS_TX_PUSH */ 6262306a36Sopenharmony_ci nla_total_size(sizeof(u8))) + /* _RINGS_RX_PUSH */ 6362306a36Sopenharmony_ci nla_total_size(sizeof(u32)) + /* _RINGS_TX_PUSH_BUF_LEN */ 6462306a36Sopenharmony_ci nla_total_size(sizeof(u32)); /* _RINGS_TX_PUSH_BUF_LEN_MAX */ 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int rings_fill_reply(struct sk_buff *skb, 6862306a36Sopenharmony_ci const struct ethnl_req_info *req_base, 6962306a36Sopenharmony_ci const struct ethnl_reply_data *reply_base) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci const struct rings_reply_data *data = RINGS_REPDATA(reply_base); 7262306a36Sopenharmony_ci const struct kernel_ethtool_ringparam *kr = &data->kernel_ringparam; 7362306a36Sopenharmony_ci const struct ethtool_ringparam *ringparam = &data->ringparam; 7462306a36Sopenharmony_ci u32 supported_ring_params = data->supported_ring_params; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci WARN_ON(kr->tcp_data_split > ETHTOOL_TCP_DATA_SPLIT_ENABLED); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if ((ringparam->rx_max_pending && 7962306a36Sopenharmony_ci (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MAX, 8062306a36Sopenharmony_ci ringparam->rx_max_pending) || 8162306a36Sopenharmony_ci nla_put_u32(skb, ETHTOOL_A_RINGS_RX, 8262306a36Sopenharmony_ci ringparam->rx_pending))) || 8362306a36Sopenharmony_ci (ringparam->rx_mini_max_pending && 8462306a36Sopenharmony_ci (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI_MAX, 8562306a36Sopenharmony_ci ringparam->rx_mini_max_pending) || 8662306a36Sopenharmony_ci nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI, 8762306a36Sopenharmony_ci ringparam->rx_mini_pending))) || 8862306a36Sopenharmony_ci (ringparam->rx_jumbo_max_pending && 8962306a36Sopenharmony_ci (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO_MAX, 9062306a36Sopenharmony_ci ringparam->rx_jumbo_max_pending) || 9162306a36Sopenharmony_ci nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO, 9262306a36Sopenharmony_ci ringparam->rx_jumbo_pending))) || 9362306a36Sopenharmony_ci (ringparam->tx_max_pending && 9462306a36Sopenharmony_ci (nla_put_u32(skb, ETHTOOL_A_RINGS_TX_MAX, 9562306a36Sopenharmony_ci ringparam->tx_max_pending) || 9662306a36Sopenharmony_ci nla_put_u32(skb, ETHTOOL_A_RINGS_TX, 9762306a36Sopenharmony_ci ringparam->tx_pending))) || 9862306a36Sopenharmony_ci (kr->rx_buf_len && 9962306a36Sopenharmony_ci (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_BUF_LEN, kr->rx_buf_len))) || 10062306a36Sopenharmony_ci (kr->tcp_data_split && 10162306a36Sopenharmony_ci (nla_put_u8(skb, ETHTOOL_A_RINGS_TCP_DATA_SPLIT, 10262306a36Sopenharmony_ci kr->tcp_data_split))) || 10362306a36Sopenharmony_ci (kr->cqe_size && 10462306a36Sopenharmony_ci (nla_put_u32(skb, ETHTOOL_A_RINGS_CQE_SIZE, kr->cqe_size))) || 10562306a36Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_RINGS_TX_PUSH, !!kr->tx_push) || 10662306a36Sopenharmony_ci nla_put_u8(skb, ETHTOOL_A_RINGS_RX_PUSH, !!kr->rx_push) || 10762306a36Sopenharmony_ci ((supported_ring_params & ETHTOOL_RING_USE_TX_PUSH_BUF_LEN) && 10862306a36Sopenharmony_ci (nla_put_u32(skb, ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX, 10962306a36Sopenharmony_ci kr->tx_push_buf_max_len) || 11062306a36Sopenharmony_ci nla_put_u32(skb, ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN, 11162306a36Sopenharmony_ci kr->tx_push_buf_len)))) 11262306a36Sopenharmony_ci return -EMSGSIZE; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* RINGS_SET */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ciconst struct nla_policy ethnl_rings_set_policy[] = { 12062306a36Sopenharmony_ci [ETHTOOL_A_RINGS_HEADER] = 12162306a36Sopenharmony_ci NLA_POLICY_NESTED(ethnl_header_policy), 12262306a36Sopenharmony_ci [ETHTOOL_A_RINGS_RX] = { .type = NLA_U32 }, 12362306a36Sopenharmony_ci [ETHTOOL_A_RINGS_RX_MINI] = { .type = NLA_U32 }, 12462306a36Sopenharmony_ci [ETHTOOL_A_RINGS_RX_JUMBO] = { .type = NLA_U32 }, 12562306a36Sopenharmony_ci [ETHTOOL_A_RINGS_TX] = { .type = NLA_U32 }, 12662306a36Sopenharmony_ci [ETHTOOL_A_RINGS_RX_BUF_LEN] = NLA_POLICY_MIN(NLA_U32, 1), 12762306a36Sopenharmony_ci [ETHTOOL_A_RINGS_CQE_SIZE] = NLA_POLICY_MIN(NLA_U32, 1), 12862306a36Sopenharmony_ci [ETHTOOL_A_RINGS_TX_PUSH] = NLA_POLICY_MAX(NLA_U8, 1), 12962306a36Sopenharmony_ci [ETHTOOL_A_RINGS_RX_PUSH] = NLA_POLICY_MAX(NLA_U8, 1), 13062306a36Sopenharmony_ci [ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN] = { .type = NLA_U32 }, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int 13462306a36Sopenharmony_ciethnl_set_rings_validate(struct ethnl_req_info *req_info, 13562306a36Sopenharmony_ci struct genl_info *info) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci const struct ethtool_ops *ops = req_info->dev->ethtool_ops; 13862306a36Sopenharmony_ci struct nlattr **tb = info->attrs; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (tb[ETHTOOL_A_RINGS_RX_BUF_LEN] && 14162306a36Sopenharmony_ci !(ops->supported_ring_params & ETHTOOL_RING_USE_RX_BUF_LEN)) { 14262306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 14362306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_RX_BUF_LEN], 14462306a36Sopenharmony_ci "setting rx buf len not supported"); 14562306a36Sopenharmony_ci return -EOPNOTSUPP; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (tb[ETHTOOL_A_RINGS_CQE_SIZE] && 14962306a36Sopenharmony_ci !(ops->supported_ring_params & ETHTOOL_RING_USE_CQE_SIZE)) { 15062306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 15162306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_CQE_SIZE], 15262306a36Sopenharmony_ci "setting cqe size not supported"); 15362306a36Sopenharmony_ci return -EOPNOTSUPP; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (tb[ETHTOOL_A_RINGS_TX_PUSH] && 15762306a36Sopenharmony_ci !(ops->supported_ring_params & ETHTOOL_RING_USE_TX_PUSH)) { 15862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 15962306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_TX_PUSH], 16062306a36Sopenharmony_ci "setting tx push not supported"); 16162306a36Sopenharmony_ci return -EOPNOTSUPP; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (tb[ETHTOOL_A_RINGS_RX_PUSH] && 16562306a36Sopenharmony_ci !(ops->supported_ring_params & ETHTOOL_RING_USE_RX_PUSH)) { 16662306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 16762306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_RX_PUSH], 16862306a36Sopenharmony_ci "setting rx push not supported"); 16962306a36Sopenharmony_ci return -EOPNOTSUPP; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN] && 17362306a36Sopenharmony_ci !(ops->supported_ring_params & ETHTOOL_RING_USE_TX_PUSH_BUF_LEN)) { 17462306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, 17562306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN], 17662306a36Sopenharmony_ci "setting tx push buf len is not supported"); 17762306a36Sopenharmony_ci return -EOPNOTSUPP; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return ops->get_ringparam && ops->set_ringparam ? 1 : -EOPNOTSUPP; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int 18462306a36Sopenharmony_ciethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct kernel_ethtool_ringparam kernel_ringparam = {}; 18762306a36Sopenharmony_ci struct ethtool_ringparam ringparam = {}; 18862306a36Sopenharmony_ci struct net_device *dev = req_info->dev; 18962306a36Sopenharmony_ci struct nlattr **tb = info->attrs; 19062306a36Sopenharmony_ci const struct nlattr *err_attr; 19162306a36Sopenharmony_ci bool mod = false; 19262306a36Sopenharmony_ci int ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci dev->ethtool_ops->get_ringparam(dev, &ringparam, 19562306a36Sopenharmony_ci &kernel_ringparam, info->extack); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ethnl_update_u32(&ringparam.rx_pending, tb[ETHTOOL_A_RINGS_RX], &mod); 19862306a36Sopenharmony_ci ethnl_update_u32(&ringparam.rx_mini_pending, 19962306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_RX_MINI], &mod); 20062306a36Sopenharmony_ci ethnl_update_u32(&ringparam.rx_jumbo_pending, 20162306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_RX_JUMBO], &mod); 20262306a36Sopenharmony_ci ethnl_update_u32(&ringparam.tx_pending, tb[ETHTOOL_A_RINGS_TX], &mod); 20362306a36Sopenharmony_ci ethnl_update_u32(&kernel_ringparam.rx_buf_len, 20462306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_RX_BUF_LEN], &mod); 20562306a36Sopenharmony_ci ethnl_update_u32(&kernel_ringparam.cqe_size, 20662306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_CQE_SIZE], &mod); 20762306a36Sopenharmony_ci ethnl_update_u8(&kernel_ringparam.tx_push, 20862306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_TX_PUSH], &mod); 20962306a36Sopenharmony_ci ethnl_update_u8(&kernel_ringparam.rx_push, 21062306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_RX_PUSH], &mod); 21162306a36Sopenharmony_ci ethnl_update_u32(&kernel_ringparam.tx_push_buf_len, 21262306a36Sopenharmony_ci tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN], &mod); 21362306a36Sopenharmony_ci if (!mod) 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* ensure new ring parameters are within limits */ 21762306a36Sopenharmony_ci if (ringparam.rx_pending > ringparam.rx_max_pending) 21862306a36Sopenharmony_ci err_attr = tb[ETHTOOL_A_RINGS_RX]; 21962306a36Sopenharmony_ci else if (ringparam.rx_mini_pending > ringparam.rx_mini_max_pending) 22062306a36Sopenharmony_ci err_attr = tb[ETHTOOL_A_RINGS_RX_MINI]; 22162306a36Sopenharmony_ci else if (ringparam.rx_jumbo_pending > ringparam.rx_jumbo_max_pending) 22262306a36Sopenharmony_ci err_attr = tb[ETHTOOL_A_RINGS_RX_JUMBO]; 22362306a36Sopenharmony_ci else if (ringparam.tx_pending > ringparam.tx_max_pending) 22462306a36Sopenharmony_ci err_attr = tb[ETHTOOL_A_RINGS_TX]; 22562306a36Sopenharmony_ci else 22662306a36Sopenharmony_ci err_attr = NULL; 22762306a36Sopenharmony_ci if (err_attr) { 22862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(info->extack, err_attr, 22962306a36Sopenharmony_ci "requested ring size exceeds maximum"); 23062306a36Sopenharmony_ci return -EINVAL; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (kernel_ringparam.tx_push_buf_len > kernel_ringparam.tx_push_buf_max_len) { 23462306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR_FMT(info->extack, tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN], 23562306a36Sopenharmony_ci "Requested TX push buffer exceeds the maximum of %u", 23662306a36Sopenharmony_ci kernel_ringparam.tx_push_buf_max_len); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ret = dev->ethtool_ops->set_ringparam(dev, &ringparam, 24262306a36Sopenharmony_ci &kernel_ringparam, info->extack); 24362306a36Sopenharmony_ci return ret < 0 ? ret : 1; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciconst struct ethnl_request_ops ethnl_rings_request_ops = { 24762306a36Sopenharmony_ci .request_cmd = ETHTOOL_MSG_RINGS_GET, 24862306a36Sopenharmony_ci .reply_cmd = ETHTOOL_MSG_RINGS_GET_REPLY, 24962306a36Sopenharmony_ci .hdr_attr = ETHTOOL_A_RINGS_HEADER, 25062306a36Sopenharmony_ci .req_info_size = sizeof(struct rings_req_info), 25162306a36Sopenharmony_ci .reply_data_size = sizeof(struct rings_reply_data), 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci .prepare_data = rings_prepare_data, 25462306a36Sopenharmony_ci .reply_size = rings_reply_size, 25562306a36Sopenharmony_ci .fill_reply = rings_fill_reply, 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci .set_validate = ethnl_set_rings_validate, 25862306a36Sopenharmony_ci .set = ethnl_set_rings, 25962306a36Sopenharmony_ci .set_ntf_cmd = ETHTOOL_MSG_RINGS_NTF, 26062306a36Sopenharmony_ci}; 261