Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ethtool/rss.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
3
#include <net/netdev_lock.h>
4
5
#include "netlink.h"
6
#include "common.h"
7
8
struct rss_req_info {
9
struct ethnl_req_info base;
10
u32 rss_context;
11
};
12
13
struct rss_reply_data {
14
struct ethnl_reply_data base;
15
bool has_flow_hash;
16
bool no_key_fields;
17
u32 indir_size;
18
u32 hkey_size;
19
u32 hfunc;
20
u32 input_xfrm;
21
u32 *indir_table;
22
u8 *hkey;
23
int flow_hash[__ETHTOOL_A_FLOW_CNT];
24
};
25
26
static const u8 ethtool_rxfh_ft_nl2ioctl[] = {
27
[ETHTOOL_A_FLOW_ETHER] = ETHER_FLOW,
28
[ETHTOOL_A_FLOW_IP4] = IPV4_FLOW,
29
[ETHTOOL_A_FLOW_IP6] = IPV6_FLOW,
30
[ETHTOOL_A_FLOW_TCP4] = TCP_V4_FLOW,
31
[ETHTOOL_A_FLOW_UDP4] = UDP_V4_FLOW,
32
[ETHTOOL_A_FLOW_SCTP4] = SCTP_V4_FLOW,
33
[ETHTOOL_A_FLOW_AH_ESP4] = AH_ESP_V4_FLOW,
34
[ETHTOOL_A_FLOW_TCP6] = TCP_V6_FLOW,
35
[ETHTOOL_A_FLOW_UDP6] = UDP_V6_FLOW,
36
[ETHTOOL_A_FLOW_SCTP6] = SCTP_V6_FLOW,
37
[ETHTOOL_A_FLOW_AH_ESP6] = AH_ESP_V6_FLOW,
38
[ETHTOOL_A_FLOW_AH4] = AH_V4_FLOW,
39
[ETHTOOL_A_FLOW_ESP4] = ESP_V4_FLOW,
40
[ETHTOOL_A_FLOW_AH6] = AH_V6_FLOW,
41
[ETHTOOL_A_FLOW_ESP6] = ESP_V6_FLOW,
42
[ETHTOOL_A_FLOW_GTPU4] = GTPU_V4_FLOW,
43
[ETHTOOL_A_FLOW_GTPU6] = GTPU_V6_FLOW,
44
[ETHTOOL_A_FLOW_GTPC4] = GTPC_V4_FLOW,
45
[ETHTOOL_A_FLOW_GTPC6] = GTPC_V6_FLOW,
46
[ETHTOOL_A_FLOW_GTPC_TEID4] = GTPC_TEID_V4_FLOW,
47
[ETHTOOL_A_FLOW_GTPC_TEID6] = GTPC_TEID_V6_FLOW,
48
[ETHTOOL_A_FLOW_GTPU_EH4] = GTPU_EH_V4_FLOW,
49
[ETHTOOL_A_FLOW_GTPU_EH6] = GTPU_EH_V6_FLOW,
50
[ETHTOOL_A_FLOW_GTPU_UL4] = GTPU_UL_V4_FLOW,
51
[ETHTOOL_A_FLOW_GTPU_UL6] = GTPU_UL_V6_FLOW,
52
[ETHTOOL_A_FLOW_GTPU_DL4] = GTPU_DL_V4_FLOW,
53
[ETHTOOL_A_FLOW_GTPU_DL6] = GTPU_DL_V6_FLOW,
54
};
55
56
#define RSS_REQINFO(__req_base) \
57
container_of(__req_base, struct rss_req_info, base)
58
59
#define RSS_REPDATA(__reply_base) \
60
container_of(__reply_base, struct rss_reply_data, base)
61
62
const struct nla_policy ethnl_rss_get_policy[] = {
63
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
64
[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32 },
65
[ETHTOOL_A_RSS_START_CONTEXT] = { .type = NLA_U32 },
66
};
67
68
static int
69
rss_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb,
70
struct netlink_ext_ack *extack)
71
{
72
struct rss_req_info *request = RSS_REQINFO(req_info);
73
74
if (tb[ETHTOOL_A_RSS_CONTEXT])
75
request->rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]);
76
if (tb[ETHTOOL_A_RSS_START_CONTEXT]) {
77
NL_SET_BAD_ATTR(extack, tb[ETHTOOL_A_RSS_START_CONTEXT]);
78
return -EINVAL;
79
}
80
81
return 0;
82
}
83
84
static void
85
rss_prepare_flow_hash(const struct rss_req_info *req, struct net_device *dev,
86
struct rss_reply_data *data, const struct genl_info *info)
87
{
88
int i;
89
90
data->has_flow_hash = false;
91
92
if (!dev->ethtool_ops->get_rxfh_fields)
93
return;
94
if (req->rss_context && !dev->ethtool_ops->rxfh_per_ctx_fields)
95
return;
96
97
mutex_lock(&dev->ethtool->rss_lock);
98
for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) {
99
struct ethtool_rxfh_fields fields = {
100
.flow_type = ethtool_rxfh_ft_nl2ioctl[i],
101
.rss_context = req->rss_context,
102
};
103
104
if (dev->ethtool_ops->get_rxfh_fields(dev, &fields)) {
105
data->flow_hash[i] = -1; /* Unsupported */
106
continue;
107
}
108
109
data->flow_hash[i] = fields.data;
110
data->has_flow_hash = true;
111
}
112
mutex_unlock(&dev->ethtool->rss_lock);
113
}
114
115
static int
116
rss_get_data_alloc(struct net_device *dev, struct rss_reply_data *data)
117
{
118
const struct ethtool_ops *ops = dev->ethtool_ops;
119
u32 total_size, indir_bytes;
120
u8 *rss_config;
121
122
data->indir_size = 0;
123
data->hkey_size = 0;
124
if (ops->get_rxfh_indir_size)
125
data->indir_size = ops->get_rxfh_indir_size(dev);
126
if (ops->get_rxfh_key_size)
127
data->hkey_size = ops->get_rxfh_key_size(dev);
128
129
indir_bytes = data->indir_size * sizeof(u32);
130
total_size = indir_bytes + data->hkey_size;
131
rss_config = kzalloc(total_size, GFP_KERNEL);
132
if (!rss_config)
133
return -ENOMEM;
134
135
if (data->indir_size)
136
data->indir_table = (u32 *)rss_config;
137
if (data->hkey_size)
138
data->hkey = rss_config + indir_bytes;
139
140
return 0;
141
}
142
143
static void rss_get_data_free(const struct rss_reply_data *data)
144
{
145
kfree(data->indir_table);
146
}
147
148
static int
149
rss_prepare_get(const struct rss_req_info *request, struct net_device *dev,
150
struct rss_reply_data *data, const struct genl_info *info)
151
{
152
const struct ethtool_ops *ops = dev->ethtool_ops;
153
struct ethtool_rxfh_param rxfh = {};
154
int ret;
155
156
ret = ethnl_ops_begin(dev);
157
if (ret < 0)
158
return ret;
159
mutex_lock(&dev->ethtool->rss_lock);
160
161
ret = rss_get_data_alloc(dev, data);
162
if (ret)
163
goto out_unlock;
164
165
rxfh.indir_size = data->indir_size;
166
rxfh.indir = data->indir_table;
167
rxfh.key_size = data->hkey_size;
168
rxfh.key = data->hkey;
169
170
ret = ops->get_rxfh(dev, &rxfh);
171
if (ret)
172
goto out_unlock;
173
174
data->hfunc = rxfh.hfunc;
175
data->input_xfrm = rxfh.input_xfrm;
176
out_unlock:
177
mutex_unlock(&dev->ethtool->rss_lock);
178
ethnl_ops_complete(dev);
179
return ret;
180
}
181
182
static void
183
__rss_prepare_ctx(struct net_device *dev, struct rss_reply_data *data,
184
struct ethtool_rxfh_context *ctx)
185
{
186
if (WARN_ON_ONCE(data->indir_size != ctx->indir_size ||
187
data->hkey_size != ctx->key_size))
188
return;
189
190
data->no_key_fields = !dev->ethtool_ops->rxfh_per_ctx_key;
191
192
data->hfunc = ctx->hfunc;
193
data->input_xfrm = ctx->input_xfrm;
194
memcpy(data->indir_table, ethtool_rxfh_context_indir(ctx),
195
data->indir_size * sizeof(u32));
196
if (data->hkey_size)
197
memcpy(data->hkey, ethtool_rxfh_context_key(ctx),
198
data->hkey_size);
199
}
200
201
static int
202
rss_prepare_ctx(const struct rss_req_info *request, struct net_device *dev,
203
struct rss_reply_data *data, const struct genl_info *info)
204
{
205
struct ethtool_rxfh_context *ctx;
206
u32 total_size, indir_bytes;
207
u8 *rss_config;
208
int ret;
209
210
mutex_lock(&dev->ethtool->rss_lock);
211
ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context);
212
if (!ctx) {
213
ret = -ENOENT;
214
goto out_unlock;
215
}
216
217
data->indir_size = ctx->indir_size;
218
data->hkey_size = ctx->key_size;
219
220
indir_bytes = data->indir_size * sizeof(u32);
221
total_size = indir_bytes + data->hkey_size;
222
rss_config = kzalloc(total_size, GFP_KERNEL);
223
if (!rss_config) {
224
ret = -ENOMEM;
225
goto out_unlock;
226
}
227
228
data->indir_table = (u32 *)rss_config;
229
if (data->hkey_size)
230
data->hkey = rss_config + indir_bytes;
231
232
__rss_prepare_ctx(dev, data, ctx);
233
234
ret = 0;
235
out_unlock:
236
mutex_unlock(&dev->ethtool->rss_lock);
237
return ret;
238
}
239
240
static int
241
rss_prepare(const struct rss_req_info *request, struct net_device *dev,
242
struct rss_reply_data *data, const struct genl_info *info)
243
{
244
rss_prepare_flow_hash(request, dev, data, info);
245
246
/* Coming from RSS_SET, driver may only have flow_hash_fields ops */
247
if (!dev->ethtool_ops->get_rxfh)
248
return 0;
249
250
if (request->rss_context)
251
return rss_prepare_ctx(request, dev, data, info);
252
return rss_prepare_get(request, dev, data, info);
253
}
254
255
static int
256
rss_prepare_data(const struct ethnl_req_info *req_base,
257
struct ethnl_reply_data *reply_base,
258
const struct genl_info *info)
259
{
260
struct rss_reply_data *data = RSS_REPDATA(reply_base);
261
struct rss_req_info *request = RSS_REQINFO(req_base);
262
struct net_device *dev = reply_base->dev;
263
const struct ethtool_ops *ops;
264
265
ops = dev->ethtool_ops;
266
if (!ops->get_rxfh)
267
return -EOPNOTSUPP;
268
269
/* Some drivers don't handle rss_context */
270
if (request->rss_context && !ops->create_rxfh_context)
271
return -EOPNOTSUPP;
272
273
return rss_prepare(request, dev, data, info);
274
}
275
276
static int
277
rss_reply_size(const struct ethnl_req_info *req_base,
278
const struct ethnl_reply_data *reply_base)
279
{
280
const struct rss_reply_data *data = RSS_REPDATA(reply_base);
281
int len;
282
283
len = nla_total_size(sizeof(u32)) + /* _RSS_CONTEXT */
284
nla_total_size(sizeof(u32)) + /* _RSS_HFUNC */
285
nla_total_size(sizeof(u32)) + /* _RSS_INPUT_XFRM */
286
nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */
287
nla_total_size(data->hkey_size) + /* _RSS_HKEY */
288
nla_total_size(0) + /* _RSS_FLOW_HASH */
289
nla_total_size(sizeof(u32)) * ETHTOOL_A_FLOW_MAX +
290
0;
291
292
return len;
293
}
294
295
static int
296
rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base,
297
const struct ethnl_reply_data *reply_base)
298
{
299
const struct rss_reply_data *data = RSS_REPDATA(reply_base);
300
struct rss_req_info *request = RSS_REQINFO(req_base);
301
302
if (request->rss_context &&
303
nla_put_u32(skb, ETHTOOL_A_RSS_CONTEXT, request->rss_context))
304
return -EMSGSIZE;
305
306
if ((data->indir_size &&
307
nla_put(skb, ETHTOOL_A_RSS_INDIR,
308
sizeof(u32) * data->indir_size, data->indir_table)))
309
return -EMSGSIZE;
310
311
if (!data->no_key_fields &&
312
((data->hfunc &&
313
nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) ||
314
(data->input_xfrm &&
315
nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) ||
316
(data->hkey_size &&
317
nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))))
318
return -EMSGSIZE;
319
320
if (data->has_flow_hash) {
321
struct nlattr *nest;
322
int i;
323
324
nest = nla_nest_start(skb, ETHTOOL_A_RSS_FLOW_HASH);
325
if (!nest)
326
return -EMSGSIZE;
327
328
for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) {
329
if (data->flow_hash[i] >= 0 &&
330
nla_put_uint(skb, i, data->flow_hash[i])) {
331
nla_nest_cancel(skb, nest);
332
return -EMSGSIZE;
333
}
334
}
335
336
nla_nest_end(skb, nest);
337
}
338
339
return 0;
340
}
341
342
static void rss_cleanup_data(struct ethnl_reply_data *reply_base)
343
{
344
const struct rss_reply_data *data = RSS_REPDATA(reply_base);
345
346
rss_get_data_free(data);
347
}
348
349
struct rss_nl_dump_ctx {
350
unsigned long ifindex;
351
unsigned long ctx_idx;
352
353
/* User wants to only dump contexts from given ifindex */
354
unsigned int match_ifindex;
355
unsigned int start_ctx;
356
};
357
358
static struct rss_nl_dump_ctx *rss_dump_ctx(struct netlink_callback *cb)
359
{
360
NL_ASSERT_CTX_FITS(struct rss_nl_dump_ctx);
361
362
return (struct rss_nl_dump_ctx *)cb->ctx;
363
}
364
365
int ethnl_rss_dump_start(struct netlink_callback *cb)
366
{
367
const struct genl_info *info = genl_info_dump(cb);
368
struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb);
369
struct ethnl_req_info req_info = {};
370
struct nlattr **tb = info->attrs;
371
int ret;
372
373
/* Filtering by context not supported */
374
if (tb[ETHTOOL_A_RSS_CONTEXT]) {
375
NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT]);
376
return -EINVAL;
377
}
378
if (tb[ETHTOOL_A_RSS_START_CONTEXT]) {
379
ctx->start_ctx = nla_get_u32(tb[ETHTOOL_A_RSS_START_CONTEXT]);
380
ctx->ctx_idx = ctx->start_ctx;
381
}
382
383
ret = ethnl_parse_header_dev_get(&req_info,
384
tb[ETHTOOL_A_RSS_HEADER],
385
sock_net(cb->skb->sk), cb->extack,
386
false);
387
if (req_info.dev) {
388
ctx->match_ifindex = req_info.dev->ifindex;
389
ctx->ifindex = ctx->match_ifindex;
390
ethnl_parse_header_dev_put(&req_info);
391
req_info.dev = NULL;
392
}
393
394
return ret;
395
}
396
397
static int
398
rss_dump_one_ctx(struct sk_buff *skb, struct netlink_callback *cb,
399
struct net_device *dev, u32 rss_context)
400
{
401
const struct genl_info *info = genl_info_dump(cb);
402
struct rss_reply_data data = {};
403
struct rss_req_info req = {};
404
void *ehdr;
405
int ret;
406
407
req.rss_context = rss_context;
408
409
ehdr = ethnl_dump_put(skb, cb, ETHTOOL_MSG_RSS_GET_REPLY);
410
if (!ehdr)
411
return -EMSGSIZE;
412
413
ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_RSS_HEADER);
414
if (ret < 0)
415
goto err_cancel;
416
417
ret = rss_prepare(&req, dev, &data, info);
418
if (ret)
419
goto err_cancel;
420
421
ret = rss_fill_reply(skb, &req.base, &data.base);
422
if (ret)
423
goto err_cleanup;
424
genlmsg_end(skb, ehdr);
425
426
rss_cleanup_data(&data.base);
427
return 0;
428
429
err_cleanup:
430
rss_cleanup_data(&data.base);
431
err_cancel:
432
genlmsg_cancel(skb, ehdr);
433
return ret;
434
}
435
436
static int
437
rss_dump_one_dev(struct sk_buff *skb, struct netlink_callback *cb,
438
struct net_device *dev)
439
{
440
struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb);
441
int ret;
442
443
if (!dev->ethtool_ops->get_rxfh)
444
return 0;
445
446
if (!ctx->ctx_idx) {
447
ret = rss_dump_one_ctx(skb, cb, dev, 0);
448
if (ret)
449
return ret;
450
ctx->ctx_idx++;
451
}
452
453
for (; xa_find(&dev->ethtool->rss_ctx, &ctx->ctx_idx,
454
ULONG_MAX, XA_PRESENT); ctx->ctx_idx++) {
455
ret = rss_dump_one_ctx(skb, cb, dev, ctx->ctx_idx);
456
if (ret)
457
return ret;
458
}
459
ctx->ctx_idx = ctx->start_ctx;
460
461
return 0;
462
}
463
464
int ethnl_rss_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
465
{
466
struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb);
467
struct net *net = sock_net(skb->sk);
468
struct net_device *dev;
469
int ret = 0;
470
471
rtnl_lock();
472
for_each_netdev_dump(net, dev, ctx->ifindex) {
473
if (ctx->match_ifindex && ctx->match_ifindex != ctx->ifindex)
474
break;
475
476
netdev_lock_ops(dev);
477
ret = rss_dump_one_dev(skb, cb, dev);
478
netdev_unlock_ops(dev);
479
if (ret)
480
break;
481
}
482
rtnl_unlock();
483
484
return ret;
485
}
486
487
/* RSS_NTF */
488
489
static void ethnl_rss_delete_notify(struct net_device *dev, u32 rss_context)
490
{
491
struct sk_buff *ntf;
492
size_t ntf_size;
493
void *hdr;
494
495
ntf_size = ethnl_reply_header_size() +
496
nla_total_size(sizeof(u32)); /* _RSS_CONTEXT */
497
498
ntf = genlmsg_new(ntf_size, GFP_KERNEL);
499
if (!ntf)
500
goto out_warn;
501
502
hdr = ethnl_bcastmsg_put(ntf, ETHTOOL_MSG_RSS_DELETE_NTF);
503
if (!hdr)
504
goto out_free_ntf;
505
506
if (ethnl_fill_reply_header(ntf, dev, ETHTOOL_A_RSS_HEADER) ||
507
nla_put_u32(ntf, ETHTOOL_A_RSS_CONTEXT, rss_context))
508
goto out_free_ntf;
509
510
genlmsg_end(ntf, hdr);
511
if (ethnl_multicast(ntf, dev))
512
goto out_warn;
513
514
return;
515
516
out_free_ntf:
517
nlmsg_free(ntf);
518
out_warn:
519
pr_warn_once("Failed to send a RSS delete notification");
520
}
521
522
void ethtool_rss_notify(struct net_device *dev, u32 type, u32 rss_context)
523
{
524
struct rss_req_info req_info = {
525
.rss_context = rss_context,
526
};
527
528
if (type == ETHTOOL_MSG_RSS_DELETE_NTF)
529
ethnl_rss_delete_notify(dev, rss_context);
530
else
531
ethnl_notify(dev, type, &req_info.base);
532
}
533
534
/* RSS_SET */
535
536
#define RFH_MASK (RXH_L2DA | RXH_VLAN | RXH_IP_SRC | RXH_IP_DST | \
537
RXH_L3_PROTO | RXH_L4_B_0_1 | RXH_L4_B_2_3 | \
538
RXH_GTP_TEID | RXH_DISCARD)
539
540
static const struct nla_policy ethnl_rss_flows_policy[] = {
541
[ETHTOOL_A_FLOW_ETHER] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
542
[ETHTOOL_A_FLOW_IP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
543
[ETHTOOL_A_FLOW_IP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
544
[ETHTOOL_A_FLOW_TCP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
545
[ETHTOOL_A_FLOW_UDP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
546
[ETHTOOL_A_FLOW_SCTP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
547
[ETHTOOL_A_FLOW_AH_ESP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
548
[ETHTOOL_A_FLOW_TCP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
549
[ETHTOOL_A_FLOW_UDP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
550
[ETHTOOL_A_FLOW_SCTP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
551
[ETHTOOL_A_FLOW_AH_ESP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
552
[ETHTOOL_A_FLOW_AH4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
553
[ETHTOOL_A_FLOW_ESP4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
554
[ETHTOOL_A_FLOW_AH6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
555
[ETHTOOL_A_FLOW_ESP6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
556
[ETHTOOL_A_FLOW_GTPU4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
557
[ETHTOOL_A_FLOW_GTPU6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
558
[ETHTOOL_A_FLOW_GTPC4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
559
[ETHTOOL_A_FLOW_GTPC6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
560
[ETHTOOL_A_FLOW_GTPC_TEID4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
561
[ETHTOOL_A_FLOW_GTPC_TEID6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
562
[ETHTOOL_A_FLOW_GTPU_EH4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
563
[ETHTOOL_A_FLOW_GTPU_EH6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
564
[ETHTOOL_A_FLOW_GTPU_UL4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
565
[ETHTOOL_A_FLOW_GTPU_UL6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
566
[ETHTOOL_A_FLOW_GTPU_DL4] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
567
[ETHTOOL_A_FLOW_GTPU_DL6] = NLA_POLICY_MASK(NLA_UINT, RFH_MASK),
568
};
569
570
const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = {
571
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
572
[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
573
[ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
574
[ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
575
[ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
576
[ETHTOOL_A_RSS_INPUT_XFRM] =
577
NLA_POLICY_MAX(NLA_U32, RXH_XFRM_SYM_OR_XOR),
578
[ETHTOOL_A_RSS_FLOW_HASH] = NLA_POLICY_NESTED(ethnl_rss_flows_policy),
579
};
580
581
static int
582
ethnl_rss_set_validate(struct ethnl_req_info *req_info, struct genl_info *info)
583
{
584
const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
585
struct rss_req_info *request = RSS_REQINFO(req_info);
586
struct nlattr **tb = info->attrs;
587
struct nlattr *bad_attr = NULL;
588
u32 input_xfrm;
589
590
if (request->rss_context && !ops->create_rxfh_context)
591
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_CONTEXT];
592
593
if (request->rss_context && !ops->rxfh_per_ctx_key) {
594
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HFUNC];
595
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HKEY];
596
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM];
597
}
598
599
input_xfrm = nla_get_u32_default(tb[ETHTOOL_A_RSS_INPUT_XFRM], 0);
600
if (input_xfrm & ~ops->supported_input_xfrm)
601
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM];
602
603
if (tb[ETHTOOL_A_RSS_FLOW_HASH] && !ops->set_rxfh_fields)
604
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_FLOW_HASH];
605
if (request->rss_context &&
606
tb[ETHTOOL_A_RSS_FLOW_HASH] && !ops->rxfh_per_ctx_fields)
607
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_FLOW_HASH];
608
609
if (bad_attr) {
610
NL_SET_BAD_ATTR(info->extack, bad_attr);
611
return -EOPNOTSUPP;
612
}
613
614
return 1;
615
}
616
617
static int
618
rss_set_prep_indir(struct net_device *dev, struct genl_info *info,
619
struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh,
620
bool *reset, bool *mod)
621
{
622
const struct ethtool_ops *ops = dev->ethtool_ops;
623
struct netlink_ext_ack *extack = info->extack;
624
struct nlattr **tb = info->attrs;
625
struct ethtool_rxnfc rx_rings;
626
size_t alloc_size;
627
u32 user_size;
628
int i, err;
629
630
if (!tb[ETHTOOL_A_RSS_INDIR])
631
return 0;
632
if (!data->indir_size || !ops->get_rxnfc)
633
return -EOPNOTSUPP;
634
635
rx_rings.cmd = ETHTOOL_GRXRINGS;
636
err = ops->get_rxnfc(dev, &rx_rings, NULL);
637
if (err)
638
return err;
639
640
if (nla_len(tb[ETHTOOL_A_RSS_INDIR]) % 4) {
641
NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_INDIR]);
642
return -EINVAL;
643
}
644
user_size = nla_len(tb[ETHTOOL_A_RSS_INDIR]) / 4;
645
if (!user_size) {
646
if (rxfh->rss_context) {
647
NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_RSS_INDIR],
648
"can't reset table for a context");
649
return -EINVAL;
650
}
651
*reset = true;
652
} else if (data->indir_size % user_size) {
653
NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR],
654
"size (%d) mismatch with device indir table (%d)",
655
user_size, data->indir_size);
656
return -EINVAL;
657
}
658
659
rxfh->indir_size = data->indir_size;
660
alloc_size = array_size(data->indir_size, sizeof(rxfh->indir[0]));
661
rxfh->indir = kzalloc(alloc_size, GFP_KERNEL);
662
if (!rxfh->indir)
663
return -ENOMEM;
664
665
nla_memcpy(rxfh->indir, tb[ETHTOOL_A_RSS_INDIR], alloc_size);
666
for (i = 0; i < user_size; i++) {
667
if (rxfh->indir[i] < rx_rings.data)
668
continue;
669
670
NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR],
671
"entry %d: queue out of range (%d)",
672
i, rxfh->indir[i]);
673
err = -EINVAL;
674
goto err_free;
675
}
676
677
if (user_size) {
678
/* Replicate the user-provided table to fill the device table */
679
for (i = user_size; i < data->indir_size; i++)
680
rxfh->indir[i] = rxfh->indir[i % user_size];
681
} else {
682
for (i = 0; i < data->indir_size; i++)
683
rxfh->indir[i] =
684
ethtool_rxfh_indir_default(i, rx_rings.data);
685
}
686
687
*mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size);
688
689
return 0;
690
691
err_free:
692
kfree(rxfh->indir);
693
rxfh->indir = NULL;
694
return err;
695
}
696
697
static int
698
rss_set_prep_hkey(struct net_device *dev, struct genl_info *info,
699
struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh,
700
bool *mod)
701
{
702
struct nlattr **tb = info->attrs;
703
704
if (!tb[ETHTOOL_A_RSS_HKEY])
705
return 0;
706
707
if (nla_len(tb[ETHTOOL_A_RSS_HKEY]) != data->hkey_size) {
708
NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_HKEY]);
709
return -EINVAL;
710
}
711
712
rxfh->key_size = data->hkey_size;
713
rxfh->key = kmemdup(data->hkey, data->hkey_size, GFP_KERNEL);
714
if (!rxfh->key)
715
return -ENOMEM;
716
717
ethnl_update_binary(rxfh->key, rxfh->key_size, tb[ETHTOOL_A_RSS_HKEY],
718
mod);
719
return 0;
720
}
721
722
static int
723
rss_check_rxfh_fields_sym(struct net_device *dev, struct genl_info *info,
724
struct rss_reply_data *data, bool xfrm_sym)
725
{
726
struct nlattr **tb = info->attrs;
727
int i;
728
729
if (!xfrm_sym)
730
return 0;
731
if (!data->has_flow_hash) {
732
NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_RSS_INPUT_XFRM],
733
"hash field config not reported");
734
return -EINVAL;
735
}
736
737
for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++)
738
if (data->flow_hash[i] >= 0 &&
739
!ethtool_rxfh_config_is_sym(data->flow_hash[i])) {
740
NL_SET_ERR_MSG_ATTR(info->extack,
741
tb[ETHTOOL_A_RSS_INPUT_XFRM],
742
"hash field config is not symmetric");
743
return -EINVAL;
744
}
745
746
return 0;
747
}
748
749
static int
750
ethnl_set_rss_fields(struct net_device *dev, struct genl_info *info,
751
u32 rss_context, struct rss_reply_data *data,
752
bool xfrm_sym, bool *mod)
753
{
754
struct nlattr *flow_nest = info->attrs[ETHTOOL_A_RSS_FLOW_HASH];
755
struct nlattr *flows[ETHTOOL_A_FLOW_MAX + 1];
756
const struct ethtool_ops *ops;
757
int i, ret;
758
759
ops = dev->ethtool_ops;
760
761
ret = rss_check_rxfh_fields_sym(dev, info, data, xfrm_sym);
762
if (ret)
763
return ret;
764
765
if (!flow_nest)
766
return 0;
767
768
ret = nla_parse_nested(flows, ARRAY_SIZE(ethnl_rss_flows_policy) - 1,
769
flow_nest, ethnl_rss_flows_policy, info->extack);
770
if (ret < 0)
771
return ret;
772
773
for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) {
774
struct ethtool_rxfh_fields fields = {
775
.flow_type = ethtool_rxfh_ft_nl2ioctl[i],
776
.rss_context = rss_context,
777
};
778
779
if (!flows[i])
780
continue;
781
782
fields.data = nla_get_u32(flows[i]);
783
if (data->has_flow_hash && data->flow_hash[i] == fields.data)
784
continue;
785
786
if (xfrm_sym && !ethtool_rxfh_config_is_sym(fields.data)) {
787
NL_SET_ERR_MSG_ATTR(info->extack, flows[i],
788
"conflict with xfrm-input");
789
return -EINVAL;
790
}
791
792
ret = ops->set_rxfh_fields(dev, &fields, info->extack);
793
if (ret)
794
return ret;
795
796
*mod = true;
797
}
798
799
return 0;
800
}
801
802
static void
803
rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
804
struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh)
805
{
806
int i;
807
808
if (rxfh->indir) {
809
for (i = 0; i < data->indir_size; i++)
810
ethtool_rxfh_context_indir(ctx)[i] = rxfh->indir[i];
811
ctx->indir_configured = !!nla_len(tb[ETHTOOL_A_RSS_INDIR]);
812
}
813
if (rxfh->key) {
814
memcpy(ethtool_rxfh_context_key(ctx), rxfh->key,
815
data->hkey_size);
816
ctx->key_configured = !!rxfh->key_size;
817
}
818
if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE)
819
ctx->hfunc = rxfh->hfunc;
820
if (rxfh->input_xfrm != RXH_XFRM_NO_CHANGE)
821
ctx->input_xfrm = rxfh->input_xfrm;
822
}
823
824
static int
825
ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
826
{
827
bool indir_reset = false, indir_mod, xfrm_sym = false;
828
struct rss_req_info *request = RSS_REQINFO(req_info);
829
struct ethtool_rxfh_context *ctx = NULL;
830
struct net_device *dev = req_info->dev;
831
bool mod = false, fields_mod = false;
832
struct ethtool_rxfh_param rxfh = {};
833
struct nlattr **tb = info->attrs;
834
struct rss_reply_data data = {};
835
const struct ethtool_ops *ops;
836
int ret;
837
838
ops = dev->ethtool_ops;
839
data.base.dev = dev;
840
841
ret = rss_prepare(request, dev, &data, info);
842
if (ret)
843
return ret;
844
845
rxfh.rss_context = request->rss_context;
846
847
ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_reset, &mod);
848
if (ret)
849
goto exit_clean_data;
850
indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
851
852
rxfh.hfunc = data.hfunc;
853
ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
854
if (rxfh.hfunc == data.hfunc)
855
rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
856
857
ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod);
858
if (ret)
859
goto exit_free_indir;
860
861
rxfh.input_xfrm = data.input_xfrm;
862
ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
863
/* For drivers which don't support input_xfrm it will be set to 0xff
864
* in the RSS context info. In all other case input_xfrm != 0 means
865
* symmetric hashing is requested.
866
*/
867
if (!request->rss_context || ops->rxfh_per_ctx_key)
868
xfrm_sym = rxfh.input_xfrm || data.input_xfrm;
869
if (rxfh.input_xfrm == data.input_xfrm)
870
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
871
872
mutex_lock(&dev->ethtool->rss_lock);
873
if (request->rss_context) {
874
ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context);
875
if (!ctx) {
876
ret = -ENOENT;
877
goto exit_unlock;
878
}
879
}
880
881
ret = ethnl_set_rss_fields(dev, info, request->rss_context,
882
&data, xfrm_sym, &fields_mod);
883
if (ret)
884
goto exit_unlock;
885
886
if (!mod)
887
ret = 0; /* nothing to tell the driver */
888
else if (!ops->set_rxfh)
889
ret = -EOPNOTSUPP;
890
else if (!rxfh.rss_context)
891
ret = ops->set_rxfh(dev, &rxfh, info->extack);
892
else
893
ret = ops->modify_rxfh_context(dev, ctx, &rxfh, info->extack);
894
if (ret)
895
goto exit_unlock;
896
897
if (ctx)
898
rss_set_ctx_update(ctx, tb, &data, &rxfh);
899
else if (indir_reset)
900
dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
901
else if (indir_mod)
902
dev->priv_flags |= IFF_RXFH_CONFIGURED;
903
904
exit_unlock:
905
mutex_unlock(&dev->ethtool->rss_lock);
906
kfree(rxfh.key);
907
exit_free_indir:
908
kfree(rxfh.indir);
909
exit_clean_data:
910
rss_cleanup_data(&data.base);
911
912
return ret ?: mod || fields_mod;
913
}
914
915
const struct ethnl_request_ops ethnl_rss_request_ops = {
916
.request_cmd = ETHTOOL_MSG_RSS_GET,
917
.reply_cmd = ETHTOOL_MSG_RSS_GET_REPLY,
918
.hdr_attr = ETHTOOL_A_RSS_HEADER,
919
.req_info_size = sizeof(struct rss_req_info),
920
.reply_data_size = sizeof(struct rss_reply_data),
921
922
.parse_request = rss_parse_request,
923
.prepare_data = rss_prepare_data,
924
.reply_size = rss_reply_size,
925
.fill_reply = rss_fill_reply,
926
.cleanup_data = rss_cleanup_data,
927
928
.set_validate = ethnl_rss_set_validate,
929
.set = ethnl_rss_set,
930
.set_ntf_cmd = ETHTOOL_MSG_RSS_NTF,
931
};
932
933
/* RSS_CREATE */
934
935
const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = {
936
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
937
[ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1),
938
[ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
939
[ETHTOOL_A_RSS_INDIR] = NLA_POLICY_MIN(NLA_BINARY, 1),
940
[ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
941
[ETHTOOL_A_RSS_INPUT_XFRM] =
942
NLA_POLICY_MAX(NLA_U32, RXH_XFRM_SYM_OR_XOR),
943
};
944
945
static int
946
ethnl_rss_create_validate(struct net_device *dev, struct genl_info *info)
947
{
948
const struct ethtool_ops *ops = dev->ethtool_ops;
949
struct nlattr **tb = info->attrs;
950
struct nlattr *bad_attr = NULL;
951
u32 rss_context, input_xfrm;
952
953
if (!ops->create_rxfh_context)
954
return -EOPNOTSUPP;
955
956
rss_context = nla_get_u32_default(tb[ETHTOOL_A_RSS_CONTEXT], 0);
957
if (ops->rxfh_max_num_contexts &&
958
ops->rxfh_max_num_contexts <= rss_context) {
959
NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT]);
960
return -ERANGE;
961
}
962
963
if (!ops->rxfh_per_ctx_key) {
964
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HFUNC];
965
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_HKEY];
966
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM];
967
}
968
969
input_xfrm = nla_get_u32_default(tb[ETHTOOL_A_RSS_INPUT_XFRM], 0);
970
if (input_xfrm & ~ops->supported_input_xfrm)
971
bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_INPUT_XFRM];
972
973
if (bad_attr) {
974
NL_SET_BAD_ATTR(info->extack, bad_attr);
975
return -EOPNOTSUPP;
976
}
977
978
return 0;
979
}
980
981
static void
982
ethnl_rss_create_send_ntf(struct sk_buff *rsp, struct net_device *dev)
983
{
984
struct nlmsghdr *nlh = (void *)rsp->data;
985
struct genlmsghdr *genl_hdr;
986
987
/* Convert the reply into a notification */
988
nlh->nlmsg_pid = 0;
989
nlh->nlmsg_seq = ethnl_bcast_seq_next();
990
991
genl_hdr = nlmsg_data(nlh);
992
genl_hdr->cmd = ETHTOOL_MSG_RSS_CREATE_NTF;
993
994
ethnl_multicast(rsp, dev);
995
}
996
997
int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
998
{
999
bool indir_dflt = false, mod = false, ntf_fail = false;
1000
struct ethtool_rxfh_param rxfh = {};
1001
struct ethtool_rxfh_context *ctx;
1002
struct nlattr **tb = info->attrs;
1003
struct rss_reply_data data = {};
1004
const struct ethtool_ops *ops;
1005
struct rss_req_info req = {};
1006
struct net_device *dev;
1007
struct sk_buff *rsp;
1008
void *hdr;
1009
u32 limit;
1010
int ret;
1011
1012
rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
1013
if (!rsp)
1014
return -ENOMEM;
1015
1016
ret = ethnl_parse_header_dev_get(&req.base, tb[ETHTOOL_A_RSS_HEADER],
1017
genl_info_net(info), info->extack,
1018
true);
1019
if (ret < 0)
1020
goto exit_free_rsp;
1021
1022
dev = req.base.dev;
1023
ops = dev->ethtool_ops;
1024
1025
req.rss_context = nla_get_u32_default(tb[ETHTOOL_A_RSS_CONTEXT], 0);
1026
1027
ret = ethnl_rss_create_validate(dev, info);
1028
if (ret)
1029
goto exit_free_dev;
1030
1031
rtnl_lock();
1032
netdev_lock_ops(dev);
1033
1034
ret = ethnl_ops_begin(dev);
1035
if (ret < 0)
1036
goto exit_dev_unlock;
1037
1038
ret = rss_get_data_alloc(dev, &data);
1039
if (ret)
1040
goto exit_ops;
1041
1042
ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_dflt, &mod);
1043
if (ret)
1044
goto exit_clean_data;
1045
1046
ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
1047
1048
ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod);
1049
if (ret)
1050
goto exit_free_indir;
1051
1052
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
1053
ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
1054
1055
ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size);
1056
if (!ctx) {
1057
ret = -ENOMEM;
1058
goto exit_free_hkey;
1059
}
1060
1061
mutex_lock(&dev->ethtool->rss_lock);
1062
if (!req.rss_context) {
1063
limit = ops->rxfh_max_num_contexts ?: U32_MAX;
1064
ret = xa_alloc(&dev->ethtool->rss_ctx, &req.rss_context, ctx,
1065
XA_LIMIT(1, limit - 1), GFP_KERNEL_ACCOUNT);
1066
} else {
1067
ret = xa_insert(&dev->ethtool->rss_ctx,
1068
req.rss_context, ctx, GFP_KERNEL_ACCOUNT);
1069
}
1070
if (ret < 0) {
1071
NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT],
1072
"error allocating context ID");
1073
goto err_unlock_free_ctx;
1074
}
1075
rxfh.rss_context = req.rss_context;
1076
1077
ret = ops->create_rxfh_context(dev, ctx, &rxfh, info->extack);
1078
if (ret)
1079
goto err_ctx_id_free;
1080
1081
/* Make sure driver populates defaults */
1082
WARN_ON_ONCE(!rxfh.key && ops->rxfh_per_ctx_key &&
1083
!memchr_inv(ethtool_rxfh_context_key(ctx), 0,
1084
ctx->key_size));
1085
1086
/* Store the config from rxfh to Xarray.. */
1087
rss_set_ctx_update(ctx, tb, &data, &rxfh);
1088
/* .. copy from Xarray to data. */
1089
__rss_prepare_ctx(dev, &data, ctx);
1090
1091
hdr = ethnl_unicast_put(rsp, info->snd_portid, info->snd_seq,
1092
ETHTOOL_MSG_RSS_CREATE_ACT_REPLY);
1093
ntf_fail = ethnl_fill_reply_header(rsp, dev, ETHTOOL_A_RSS_HEADER);
1094
ntf_fail |= rss_fill_reply(rsp, &req.base, &data.base);
1095
if (WARN_ON(!hdr || ntf_fail)) {
1096
ret = -EMSGSIZE;
1097
goto exit_unlock;
1098
}
1099
1100
genlmsg_end(rsp, hdr);
1101
1102
/* Use the same skb for the response and the notification,
1103
* genlmsg_reply() will copy the skb if it has elevated user count.
1104
*/
1105
skb_get(rsp);
1106
ret = genlmsg_reply(rsp, info);
1107
ethnl_rss_create_send_ntf(rsp, dev);
1108
rsp = NULL;
1109
1110
exit_unlock:
1111
mutex_unlock(&dev->ethtool->rss_lock);
1112
exit_free_hkey:
1113
kfree(rxfh.key);
1114
exit_free_indir:
1115
kfree(rxfh.indir);
1116
exit_clean_data:
1117
rss_get_data_free(&data);
1118
exit_ops:
1119
ethnl_ops_complete(dev);
1120
exit_dev_unlock:
1121
netdev_unlock_ops(dev);
1122
rtnl_unlock();
1123
exit_free_dev:
1124
ethnl_parse_header_dev_put(&req.base);
1125
exit_free_rsp:
1126
nlmsg_free(rsp);
1127
return ret;
1128
1129
err_ctx_id_free:
1130
xa_erase(&dev->ethtool->rss_ctx, req.rss_context);
1131
err_unlock_free_ctx:
1132
kfree(ctx);
1133
goto exit_unlock;
1134
}
1135
1136
/* RSS_DELETE */
1137
1138
const struct nla_policy ethnl_rss_delete_policy[ETHTOOL_A_RSS_CONTEXT + 1] = {
1139
[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
1140
[ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1),
1141
};
1142
1143
int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info)
1144
{
1145
struct ethtool_rxfh_context *ctx;
1146
struct nlattr **tb = info->attrs;
1147
struct ethnl_req_info req = {};
1148
const struct ethtool_ops *ops;
1149
struct net_device *dev;
1150
u32 rss_context;
1151
int ret;
1152
1153
if (GENL_REQ_ATTR_CHECK(info, ETHTOOL_A_RSS_CONTEXT))
1154
return -EINVAL;
1155
rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]);
1156
1157
ret = ethnl_parse_header_dev_get(&req, tb[ETHTOOL_A_RSS_HEADER],
1158
genl_info_net(info), info->extack,
1159
true);
1160
if (ret < 0)
1161
return ret;
1162
1163
dev = req.dev;
1164
ops = dev->ethtool_ops;
1165
1166
if (!ops->create_rxfh_context)
1167
goto exit_free_dev;
1168
1169
rtnl_lock();
1170
netdev_lock_ops(dev);
1171
1172
ret = ethnl_ops_begin(dev);
1173
if (ret < 0)
1174
goto exit_dev_unlock;
1175
1176
mutex_lock(&dev->ethtool->rss_lock);
1177
ret = ethtool_check_rss_ctx_busy(dev, rss_context);
1178
if (ret)
1179
goto exit_unlock;
1180
1181
ctx = xa_load(&dev->ethtool->rss_ctx, rss_context);
1182
if (!ctx) {
1183
ret = -ENOENT;
1184
goto exit_unlock;
1185
}
1186
1187
ret = ops->remove_rxfh_context(dev, ctx, rss_context, info->extack);
1188
if (ret)
1189
goto exit_unlock;
1190
1191
WARN_ON(xa_erase(&dev->ethtool->rss_ctx, rss_context) != ctx);
1192
kfree(ctx);
1193
1194
ethnl_rss_delete_notify(dev, rss_context);
1195
1196
exit_unlock:
1197
mutex_unlock(&dev->ethtool->rss_lock);
1198
ethnl_ops_complete(dev);
1199
exit_dev_unlock:
1200
netdev_unlock_ops(dev);
1201
rtnl_unlock();
1202
exit_free_dev:
1203
ethnl_parse_header_dev_put(&req);
1204
return ret;
1205
}
1206
1207