hc
2023-12-11 d2ccde1c8e90d38cee87a1b0309ad2827f3fd30d
kernel/drivers/net/netdevsim/fib.c
....@@ -14,11 +14,17 @@
1414 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
1515 */
1616
17
+#include <linux/in6.h>
18
+#include <linux/kernel.h>
19
+#include <linux/list.h>
20
+#include <linux/rhashtable.h>
21
+#include <linux/spinlock_types.h>
22
+#include <linux/types.h>
1723 #include <net/fib_notifier.h>
1824 #include <net/ip_fib.h>
1925 #include <net/ip6_fib.h>
2026 #include <net/fib_rules.h>
21
-#include <net/netns/generic.h>
27
+#include <net/net_namespace.h>
2228
2329 #include "netdevsim.h"
2430
....@@ -33,15 +39,56 @@
3339 };
3440
3541 struct nsim_fib_data {
42
+ struct notifier_block fib_nb;
3643 struct nsim_per_fib_data ipv4;
3744 struct nsim_per_fib_data ipv6;
45
+ struct rhashtable fib_rt_ht;
46
+ struct list_head fib_rt_list;
47
+ spinlock_t fib_lock; /* Protects hashtable, list and accounting */
48
+ struct devlink *devlink;
3849 };
3950
40
-static unsigned int nsim_fib_net_id;
51
+struct nsim_fib_rt_key {
52
+ unsigned char addr[sizeof(struct in6_addr)];
53
+ unsigned char prefix_len;
54
+ int family;
55
+ u32 tb_id;
56
+};
4157
42
-u64 nsim_fib_get_val(struct net *net, enum nsim_resource_id res_id, bool max)
58
+struct nsim_fib_rt {
59
+ struct nsim_fib_rt_key key;
60
+ struct rhash_head ht_node;
61
+ struct list_head list; /* Member of fib_rt_list */
62
+};
63
+
64
+struct nsim_fib4_rt {
65
+ struct nsim_fib_rt common;
66
+ struct fib_info *fi;
67
+ u8 tos;
68
+ u8 type;
69
+};
70
+
71
+struct nsim_fib6_rt {
72
+ struct nsim_fib_rt common;
73
+ struct list_head nh_list;
74
+ unsigned int nhs;
75
+};
76
+
77
+struct nsim_fib6_rt_nh {
78
+ struct list_head list; /* Member of nh_list */
79
+ struct fib6_info *rt;
80
+};
81
+
82
+static const struct rhashtable_params nsim_fib_rt_ht_params = {
83
+ .key_offset = offsetof(struct nsim_fib_rt, key),
84
+ .head_offset = offsetof(struct nsim_fib_rt, ht_node),
85
+ .key_len = sizeof(struct nsim_fib_rt_key),
86
+ .automatic_shrinking = true,
87
+};
88
+
89
+u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
90
+ enum nsim_resource_id res_id, bool max)
4391 {
44
- struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
4592 struct nsim_fib_entry *entry;
4693
4794 switch (res_id) {
....@@ -64,12 +111,10 @@
64111 return max ? entry->max : entry->num;
65112 }
66113
67
-int nsim_fib_set_max(struct net *net, enum nsim_resource_id res_id, u64 val,
68
- struct netlink_ext_ack *extack)
114
+static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
115
+ enum nsim_resource_id res_id, u64 val)
69116 {
70
- struct nsim_fib_data *fib_data = net_generic(net, nsim_fib_net_id);
71117 struct nsim_fib_entry *entry;
72
- int err = 0;
73118
74119 switch (res_id) {
75120 case NSIM_RESOURCE_IPV4_FIB:
....@@ -85,20 +130,10 @@
85130 entry = &fib_data->ipv6.rules;
86131 break;
87132 default:
88
- return 0;
133
+ WARN_ON(1);
134
+ return;
89135 }
90
-
91
- /* not allowing a new max to be less than curren occupancy
92
- * --> no means of evicting entries
93
- */
94
- if (val < entry->num) {
95
- NL_SET_ERR_MSG_MOD(extack, "New size is less than current occupancy");
96
- err = -EINVAL;
97
- } else {
98
- entry->max = val;
99
- }
100
-
101
- return err;
136
+ entry->max = val;
102137 }
103138
104139 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
....@@ -120,9 +155,9 @@
120155 return err;
121156 }
122157
123
-static int nsim_fib_rule_event(struct fib_notifier_info *info, bool add)
158
+static int nsim_fib_rule_event(struct nsim_fib_data *data,
159
+ struct fib_notifier_info *info, bool add)
124160 {
125
- struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
126161 struct netlink_ext_ack *extack = info->extack;
127162 int err = 0;
128163
....@@ -157,18 +192,556 @@
157192 return err;
158193 }
159194
160
-static int nsim_fib_event(struct fib_notifier_info *info, bool add)
195
+static void nsim_fib_rt_init(struct nsim_fib_data *data,
196
+ struct nsim_fib_rt *fib_rt, const void *addr,
197
+ size_t addr_len, unsigned int prefix_len,
198
+ int family, u32 tb_id)
161199 {
162
- struct nsim_fib_data *data = net_generic(info->net, nsim_fib_net_id);
163
- struct netlink_ext_ack *extack = info->extack;
200
+ memcpy(fib_rt->key.addr, addr, addr_len);
201
+ fib_rt->key.prefix_len = prefix_len;
202
+ fib_rt->key.family = family;
203
+ fib_rt->key.tb_id = tb_id;
204
+ list_add(&fib_rt->list, &data->fib_rt_list);
205
+}
206
+
207
+static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
208
+{
209
+ list_del(&fib_rt->list);
210
+}
211
+
212
+static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht,
213
+ const void *addr, size_t addr_len,
214
+ unsigned int prefix_len,
215
+ int family, u32 tb_id)
216
+{
217
+ struct nsim_fib_rt_key key;
218
+
219
+ memset(&key, 0, sizeof(key));
220
+ memcpy(key.addr, addr, addr_len);
221
+ key.prefix_len = prefix_len;
222
+ key.family = family;
223
+ key.tb_id = tb_id;
224
+
225
+ return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params);
226
+}
227
+
228
+static struct nsim_fib4_rt *
229
+nsim_fib4_rt_create(struct nsim_fib_data *data,
230
+ struct fib_entry_notifier_info *fen_info)
231
+{
232
+ struct nsim_fib4_rt *fib4_rt;
233
+
234
+ fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_ATOMIC);
235
+ if (!fib4_rt)
236
+ return NULL;
237
+
238
+ nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32),
239
+ fen_info->dst_len, AF_INET, fen_info->tb_id);
240
+
241
+ fib4_rt->fi = fen_info->fi;
242
+ fib_info_hold(fib4_rt->fi);
243
+ fib4_rt->tos = fen_info->tos;
244
+ fib4_rt->type = fen_info->type;
245
+
246
+ return fib4_rt;
247
+}
248
+
249
+static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt)
250
+{
251
+ fib_info_put(fib4_rt->fi);
252
+ nsim_fib_rt_fini(&fib4_rt->common);
253
+ kfree(fib4_rt);
254
+}
255
+
256
+static struct nsim_fib4_rt *
257
+nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
258
+ const struct fib_entry_notifier_info *fen_info)
259
+{
260
+ struct nsim_fib_rt *fib_rt;
261
+
262
+ fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32),
263
+ fen_info->dst_len, AF_INET,
264
+ fen_info->tb_id);
265
+ if (!fib_rt)
266
+ return NULL;
267
+
268
+ return container_of(fib_rt, struct nsim_fib4_rt, common);
269
+}
270
+
271
+static void nsim_fib4_rt_hw_flags_set(struct net *net,
272
+ const struct nsim_fib4_rt *fib4_rt,
273
+ bool trap)
274
+{
275
+ u32 *p_dst = (u32 *) fib4_rt->common.key.addr;
276
+ int dst_len = fib4_rt->common.key.prefix_len;
277
+ struct fib_rt_info fri;
278
+
279
+ fri.fi = fib4_rt->fi;
280
+ fri.tb_id = fib4_rt->common.key.tb_id;
281
+ fri.dst = cpu_to_be32(*p_dst);
282
+ fri.dst_len = dst_len;
283
+ fri.tos = fib4_rt->tos;
284
+ fri.type = fib4_rt->type;
285
+ fri.offload = false;
286
+ fri.trap = trap;
287
+ fib_alias_hw_flags_set(net, &fri);
288
+}
289
+
290
+static int nsim_fib4_rt_add(struct nsim_fib_data *data,
291
+ struct nsim_fib4_rt *fib4_rt,
292
+ struct netlink_ext_ack *extack)
293
+{
294
+ struct net *net = devlink_net(data->devlink);
295
+ int err;
296
+
297
+ err = nsim_fib_account(&data->ipv4.fib, true, extack);
298
+ if (err)
299
+ return err;
300
+
301
+ err = rhashtable_insert_fast(&data->fib_rt_ht,
302
+ &fib4_rt->common.ht_node,
303
+ nsim_fib_rt_ht_params);
304
+ if (err) {
305
+ NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv4 route");
306
+ goto err_fib_dismiss;
307
+ }
308
+
309
+ nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
310
+
311
+ return 0;
312
+
313
+err_fib_dismiss:
314
+ nsim_fib_account(&data->ipv4.fib, false, extack);
315
+ return err;
316
+}
317
+
318
+static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
319
+ struct nsim_fib4_rt *fib4_rt,
320
+ struct nsim_fib4_rt *fib4_rt_old,
321
+ struct netlink_ext_ack *extack)
322
+{
323
+ struct net *net = devlink_net(data->devlink);
324
+ int err;
325
+
326
+ /* We are replacing a route, so no need to change the accounting. */
327
+ err = rhashtable_replace_fast(&data->fib_rt_ht,
328
+ &fib4_rt_old->common.ht_node,
329
+ &fib4_rt->common.ht_node,
330
+ nsim_fib_rt_ht_params);
331
+ if (err) {
332
+ NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv4 route");
333
+ return err;
334
+ }
335
+
336
+ nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
337
+
338
+ nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
339
+ nsim_fib4_rt_destroy(fib4_rt_old);
340
+
341
+ return 0;
342
+}
343
+
344
+static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
345
+ struct fib_entry_notifier_info *fen_info)
346
+{
347
+ struct netlink_ext_ack *extack = fen_info->info.extack;
348
+ struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
349
+ int err;
350
+
351
+ fib4_rt = nsim_fib4_rt_create(data, fen_info);
352
+ if (!fib4_rt)
353
+ return -ENOMEM;
354
+
355
+ fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
356
+ if (!fib4_rt_old)
357
+ err = nsim_fib4_rt_add(data, fib4_rt, extack);
358
+ else
359
+ err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old, extack);
360
+
361
+ if (err)
362
+ nsim_fib4_rt_destroy(fib4_rt);
363
+
364
+ return err;
365
+}
366
+
367
+static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
368
+ const struct fib_entry_notifier_info *fen_info)
369
+{
370
+ struct netlink_ext_ack *extack = fen_info->info.extack;
371
+ struct nsim_fib4_rt *fib4_rt;
372
+
373
+ fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
374
+ if (WARN_ON_ONCE(!fib4_rt))
375
+ return;
376
+
377
+ rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
378
+ nsim_fib_rt_ht_params);
379
+ nsim_fib_account(&data->ipv4.fib, false, extack);
380
+ nsim_fib4_rt_destroy(fib4_rt);
381
+}
382
+
383
+static int nsim_fib4_event(struct nsim_fib_data *data,
384
+ struct fib_notifier_info *info,
385
+ unsigned long event)
386
+{
387
+ struct fib_entry_notifier_info *fen_info;
388
+ int err = 0;
389
+
390
+ fen_info = container_of(info, struct fib_entry_notifier_info, info);
391
+
392
+ if (fen_info->fi->nh) {
393
+ NL_SET_ERR_MSG_MOD(info->extack, "IPv4 route with nexthop objects is not supported");
394
+ return 0;
395
+ }
396
+
397
+ switch (event) {
398
+ case FIB_EVENT_ENTRY_REPLACE:
399
+ err = nsim_fib4_rt_insert(data, fen_info);
400
+ break;
401
+ case FIB_EVENT_ENTRY_DEL:
402
+ nsim_fib4_rt_remove(data, fen_info);
403
+ break;
404
+ default:
405
+ break;
406
+ }
407
+
408
+ return err;
409
+}
410
+
411
+static struct nsim_fib6_rt_nh *
412
+nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt,
413
+ const struct fib6_info *rt)
414
+{
415
+ struct nsim_fib6_rt_nh *fib6_rt_nh;
416
+
417
+ list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) {
418
+ if (fib6_rt_nh->rt == rt)
419
+ return fib6_rt_nh;
420
+ }
421
+
422
+ return NULL;
423
+}
424
+
425
+static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt,
426
+ struct fib6_info *rt)
427
+{
428
+ struct nsim_fib6_rt_nh *fib6_rt_nh;
429
+
430
+ fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_ATOMIC);
431
+ if (!fib6_rt_nh)
432
+ return -ENOMEM;
433
+
434
+ fib6_info_hold(rt);
435
+ fib6_rt_nh->rt = rt;
436
+ list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
437
+ fib6_rt->nhs++;
438
+
439
+ return 0;
440
+}
441
+
442
+static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
443
+ const struct fib6_info *rt)
444
+{
445
+ struct nsim_fib6_rt_nh *fib6_rt_nh;
446
+
447
+ fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
448
+ if (WARN_ON_ONCE(!fib6_rt_nh))
449
+ return;
450
+
451
+ fib6_rt->nhs--;
452
+ list_del(&fib6_rt_nh->list);
453
+#if IS_ENABLED(CONFIG_IPV6)
454
+ fib6_info_release(fib6_rt_nh->rt);
455
+#endif
456
+ kfree(fib6_rt_nh);
457
+}
458
+
459
+static struct nsim_fib6_rt *
460
+nsim_fib6_rt_create(struct nsim_fib_data *data,
461
+ struct fib6_entry_notifier_info *fen6_info)
462
+{
463
+ struct fib6_info *iter, *rt = fen6_info->rt;
464
+ struct nsim_fib6_rt *fib6_rt;
465
+ int i = 0;
466
+ int err;
467
+
468
+ fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_ATOMIC);
469
+ if (!fib6_rt)
470
+ return ERR_PTR(-ENOMEM);
471
+
472
+ nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr,
473
+ sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6,
474
+ rt->fib6_table->tb6_id);
475
+
476
+ /* We consider a multipath IPv6 route as one entry, but it can be made
477
+ * up from several fib6_info structs (one for each nexthop), so we
478
+ * add them all to the same list under the entry.
479
+ */
480
+ INIT_LIST_HEAD(&fib6_rt->nh_list);
481
+
482
+ err = nsim_fib6_rt_nh_add(fib6_rt, rt);
483
+ if (err)
484
+ goto err_fib_rt_fini;
485
+
486
+ if (!fen6_info->nsiblings)
487
+ return fib6_rt;
488
+
489
+ list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
490
+ if (i == fen6_info->nsiblings)
491
+ break;
492
+
493
+ err = nsim_fib6_rt_nh_add(fib6_rt, iter);
494
+ if (err)
495
+ goto err_fib6_rt_nh_del;
496
+ i++;
497
+ }
498
+ WARN_ON_ONCE(i != fen6_info->nsiblings);
499
+
500
+ return fib6_rt;
501
+
502
+err_fib6_rt_nh_del:
503
+ list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
504
+ fib6_siblings)
505
+ nsim_fib6_rt_nh_del(fib6_rt, iter);
506
+ nsim_fib6_rt_nh_del(fib6_rt, rt);
507
+err_fib_rt_fini:
508
+ nsim_fib_rt_fini(&fib6_rt->common);
509
+ kfree(fib6_rt);
510
+ return ERR_PTR(err);
511
+}
512
+
513
+static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt)
514
+{
515
+ struct nsim_fib6_rt_nh *iter, *tmp;
516
+
517
+ list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list)
518
+ nsim_fib6_rt_nh_del(fib6_rt, iter->rt);
519
+ WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list));
520
+ nsim_fib_rt_fini(&fib6_rt->common);
521
+ kfree(fib6_rt);
522
+}
523
+
524
+static struct nsim_fib6_rt *
525
+nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt)
526
+{
527
+ struct nsim_fib_rt *fib_rt;
528
+
529
+ fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr,
530
+ sizeof(rt->fib6_dst.addr),
531
+ rt->fib6_dst.plen, AF_INET6,
532
+ rt->fib6_table->tb6_id);
533
+ if (!fib_rt)
534
+ return NULL;
535
+
536
+ return container_of(fib_rt, struct nsim_fib6_rt, common);
537
+}
538
+
539
+static int nsim_fib6_rt_append(struct nsim_fib_data *data,
540
+ struct fib6_entry_notifier_info *fen6_info)
541
+{
542
+ struct fib6_info *iter, *rt = fen6_info->rt;
543
+ struct nsim_fib6_rt *fib6_rt;
544
+ int i = 0;
545
+ int err;
546
+
547
+ fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
548
+ if (WARN_ON_ONCE(!fib6_rt))
549
+ return -EINVAL;
550
+
551
+ err = nsim_fib6_rt_nh_add(fib6_rt, rt);
552
+ if (err)
553
+ return err;
554
+ rt->trap = true;
555
+
556
+ if (!fen6_info->nsiblings)
557
+ return 0;
558
+
559
+ list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
560
+ if (i == fen6_info->nsiblings)
561
+ break;
562
+
563
+ err = nsim_fib6_rt_nh_add(fib6_rt, iter);
564
+ if (err)
565
+ goto err_fib6_rt_nh_del;
566
+ iter->trap = true;
567
+ i++;
568
+ }
569
+ WARN_ON_ONCE(i != fen6_info->nsiblings);
570
+
571
+ return 0;
572
+
573
+err_fib6_rt_nh_del:
574
+ list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
575
+ fib6_siblings) {
576
+ iter->trap = false;
577
+ nsim_fib6_rt_nh_del(fib6_rt, iter);
578
+ }
579
+ rt->trap = false;
580
+ nsim_fib6_rt_nh_del(fib6_rt, rt);
581
+ return err;
582
+}
583
+
584
+static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt *fib6_rt,
585
+ bool trap)
586
+{
587
+ struct nsim_fib6_rt_nh *fib6_rt_nh;
588
+
589
+ list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
590
+ fib6_info_hw_flags_set(fib6_rt_nh->rt, false, trap);
591
+}
592
+
593
+static int nsim_fib6_rt_add(struct nsim_fib_data *data,
594
+ struct nsim_fib6_rt *fib6_rt,
595
+ struct netlink_ext_ack *extack)
596
+{
597
+ int err;
598
+
599
+ err = nsim_fib_account(&data->ipv6.fib, true, extack);
600
+ if (err)
601
+ return err;
602
+
603
+ err = rhashtable_insert_fast(&data->fib_rt_ht,
604
+ &fib6_rt->common.ht_node,
605
+ nsim_fib_rt_ht_params);
606
+ if (err) {
607
+ NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv6 route");
608
+ goto err_fib_dismiss;
609
+ }
610
+
611
+ nsim_fib6_rt_hw_flags_set(fib6_rt, true);
612
+
613
+ return 0;
614
+
615
+err_fib_dismiss:
616
+ nsim_fib_account(&data->ipv6.fib, false, extack);
617
+ return err;
618
+}
619
+
620
+static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
621
+ struct nsim_fib6_rt *fib6_rt,
622
+ struct nsim_fib6_rt *fib6_rt_old,
623
+ struct netlink_ext_ack *extack)
624
+{
625
+ int err;
626
+
627
+ /* We are replacing a route, so no need to change the accounting. */
628
+ err = rhashtable_replace_fast(&data->fib_rt_ht,
629
+ &fib6_rt_old->common.ht_node,
630
+ &fib6_rt->common.ht_node,
631
+ nsim_fib_rt_ht_params);
632
+ if (err) {
633
+ NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv6 route");
634
+ return err;
635
+ }
636
+
637
+ nsim_fib6_rt_hw_flags_set(fib6_rt, true);
638
+
639
+ nsim_fib6_rt_hw_flags_set(fib6_rt_old, false);
640
+ nsim_fib6_rt_destroy(fib6_rt_old);
641
+
642
+ return 0;
643
+}
644
+
645
+static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
646
+ struct fib6_entry_notifier_info *fen6_info)
647
+{
648
+ struct netlink_ext_ack *extack = fen6_info->info.extack;
649
+ struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
650
+ int err;
651
+
652
+ fib6_rt = nsim_fib6_rt_create(data, fen6_info);
653
+ if (IS_ERR(fib6_rt))
654
+ return PTR_ERR(fib6_rt);
655
+
656
+ fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
657
+ if (!fib6_rt_old)
658
+ err = nsim_fib6_rt_add(data, fib6_rt, extack);
659
+ else
660
+ err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old, extack);
661
+
662
+ if (err)
663
+ nsim_fib6_rt_destroy(fib6_rt);
664
+
665
+ return err;
666
+}
667
+
668
+static void
669
+nsim_fib6_rt_remove(struct nsim_fib_data *data,
670
+ const struct fib6_entry_notifier_info *fen6_info)
671
+{
672
+ struct netlink_ext_ack *extack = fen6_info->info.extack;
673
+ struct nsim_fib6_rt *fib6_rt;
674
+
675
+ /* Multipath routes are first added to the FIB trie and only then
676
+ * notified. If we vetoed the addition, we will get a delete
677
+ * notification for a route we do not have. Therefore, do not warn if
678
+ * route was not found.
679
+ */
680
+ fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
681
+ if (!fib6_rt)
682
+ return;
683
+
684
+ /* If not all the nexthops are deleted, then only reduce the nexthop
685
+ * group.
686
+ */
687
+ if (fen6_info->nsiblings + 1 != fib6_rt->nhs) {
688
+ nsim_fib6_rt_nh_del(fib6_rt, fen6_info->rt);
689
+ return;
690
+ }
691
+
692
+ rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
693
+ nsim_fib_rt_ht_params);
694
+ nsim_fib_account(&data->ipv6.fib, false, extack);
695
+ nsim_fib6_rt_destroy(fib6_rt);
696
+}
697
+
698
+static int nsim_fib6_event(struct nsim_fib_data *data,
699
+ struct fib_notifier_info *info,
700
+ unsigned long event)
701
+{
702
+ struct fib6_entry_notifier_info *fen6_info;
703
+ int err = 0;
704
+
705
+ fen6_info = container_of(info, struct fib6_entry_notifier_info, info);
706
+
707
+ if (fen6_info->rt->nh) {
708
+ NL_SET_ERR_MSG_MOD(info->extack, "IPv6 route with nexthop objects is not supported");
709
+ return 0;
710
+ }
711
+
712
+ if (fen6_info->rt->fib6_src.plen) {
713
+ NL_SET_ERR_MSG_MOD(info->extack, "IPv6 source-specific route is not supported");
714
+ return 0;
715
+ }
716
+
717
+ switch (event) {
718
+ case FIB_EVENT_ENTRY_REPLACE:
719
+ err = nsim_fib6_rt_insert(data, fen6_info);
720
+ break;
721
+ case FIB_EVENT_ENTRY_APPEND:
722
+ err = nsim_fib6_rt_append(data, fen6_info);
723
+ break;
724
+ case FIB_EVENT_ENTRY_DEL:
725
+ nsim_fib6_rt_remove(data, fen6_info);
726
+ break;
727
+ default:
728
+ break;
729
+ }
730
+
731
+ return err;
732
+}
733
+
734
+static int nsim_fib_event(struct nsim_fib_data *data,
735
+ struct fib_notifier_info *info, unsigned long event)
736
+{
164737 int err = 0;
165738
166739 switch (info->family) {
167740 case AF_INET:
168
- err = nsim_fib_account(&data->ipv4.fib, add, extack);
741
+ err = nsim_fib4_event(data, info, event);
169742 break;
170743 case AF_INET6:
171
- err = nsim_fib_account(&data->ipv6.fib, add, extack);
744
+ err = nsim_fib6_event(data, info, event);
172745 break;
173746 }
174747
....@@ -178,89 +751,207 @@
178751 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
179752 void *ptr)
180753 {
754
+ struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
755
+ fib_nb);
181756 struct fib_notifier_info *info = ptr;
182757 int err = 0;
183758
759
+ /* IPv6 routes can be added via RAs from softIRQ. */
760
+ spin_lock_bh(&data->fib_lock);
761
+
184762 switch (event) {
185
- case FIB_EVENT_RULE_ADD: /* fall through */
763
+ case FIB_EVENT_RULE_ADD:
186764 case FIB_EVENT_RULE_DEL:
187
- err = nsim_fib_rule_event(info, event == FIB_EVENT_RULE_ADD);
765
+ err = nsim_fib_rule_event(data, info,
766
+ event == FIB_EVENT_RULE_ADD);
188767 break;
189768
190
- case FIB_EVENT_ENTRY_ADD: /* fall through */
769
+ case FIB_EVENT_ENTRY_REPLACE:
770
+ case FIB_EVENT_ENTRY_APPEND:
191771 case FIB_EVENT_ENTRY_DEL:
192
- err = nsim_fib_event(info, event == FIB_EVENT_ENTRY_ADD);
772
+ err = nsim_fib_event(data, info, event);
193773 break;
194774 }
195775
776
+ spin_unlock_bh(&data->fib_lock);
777
+
196778 return notifier_from_errno(err);
779
+}
780
+
781
+static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt,
782
+ struct nsim_fib_data *data)
783
+{
784
+ struct devlink *devlink = data->devlink;
785
+ struct nsim_fib4_rt *fib4_rt;
786
+
787
+ fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common);
788
+ nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false);
789
+ nsim_fib_account(&data->ipv4.fib, false, NULL);
790
+ nsim_fib4_rt_destroy(fib4_rt);
791
+}
792
+
793
+static void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt,
794
+ struct nsim_fib_data *data)
795
+{
796
+ struct nsim_fib6_rt *fib6_rt;
797
+
798
+ fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common);
799
+ nsim_fib6_rt_hw_flags_set(fib6_rt, false);
800
+ nsim_fib_account(&data->ipv6.fib, false, NULL);
801
+ nsim_fib6_rt_destroy(fib6_rt);
802
+}
803
+
804
+static void nsim_fib_rt_free(void *ptr, void *arg)
805
+{
806
+ struct nsim_fib_rt *fib_rt = ptr;
807
+ struct nsim_fib_data *data = arg;
808
+
809
+ switch (fib_rt->key.family) {
810
+ case AF_INET:
811
+ nsim_fib4_rt_free(fib_rt, data);
812
+ break;
813
+ case AF_INET6:
814
+ nsim_fib6_rt_free(fib_rt, data);
815
+ break;
816
+ default:
817
+ WARN_ON_ONCE(1);
818
+ }
197819 }
198820
199821 /* inconsistent dump, trying again */
200822 static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
201823 {
202
- struct nsim_fib_data *data;
203
- struct net *net;
824
+ struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
825
+ fib_nb);
826
+ struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
204827
205
- rcu_read_lock();
206
- for_each_net_rcu(net) {
207
- data = net_generic(net, nsim_fib_net_id);
208
-
209
- data->ipv4.fib.num = 0ULL;
210
- data->ipv4.rules.num = 0ULL;
211
-
212
- data->ipv6.fib.num = 0ULL;
213
- data->ipv6.rules.num = 0ULL;
828
+ /* The notifier block is still not registered, so we do not need to
829
+ * take any locks here.
830
+ */
831
+ list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
832
+ rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
833
+ nsim_fib_rt_ht_params);
834
+ nsim_fib_rt_free(fib_rt, data);
214835 }
215
- rcu_read_unlock();
836
+
837
+ data->ipv4.rules.num = 0ULL;
838
+ data->ipv6.rules.num = 0ULL;
216839 }
217840
218
-static struct notifier_block nsim_fib_nb = {
219
- .notifier_call = nsim_fib_event_nb,
220
-};
221
-
222
-/* Initialize per network namespace state */
223
-static int __net_init nsim_fib_netns_init(struct net *net)
841
+static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
224842 {
225
- struct nsim_fib_data *data = net_generic(net, nsim_fib_net_id);
843
+ struct nsim_fib_data *data = priv;
226844
227
- data->ipv4.fib.max = (u64)-1;
228
- data->ipv4.rules.max = (u64)-1;
229
-
230
- data->ipv6.fib.max = (u64)-1;
231
- data->ipv6.rules.max = (u64)-1;
232
-
233
- return 0;
845
+ return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
234846 }
235847
236
-static struct pernet_operations nsim_fib_net_ops = {
237
- .init = nsim_fib_netns_init,
238
- .id = &nsim_fib_net_id,
239
- .size = sizeof(struct nsim_fib_data),
240
-};
241
-
242
-void nsim_fib_exit(void)
848
+static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
243849 {
244
- unregister_pernet_subsys(&nsim_fib_net_ops);
245
- unregister_fib_notifier(&nsim_fib_nb);
850
+ struct nsim_fib_data *data = priv;
851
+
852
+ return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
246853 }
247854
248
-int nsim_fib_init(void)
855
+static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
249856 {
857
+ struct nsim_fib_data *data = priv;
858
+
859
+ return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
860
+}
861
+
862
+static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
863
+{
864
+ struct nsim_fib_data *data = priv;
865
+
866
+ return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
867
+}
868
+
869
+static void nsim_fib_set_max_all(struct nsim_fib_data *data,
870
+ struct devlink *devlink)
871
+{
872
+ enum nsim_resource_id res_ids[] = {
873
+ NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
874
+ NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
875
+ };
876
+ int i;
877
+
878
+ for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
879
+ int err;
880
+ u64 val;
881
+
882
+ err = devlink_resource_size_get(devlink, res_ids[i], &val);
883
+ if (err)
884
+ val = (u64) -1;
885
+ nsim_fib_set_max(data, res_ids[i], val);
886
+ }
887
+}
888
+
889
+struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
890
+ struct netlink_ext_ack *extack)
891
+{
892
+ struct nsim_fib_data *data;
250893 int err;
251894
252
- err = register_pernet_subsys(&nsim_fib_net_ops);
253
- if (err < 0) {
254
- pr_err("Failed to register pernet subsystem\n");
255
- goto err_out;
256
- }
895
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
896
+ if (!data)
897
+ return ERR_PTR(-ENOMEM);
898
+ data->devlink = devlink;
257899
258
- err = register_fib_notifier(&nsim_fib_nb, nsim_fib_dump_inconsistent);
259
- if (err < 0) {
900
+ spin_lock_init(&data->fib_lock);
901
+ INIT_LIST_HEAD(&data->fib_rt_list);
902
+ err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
903
+ if (err)
904
+ goto err_data_free;
905
+
906
+ nsim_fib_set_max_all(data, devlink);
907
+
908
+ data->fib_nb.notifier_call = nsim_fib_event_nb;
909
+ err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
910
+ nsim_fib_dump_inconsistent, extack);
911
+ if (err) {
260912 pr_err("Failed to register fib notifier\n");
261
- goto err_out;
913
+ goto err_rhashtable_destroy;
262914 }
263915
264
-err_out:
265
- return err;
916
+ devlink_resource_occ_get_register(devlink,
917
+ NSIM_RESOURCE_IPV4_FIB,
918
+ nsim_fib_ipv4_resource_occ_get,
919
+ data);
920
+ devlink_resource_occ_get_register(devlink,
921
+ NSIM_RESOURCE_IPV4_FIB_RULES,
922
+ nsim_fib_ipv4_rules_res_occ_get,
923
+ data);
924
+ devlink_resource_occ_get_register(devlink,
925
+ NSIM_RESOURCE_IPV6_FIB,
926
+ nsim_fib_ipv6_resource_occ_get,
927
+ data);
928
+ devlink_resource_occ_get_register(devlink,
929
+ NSIM_RESOURCE_IPV6_FIB_RULES,
930
+ nsim_fib_ipv6_rules_res_occ_get,
931
+ data);
932
+ return data;
933
+
934
+err_rhashtable_destroy:
935
+ rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
936
+ data);
937
+err_data_free:
938
+ kfree(data);
939
+ return ERR_PTR(err);
940
+}
941
+
942
+void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
943
+{
944
+ devlink_resource_occ_get_unregister(devlink,
945
+ NSIM_RESOURCE_IPV6_FIB_RULES);
946
+ devlink_resource_occ_get_unregister(devlink,
947
+ NSIM_RESOURCE_IPV6_FIB);
948
+ devlink_resource_occ_get_unregister(devlink,
949
+ NSIM_RESOURCE_IPV4_FIB_RULES);
950
+ devlink_resource_occ_get_unregister(devlink,
951
+ NSIM_RESOURCE_IPV4_FIB);
952
+ unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
953
+ rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
954
+ data);
955
+ WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
956
+ kfree(data);
266957 }