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