2 * Copyright (c) 2018 Cumulus Networks. All rights reserved.
3 * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
5 * This software is licensed under the GNU General License Version 2,
6 * June 1991 as shown in the file COPYING in the top-level directory of this
9 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
10 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
11 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
13 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
14 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
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>
23 #include <net/fib_notifier.h>
24 #include <net/ip_fib.h>
25 #include <net/ip6_fib.h>
26 #include <net/fib_rules.h>
27 #include <net/net_namespace.h>
29 #include "netdevsim.h"
31 struct nsim_fib_entry
{
36 struct nsim_per_fib_data
{
37 struct nsim_fib_entry fib
;
38 struct nsim_fib_entry rules
;
41 struct nsim_fib_data
{
42 struct notifier_block fib_nb
;
43 struct nsim_per_fib_data ipv4
;
44 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
;
51 struct nsim_fib_rt_key
{
52 unsigned char addr
[sizeof(struct in6_addr
)];
53 unsigned char prefix_len
;
59 struct nsim_fib_rt_key key
;
60 struct rhash_head ht_node
;
61 struct list_head list
; /* Member of fib_rt_list */
65 struct nsim_fib_rt common
;
72 struct nsim_fib_rt common
;
73 struct list_head nh_list
;
77 struct nsim_fib6_rt_nh
{
78 struct list_head list
; /* Member of nh_list */
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,
89 u64
nsim_fib_get_val(struct nsim_fib_data
*fib_data
,
90 enum nsim_resource_id res_id
, bool max
)
92 struct nsim_fib_entry
*entry
;
95 case NSIM_RESOURCE_IPV4_FIB
:
96 entry
= &fib_data
->ipv4
.fib
;
98 case NSIM_RESOURCE_IPV4_FIB_RULES
:
99 entry
= &fib_data
->ipv4
.rules
;
101 case NSIM_RESOURCE_IPV6_FIB
:
102 entry
= &fib_data
->ipv6
.fib
;
104 case NSIM_RESOURCE_IPV6_FIB_RULES
:
105 entry
= &fib_data
->ipv6
.rules
;
111 return max
? entry
->max
: entry
->num
;
114 static void nsim_fib_set_max(struct nsim_fib_data
*fib_data
,
115 enum nsim_resource_id res_id
, u64 val
)
117 struct nsim_fib_entry
*entry
;
120 case NSIM_RESOURCE_IPV4_FIB
:
121 entry
= &fib_data
->ipv4
.fib
;
123 case NSIM_RESOURCE_IPV4_FIB_RULES
:
124 entry
= &fib_data
->ipv4
.rules
;
126 case NSIM_RESOURCE_IPV6_FIB
:
127 entry
= &fib_data
->ipv6
.fib
;
129 case NSIM_RESOURCE_IPV6_FIB_RULES
:
130 entry
= &fib_data
->ipv6
.rules
;
139 static int nsim_fib_rule_account(struct nsim_fib_entry
*entry
, bool add
,
140 struct netlink_ext_ack
*extack
)
145 if (entry
->num
< entry
->max
) {
149 NL_SET_ERR_MSG_MOD(extack
, "Exceeded number of supported fib rule entries");
158 static int nsim_fib_rule_event(struct nsim_fib_data
*data
,
159 struct fib_notifier_info
*info
, bool add
)
161 struct netlink_ext_ack
*extack
= info
->extack
;
164 switch (info
->family
) {
166 err
= nsim_fib_rule_account(&data
->ipv4
.rules
, add
, extack
);
169 err
= nsim_fib_rule_account(&data
->ipv6
.rules
, add
, extack
);
176 static int nsim_fib_account(struct nsim_fib_entry
*entry
, bool add
,
177 struct netlink_ext_ack
*extack
)
182 if (entry
->num
< entry
->max
) {
186 NL_SET_ERR_MSG_MOD(extack
, "Exceeded number of supported fib entries");
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
)
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
);
207 static void nsim_fib_rt_fini(struct nsim_fib_rt
*fib_rt
)
209 list_del(&fib_rt
->list
);
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
)
217 struct nsim_fib_rt_key key
;
219 memset(&key
, 0, sizeof(key
));
220 memcpy(key
.addr
, addr
, addr_len
);
221 key
.prefix_len
= prefix_len
;
225 return rhashtable_lookup_fast(fib_rt_ht
, &key
, nsim_fib_rt_ht_params
);
228 static struct nsim_fib4_rt
*
229 nsim_fib4_rt_create(struct nsim_fib_data
*data
,
230 struct fib_entry_notifier_info
*fen_info
)
232 struct nsim_fib4_rt
*fib4_rt
;
234 fib4_rt
= kzalloc(sizeof(*fib4_rt
), GFP_ATOMIC
);
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
);
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
;
249 static void nsim_fib4_rt_destroy(struct nsim_fib4_rt
*fib4_rt
)
251 fib_info_put(fib4_rt
->fi
);
252 nsim_fib_rt_fini(&fib4_rt
->common
);
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
)
260 struct nsim_fib_rt
*fib_rt
;
262 fib_rt
= nsim_fib_rt_lookup(fib_rt_ht
, &fen_info
->dst
, sizeof(u32
),
263 fen_info
->dst_len
, AF_INET
,
268 return container_of(fib_rt
, struct nsim_fib4_rt
, common
);
271 static void nsim_fib4_rt_hw_flags_set(struct net
*net
,
272 const struct nsim_fib4_rt
*fib4_rt
,
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
;
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
;
287 fib_alias_hw_flags_set(net
, &fri
);
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
)
294 struct net
*net
= devlink_net(data
->devlink
);
297 err
= nsim_fib_account(&data
->ipv4
.fib
, true, extack
);
301 err
= rhashtable_insert_fast(&data
->fib_rt_ht
,
302 &fib4_rt
->common
.ht_node
,
303 nsim_fib_rt_ht_params
);
305 NL_SET_ERR_MSG_MOD(extack
, "Failed to insert IPv4 route");
306 goto err_fib_dismiss
;
309 nsim_fib4_rt_hw_flags_set(net
, fib4_rt
, true);
314 nsim_fib_account(&data
->ipv4
.fib
, false, extack
);
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
)
323 struct net
*net
= devlink_net(data
->devlink
);
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
);
332 NL_SET_ERR_MSG_MOD(extack
, "Failed to replace IPv4 route");
336 nsim_fib4_rt_hw_flags_set(net
, fib4_rt
, true);
338 nsim_fib4_rt_hw_flags_set(net
, fib4_rt_old
, false);
339 nsim_fib4_rt_destroy(fib4_rt_old
);
344 static int nsim_fib4_rt_insert(struct nsim_fib_data
*data
,
345 struct fib_entry_notifier_info
*fen_info
)
347 struct netlink_ext_ack
*extack
= fen_info
->info
.extack
;
348 struct nsim_fib4_rt
*fib4_rt
, *fib4_rt_old
;
351 fib4_rt
= nsim_fib4_rt_create(data
, fen_info
);
355 fib4_rt_old
= nsim_fib4_rt_lookup(&data
->fib_rt_ht
, fen_info
);
357 err
= nsim_fib4_rt_add(data
, fib4_rt
, extack
);
359 err
= nsim_fib4_rt_replace(data
, fib4_rt
, fib4_rt_old
, extack
);
362 nsim_fib4_rt_destroy(fib4_rt
);
367 static void nsim_fib4_rt_remove(struct nsim_fib_data
*data
,
368 const struct fib_entry_notifier_info
*fen_info
)
370 struct netlink_ext_ack
*extack
= fen_info
->info
.extack
;
371 struct nsim_fib4_rt
*fib4_rt
;
373 fib4_rt
= nsim_fib4_rt_lookup(&data
->fib_rt_ht
, fen_info
);
374 if (WARN_ON_ONCE(!fib4_rt
))
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
);
383 static int nsim_fib4_event(struct nsim_fib_data
*data
,
384 struct fib_notifier_info
*info
,
387 struct fib_entry_notifier_info
*fen_info
;
390 fen_info
= container_of(info
, struct fib_entry_notifier_info
, info
);
392 if (fen_info
->fi
->nh
) {
393 NL_SET_ERR_MSG_MOD(info
->extack
, "IPv4 route with nexthop objects is not supported");
398 case FIB_EVENT_ENTRY_REPLACE
:
399 err
= nsim_fib4_rt_insert(data
, fen_info
);
401 case FIB_EVENT_ENTRY_DEL
:
402 nsim_fib4_rt_remove(data
, fen_info
);
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
)
415 struct nsim_fib6_rt_nh
*fib6_rt_nh
;
417 list_for_each_entry(fib6_rt_nh
, &fib6_rt
->nh_list
, list
) {
418 if (fib6_rt_nh
->rt
== rt
)
425 static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt
*fib6_rt
,
426 struct fib6_info
*rt
)
428 struct nsim_fib6_rt_nh
*fib6_rt_nh
;
430 fib6_rt_nh
= kzalloc(sizeof(*fib6_rt_nh
), GFP_ATOMIC
);
436 list_add_tail(&fib6_rt_nh
->list
, &fib6_rt
->nh_list
);
442 static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt
*fib6_rt
,
443 const struct fib6_info
*rt
)
445 struct nsim_fib6_rt_nh
*fib6_rt_nh
;
447 fib6_rt_nh
= nsim_fib6_rt_nh_find(fib6_rt
, rt
);
448 if (WARN_ON_ONCE(!fib6_rt_nh
))
452 list_del(&fib6_rt_nh
->list
);
453 #if IS_ENABLED(CONFIG_IPV6)
454 fib6_info_release(fib6_rt_nh
->rt
);
459 static struct nsim_fib6_rt
*
460 nsim_fib6_rt_create(struct nsim_fib_data
*data
,
461 struct fib6_entry_notifier_info
*fen6_info
)
463 struct fib6_info
*iter
, *rt
= fen6_info
->rt
;
464 struct nsim_fib6_rt
*fib6_rt
;
468 fib6_rt
= kzalloc(sizeof(*fib6_rt
), GFP_ATOMIC
);
470 return ERR_PTR(-ENOMEM
);
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
);
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.
480 INIT_LIST_HEAD(&fib6_rt
->nh_list
);
482 err
= nsim_fib6_rt_nh_add(fib6_rt
, rt
);
484 goto err_fib_rt_fini
;
486 if (!fen6_info
->nsiblings
)
489 list_for_each_entry(iter
, &rt
->fib6_siblings
, fib6_siblings
) {
490 if (i
== fen6_info
->nsiblings
)
493 err
= nsim_fib6_rt_nh_add(fib6_rt
, iter
);
495 goto err_fib6_rt_nh_del
;
498 WARN_ON_ONCE(i
!= fen6_info
->nsiblings
);
503 list_for_each_entry_continue_reverse(iter
, &rt
->fib6_siblings
,
505 nsim_fib6_rt_nh_del(fib6_rt
, iter
);
506 nsim_fib6_rt_nh_del(fib6_rt
, rt
);
508 nsim_fib_rt_fini(&fib6_rt
->common
);
513 static void nsim_fib6_rt_destroy(struct nsim_fib6_rt
*fib6_rt
)
515 struct nsim_fib6_rt_nh
*iter
, *tmp
;
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
);
524 static struct nsim_fib6_rt
*
525 nsim_fib6_rt_lookup(struct rhashtable
*fib_rt_ht
, const struct fib6_info
*rt
)
527 struct nsim_fib_rt
*fib_rt
;
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
);
536 return container_of(fib_rt
, struct nsim_fib6_rt
, common
);
539 static int nsim_fib6_rt_append(struct nsim_fib_data
*data
,
540 struct fib6_entry_notifier_info
*fen6_info
)
542 struct fib6_info
*iter
, *rt
= fen6_info
->rt
;
543 struct nsim_fib6_rt
*fib6_rt
;
547 fib6_rt
= nsim_fib6_rt_lookup(&data
->fib_rt_ht
, rt
);
548 if (WARN_ON_ONCE(!fib6_rt
))
551 err
= nsim_fib6_rt_nh_add(fib6_rt
, rt
);
556 if (!fen6_info
->nsiblings
)
559 list_for_each_entry(iter
, &rt
->fib6_siblings
, fib6_siblings
) {
560 if (i
== fen6_info
->nsiblings
)
563 err
= nsim_fib6_rt_nh_add(fib6_rt
, iter
);
565 goto err_fib6_rt_nh_del
;
569 WARN_ON_ONCE(i
!= fen6_info
->nsiblings
);
574 list_for_each_entry_continue_reverse(iter
, &rt
->fib6_siblings
,
577 nsim_fib6_rt_nh_del(fib6_rt
, iter
);
580 nsim_fib6_rt_nh_del(fib6_rt
, rt
);
584 static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt
*fib6_rt
,
587 struct nsim_fib6_rt_nh
*fib6_rt_nh
;
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
);
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
)
599 err
= nsim_fib_account(&data
->ipv6
.fib
, true, extack
);
603 err
= rhashtable_insert_fast(&data
->fib_rt_ht
,
604 &fib6_rt
->common
.ht_node
,
605 nsim_fib_rt_ht_params
);
607 NL_SET_ERR_MSG_MOD(extack
, "Failed to insert IPv6 route");
608 goto err_fib_dismiss
;
611 nsim_fib6_rt_hw_flags_set(fib6_rt
, true);
616 nsim_fib_account(&data
->ipv6
.fib
, false, extack
);
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
)
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
);
633 NL_SET_ERR_MSG_MOD(extack
, "Failed to replace IPv6 route");
637 nsim_fib6_rt_hw_flags_set(fib6_rt
, true);
639 nsim_fib6_rt_hw_flags_set(fib6_rt_old
, false);
640 nsim_fib6_rt_destroy(fib6_rt_old
);
645 static int nsim_fib6_rt_insert(struct nsim_fib_data
*data
,
646 struct fib6_entry_notifier_info
*fen6_info
)
648 struct netlink_ext_ack
*extack
= fen6_info
->info
.extack
;
649 struct nsim_fib6_rt
*fib6_rt
, *fib6_rt_old
;
652 fib6_rt
= nsim_fib6_rt_create(data
, fen6_info
);
654 return PTR_ERR(fib6_rt
);
656 fib6_rt_old
= nsim_fib6_rt_lookup(&data
->fib_rt_ht
, fen6_info
->rt
);
658 err
= nsim_fib6_rt_add(data
, fib6_rt
, extack
);
660 err
= nsim_fib6_rt_replace(data
, fib6_rt
, fib6_rt_old
, extack
);
663 nsim_fib6_rt_destroy(fib6_rt
);
669 nsim_fib6_rt_remove(struct nsim_fib_data
*data
,
670 const struct fib6_entry_notifier_info
*fen6_info
)
672 struct netlink_ext_ack
*extack
= fen6_info
->info
.extack
;
673 struct nsim_fib6_rt
*fib6_rt
;
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.
680 fib6_rt
= nsim_fib6_rt_lookup(&data
->fib_rt_ht
, fen6_info
->rt
);
684 /* If not all the nexthops are deleted, then only reduce the nexthop
687 if (fen6_info
->nsiblings
+ 1 != fib6_rt
->nhs
) {
688 nsim_fib6_rt_nh_del(fib6_rt
, fen6_info
->rt
);
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
);
698 static int nsim_fib6_event(struct nsim_fib_data
*data
,
699 struct fib_notifier_info
*info
,
702 struct fib6_entry_notifier_info
*fen6_info
;
705 fen6_info
= container_of(info
, struct fib6_entry_notifier_info
, info
);
707 if (fen6_info
->rt
->nh
) {
708 NL_SET_ERR_MSG_MOD(info
->extack
, "IPv6 route with nexthop objects is not supported");
712 if (fen6_info
->rt
->fib6_src
.plen
) {
713 NL_SET_ERR_MSG_MOD(info
->extack
, "IPv6 source-specific route is not supported");
718 case FIB_EVENT_ENTRY_REPLACE
:
719 err
= nsim_fib6_rt_insert(data
, fen6_info
);
721 case FIB_EVENT_ENTRY_APPEND
:
722 err
= nsim_fib6_rt_append(data
, fen6_info
);
724 case FIB_EVENT_ENTRY_DEL
:
725 nsim_fib6_rt_remove(data
, fen6_info
);
734 static int nsim_fib_event(struct nsim_fib_data
*data
,
735 struct fib_notifier_info
*info
, unsigned long event
)
739 switch (info
->family
) {
741 err
= nsim_fib4_event(data
, info
, event
);
744 err
= nsim_fib6_event(data
, info
, event
);
751 static int nsim_fib_event_nb(struct notifier_block
*nb
, unsigned long event
,
754 struct nsim_fib_data
*data
= container_of(nb
, struct nsim_fib_data
,
756 struct fib_notifier_info
*info
= ptr
;
759 /* IPv6 routes can be added via RAs from softIRQ. */
760 spin_lock_bh(&data
->fib_lock
);
763 case FIB_EVENT_RULE_ADD
: /* fall through */
764 case FIB_EVENT_RULE_DEL
:
765 err
= nsim_fib_rule_event(data
, info
,
766 event
== FIB_EVENT_RULE_ADD
);
769 case FIB_EVENT_ENTRY_REPLACE
: /* fall through */
770 case FIB_EVENT_ENTRY_APPEND
: /* fall through */
771 case FIB_EVENT_ENTRY_DEL
:
772 err
= nsim_fib_event(data
, info
, event
);
776 spin_unlock_bh(&data
->fib_lock
);
778 return notifier_from_errno(err
);
781 static void nsim_fib4_rt_free(struct nsim_fib_rt
*fib_rt
,
782 struct nsim_fib_data
*data
)
784 struct devlink
*devlink
= data
->devlink
;
785 struct nsim_fib4_rt
*fib4_rt
;
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
);
793 static void nsim_fib6_rt_free(struct nsim_fib_rt
*fib_rt
,
794 struct nsim_fib_data
*data
)
796 struct nsim_fib6_rt
*fib6_rt
;
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
);
804 static void nsim_fib_rt_free(void *ptr
, void *arg
)
806 struct nsim_fib_rt
*fib_rt
= ptr
;
807 struct nsim_fib_data
*data
= arg
;
809 switch (fib_rt
->key
.family
) {
811 nsim_fib4_rt_free(fib_rt
, data
);
814 nsim_fib6_rt_free(fib_rt
, data
);
821 /* inconsistent dump, trying again */
822 static void nsim_fib_dump_inconsistent(struct notifier_block
*nb
)
824 struct nsim_fib_data
*data
= container_of(nb
, struct nsim_fib_data
,
826 struct nsim_fib_rt
*fib_rt
, *fib_rt_tmp
;
828 /* The notifier block is still not registered, so we do not need to
829 * take any locks here.
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
);
837 data
->ipv4
.rules
.num
= 0ULL;
838 data
->ipv6
.rules
.num
= 0ULL;
841 static u64
nsim_fib_ipv4_resource_occ_get(void *priv
)
843 struct nsim_fib_data
*data
= priv
;
845 return nsim_fib_get_val(data
, NSIM_RESOURCE_IPV4_FIB
, false);
848 static u64
nsim_fib_ipv4_rules_res_occ_get(void *priv
)
850 struct nsim_fib_data
*data
= priv
;
852 return nsim_fib_get_val(data
, NSIM_RESOURCE_IPV4_FIB_RULES
, false);
855 static u64
nsim_fib_ipv6_resource_occ_get(void *priv
)
857 struct nsim_fib_data
*data
= priv
;
859 return nsim_fib_get_val(data
, NSIM_RESOURCE_IPV6_FIB
, false);
862 static u64
nsim_fib_ipv6_rules_res_occ_get(void *priv
)
864 struct nsim_fib_data
*data
= priv
;
866 return nsim_fib_get_val(data
, NSIM_RESOURCE_IPV6_FIB_RULES
, false);
869 static void nsim_fib_set_max_all(struct nsim_fib_data
*data
,
870 struct devlink
*devlink
)
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
878 for (i
= 0; i
< ARRAY_SIZE(res_ids
); i
++) {
882 err
= devlink_resource_size_get(devlink
, res_ids
[i
], &val
);
885 nsim_fib_set_max(data
, res_ids
[i
], val
);
889 struct nsim_fib_data
*nsim_fib_create(struct devlink
*devlink
,
890 struct netlink_ext_ack
*extack
)
892 struct nsim_fib_data
*data
;
895 data
= kzalloc(sizeof(*data
), GFP_KERNEL
);
897 return ERR_PTR(-ENOMEM
);
898 data
->devlink
= devlink
;
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
);
906 nsim_fib_set_max_all(data
, devlink
);
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
);
912 pr_err("Failed to register fib notifier\n");
913 goto err_rhashtable_destroy
;
916 devlink_resource_occ_get_register(devlink
,
917 NSIM_RESOURCE_IPV4_FIB
,
918 nsim_fib_ipv4_resource_occ_get
,
920 devlink_resource_occ_get_register(devlink
,
921 NSIM_RESOURCE_IPV4_FIB_RULES
,
922 nsim_fib_ipv4_rules_res_occ_get
,
924 devlink_resource_occ_get_register(devlink
,
925 NSIM_RESOURCE_IPV6_FIB
,
926 nsim_fib_ipv6_resource_occ_get
,
928 devlink_resource_occ_get_register(devlink
,
929 NSIM_RESOURCE_IPV6_FIB_RULES
,
930 nsim_fib_ipv6_rules_res_occ_get
,
934 err_rhashtable_destroy
:
935 rhashtable_free_and_destroy(&data
->fib_rt_ht
, nsim_fib_rt_free
,
942 void nsim_fib_destroy(struct devlink
*devlink
, struct nsim_fib_data
*data
)
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
,
955 WARN_ON_ONCE(!list_empty(&data
->fib_rt_list
));