xref: /openbmc/linux/net/ethtool/rss.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
17112a046SSudheer Mogilappagari // SPDX-License-Identifier: GPL-2.0-only
27112a046SSudheer Mogilappagari 
37112a046SSudheer Mogilappagari #include "netlink.h"
47112a046SSudheer Mogilappagari #include "common.h"
57112a046SSudheer Mogilappagari 
67112a046SSudheer Mogilappagari struct rss_req_info {
77112a046SSudheer Mogilappagari 	struct ethnl_req_info		base;
87112a046SSudheer Mogilappagari 	u32				rss_context;
97112a046SSudheer Mogilappagari };
107112a046SSudheer Mogilappagari 
117112a046SSudheer Mogilappagari struct rss_reply_data {
127112a046SSudheer Mogilappagari 	struct ethnl_reply_data		base;
137112a046SSudheer Mogilappagari 	u32				indir_size;
147112a046SSudheer Mogilappagari 	u32				hkey_size;
157112a046SSudheer Mogilappagari 	u32				hfunc;
167112a046SSudheer Mogilappagari 	u32				*indir_table;
177112a046SSudheer Mogilappagari 	u8				*hkey;
187112a046SSudheer Mogilappagari };
197112a046SSudheer Mogilappagari 
207112a046SSudheer Mogilappagari #define RSS_REQINFO(__req_base) \
217112a046SSudheer Mogilappagari 	container_of(__req_base, struct rss_req_info, base)
227112a046SSudheer Mogilappagari 
237112a046SSudheer Mogilappagari #define RSS_REPDATA(__reply_base) \
247112a046SSudheer Mogilappagari 	container_of(__reply_base, struct rss_reply_data, base)
257112a046SSudheer Mogilappagari 
267112a046SSudheer Mogilappagari const struct nla_policy ethnl_rss_get_policy[] = {
277112a046SSudheer Mogilappagari 	[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
287112a046SSudheer Mogilappagari 	[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32 },
297112a046SSudheer Mogilappagari };
307112a046SSudheer Mogilappagari 
317112a046SSudheer Mogilappagari static int
rss_parse_request(struct ethnl_req_info * req_info,struct nlattr ** tb,struct netlink_ext_ack * extack)327112a046SSudheer Mogilappagari rss_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb,
337112a046SSudheer Mogilappagari 		  struct netlink_ext_ack *extack)
347112a046SSudheer Mogilappagari {
357112a046SSudheer Mogilappagari 	struct rss_req_info *request = RSS_REQINFO(req_info);
367112a046SSudheer Mogilappagari 
377112a046SSudheer Mogilappagari 	if (tb[ETHTOOL_A_RSS_CONTEXT])
387112a046SSudheer Mogilappagari 		request->rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]);
397112a046SSudheer Mogilappagari 
407112a046SSudheer Mogilappagari 	return 0;
417112a046SSudheer Mogilappagari }
427112a046SSudheer Mogilappagari 
437112a046SSudheer Mogilappagari static int
rss_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)447112a046SSudheer Mogilappagari rss_prepare_data(const struct ethnl_req_info *req_base,
45*f946270dSJakub Kicinski 		 struct ethnl_reply_data *reply_base,
46*f946270dSJakub Kicinski 		 const struct genl_info *info)
477112a046SSudheer Mogilappagari {
487112a046SSudheer Mogilappagari 	struct rss_reply_data *data = RSS_REPDATA(reply_base);
497112a046SSudheer Mogilappagari 	struct rss_req_info *request = RSS_REQINFO(req_base);
507112a046SSudheer Mogilappagari 	struct net_device *dev = reply_base->dev;
517112a046SSudheer Mogilappagari 	const struct ethtool_ops *ops;
527112a046SSudheer Mogilappagari 	u32 total_size, indir_bytes;
537112a046SSudheer Mogilappagari 	u8 dev_hfunc = 0;
547112a046SSudheer Mogilappagari 	u8 *rss_config;
557112a046SSudheer Mogilappagari 	int ret;
567112a046SSudheer Mogilappagari 
577112a046SSudheer Mogilappagari 	ops = dev->ethtool_ops;
587112a046SSudheer Mogilappagari 	if (!ops->get_rxfh)
597112a046SSudheer Mogilappagari 		return -EOPNOTSUPP;
607112a046SSudheer Mogilappagari 
617112a046SSudheer Mogilappagari 	/* Some drivers don't handle rss_context */
627112a046SSudheer Mogilappagari 	if (request->rss_context && !ops->get_rxfh_context)
637112a046SSudheer Mogilappagari 		return -EOPNOTSUPP;
647112a046SSudheer Mogilappagari 
657112a046SSudheer Mogilappagari 	ret = ethnl_ops_begin(dev);
667112a046SSudheer Mogilappagari 	if (ret < 0)
677112a046SSudheer Mogilappagari 		return ret;
687112a046SSudheer Mogilappagari 
697112a046SSudheer Mogilappagari 	data->indir_size = 0;
707112a046SSudheer Mogilappagari 	data->hkey_size = 0;
717112a046SSudheer Mogilappagari 	if (ops->get_rxfh_indir_size)
727112a046SSudheer Mogilappagari 		data->indir_size = ops->get_rxfh_indir_size(dev);
737112a046SSudheer Mogilappagari 	if (ops->get_rxfh_key_size)
747112a046SSudheer Mogilappagari 		data->hkey_size = ops->get_rxfh_key_size(dev);
757112a046SSudheer Mogilappagari 
767112a046SSudheer Mogilappagari 	indir_bytes = data->indir_size * sizeof(u32);
777112a046SSudheer Mogilappagari 	total_size = indir_bytes + data->hkey_size;
787112a046SSudheer Mogilappagari 	rss_config = kzalloc(total_size, GFP_KERNEL);
797112a046SSudheer Mogilappagari 	if (!rss_config) {
807112a046SSudheer Mogilappagari 		ret = -ENOMEM;
817112a046SSudheer Mogilappagari 		goto out_ops;
827112a046SSudheer Mogilappagari 	}
837112a046SSudheer Mogilappagari 
847112a046SSudheer Mogilappagari 	if (data->indir_size)
857112a046SSudheer Mogilappagari 		data->indir_table = (u32 *)rss_config;
867112a046SSudheer Mogilappagari 
877112a046SSudheer Mogilappagari 	if (data->hkey_size)
887112a046SSudheer Mogilappagari 		data->hkey = rss_config + indir_bytes;
897112a046SSudheer Mogilappagari 
907112a046SSudheer Mogilappagari 	if (request->rss_context)
917112a046SSudheer Mogilappagari 		ret = ops->get_rxfh_context(dev, data->indir_table, data->hkey,
927112a046SSudheer Mogilappagari 					    &dev_hfunc, request->rss_context);
937112a046SSudheer Mogilappagari 	else
947112a046SSudheer Mogilappagari 		ret = ops->get_rxfh(dev, data->indir_table, data->hkey,
957112a046SSudheer Mogilappagari 				    &dev_hfunc);
967112a046SSudheer Mogilappagari 
977112a046SSudheer Mogilappagari 	if (ret)
987112a046SSudheer Mogilappagari 		goto out_ops;
997112a046SSudheer Mogilappagari 
1007112a046SSudheer Mogilappagari 	data->hfunc = dev_hfunc;
1017112a046SSudheer Mogilappagari out_ops:
1027112a046SSudheer Mogilappagari 	ethnl_ops_complete(dev);
1037112a046SSudheer Mogilappagari 	return ret;
1047112a046SSudheer Mogilappagari }
1057112a046SSudheer Mogilappagari 
1067112a046SSudheer Mogilappagari static int
rss_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)1077112a046SSudheer Mogilappagari rss_reply_size(const struct ethnl_req_info *req_base,
1087112a046SSudheer Mogilappagari 	       const struct ethnl_reply_data *reply_base)
1097112a046SSudheer Mogilappagari {
1107112a046SSudheer Mogilappagari 	const struct rss_reply_data *data = RSS_REPDATA(reply_base);
1117112a046SSudheer Mogilappagari 	int len;
1127112a046SSudheer Mogilappagari 
1137112a046SSudheer Mogilappagari 	len = nla_total_size(sizeof(u32)) +	/* _RSS_HFUNC */
1147112a046SSudheer Mogilappagari 	      nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */
1157112a046SSudheer Mogilappagari 	      nla_total_size(data->hkey_size);	/* _RSS_HKEY */
1167112a046SSudheer Mogilappagari 
1177112a046SSudheer Mogilappagari 	return len;
1187112a046SSudheer Mogilappagari }
1197112a046SSudheer Mogilappagari 
1207112a046SSudheer Mogilappagari static int
rss_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)1217112a046SSudheer Mogilappagari rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base,
1227112a046SSudheer Mogilappagari 	       const struct ethnl_reply_data *reply_base)
1237112a046SSudheer Mogilappagari {
1247112a046SSudheer Mogilappagari 	const struct rss_reply_data *data = RSS_REPDATA(reply_base);
1257112a046SSudheer Mogilappagari 
126ea22f431SSudheer Mogilappagari 	if ((data->hfunc &&
127ea22f431SSudheer Mogilappagari 	     nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) ||
128ea22f431SSudheer Mogilappagari 	    (data->indir_size &&
1297112a046SSudheer Mogilappagari 	     nla_put(skb, ETHTOOL_A_RSS_INDIR,
130ea22f431SSudheer Mogilappagari 		     sizeof(u32) * data->indir_size, data->indir_table)) ||
131ea22f431SSudheer Mogilappagari 	    (data->hkey_size &&
132ea22f431SSudheer Mogilappagari 	     nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey)))
1337112a046SSudheer Mogilappagari 		return -EMSGSIZE;
1347112a046SSudheer Mogilappagari 
1357112a046SSudheer Mogilappagari 	return 0;
1367112a046SSudheer Mogilappagari }
1377112a046SSudheer Mogilappagari 
rss_cleanup_data(struct ethnl_reply_data * reply_base)1387112a046SSudheer Mogilappagari static void rss_cleanup_data(struct ethnl_reply_data *reply_base)
1397112a046SSudheer Mogilappagari {
1407112a046SSudheer Mogilappagari 	const struct rss_reply_data *data = RSS_REPDATA(reply_base);
1417112a046SSudheer Mogilappagari 
1427112a046SSudheer Mogilappagari 	kfree(data->indir_table);
1437112a046SSudheer Mogilappagari }
1447112a046SSudheer Mogilappagari 
1457112a046SSudheer Mogilappagari const struct ethnl_request_ops ethnl_rss_request_ops = {
1467112a046SSudheer Mogilappagari 	.request_cmd		= ETHTOOL_MSG_RSS_GET,
1477112a046SSudheer Mogilappagari 	.reply_cmd		= ETHTOOL_MSG_RSS_GET_REPLY,
1487112a046SSudheer Mogilappagari 	.hdr_attr		= ETHTOOL_A_RSS_HEADER,
1497112a046SSudheer Mogilappagari 	.req_info_size		= sizeof(struct rss_req_info),
1507112a046SSudheer Mogilappagari 	.reply_data_size	= sizeof(struct rss_reply_data),
1517112a046SSudheer Mogilappagari 
1527112a046SSudheer Mogilappagari 	.parse_request		= rss_parse_request,
1537112a046SSudheer Mogilappagari 	.prepare_data		= rss_prepare_data,
1547112a046SSudheer Mogilappagari 	.reply_size		= rss_reply_size,
1557112a046SSudheer Mogilappagari 	.fill_reply		= rss_fill_reply,
1567112a046SSudheer Mogilappagari 	.cleanup_data		= rss_cleanup_data,
1577112a046SSudheer Mogilappagari };
158