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