1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/bitmap.h>
18#include <linux/in6.h>
19#include <linux/kernel.h>
20#include <linux/list.h>
21#include <linux/rhashtable.h>
22#include <linux/spinlock_types.h>
23#include <linux/types.h>
24#include <net/fib_notifier.h>
25#include <net/inet_dscp.h>
26#include <net/ip_fib.h>
27#include <net/ip6_fib.h>
28#include <net/fib_rules.h>
29#include <net/net_namespace.h>
30#include <net/nexthop.h>
31#include <linux/debugfs.h>
32
33#include "netdevsim.h"
34
35struct nsim_fib_entry {
36 u64 max;
37 atomic64_t num;
38};
39
40struct nsim_per_fib_data {
41 struct nsim_fib_entry fib;
42 struct nsim_fib_entry rules;
43};
44
45struct nsim_fib_data {
46 struct notifier_block fib_nb;
47 struct nsim_per_fib_data ipv4;
48 struct nsim_per_fib_data ipv6;
49 struct nsim_fib_entry nexthops;
50 struct rhashtable fib_rt_ht;
51 struct list_head fib_rt_list;
52 struct mutex fib_lock;
53 struct notifier_block nexthop_nb;
54 struct rhashtable nexthop_ht;
55 struct devlink *devlink;
56 struct work_struct fib_event_work;
57 struct list_head fib_event_queue;
58 spinlock_t fib_event_queue_lock;
59 struct mutex nh_lock;
60 struct dentry *ddir;
61 bool fail_route_offload;
62 bool fail_res_nexthop_group_replace;
63 bool fail_nexthop_bucket_replace;
64};
65
66struct nsim_fib_rt_key {
67 unsigned char addr[sizeof(struct in6_addr)];
68 unsigned char prefix_len;
69 int family;
70 u32 tb_id;
71};
72
73struct nsim_fib_rt {
74 struct nsim_fib_rt_key key;
75 struct rhash_head ht_node;
76 struct list_head list;
77};
78
79struct nsim_fib4_rt {
80 struct nsim_fib_rt common;
81 struct fib_info *fi;
82 dscp_t dscp;
83 u8 type;
84};
85
86struct nsim_fib6_rt {
87 struct nsim_fib_rt common;
88 struct list_head nh_list;
89 unsigned int nhs;
90};
91
92struct nsim_fib6_rt_nh {
93 struct list_head list;
94 struct fib6_info *rt;
95};
96
97struct nsim_fib6_event {
98 struct fib6_info **rt_arr;
99 unsigned int nrt6;
100};
101
102struct nsim_fib_event {
103 struct list_head list;
104 union {
105 struct fib_entry_notifier_info fen_info;
106 struct nsim_fib6_event fib6_event;
107 };
108 struct nsim_fib_data *data;
109 unsigned long event;
110 int family;
111};
112
113static const struct rhashtable_params nsim_fib_rt_ht_params = {
114 .key_offset = offsetof(struct nsim_fib_rt, key),
115 .head_offset = offsetof(struct nsim_fib_rt, ht_node),
116 .key_len = sizeof(struct nsim_fib_rt_key),
117 .automatic_shrinking = true,
118};
119
120struct nsim_nexthop {
121 struct rhash_head ht_node;
122 u64 occ;
123 u32 id;
124 bool is_resilient;
125};
126
127static const struct rhashtable_params nsim_nexthop_ht_params = {
128 .key_offset = offsetof(struct nsim_nexthop, id),
129 .head_offset = offsetof(struct nsim_nexthop, ht_node),
130 .key_len = sizeof(u32),
131 .automatic_shrinking = true,
132};
133
134u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
135 enum nsim_resource_id res_id, bool max)
136{
137 struct nsim_fib_entry *entry;
138
139 switch (res_id) {
140 case NSIM_RESOURCE_IPV4_FIB:
141 entry = &fib_data->ipv4.fib;
142 break;
143 case NSIM_RESOURCE_IPV4_FIB_RULES:
144 entry = &fib_data->ipv4.rules;
145 break;
146 case NSIM_RESOURCE_IPV6_FIB:
147 entry = &fib_data->ipv6.fib;
148 break;
149 case NSIM_RESOURCE_IPV6_FIB_RULES:
150 entry = &fib_data->ipv6.rules;
151 break;
152 case NSIM_RESOURCE_NEXTHOPS:
153 entry = &fib_data->nexthops;
154 break;
155 default:
156 return 0;
157 }
158
159 return max ? entry->max : atomic64_read(&entry->num);
160}
161
162static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
163 enum nsim_resource_id res_id, u64 val)
164{
165 struct nsim_fib_entry *entry;
166
167 switch (res_id) {
168 case NSIM_RESOURCE_IPV4_FIB:
169 entry = &fib_data->ipv4.fib;
170 break;
171 case NSIM_RESOURCE_IPV4_FIB_RULES:
172 entry = &fib_data->ipv4.rules;
173 break;
174 case NSIM_RESOURCE_IPV6_FIB:
175 entry = &fib_data->ipv6.fib;
176 break;
177 case NSIM_RESOURCE_IPV6_FIB_RULES:
178 entry = &fib_data->ipv6.rules;
179 break;
180 case NSIM_RESOURCE_NEXTHOPS:
181 entry = &fib_data->nexthops;
182 break;
183 default:
184 WARN_ON(1);
185 return;
186 }
187 entry->max = val;
188}
189
190static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
191 struct netlink_ext_ack *extack)
192{
193 int err = 0;
194
195 if (add) {
196 if (!atomic64_add_unless(&entry->num, 1, entry->max)) {
197 err = -ENOSPC;
198 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
199 }
200 } else {
201 atomic64_dec_if_positive(&entry->num);
202 }
203
204 return err;
205}
206
207static int nsim_fib_rule_event(struct nsim_fib_data *data,
208 struct fib_notifier_info *info, bool add)
209{
210 struct netlink_ext_ack *extack = info->extack;
211 int err = 0;
212
213 switch (info->family) {
214 case AF_INET:
215 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
216 break;
217 case AF_INET6:
218 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
219 break;
220 }
221
222 return err;
223}
224
225static int nsim_fib_account(struct nsim_fib_entry *entry, bool add)
226{
227 int err = 0;
228
229 if (add) {
230 if (!atomic64_add_unless(&entry->num, 1, entry->max))
231 err = -ENOSPC;
232 } else {
233 atomic64_dec_if_positive(&entry->num);
234 }
235
236 return err;
237}
238
239static void nsim_fib_rt_init(struct nsim_fib_data *data,
240 struct nsim_fib_rt *fib_rt, const void *addr,
241 size_t addr_len, unsigned int prefix_len,
242 int family, u32 tb_id)
243{
244 memcpy(fib_rt->key.addr, addr, addr_len);
245 fib_rt->key.prefix_len = prefix_len;
246 fib_rt->key.family = family;
247 fib_rt->key.tb_id = tb_id;
248 list_add(&fib_rt->list, &data->fib_rt_list);
249}
250
251static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
252{
253 list_del(&fib_rt->list);
254}
255
256static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht,
257 const void *addr, size_t addr_len,
258 unsigned int prefix_len,
259 int family, u32 tb_id)
260{
261 struct nsim_fib_rt_key key;
262
263 memset(&key, 0, sizeof(key));
264 memcpy(key.addr, addr, addr_len);
265 key.prefix_len = prefix_len;
266 key.family = family;
267 key.tb_id = tb_id;
268
269 return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params);
270}
271
272static struct nsim_fib4_rt *
273nsim_fib4_rt_create(struct nsim_fib_data *data,
274 struct fib_entry_notifier_info *fen_info)
275{
276 struct nsim_fib4_rt *fib4_rt;
277
278 fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_KERNEL);
279 if (!fib4_rt)
280 return NULL;
281
282 nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32),
283 fen_info->dst_len, AF_INET, fen_info->tb_id);
284
285 fib4_rt->fi = fen_info->fi;
286 fib_info_hold(fib4_rt->fi);
287 fib4_rt->dscp = fen_info->dscp;
288 fib4_rt->type = fen_info->type;
289
290 return fib4_rt;
291}
292
293static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt)
294{
295 fib_info_put(fib4_rt->fi);
296 nsim_fib_rt_fini(&fib4_rt->common);
297 kfree(fib4_rt);
298}
299
300static struct nsim_fib4_rt *
301nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
302 const struct fib_entry_notifier_info *fen_info)
303{
304 struct nsim_fib_rt *fib_rt;
305
306 fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32),
307 fen_info->dst_len, AF_INET,
308 fen_info->tb_id);
309 if (!fib_rt)
310 return NULL;
311
312 return container_of(fib_rt, struct nsim_fib4_rt, common);
313}
314
315static void
316nsim_fib4_rt_offload_failed_flag_set(struct net *net,
317 struct fib_entry_notifier_info *fen_info)
318{
319 u32 *p_dst = (u32 *)&fen_info->dst;
320 struct fib_rt_info fri;
321
322 fri.fi = fen_info->fi;
323 fri.tb_id = fen_info->tb_id;
324 fri.dst = cpu_to_be32(*p_dst);
325 fri.dst_len = fen_info->dst_len;
326 fri.dscp = fen_info->dscp;
327 fri.type = fen_info->type;
328 fri.offload = false;
329 fri.trap = false;
330 fri.offload_failed = true;
331 fib_alias_hw_flags_set(net, &fri);
332}
333
334static void nsim_fib4_rt_hw_flags_set(struct net *net,
335 const struct nsim_fib4_rt *fib4_rt,
336 bool trap)
337{
338 u32 *p_dst = (u32 *) fib4_rt->common.key.addr;
339 int dst_len = fib4_rt->common.key.prefix_len;
340 struct fib_rt_info fri;
341
342 fri.fi = fib4_rt->fi;
343 fri.tb_id = fib4_rt->common.key.tb_id;
344 fri.dst = cpu_to_be32(*p_dst);
345 fri.dst_len = dst_len;
346 fri.dscp = fib4_rt->dscp;
347 fri.type = fib4_rt->type;
348 fri.offload = false;
349 fri.trap = trap;
350 fri.offload_failed = false;
351 fib_alias_hw_flags_set(net, &fri);
352}
353
354static int nsim_fib4_rt_add(struct nsim_fib_data *data,
355 struct nsim_fib4_rt *fib4_rt)
356{
357 struct net *net = devlink_net(data->devlink);
358 int err;
359
360 err = rhashtable_insert_fast(&data->fib_rt_ht,
361 &fib4_rt->common.ht_node,
362 nsim_fib_rt_ht_params);
363 if (err)
364 goto err_fib_dismiss;
365
366
367 msleep(1);
368 nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
369
370 return 0;
371
372err_fib_dismiss:
373
374
375
376 nsim_fib_account(&data->ipv4.fib, false);
377 return err;
378}
379
380static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
381 struct nsim_fib4_rt *fib4_rt,
382 struct nsim_fib4_rt *fib4_rt_old)
383{
384 struct net *net = devlink_net(data->devlink);
385 int err;
386
387
388
389
390 err = nsim_fib_account(&data->ipv4.fib, false);
391 if (err)
392 return err;
393 err = rhashtable_replace_fast(&data->fib_rt_ht,
394 &fib4_rt_old->common.ht_node,
395 &fib4_rt->common.ht_node,
396 nsim_fib_rt_ht_params);
397 if (err)
398 return err;
399
400 msleep(1);
401 nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
402
403 nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
404 nsim_fib4_rt_destroy(fib4_rt_old);
405
406 return 0;
407}
408
409static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
410 struct fib_entry_notifier_info *fen_info)
411{
412 struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
413 int err;
414
415 if (data->fail_route_offload) {
416
417
418
419
420 msleep(1);
421 return -EINVAL;
422 }
423
424 fib4_rt = nsim_fib4_rt_create(data, fen_info);
425 if (!fib4_rt)
426 return -ENOMEM;
427
428 fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
429 if (!fib4_rt_old)
430 err = nsim_fib4_rt_add(data, fib4_rt);
431 else
432 err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old);
433
434 if (err)
435 nsim_fib4_rt_destroy(fib4_rt);
436
437 return err;
438}
439
440static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
441 const struct fib_entry_notifier_info *fen_info)
442{
443 struct nsim_fib4_rt *fib4_rt;
444
445 fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
446 if (!fib4_rt)
447 return;
448
449 rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
450 nsim_fib_rt_ht_params);
451 nsim_fib4_rt_destroy(fib4_rt);
452}
453
454static int nsim_fib4_event(struct nsim_fib_data *data,
455 struct fib_entry_notifier_info *fen_info,
456 unsigned long event)
457{
458 int err = 0;
459
460 switch (event) {
461 case FIB_EVENT_ENTRY_REPLACE:
462 err = nsim_fib4_rt_insert(data, fen_info);
463 if (err) {
464 struct net *net = devlink_net(data->devlink);
465
466 nsim_fib4_rt_offload_failed_flag_set(net, fen_info);
467 }
468 break;
469 case FIB_EVENT_ENTRY_DEL:
470 nsim_fib4_rt_remove(data, fen_info);
471 break;
472 default:
473 break;
474 }
475
476 return err;
477}
478
479static struct nsim_fib6_rt_nh *
480nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt,
481 const struct fib6_info *rt)
482{
483 struct nsim_fib6_rt_nh *fib6_rt_nh;
484
485 list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) {
486 if (fib6_rt_nh->rt == rt)
487 return fib6_rt_nh;
488 }
489
490 return NULL;
491}
492
493static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt,
494 struct fib6_info *rt)
495{
496 struct nsim_fib6_rt_nh *fib6_rt_nh;
497
498 fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_KERNEL);
499 if (!fib6_rt_nh)
500 return -ENOMEM;
501
502 fib6_info_hold(rt);
503 fib6_rt_nh->rt = rt;
504 list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
505 fib6_rt->nhs++;
506
507 return 0;
508}
509
510#if IS_ENABLED(CONFIG_IPV6)
511static void nsim_rt6_release(struct fib6_info *rt)
512{
513 fib6_info_release(rt);
514}
515#else
516static void nsim_rt6_release(struct fib6_info *rt)
517{
518}
519#endif
520
521static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
522 const struct fib6_info *rt)
523{
524 struct nsim_fib6_rt_nh *fib6_rt_nh;
525
526 fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
527 if (!fib6_rt_nh)
528 return;
529
530 fib6_rt->nhs--;
531 list_del(&fib6_rt_nh->list);
532 nsim_rt6_release(fib6_rt_nh->rt);
533 kfree(fib6_rt_nh);
534}
535
536static struct nsim_fib6_rt *
537nsim_fib6_rt_create(struct nsim_fib_data *data,
538 struct fib6_info **rt_arr, unsigned int nrt6)
539{
540 struct fib6_info *rt = rt_arr[0];
541 struct nsim_fib6_rt *fib6_rt;
542 int i = 0;
543 int err;
544
545 fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_KERNEL);
546 if (!fib6_rt)
547 return ERR_PTR(-ENOMEM);
548
549 nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr,
550 sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6,
551 rt->fib6_table->tb6_id);
552
553
554
555
556
557 INIT_LIST_HEAD(&fib6_rt->nh_list);
558
559 for (i = 0; i < nrt6; i++) {
560 err = nsim_fib6_rt_nh_add(fib6_rt, rt_arr[i]);
561 if (err)
562 goto err_fib6_rt_nh_del;
563 }
564
565 return fib6_rt;
566
567err_fib6_rt_nh_del:
568 for (i--; i >= 0; i--) {
569 nsim_fib6_rt_nh_del(fib6_rt, rt_arr[i]);
570 }
571 nsim_fib_rt_fini(&fib6_rt->common);
572 kfree(fib6_rt);
573 return ERR_PTR(err);
574}
575
576static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt)
577{
578 struct nsim_fib6_rt_nh *iter, *tmp;
579
580 list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list)
581 nsim_fib6_rt_nh_del(fib6_rt, iter->rt);
582 WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list));
583 nsim_fib_rt_fini(&fib6_rt->common);
584 kfree(fib6_rt);
585}
586
587static struct nsim_fib6_rt *
588nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt)
589{
590 struct nsim_fib_rt *fib_rt;
591
592 fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr,
593 sizeof(rt->fib6_dst.addr),
594 rt->fib6_dst.plen, AF_INET6,
595 rt->fib6_table->tb6_id);
596 if (!fib_rt)
597 return NULL;
598
599 return container_of(fib_rt, struct nsim_fib6_rt, common);
600}
601
602static int nsim_fib6_rt_append(struct nsim_fib_data *data,
603 struct nsim_fib6_event *fib6_event)
604{
605 struct fib6_info *rt = fib6_event->rt_arr[0];
606 struct nsim_fib6_rt *fib6_rt;
607 int i, err;
608
609 if (data->fail_route_offload) {
610
611
612
613
614 msleep(1);
615 return -EINVAL;
616 }
617
618 fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
619 if (!fib6_rt)
620 return -EINVAL;
621
622 for (i = 0; i < fib6_event->nrt6; i++) {
623 err = nsim_fib6_rt_nh_add(fib6_rt, fib6_event->rt_arr[i]);
624 if (err)
625 goto err_fib6_rt_nh_del;
626
627 WRITE_ONCE(fib6_event->rt_arr[i]->trap, true);
628 }
629
630 return 0;
631
632err_fib6_rt_nh_del:
633 for (i--; i >= 0; i--) {
634 WRITE_ONCE(fib6_event->rt_arr[i]->trap, false);
635 nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
636 }
637 return err;
638}
639
640#if IS_ENABLED(CONFIG_IPV6)
641static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
642 struct fib6_info **rt_arr,
643 unsigned int nrt6)
644
645{
646 struct net *net = devlink_net(data->devlink);
647 int i;
648
649 for (i = 0; i < nrt6; i++)
650 fib6_info_hw_flags_set(net, rt_arr[i], false, false, true);
651}
652#else
653static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
654 struct fib6_info **rt_arr,
655 unsigned int nrt6)
656{
657}
658#endif
659
660#if IS_ENABLED(CONFIG_IPV6)
661static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
662 const struct nsim_fib6_rt *fib6_rt,
663 bool trap)
664{
665 struct net *net = devlink_net(data->devlink);
666 struct nsim_fib6_rt_nh *fib6_rt_nh;
667
668 list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
669 fib6_info_hw_flags_set(net, fib6_rt_nh->rt, false, trap, false);
670}
671#else
672static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
673 const struct nsim_fib6_rt *fib6_rt,
674 bool trap)
675{
676}
677#endif
678
679static int nsim_fib6_rt_add(struct nsim_fib_data *data,
680 struct nsim_fib6_rt *fib6_rt)
681{
682 int err;
683
684 err = rhashtable_insert_fast(&data->fib_rt_ht,
685 &fib6_rt->common.ht_node,
686 nsim_fib_rt_ht_params);
687
688 if (err)
689 goto err_fib_dismiss;
690
691 msleep(1);
692 nsim_fib6_rt_hw_flags_set(data, fib6_rt, true);
693
694 return 0;
695
696err_fib_dismiss:
697
698
699
700 nsim_fib_account(&data->ipv6.fib, false);
701 return err;
702}
703
704static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
705 struct nsim_fib6_rt *fib6_rt,
706 struct nsim_fib6_rt *fib6_rt_old)
707{
708 int err;
709
710
711
712
713 err = nsim_fib_account(&data->ipv6.fib, false);
714 if (err)
715 return err;
716
717 err = rhashtable_replace_fast(&data->fib_rt_ht,
718 &fib6_rt_old->common.ht_node,
719 &fib6_rt->common.ht_node,
720 nsim_fib_rt_ht_params);
721
722 if (err)
723 return err;
724
725 msleep(1);
726 nsim_fib6_rt_hw_flags_set(data, fib6_rt, true);
727
728 nsim_fib6_rt_hw_flags_set(data, fib6_rt_old, false);
729 nsim_fib6_rt_destroy(fib6_rt_old);
730
731 return 0;
732}
733
734static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
735 struct nsim_fib6_event *fib6_event)
736{
737 struct fib6_info *rt = fib6_event->rt_arr[0];
738 struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
739 int err;
740
741 if (data->fail_route_offload) {
742
743
744
745
746 msleep(1);
747 return -EINVAL;
748 }
749
750 fib6_rt = nsim_fib6_rt_create(data, fib6_event->rt_arr,
751 fib6_event->nrt6);
752 if (IS_ERR(fib6_rt))
753 return PTR_ERR(fib6_rt);
754
755 fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
756 if (!fib6_rt_old)
757 err = nsim_fib6_rt_add(data, fib6_rt);
758 else
759 err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old);
760
761 if (err)
762 nsim_fib6_rt_destroy(fib6_rt);
763
764 return err;
765}
766
767static void nsim_fib6_rt_remove(struct nsim_fib_data *data,
768 struct nsim_fib6_event *fib6_event)
769{
770 struct fib6_info *rt = fib6_event->rt_arr[0];
771 struct nsim_fib6_rt *fib6_rt;
772 int i;
773
774
775
776
777
778
779 fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
780 if (!fib6_rt)
781 return;
782
783
784
785
786 if (fib6_event->nrt6 != fib6_rt->nhs) {
787 for (i = 0; i < fib6_event->nrt6; i++)
788 nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
789 return;
790 }
791
792 rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
793 nsim_fib_rt_ht_params);
794 nsim_fib6_rt_destroy(fib6_rt);
795}
796
797static int nsim_fib6_event_init(struct nsim_fib6_event *fib6_event,
798 struct fib6_entry_notifier_info *fen6_info)
799{
800 struct fib6_info *rt = fen6_info->rt;
801 struct fib6_info **rt_arr;
802 struct fib6_info *iter;
803 unsigned int nrt6;
804 int i = 0;
805
806 nrt6 = fen6_info->nsiblings + 1;
807
808 rt_arr = kcalloc(nrt6, sizeof(struct fib6_info *), GFP_ATOMIC);
809 if (!rt_arr)
810 return -ENOMEM;
811
812 fib6_event->rt_arr = rt_arr;
813 fib6_event->nrt6 = nrt6;
814
815 rt_arr[0] = rt;
816 fib6_info_hold(rt);
817
818 if (!fen6_info->nsiblings)
819 return 0;
820
821 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
822 if (i == fen6_info->nsiblings)
823 break;
824
825 rt_arr[i + 1] = iter;
826 fib6_info_hold(iter);
827 i++;
828 }
829 WARN_ON_ONCE(i != fen6_info->nsiblings);
830
831 return 0;
832}
833
834static void nsim_fib6_event_fini(struct nsim_fib6_event *fib6_event)
835{
836 int i;
837
838 for (i = 0; i < fib6_event->nrt6; i++)
839 nsim_rt6_release(fib6_event->rt_arr[i]);
840 kfree(fib6_event->rt_arr);
841}
842
843static int nsim_fib6_event(struct nsim_fib_data *data,
844 struct nsim_fib6_event *fib6_event,
845 unsigned long event)
846{
847 int err;
848
849 if (fib6_event->rt_arr[0]->fib6_src.plen)
850 return 0;
851
852 switch (event) {
853 case FIB_EVENT_ENTRY_REPLACE:
854 err = nsim_fib6_rt_insert(data, fib6_event);
855 if (err)
856 goto err_rt_offload_failed_flag_set;
857 break;
858 case FIB_EVENT_ENTRY_APPEND:
859 err = nsim_fib6_rt_append(data, fib6_event);
860 if (err)
861 goto err_rt_offload_failed_flag_set;
862 break;
863 case FIB_EVENT_ENTRY_DEL:
864 nsim_fib6_rt_remove(data, fib6_event);
865 break;
866 default:
867 break;
868 }
869
870 return 0;
871
872err_rt_offload_failed_flag_set:
873 nsim_fib6_rt_offload_failed_flag_set(data, fib6_event->rt_arr,
874 fib6_event->nrt6);
875 return err;
876}
877
878static void nsim_fib_event(struct nsim_fib_event *fib_event)
879{
880 switch (fib_event->family) {
881 case AF_INET:
882 nsim_fib4_event(fib_event->data, &fib_event->fen_info,
883 fib_event->event);
884 fib_info_put(fib_event->fen_info.fi);
885 break;
886 case AF_INET6:
887 nsim_fib6_event(fib_event->data, &fib_event->fib6_event,
888 fib_event->event);
889 nsim_fib6_event_fini(&fib_event->fib6_event);
890 break;
891 }
892}
893
894static int nsim_fib4_prepare_event(struct fib_notifier_info *info,
895 struct nsim_fib_event *fib_event,
896 unsigned long event)
897{
898 struct nsim_fib_data *data = fib_event->data;
899 struct fib_entry_notifier_info *fen_info;
900 struct netlink_ext_ack *extack;
901 int err = 0;
902
903 fen_info = container_of(info, struct fib_entry_notifier_info,
904 info);
905 fib_event->fen_info = *fen_info;
906 extack = info->extack;
907
908 switch (event) {
909 case FIB_EVENT_ENTRY_REPLACE:
910 err = nsim_fib_account(&data->ipv4.fib, true);
911 if (err) {
912 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
913 return err;
914 }
915 break;
916 case FIB_EVENT_ENTRY_DEL:
917 nsim_fib_account(&data->ipv4.fib, false);
918 break;
919 }
920
921
922
923
924 fib_info_hold(fib_event->fen_info.fi);
925
926 return 0;
927}
928
929static int nsim_fib6_prepare_event(struct fib_notifier_info *info,
930 struct nsim_fib_event *fib_event,
931 unsigned long event)
932{
933 struct nsim_fib_data *data = fib_event->data;
934 struct fib6_entry_notifier_info *fen6_info;
935 struct netlink_ext_ack *extack;
936 int err = 0;
937
938 fen6_info = container_of(info, struct fib6_entry_notifier_info,
939 info);
940
941 err = nsim_fib6_event_init(&fib_event->fib6_event, fen6_info);
942 if (err)
943 return err;
944
945 extack = info->extack;
946 switch (event) {
947 case FIB_EVENT_ENTRY_REPLACE:
948 err = nsim_fib_account(&data->ipv6.fib, true);
949 if (err) {
950 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
951 goto err_fib6_event_fini;
952 }
953 break;
954 case FIB_EVENT_ENTRY_DEL:
955 nsim_fib_account(&data->ipv6.fib, false);
956 break;
957 }
958
959 return 0;
960
961err_fib6_event_fini:
962 nsim_fib6_event_fini(&fib_event->fib6_event);
963 return err;
964}
965
966static int nsim_fib_event_schedule_work(struct nsim_fib_data *data,
967 struct fib_notifier_info *info,
968 unsigned long event)
969{
970 struct nsim_fib_event *fib_event;
971 int err;
972
973 if (info->family != AF_INET && info->family != AF_INET6)
974
975
976
977 return NOTIFY_DONE;
978
979 fib_event = kzalloc(sizeof(*fib_event), GFP_ATOMIC);
980 if (!fib_event)
981 return NOTIFY_BAD;
982
983 fib_event->data = data;
984 fib_event->event = event;
985 fib_event->family = info->family;
986
987 switch (info->family) {
988 case AF_INET:
989 err = nsim_fib4_prepare_event(info, fib_event, event);
990 break;
991 case AF_INET6:
992 err = nsim_fib6_prepare_event(info, fib_event, event);
993 break;
994 }
995
996 if (err)
997 goto err_fib_prepare_event;
998
999
1000 spin_lock_bh(&data->fib_event_queue_lock);
1001 list_add_tail(&fib_event->list, &data->fib_event_queue);
1002 spin_unlock_bh(&data->fib_event_queue_lock);
1003 schedule_work(&data->fib_event_work);
1004
1005 return NOTIFY_DONE;
1006
1007err_fib_prepare_event:
1008 kfree(fib_event);
1009 return NOTIFY_BAD;
1010}
1011
1012static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
1013 void *ptr)
1014{
1015 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1016 fib_nb);
1017 struct fib_notifier_info *info = ptr;
1018 int err;
1019
1020 switch (event) {
1021 case FIB_EVENT_RULE_ADD:
1022 case FIB_EVENT_RULE_DEL:
1023 err = nsim_fib_rule_event(data, info,
1024 event == FIB_EVENT_RULE_ADD);
1025 return notifier_from_errno(err);
1026 case FIB_EVENT_ENTRY_REPLACE:
1027 case FIB_EVENT_ENTRY_APPEND:
1028 case FIB_EVENT_ENTRY_DEL:
1029 return nsim_fib_event_schedule_work(data, info, event);
1030 }
1031
1032 return NOTIFY_DONE;
1033}
1034
1035static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt,
1036 struct nsim_fib_data *data)
1037{
1038 struct devlink *devlink = data->devlink;
1039 struct nsim_fib4_rt *fib4_rt;
1040
1041 fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common);
1042 nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false);
1043 nsim_fib_account(&data->ipv4.fib, false);
1044 nsim_fib4_rt_destroy(fib4_rt);
1045}
1046
1047static void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt,
1048 struct nsim_fib_data *data)
1049{
1050 struct nsim_fib6_rt *fib6_rt;
1051
1052 fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common);
1053 nsim_fib6_rt_hw_flags_set(data, fib6_rt, false);
1054 nsim_fib_account(&data->ipv6.fib, false);
1055 nsim_fib6_rt_destroy(fib6_rt);
1056}
1057
1058static void nsim_fib_rt_free(void *ptr, void *arg)
1059{
1060 struct nsim_fib_rt *fib_rt = ptr;
1061 struct nsim_fib_data *data = arg;
1062
1063 switch (fib_rt->key.family) {
1064 case AF_INET:
1065 nsim_fib4_rt_free(fib_rt, data);
1066 break;
1067 case AF_INET6:
1068 nsim_fib6_rt_free(fib_rt, data);
1069 break;
1070 default:
1071 WARN_ON_ONCE(1);
1072 }
1073}
1074
1075
1076static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
1077{
1078 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1079 fib_nb);
1080 struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
1081
1082
1083 flush_work(&data->fib_event_work);
1084
1085
1086
1087
1088 list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
1089 rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
1090 nsim_fib_rt_ht_params);
1091 nsim_fib_rt_free(fib_rt, data);
1092 }
1093
1094 atomic64_set(&data->ipv4.rules.num, 0ULL);
1095 atomic64_set(&data->ipv6.rules.num, 0ULL);
1096}
1097
1098static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data,
1099 struct nh_notifier_info *info)
1100{
1101 struct nsim_nexthop *nexthop;
1102 u64 occ = 0;
1103 int i;
1104
1105 nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL);
1106 if (!nexthop)
1107 return ERR_PTR(-ENOMEM);
1108
1109 nexthop->id = info->id;
1110
1111
1112
1113
1114
1115 switch (info->type) {
1116 case NH_NOTIFIER_INFO_TYPE_SINGLE:
1117 occ = 1;
1118 break;
1119 case NH_NOTIFIER_INFO_TYPE_GRP:
1120 for (i = 0; i < info->nh_grp->num_nh; i++)
1121 occ += info->nh_grp->nh_entries[i].weight;
1122 break;
1123 case NH_NOTIFIER_INFO_TYPE_RES_TABLE:
1124 occ = info->nh_res_table->num_nh_buckets;
1125 nexthop->is_resilient = true;
1126 break;
1127 default:
1128 NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type");
1129 kfree(nexthop);
1130 return ERR_PTR(-EOPNOTSUPP);
1131 }
1132
1133 nexthop->occ = occ;
1134 return nexthop;
1135}
1136
1137static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop)
1138{
1139 kfree(nexthop);
1140}
1141
1142static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ,
1143 bool add, struct netlink_ext_ack *extack)
1144{
1145 int i, err = 0;
1146
1147 if (add) {
1148 for (i = 0; i < occ; i++)
1149 if (!atomic64_add_unless(&data->nexthops.num, 1,
1150 data->nexthops.max)) {
1151 err = -ENOSPC;
1152 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops");
1153 goto err_num_decrease;
1154 }
1155 } else {
1156 if (WARN_ON(occ > atomic64_read(&data->nexthops.num)))
1157 return -EINVAL;
1158 atomic64_sub(occ, &data->nexthops.num);
1159 }
1160
1161 return err;
1162
1163err_num_decrease:
1164 atomic64_sub(i, &data->nexthops.num);
1165 return err;
1166
1167}
1168
1169static void nsim_nexthop_hw_flags_set(struct net *net,
1170 const struct nsim_nexthop *nexthop,
1171 bool trap)
1172{
1173 int i;
1174
1175 nexthop_set_hw_flags(net, nexthop->id, false, trap);
1176
1177 if (!nexthop->is_resilient)
1178 return;
1179
1180 for (i = 0; i < nexthop->occ; i++)
1181 nexthop_bucket_set_hw_flags(net, nexthop->id, i, false, trap);
1182}
1183
1184static int nsim_nexthop_add(struct nsim_fib_data *data,
1185 struct nsim_nexthop *nexthop,
1186 struct netlink_ext_ack *extack)
1187{
1188 struct net *net = devlink_net(data->devlink);
1189 int err;
1190
1191 err = nsim_nexthop_account(data, nexthop->occ, true, extack);
1192 if (err)
1193 return err;
1194
1195 err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node,
1196 nsim_nexthop_ht_params);
1197 if (err) {
1198 NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop");
1199 goto err_nexthop_dismiss;
1200 }
1201
1202 nsim_nexthop_hw_flags_set(net, nexthop, true);
1203
1204 return 0;
1205
1206err_nexthop_dismiss:
1207 nsim_nexthop_account(data, nexthop->occ, false, extack);
1208 return err;
1209}
1210
1211static int nsim_nexthop_replace(struct nsim_fib_data *data,
1212 struct nsim_nexthop *nexthop,
1213 struct nsim_nexthop *nexthop_old,
1214 struct netlink_ext_ack *extack)
1215{
1216 struct net *net = devlink_net(data->devlink);
1217 int err;
1218
1219 err = nsim_nexthop_account(data, nexthop->occ, true, extack);
1220 if (err)
1221 return err;
1222
1223 err = rhashtable_replace_fast(&data->nexthop_ht,
1224 &nexthop_old->ht_node, &nexthop->ht_node,
1225 nsim_nexthop_ht_params);
1226 if (err) {
1227 NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop");
1228 goto err_nexthop_dismiss;
1229 }
1230
1231 nsim_nexthop_hw_flags_set(net, nexthop, true);
1232 nsim_nexthop_account(data, nexthop_old->occ, false, extack);
1233 nsim_nexthop_destroy(nexthop_old);
1234
1235 return 0;
1236
1237err_nexthop_dismiss:
1238 nsim_nexthop_account(data, nexthop->occ, false, extack);
1239 return err;
1240}
1241
1242static int nsim_nexthop_insert(struct nsim_fib_data *data,
1243 struct nh_notifier_info *info)
1244{
1245 struct nsim_nexthop *nexthop, *nexthop_old;
1246 int err;
1247
1248 nexthop = nsim_nexthop_create(data, info);
1249 if (IS_ERR(nexthop))
1250 return PTR_ERR(nexthop);
1251
1252 nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
1253 nsim_nexthop_ht_params);
1254 if (!nexthop_old)
1255 err = nsim_nexthop_add(data, nexthop, info->extack);
1256 else
1257 err = nsim_nexthop_replace(data, nexthop, nexthop_old,
1258 info->extack);
1259
1260 if (err)
1261 nsim_nexthop_destroy(nexthop);
1262
1263 return err;
1264}
1265
1266static void nsim_nexthop_remove(struct nsim_fib_data *data,
1267 struct nh_notifier_info *info)
1268{
1269 struct nsim_nexthop *nexthop;
1270
1271 nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
1272 nsim_nexthop_ht_params);
1273 if (!nexthop)
1274 return;
1275
1276 rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node,
1277 nsim_nexthop_ht_params);
1278 nsim_nexthop_account(data, nexthop->occ, false, info->extack);
1279 nsim_nexthop_destroy(nexthop);
1280}
1281
1282static int nsim_nexthop_res_table_pre_replace(struct nsim_fib_data *data,
1283 struct nh_notifier_info *info)
1284{
1285 if (data->fail_res_nexthop_group_replace) {
1286 NL_SET_ERR_MSG_MOD(info->extack, "Failed to replace a resilient nexthop group");
1287 return -EINVAL;
1288 }
1289
1290 return 0;
1291}
1292
1293static int nsim_nexthop_bucket_replace(struct nsim_fib_data *data,
1294 struct nh_notifier_info *info)
1295{
1296 if (data->fail_nexthop_bucket_replace) {
1297 NL_SET_ERR_MSG_MOD(info->extack, "Failed to replace nexthop bucket");
1298 return -EINVAL;
1299 }
1300
1301 nexthop_bucket_set_hw_flags(info->net, info->id,
1302 info->nh_res_bucket->bucket_index,
1303 false, true);
1304
1305 return 0;
1306}
1307
1308static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event,
1309 void *ptr)
1310{
1311 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1312 nexthop_nb);
1313 struct nh_notifier_info *info = ptr;
1314 int err = 0;
1315
1316 mutex_lock(&data->nh_lock);
1317 switch (event) {
1318 case NEXTHOP_EVENT_REPLACE:
1319 err = nsim_nexthop_insert(data, info);
1320 break;
1321 case NEXTHOP_EVENT_DEL:
1322 nsim_nexthop_remove(data, info);
1323 break;
1324 case NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE:
1325 err = nsim_nexthop_res_table_pre_replace(data, info);
1326 break;
1327 case NEXTHOP_EVENT_BUCKET_REPLACE:
1328 err = nsim_nexthop_bucket_replace(data, info);
1329 break;
1330 default:
1331 break;
1332 }
1333
1334 mutex_unlock(&data->nh_lock);
1335 return notifier_from_errno(err);
1336}
1337
1338static void nsim_nexthop_free(void *ptr, void *arg)
1339{
1340 struct nsim_nexthop *nexthop = ptr;
1341 struct nsim_fib_data *data = arg;
1342 struct net *net;
1343
1344 net = devlink_net(data->devlink);
1345 nsim_nexthop_hw_flags_set(net, nexthop, false);
1346 nsim_nexthop_account(data, nexthop->occ, false, NULL);
1347 nsim_nexthop_destroy(nexthop);
1348}
1349
1350static ssize_t nsim_nexthop_bucket_activity_write(struct file *file,
1351 const char __user *user_buf,
1352 size_t size, loff_t *ppos)
1353{
1354 struct nsim_fib_data *data = file->private_data;
1355 struct net *net = devlink_net(data->devlink);
1356 struct nsim_nexthop *nexthop;
1357 unsigned long *activity;
1358 loff_t pos = *ppos;
1359 u16 bucket_index;
1360 char buf[128];
1361 int err = 0;
1362 u32 nhid;
1363
1364 if (pos != 0)
1365 return -EINVAL;
1366 if (size > sizeof(buf))
1367 return -EINVAL;
1368 if (copy_from_user(buf, user_buf, size))
1369 return -EFAULT;
1370 if (sscanf(buf, "%u %hu", &nhid, &bucket_index) != 2)
1371 return -EINVAL;
1372
1373 rtnl_lock();
1374
1375 nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &nhid,
1376 nsim_nexthop_ht_params);
1377 if (!nexthop || !nexthop->is_resilient ||
1378 bucket_index >= nexthop->occ) {
1379 err = -EINVAL;
1380 goto out;
1381 }
1382
1383 activity = bitmap_zalloc(nexthop->occ, GFP_KERNEL);
1384 if (!activity) {
1385 err = -ENOMEM;
1386 goto out;
1387 }
1388
1389 bitmap_set(activity, bucket_index, 1);
1390 nexthop_res_grp_activity_update(net, nhid, nexthop->occ, activity);
1391 bitmap_free(activity);
1392
1393out:
1394 rtnl_unlock();
1395
1396 *ppos = size;
1397 return err ?: size;
1398}
1399
1400static const struct file_operations nsim_nexthop_bucket_activity_fops = {
1401 .open = simple_open,
1402 .write = nsim_nexthop_bucket_activity_write,
1403 .llseek = no_llseek,
1404 .owner = THIS_MODULE,
1405};
1406
1407static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
1408{
1409 struct nsim_fib_data *data = priv;
1410
1411 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
1412}
1413
1414static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
1415{
1416 struct nsim_fib_data *data = priv;
1417
1418 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
1419}
1420
1421static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
1422{
1423 struct nsim_fib_data *data = priv;
1424
1425 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
1426}
1427
1428static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
1429{
1430 struct nsim_fib_data *data = priv;
1431
1432 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
1433}
1434
1435static u64 nsim_fib_nexthops_res_occ_get(void *priv)
1436{
1437 struct nsim_fib_data *data = priv;
1438
1439 return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false);
1440}
1441
1442static void nsim_fib_set_max_all(struct nsim_fib_data *data,
1443 struct devlink *devlink)
1444{
1445 static const enum nsim_resource_id res_ids[] = {
1446 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
1447 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES,
1448 NSIM_RESOURCE_NEXTHOPS,
1449 };
1450 int i;
1451
1452 for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
1453 int err;
1454 u64 val;
1455
1456 err = devlink_resource_size_get(devlink, res_ids[i], &val);
1457 if (err)
1458 val = (u64) -1;
1459 nsim_fib_set_max(data, res_ids[i], val);
1460 }
1461}
1462
1463static void nsim_fib_event_work(struct work_struct *work)
1464{
1465 struct nsim_fib_data *data = container_of(work, struct nsim_fib_data,
1466 fib_event_work);
1467 struct nsim_fib_event *fib_event, *next_fib_event;
1468
1469 LIST_HEAD(fib_event_queue);
1470
1471 spin_lock_bh(&data->fib_event_queue_lock);
1472 list_splice_init(&data->fib_event_queue, &fib_event_queue);
1473 spin_unlock_bh(&data->fib_event_queue_lock);
1474
1475 mutex_lock(&data->fib_lock);
1476 list_for_each_entry_safe(fib_event, next_fib_event, &fib_event_queue,
1477 list) {
1478 nsim_fib_event(fib_event);
1479 list_del(&fib_event->list);
1480 kfree(fib_event);
1481 cond_resched();
1482 }
1483 mutex_unlock(&data->fib_lock);
1484}
1485
1486static int
1487nsim_fib_debugfs_init(struct nsim_fib_data *data, struct nsim_dev *nsim_dev)
1488{
1489 data->ddir = debugfs_create_dir("fib", nsim_dev->ddir);
1490 if (IS_ERR(data->ddir))
1491 return PTR_ERR(data->ddir);
1492
1493 data->fail_route_offload = false;
1494 debugfs_create_bool("fail_route_offload", 0600, data->ddir,
1495 &data->fail_route_offload);
1496
1497 data->fail_res_nexthop_group_replace = false;
1498 debugfs_create_bool("fail_res_nexthop_group_replace", 0600, data->ddir,
1499 &data->fail_res_nexthop_group_replace);
1500
1501 data->fail_nexthop_bucket_replace = false;
1502 debugfs_create_bool("fail_nexthop_bucket_replace", 0600, data->ddir,
1503 &data->fail_nexthop_bucket_replace);
1504
1505 debugfs_create_file("nexthop_bucket_activity", 0200, data->ddir,
1506 data, &nsim_nexthop_bucket_activity_fops);
1507 return 0;
1508}
1509
1510static void nsim_fib_debugfs_exit(struct nsim_fib_data *data)
1511{
1512 debugfs_remove_recursive(data->ddir);
1513}
1514
1515struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
1516 struct netlink_ext_ack *extack)
1517{
1518 struct nsim_fib_data *data;
1519 struct nsim_dev *nsim_dev;
1520 int err;
1521
1522 data = kzalloc(sizeof(*data), GFP_KERNEL);
1523 if (!data)
1524 return ERR_PTR(-ENOMEM);
1525 data->devlink = devlink;
1526
1527 nsim_dev = devlink_priv(devlink);
1528 err = nsim_fib_debugfs_init(data, nsim_dev);
1529 if (err)
1530 goto err_data_free;
1531
1532 mutex_init(&data->nh_lock);
1533 err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
1534 if (err)
1535 goto err_debugfs_exit;
1536
1537 mutex_init(&data->fib_lock);
1538 INIT_LIST_HEAD(&data->fib_rt_list);
1539 err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
1540 if (err)
1541 goto err_rhashtable_nexthop_destroy;
1542
1543 INIT_WORK(&data->fib_event_work, nsim_fib_event_work);
1544 INIT_LIST_HEAD(&data->fib_event_queue);
1545 spin_lock_init(&data->fib_event_queue_lock);
1546
1547 nsim_fib_set_max_all(data, devlink);
1548
1549 data->nexthop_nb.notifier_call = nsim_nexthop_event_nb;
1550 err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb,
1551 extack);
1552 if (err) {
1553 pr_err("Failed to register nexthop notifier\n");
1554 goto err_rhashtable_fib_destroy;
1555 }
1556
1557 data->fib_nb.notifier_call = nsim_fib_event_nb;
1558 err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
1559 nsim_fib_dump_inconsistent, extack);
1560 if (err) {
1561 pr_err("Failed to register fib notifier\n");
1562 goto err_nexthop_nb_unregister;
1563 }
1564
1565 devlink_resource_occ_get_register(devlink,
1566 NSIM_RESOURCE_IPV4_FIB,
1567 nsim_fib_ipv4_resource_occ_get,
1568 data);
1569 devlink_resource_occ_get_register(devlink,
1570 NSIM_RESOURCE_IPV4_FIB_RULES,
1571 nsim_fib_ipv4_rules_res_occ_get,
1572 data);
1573 devlink_resource_occ_get_register(devlink,
1574 NSIM_RESOURCE_IPV6_FIB,
1575 nsim_fib_ipv6_resource_occ_get,
1576 data);
1577 devlink_resource_occ_get_register(devlink,
1578 NSIM_RESOURCE_IPV6_FIB_RULES,
1579 nsim_fib_ipv6_rules_res_occ_get,
1580 data);
1581 devlink_resource_occ_get_register(devlink,
1582 NSIM_RESOURCE_NEXTHOPS,
1583 nsim_fib_nexthops_res_occ_get,
1584 data);
1585 return data;
1586
1587err_nexthop_nb_unregister:
1588 unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1589err_rhashtable_fib_destroy:
1590 flush_work(&data->fib_event_work);
1591 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1592 data);
1593err_rhashtable_nexthop_destroy:
1594 rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1595 data);
1596 mutex_destroy(&data->fib_lock);
1597err_debugfs_exit:
1598 mutex_destroy(&data->nh_lock);
1599 nsim_fib_debugfs_exit(data);
1600err_data_free:
1601 kfree(data);
1602 return ERR_PTR(err);
1603}
1604
1605void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
1606{
1607 devlink_resource_occ_get_unregister(devlink,
1608 NSIM_RESOURCE_NEXTHOPS);
1609 devlink_resource_occ_get_unregister(devlink,
1610 NSIM_RESOURCE_IPV6_FIB_RULES);
1611 devlink_resource_occ_get_unregister(devlink,
1612 NSIM_RESOURCE_IPV6_FIB);
1613 devlink_resource_occ_get_unregister(devlink,
1614 NSIM_RESOURCE_IPV4_FIB_RULES);
1615 devlink_resource_occ_get_unregister(devlink,
1616 NSIM_RESOURCE_IPV4_FIB);
1617 unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
1618 unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1619 flush_work(&data->fib_event_work);
1620 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1621 data);
1622 rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1623 data);
1624 WARN_ON_ONCE(!list_empty(&data->fib_event_queue));
1625 WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
1626 mutex_destroy(&data->fib_lock);
1627 mutex_destroy(&data->nh_lock);
1628 nsim_fib_debugfs_exit(data);
1629 kfree(data);
1630}
1631