Commit | Line | Data |
---|---|---|
7112a046 SM |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | ||
3 | #include "netlink.h" | |
4 | #include "common.h" | |
5 | ||
6 | struct rss_req_info { | |
7 | struct ethnl_req_info base; | |
8 | u32 rss_context; | |
9 | }; | |
10 | ||
11 | struct rss_reply_data { | |
12 | struct ethnl_reply_data base; | |
13 | u32 indir_size; | |
14 | u32 hkey_size; | |
15 | u32 hfunc; | |
16 | u32 *indir_table; | |
17 | u8 *hkey; | |
18 | }; | |
19 | ||
20 | #define RSS_REQINFO(__req_base) \ | |
21 | container_of(__req_base, struct rss_req_info, base) | |
22 | ||
23 | #define RSS_REPDATA(__reply_base) \ | |
24 | container_of(__reply_base, struct rss_reply_data, base) | |
25 | ||
26 | const struct nla_policy ethnl_rss_get_policy[] = { | |
27 | [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), | |
28 | [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32 }, | |
29 | }; | |
30 | ||
31 | static int | |
32 | rss_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb, | |
33 | struct netlink_ext_ack *extack) | |
34 | { | |
35 | struct rss_req_info *request = RSS_REQINFO(req_info); | |
36 | ||
37 | if (tb[ETHTOOL_A_RSS_CONTEXT]) | |
38 | request->rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]); | |
39 | ||
40 | return 0; | |
41 | } | |
42 | ||
43 | static int | |
44 | rss_prepare_data(const struct ethnl_req_info *req_base, | |
45 | struct ethnl_reply_data *reply_base, struct genl_info *info) | |
46 | { | |
47 | struct rss_reply_data *data = RSS_REPDATA(reply_base); | |
48 | struct rss_req_info *request = RSS_REQINFO(req_base); | |
49 | struct net_device *dev = reply_base->dev; | |
50 | const struct ethtool_ops *ops; | |
51 | u32 total_size, indir_bytes; | |
52 | u8 dev_hfunc = 0; | |
53 | u8 *rss_config; | |
54 | int ret; | |
55 | ||
56 | ops = dev->ethtool_ops; | |
57 | if (!ops->get_rxfh) | |
58 | return -EOPNOTSUPP; | |
59 | ||
60 | /* Some drivers don't handle rss_context */ | |
61 | if (request->rss_context && !ops->get_rxfh_context) | |
62 | return -EOPNOTSUPP; | |
63 | ||
64 | ret = ethnl_ops_begin(dev); | |
65 | if (ret < 0) | |
66 | return ret; | |
67 | ||
68 | data->indir_size = 0; | |
69 | data->hkey_size = 0; | |
70 | if (ops->get_rxfh_indir_size) | |
71 | data->indir_size = ops->get_rxfh_indir_size(dev); | |
72 | if (ops->get_rxfh_key_size) | |
73 | data->hkey_size = ops->get_rxfh_key_size(dev); | |
74 | ||
75 | indir_bytes = data->indir_size * sizeof(u32); | |
76 | total_size = indir_bytes + data->hkey_size; | |
77 | rss_config = kzalloc(total_size, GFP_KERNEL); | |
78 | if (!rss_config) { | |
79 | ret = -ENOMEM; | |
80 | goto out_ops; | |
81 | } | |
82 | ||
83 | if (data->indir_size) | |
84 | data->indir_table = (u32 *)rss_config; | |
85 | ||
86 | if (data->hkey_size) | |
87 | data->hkey = rss_config + indir_bytes; | |
88 | ||
89 | if (request->rss_context) | |
90 | ret = ops->get_rxfh_context(dev, data->indir_table, data->hkey, | |
91 | &dev_hfunc, request->rss_context); | |
92 | else | |
93 | ret = ops->get_rxfh(dev, data->indir_table, data->hkey, | |
94 | &dev_hfunc); | |
95 | ||
96 | if (ret) | |
97 | goto out_ops; | |
98 | ||
99 | data->hfunc = dev_hfunc; | |
100 | out_ops: | |
101 | ethnl_ops_complete(dev); | |
102 | return ret; | |
103 | } | |
104 | ||
105 | static int | |
106 | rss_reply_size(const struct ethnl_req_info *req_base, | |
107 | const struct ethnl_reply_data *reply_base) | |
108 | { | |
109 | const struct rss_reply_data *data = RSS_REPDATA(reply_base); | |
110 | int len; | |
111 | ||
112 | len = nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */ | |
113 | nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */ | |
114 | nla_total_size(data->hkey_size); /* _RSS_HKEY */ | |
115 | ||
116 | return len; | |
117 | } | |
118 | ||
119 | static int | |
120 | rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base, | |
121 | const struct ethnl_reply_data *reply_base) | |
122 | { | |
123 | const struct rss_reply_data *data = RSS_REPDATA(reply_base); | |
124 | ||
ea22f431 SM |
125 | if ((data->hfunc && |
126 | nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) || | |
127 | (data->indir_size && | |
128 | nla_put(skb, ETHTOOL_A_RSS_INDIR, | |
129 | sizeof(u32) * data->indir_size, data->indir_table)) || | |
130 | (data->hkey_size && | |
131 | nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))) | |
7112a046 SM |
132 | return -EMSGSIZE; |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | static void rss_cleanup_data(struct ethnl_reply_data *reply_base) | |
138 | { | |
139 | const struct rss_reply_data *data = RSS_REPDATA(reply_base); | |
140 | ||
141 | kfree(data->indir_table); | |
142 | } | |
143 | ||
144 | const struct ethnl_request_ops ethnl_rss_request_ops = { | |
145 | .request_cmd = ETHTOOL_MSG_RSS_GET, | |
146 | .reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY, | |
147 | .hdr_attr = ETHTOOL_A_RSS_HEADER, | |
148 | .req_info_size = sizeof(struct rss_req_info), | |
149 | .reply_data_size = sizeof(struct rss_reply_data), | |
150 | ||
151 | .parse_request = rss_parse_request, | |
152 | .prepare_data = rss_prepare_data, | |
153 | .reply_size = rss_reply_size, | |
154 | .fill_reply = rss_fill_reply, | |
155 | .cleanup_data = rss_cleanup_data, | |
156 | }; |