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 rss_req_info { 762306a36Sopenharmony_ci struct ethnl_req_info base; 862306a36Sopenharmony_ci u32 rss_context; 962306a36Sopenharmony_ci}; 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistruct rss_reply_data { 1262306a36Sopenharmony_ci struct ethnl_reply_data base; 1362306a36Sopenharmony_ci u32 indir_size; 1462306a36Sopenharmony_ci u32 hkey_size; 1562306a36Sopenharmony_ci u32 hfunc; 1662306a36Sopenharmony_ci u32 *indir_table; 1762306a36Sopenharmony_ci u8 *hkey; 1862306a36Sopenharmony_ci}; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define RSS_REQINFO(__req_base) \ 2162306a36Sopenharmony_ci container_of(__req_base, struct rss_req_info, base) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define RSS_REPDATA(__reply_base) \ 2462306a36Sopenharmony_ci container_of(__reply_base, struct rss_reply_data, base) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciconst struct nla_policy ethnl_rss_get_policy[] = { 2762306a36Sopenharmony_ci [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 2862306a36Sopenharmony_ci [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32 }, 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int 3262306a36Sopenharmony_cirss_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb, 3362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct rss_req_info *request = RSS_REQINFO(req_info); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (tb[ETHTOOL_A_RSS_CONTEXT]) 3862306a36Sopenharmony_ci request->rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int 4462306a36Sopenharmony_cirss_prepare_data(const struct ethnl_req_info *req_base, 4562306a36Sopenharmony_ci struct ethnl_reply_data *reply_base, 4662306a36Sopenharmony_ci const struct genl_info *info) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct rss_reply_data *data = RSS_REPDATA(reply_base); 4962306a36Sopenharmony_ci struct rss_req_info *request = RSS_REQINFO(req_base); 5062306a36Sopenharmony_ci struct net_device *dev = reply_base->dev; 5162306a36Sopenharmony_ci const struct ethtool_ops *ops; 5262306a36Sopenharmony_ci u32 total_size, indir_bytes; 5362306a36Sopenharmony_ci u8 dev_hfunc = 0; 5462306a36Sopenharmony_ci u8 *rss_config; 5562306a36Sopenharmony_ci int ret; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci ops = dev->ethtool_ops; 5862306a36Sopenharmony_ci if (!ops->get_rxfh) 5962306a36Sopenharmony_ci return -EOPNOTSUPP; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Some drivers don't handle rss_context */ 6262306a36Sopenharmony_ci if (request->rss_context && !ops->get_rxfh_context) 6362306a36Sopenharmony_ci return -EOPNOTSUPP; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ret = ethnl_ops_begin(dev); 6662306a36Sopenharmony_ci if (ret < 0) 6762306a36Sopenharmony_ci return ret; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci data->indir_size = 0; 7062306a36Sopenharmony_ci data->hkey_size = 0; 7162306a36Sopenharmony_ci if (ops->get_rxfh_indir_size) 7262306a36Sopenharmony_ci data->indir_size = ops->get_rxfh_indir_size(dev); 7362306a36Sopenharmony_ci if (ops->get_rxfh_key_size) 7462306a36Sopenharmony_ci data->hkey_size = ops->get_rxfh_key_size(dev); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci indir_bytes = data->indir_size * sizeof(u32); 7762306a36Sopenharmony_ci total_size = indir_bytes + data->hkey_size; 7862306a36Sopenharmony_ci rss_config = kzalloc(total_size, GFP_KERNEL); 7962306a36Sopenharmony_ci if (!rss_config) { 8062306a36Sopenharmony_ci ret = -ENOMEM; 8162306a36Sopenharmony_ci goto out_ops; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (data->indir_size) 8562306a36Sopenharmony_ci data->indir_table = (u32 *)rss_config; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (data->hkey_size) 8862306a36Sopenharmony_ci data->hkey = rss_config + indir_bytes; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (request->rss_context) 9162306a36Sopenharmony_ci ret = ops->get_rxfh_context(dev, data->indir_table, data->hkey, 9262306a36Sopenharmony_ci &dev_hfunc, request->rss_context); 9362306a36Sopenharmony_ci else 9462306a36Sopenharmony_ci ret = ops->get_rxfh(dev, data->indir_table, data->hkey, 9562306a36Sopenharmony_ci &dev_hfunc); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (ret) 9862306a36Sopenharmony_ci goto out_ops; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci data->hfunc = dev_hfunc; 10162306a36Sopenharmony_ciout_ops: 10262306a36Sopenharmony_ci ethnl_ops_complete(dev); 10362306a36Sopenharmony_ci return ret; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int 10762306a36Sopenharmony_cirss_reply_size(const struct ethnl_req_info *req_base, 10862306a36Sopenharmony_ci const struct ethnl_reply_data *reply_base) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci const struct rss_reply_data *data = RSS_REPDATA(reply_base); 11162306a36Sopenharmony_ci int len; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci len = nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */ 11462306a36Sopenharmony_ci nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */ 11562306a36Sopenharmony_ci nla_total_size(data->hkey_size); /* _RSS_HKEY */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return len; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int 12162306a36Sopenharmony_cirss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base, 12262306a36Sopenharmony_ci const struct ethnl_reply_data *reply_base) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci const struct rss_reply_data *data = RSS_REPDATA(reply_base); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if ((data->hfunc && 12762306a36Sopenharmony_ci nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) || 12862306a36Sopenharmony_ci (data->indir_size && 12962306a36Sopenharmony_ci nla_put(skb, ETHTOOL_A_RSS_INDIR, 13062306a36Sopenharmony_ci sizeof(u32) * data->indir_size, data->indir_table)) || 13162306a36Sopenharmony_ci (data->hkey_size && 13262306a36Sopenharmony_ci nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))) 13362306a36Sopenharmony_ci return -EMSGSIZE; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void rss_cleanup_data(struct ethnl_reply_data *reply_base) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci const struct rss_reply_data *data = RSS_REPDATA(reply_base); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci kfree(data->indir_table); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciconst struct ethnl_request_ops ethnl_rss_request_ops = { 14662306a36Sopenharmony_ci .request_cmd = ETHTOOL_MSG_RSS_GET, 14762306a36Sopenharmony_ci .reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY, 14862306a36Sopenharmony_ci .hdr_attr = ETHTOOL_A_RSS_HEADER, 14962306a36Sopenharmony_ci .req_info_size = sizeof(struct rss_req_info), 15062306a36Sopenharmony_ci .reply_data_size = sizeof(struct rss_reply_data), 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci .parse_request = rss_parse_request, 15362306a36Sopenharmony_ci .prepare_data = rss_prepare_data, 15462306a36Sopenharmony_ci .reply_size = rss_reply_size, 15562306a36Sopenharmony_ci .fill_reply = rss_fill_reply, 15662306a36Sopenharmony_ci .cleanup_data = rss_cleanup_data, 15762306a36Sopenharmony_ci}; 158