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>
28 #include <net/nexthop.h>
30 #include "netdevsim.h"
32 struct nsim_fib_entry
{
37 struct nsim_per_fib_data
{
38 struct nsim_fib_entry fib
;
39 struct nsim_fib_entry rules
;
42 struct nsim_fib_data
{
43 struct notifier_block fib_nb
;
44 struct nsim_per_fib_data ipv4
;
45 struct nsim_per_fib_data ipv6
;
46 struct nsim_fib_entry nexthops
;
47 struct rhashtable fib_rt_ht
;
48 struct list_head fib_rt_list
;
49 spinlock_t fib_lock
; /* Protects hashtable, list and accounting */
50 struct notifier_block nexthop_nb
;
51 struct rhashtable nexthop_ht
;
52 struct devlink
*devlink
;
55 struct nsim_fib_rt_key
{
56 unsigned char addr
[sizeof(struct in6_addr
)];
57 unsigned char prefix_len
;
63 struct nsim_fib_rt_key key
;
64 struct rhash_head ht_node
;
65 struct list_head list
; /* Member of fib_rt_list */
69 struct nsim_fib_rt common
;
76 struct nsim_fib_rt common
;
77 struct list_head nh_list
;
81 struct nsim_fib6_rt_nh
{
82 struct list_head list
; /* Member of nh_list */
86 static const struct rhashtable_params nsim_fib_rt_ht_params
= {
87 .key_offset
= offsetof(struct nsim_fib_rt
, key
),
88 .head_offset
= offsetof(struct nsim_fib_rt
, ht_node
),
89 .key_len
= sizeof(struct nsim_fib_rt_key
),
90 .automatic_shrinking
= true,
94 struct rhash_head ht_node
;
99 static const struct rhashtable_params nsim_nexthop_ht_params
= {
100 .key_offset
= offsetof(struct nsim_nexthop
, id
),
101 .head_offset
= offsetof(struct nsim_nexthop
, ht_node
),
102 .key_len
= sizeof(u32
),
103 .automatic_shrinking
= true,
106 u64
nsim_fib_get_val(struct nsim_fib_data
*fib_data
,
107 enum nsim_resource_id res_id
, bool max
)
109 struct nsim_fib_entry
*entry
;
112 case NSIM_RESOURCE_IPV4_FIB
:
113 entry
= &fib_data
->ipv4
.fib
;
115 case NSIM_RESOURCE_IPV4_FIB_RULES
:
116 entry
= &fib_data
->ipv4
.rules
;
118 case NSIM_RESOURCE_IPV6_FIB
:
119 entry
= &fib_data
->ipv6
.fib
;
121 case NSIM_RESOURCE_IPV6_FIB_RULES
:
122 entry
= &fib_data
->ipv6
.rules
;
124 case NSIM_RESOURCE_NEXTHOPS
:
125 entry
= &fib_data
->nexthops
;
131 return max
? entry
->max
: entry
->num
;
134 static void nsim_fib_set_max(struct nsim_fib_data
*fib_data
,
135 enum nsim_resource_id res_id
, u64 val
)
137 struct nsim_fib_entry
*entry
;
140 case NSIM_RESOURCE_IPV4_FIB
:
141 entry
= &fib_data
->ipv4
.fib
;
143 case NSIM_RESOURCE_IPV4_FIB_RULES
:
144 entry
= &fib_data
->ipv4
.rules
;
146 case NSIM_RESOURCE_IPV6_FIB
:
147 entry
= &fib_data
->ipv6
.fib
;
149 case NSIM_RESOURCE_IPV6_FIB_RULES
:
150 entry
= &fib_data
->ipv6
.rules
;
152 case NSIM_RESOURCE_NEXTHOPS
:
153 entry
= &fib_data
->nexthops
;
162 static int nsim_fib_rule_account(struct nsim_fib_entry
*entry
, bool add
,
163 struct netlink_ext_ack
*extack
)
168 if (entry
->num
< entry
->max
) {
172 NL_SET_ERR_MSG_MOD(extack
, "Exceeded number of supported fib rule entries");
181 static int nsim_fib_rule_event(struct nsim_fib_data
*data
,
182 struct fib_notifier_info
*info
, bool add
)
184 struct netlink_ext_ack
*extack
= info
->extack
;
187 switch (info
->family
) {
189 err
= nsim_fib_rule_account(&data
->ipv4
.rules
, add
, extack
);
192 err
= nsim_fib_rule_account(&data
->ipv6
.rules
, add
, extack
);
199 static int nsim_fib_account(struct nsim_fib_entry
*entry
, bool add
,
200 struct netlink_ext_ack
*extack
)
205 if (entry
->num
< entry
->max
) {
209 NL_SET_ERR_MSG_MOD(extack
, "Exceeded number of supported fib entries");
218 static void nsim_fib_rt_init(struct nsim_fib_data
*data
,
219 struct nsim_fib_rt
*fib_rt
, const void *addr
,
220 size_t addr_len
, unsigned int prefix_len
,
221 int family
, u32 tb_id
)
223 memcpy(fib_rt
->key
.addr
, addr
, addr_len
);
224 fib_rt
->key
.prefix_len
= prefix_len
;
225 fib_rt
->key
.family
= family
;
226 fib_rt
->key
.tb_id
= tb_id
;
227 list_add(&fib_rt
->list
, &data
->fib_rt_list
);
230 static void nsim_fib_rt_fini(struct nsim_fib_rt
*fib_rt
)
232 list_del(&fib_rt
->list
);
235 static struct nsim_fib_rt
*nsim_fib_rt_lookup(struct rhashtable
*fib_rt_ht
,
236 const void *addr
, size_t addr_len
,
237 unsigned int prefix_len
,
238 int family
, u32 tb_id
)
240 struct nsim_fib_rt_key key
;
242 memset(&key
, 0, sizeof(key
));
243 memcpy(key
.addr
, addr
, addr_len
);
244 key
.prefix_len
= prefix_len
;
248 return rhashtable_lookup_fast(fib_rt_ht
, &key
, nsim_fib_rt_ht_params
);
251 static struct nsim_fib4_rt
*
252 nsim_fib4_rt_create(struct nsim_fib_data
*data
,
253 struct fib_entry_notifier_info
*fen_info
)
255 struct nsim_fib4_rt
*fib4_rt
;
257 fib4_rt
= kzalloc(sizeof(*fib4_rt
), GFP_ATOMIC
);
261 nsim_fib_rt_init(data
, &fib4_rt
->common
, &fen_info
->dst
, sizeof(u32
),
262 fen_info
->dst_len
, AF_INET
, fen_info
->tb_id
);
264 fib4_rt
->fi
= fen_info
->fi
;
265 fib_info_hold(fib4_rt
->fi
);
266 fib4_rt
->tos
= fen_info
->tos
;
267 fib4_rt
->type
= fen_info
->type
;
272 static void nsim_fib4_rt_destroy(struct nsim_fib4_rt
*fib4_rt
)
274 fib_info_put(fib4_rt
->fi
);
275 nsim_fib_rt_fini(&fib4_rt
->common
);
279 static struct nsim_fib4_rt
*
280 nsim_fib4_rt_lookup(struct rhashtable
*fib_rt_ht
,
281 const struct fib_entry_notifier_info
*fen_info
)
283 struct nsim_fib_rt
*fib_rt
;
285 fib_rt
= nsim_fib_rt_lookup(fib_rt_ht
, &fen_info
->dst
, sizeof(u32
),
286 fen_info
->dst_len
, AF_INET
,
291 return container_of(fib_rt
, struct nsim_fib4_rt
, common
);
294 static void nsim_fib4_rt_hw_flags_set(struct net
*net
,
295 const struct nsim_fib4_rt
*fib4_rt
,
298 u32
*p_dst
= (u32
*) fib4_rt
->common
.key
.addr
;
299 int dst_len
= fib4_rt
->common
.key
.prefix_len
;
300 struct fib_rt_info fri
;
302 fri
.fi
= fib4_rt
->fi
;
303 fri
.tb_id
= fib4_rt
->common
.key
.tb_id
;
304 fri
.dst
= cpu_to_be32(*p_dst
);
305 fri
.dst_len
= dst_len
;
306 fri
.tos
= fib4_rt
->tos
;
307 fri
.type
= fib4_rt
->type
;
310 fib_alias_hw_flags_set(net
, &fri
);
313 static int nsim_fib4_rt_add(struct nsim_fib_data
*data
,
314 struct nsim_fib4_rt
*fib4_rt
,
315 struct netlink_ext_ack
*extack
)
317 struct net
*net
= devlink_net(data
->devlink
);
320 err
= nsim_fib_account(&data
->ipv4
.fib
, true, extack
);
324 err
= rhashtable_insert_fast(&data
->fib_rt_ht
,
325 &fib4_rt
->common
.ht_node
,
326 nsim_fib_rt_ht_params
);
328 NL_SET_ERR_MSG_MOD(extack
, "Failed to insert IPv4 route");
329 goto err_fib_dismiss
;
332 nsim_fib4_rt_hw_flags_set(net
, fib4_rt
, true);
337 nsim_fib_account(&data
->ipv4
.fib
, false, extack
);
341 static int nsim_fib4_rt_replace(struct nsim_fib_data
*data
,
342 struct nsim_fib4_rt
*fib4_rt
,
343 struct nsim_fib4_rt
*fib4_rt_old
,
344 struct netlink_ext_ack
*extack
)
346 struct net
*net
= devlink_net(data
->devlink
);
349 /* We are replacing a route, so no need to change the accounting. */
350 err
= rhashtable_replace_fast(&data
->fib_rt_ht
,
351 &fib4_rt_old
->common
.ht_node
,
352 &fib4_rt
->common
.ht_node
,
353 nsim_fib_rt_ht_params
);
355 NL_SET_ERR_MSG_MOD(extack
, "Failed to replace IPv4 route");
359 nsim_fib4_rt_hw_flags_set(net
, fib4_rt
, true);
361 nsim_fib4_rt_hw_flags_set(net
, fib4_rt_old
, false);
362 nsim_fib4_rt_destroy(fib4_rt_old
);
367 static int nsim_fib4_rt_insert(struct nsim_fib_data
*data
,
368 struct fib_entry_notifier_info
*fen_info
)
370 struct netlink_ext_ack
*extack
= fen_info
->info
.extack
;
371 struct nsim_fib4_rt
*fib4_rt
, *fib4_rt_old
;
374 fib4_rt
= nsim_fib4_rt_create(data
, fen_info
);
378 fib4_rt_old
= nsim_fib4_rt_lookup(&data
->fib_rt_ht
, fen_info
);
380 err
= nsim_fib4_rt_add(data
, fib4_rt
, extack
);
382 err
= nsim_fib4_rt_replace(data
, fib4_rt
, fib4_rt_old
, extack
);
385 nsim_fib4_rt_destroy(fib4_rt
);
390 static void nsim_fib4_rt_remove(struct nsim_fib_data
*data
,
391 const struct fib_entry_notifier_info
*fen_info
)
393 struct netlink_ext_ack
*extack
= fen_info
->info
.extack
;
394 struct nsim_fib4_rt
*fib4_rt
;
396 fib4_rt
= nsim_fib4_rt_lookup(&data
->fib_rt_ht
, fen_info
);
397 if (WARN_ON_ONCE(!fib4_rt
))
400 rhashtable_remove_fast(&data
->fib_rt_ht
, &fib4_rt
->common
.ht_node
,
401 nsim_fib_rt_ht_params
);
402 nsim_fib_account(&data
->ipv4
.fib
, false, extack
);
403 nsim_fib4_rt_destroy(fib4_rt
);
406 static int nsim_fib4_event(struct nsim_fib_data
*data
,
407 struct fib_notifier_info
*info
,
410 struct fib_entry_notifier_info
*fen_info
;
413 fen_info
= container_of(info
, struct fib_entry_notifier_info
, info
);
416 case FIB_EVENT_ENTRY_REPLACE
:
417 err
= nsim_fib4_rt_insert(data
, fen_info
);
419 case FIB_EVENT_ENTRY_DEL
:
420 nsim_fib4_rt_remove(data
, fen_info
);
429 static struct nsim_fib6_rt_nh
*
430 nsim_fib6_rt_nh_find(const struct nsim_fib6_rt
*fib6_rt
,
431 const struct fib6_info
*rt
)
433 struct nsim_fib6_rt_nh
*fib6_rt_nh
;
435 list_for_each_entry(fib6_rt_nh
, &fib6_rt
->nh_list
, list
) {
436 if (fib6_rt_nh
->rt
== rt
)
443 static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt
*fib6_rt
,
444 struct fib6_info
*rt
)
446 struct nsim_fib6_rt_nh
*fib6_rt_nh
;
448 fib6_rt_nh
= kzalloc(sizeof(*fib6_rt_nh
), GFP_ATOMIC
);
454 list_add_tail(&fib6_rt_nh
->list
, &fib6_rt
->nh_list
);
460 static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt
*fib6_rt
,
461 const struct fib6_info
*rt
)
463 struct nsim_fib6_rt_nh
*fib6_rt_nh
;
465 fib6_rt_nh
= nsim_fib6_rt_nh_find(fib6_rt
, rt
);
466 if (WARN_ON_ONCE(!fib6_rt_nh
))
470 list_del(&fib6_rt_nh
->list
);
471 #if IS_ENABLED(CONFIG_IPV6)
472 fib6_info_release(fib6_rt_nh
->rt
);
477 static struct nsim_fib6_rt
*
478 nsim_fib6_rt_create(struct nsim_fib_data
*data
,
479 struct fib6_entry_notifier_info
*fen6_info
)
481 struct fib6_info
*iter
, *rt
= fen6_info
->rt
;
482 struct nsim_fib6_rt
*fib6_rt
;
486 fib6_rt
= kzalloc(sizeof(*fib6_rt
), GFP_ATOMIC
);
488 return ERR_PTR(-ENOMEM
);
490 nsim_fib_rt_init(data
, &fib6_rt
->common
, &rt
->fib6_dst
.addr
,
491 sizeof(rt
->fib6_dst
.addr
), rt
->fib6_dst
.plen
, AF_INET6
,
492 rt
->fib6_table
->tb6_id
);
494 /* We consider a multipath IPv6 route as one entry, but it can be made
495 * up from several fib6_info structs (one for each nexthop), so we
496 * add them all to the same list under the entry.
498 INIT_LIST_HEAD(&fib6_rt
->nh_list
);
500 err
= nsim_fib6_rt_nh_add(fib6_rt
, rt
);
502 goto err_fib_rt_fini
;
504 if (!fen6_info
->nsiblings
)
507 list_for_each_entry(iter
, &rt
->fib6_siblings
, fib6_siblings
) {
508 if (i
== fen6_info
->nsiblings
)
511 err
= nsim_fib6_rt_nh_add(fib6_rt
, iter
);
513 goto err_fib6_rt_nh_del
;
516 WARN_ON_ONCE(i
!= fen6_info
->nsiblings
);
521 list_for_each_entry_continue_reverse(iter
, &rt
->fib6_siblings
,
523 nsim_fib6_rt_nh_del(fib6_rt
, iter
);
524 nsim_fib6_rt_nh_del(fib6_rt
, rt
);
526 nsim_fib_rt_fini(&fib6_rt
->common
);
531 static void nsim_fib6_rt_destroy(struct nsim_fib6_rt
*fib6_rt
)
533 struct nsim_fib6_rt_nh
*iter
, *tmp
;
535 list_for_each_entry_safe(iter
, tmp
, &fib6_rt
->nh_list
, list
)
536 nsim_fib6_rt_nh_del(fib6_rt
, iter
->rt
);
537 WARN_ON_ONCE(!list_empty(&fib6_rt
->nh_list
));
538 nsim_fib_rt_fini(&fib6_rt
->common
);
542 static struct nsim_fib6_rt
*
543 nsim_fib6_rt_lookup(struct rhashtable
*fib_rt_ht
, const struct fib6_info
*rt
)
545 struct nsim_fib_rt
*fib_rt
;
547 fib_rt
= nsim_fib_rt_lookup(fib_rt_ht
, &rt
->fib6_dst
.addr
,
548 sizeof(rt
->fib6_dst
.addr
),
549 rt
->fib6_dst
.plen
, AF_INET6
,
550 rt
->fib6_table
->tb6_id
);
554 return container_of(fib_rt
, struct nsim_fib6_rt
, common
);
557 static int nsim_fib6_rt_append(struct nsim_fib_data
*data
,
558 struct fib6_entry_notifier_info
*fen6_info
)
560 struct fib6_info
*iter
, *rt
= fen6_info
->rt
;
561 struct nsim_fib6_rt
*fib6_rt
;
565 fib6_rt
= nsim_fib6_rt_lookup(&data
->fib_rt_ht
, rt
);
566 if (WARN_ON_ONCE(!fib6_rt
))
569 err
= nsim_fib6_rt_nh_add(fib6_rt
, rt
);
574 if (!fen6_info
->nsiblings
)
577 list_for_each_entry(iter
, &rt
->fib6_siblings
, fib6_siblings
) {
578 if (i
== fen6_info
->nsiblings
)
581 err
= nsim_fib6_rt_nh_add(fib6_rt
, iter
);
583 goto err_fib6_rt_nh_del
;
587 WARN_ON_ONCE(i
!= fen6_info
->nsiblings
);
592 list_for_each_entry_continue_reverse(iter
, &rt
->fib6_siblings
,
595 nsim_fib6_rt_nh_del(fib6_rt
, iter
);
598 nsim_fib6_rt_nh_del(fib6_rt
, rt
);
602 static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt
*fib6_rt
,
605 struct nsim_fib6_rt_nh
*fib6_rt_nh
;
607 list_for_each_entry(fib6_rt_nh
, &fib6_rt
->nh_list
, list
)
608 fib6_info_hw_flags_set(fib6_rt_nh
->rt
, false, trap
);
611 static int nsim_fib6_rt_add(struct nsim_fib_data
*data
,
612 struct nsim_fib6_rt
*fib6_rt
,
613 struct netlink_ext_ack
*extack
)
617 err
= nsim_fib_account(&data
->ipv6
.fib
, true, extack
);
621 err
= rhashtable_insert_fast(&data
->fib_rt_ht
,
622 &fib6_rt
->common
.ht_node
,
623 nsim_fib_rt_ht_params
);
625 NL_SET_ERR_MSG_MOD(extack
, "Failed to insert IPv6 route");
626 goto err_fib_dismiss
;
629 nsim_fib6_rt_hw_flags_set(fib6_rt
, true);
634 nsim_fib_account(&data
->ipv6
.fib
, false, extack
);
638 static int nsim_fib6_rt_replace(struct nsim_fib_data
*data
,
639 struct nsim_fib6_rt
*fib6_rt
,
640 struct nsim_fib6_rt
*fib6_rt_old
,
641 struct netlink_ext_ack
*extack
)
645 /* We are replacing a route, so no need to change the accounting. */
646 err
= rhashtable_replace_fast(&data
->fib_rt_ht
,
647 &fib6_rt_old
->common
.ht_node
,
648 &fib6_rt
->common
.ht_node
,
649 nsim_fib_rt_ht_params
);
651 NL_SET_ERR_MSG_MOD(extack
, "Failed to replace IPv6 route");
655 nsim_fib6_rt_hw_flags_set(fib6_rt
, true);
657 nsim_fib6_rt_hw_flags_set(fib6_rt_old
, false);
658 nsim_fib6_rt_destroy(fib6_rt_old
);
663 static int nsim_fib6_rt_insert(struct nsim_fib_data
*data
,
664 struct fib6_entry_notifier_info
*fen6_info
)
666 struct netlink_ext_ack
*extack
= fen6_info
->info
.extack
;
667 struct nsim_fib6_rt
*fib6_rt
, *fib6_rt_old
;
670 fib6_rt
= nsim_fib6_rt_create(data
, fen6_info
);
672 return PTR_ERR(fib6_rt
);
674 fib6_rt_old
= nsim_fib6_rt_lookup(&data
->fib_rt_ht
, fen6_info
->rt
);
676 err
= nsim_fib6_rt_add(data
, fib6_rt
, extack
);
678 err
= nsim_fib6_rt_replace(data
, fib6_rt
, fib6_rt_old
, extack
);
681 nsim_fib6_rt_destroy(fib6_rt
);
687 nsim_fib6_rt_remove(struct nsim_fib_data
*data
,
688 const struct fib6_entry_notifier_info
*fen6_info
)
690 struct netlink_ext_ack
*extack
= fen6_info
->info
.extack
;
691 struct nsim_fib6_rt
*fib6_rt
;
693 /* Multipath routes are first added to the FIB trie and only then
694 * notified. If we vetoed the addition, we will get a delete
695 * notification for a route we do not have. Therefore, do not warn if
696 * route was not found.
698 fib6_rt
= nsim_fib6_rt_lookup(&data
->fib_rt_ht
, fen6_info
->rt
);
702 /* If not all the nexthops are deleted, then only reduce the nexthop
705 if (fen6_info
->nsiblings
+ 1 != fib6_rt
->nhs
) {
706 nsim_fib6_rt_nh_del(fib6_rt
, fen6_info
->rt
);
710 rhashtable_remove_fast(&data
->fib_rt_ht
, &fib6_rt
->common
.ht_node
,
711 nsim_fib_rt_ht_params
);
712 nsim_fib_account(&data
->ipv6
.fib
, false, extack
);
713 nsim_fib6_rt_destroy(fib6_rt
);
716 static int nsim_fib6_event(struct nsim_fib_data
*data
,
717 struct fib_notifier_info
*info
,
720 struct fib6_entry_notifier_info
*fen6_info
;
723 fen6_info
= container_of(info
, struct fib6_entry_notifier_info
, info
);
725 if (fen6_info
->rt
->fib6_src
.plen
) {
726 NL_SET_ERR_MSG_MOD(info
->extack
, "IPv6 source-specific route is not supported");
731 case FIB_EVENT_ENTRY_REPLACE
:
732 err
= nsim_fib6_rt_insert(data
, fen6_info
);
734 case FIB_EVENT_ENTRY_APPEND
:
735 err
= nsim_fib6_rt_append(data
, fen6_info
);
737 case FIB_EVENT_ENTRY_DEL
:
738 nsim_fib6_rt_remove(data
, fen6_info
);
747 static int nsim_fib_event(struct nsim_fib_data
*data
,
748 struct fib_notifier_info
*info
, unsigned long event
)
752 switch (info
->family
) {
754 err
= nsim_fib4_event(data
, info
, event
);
757 err
= nsim_fib6_event(data
, info
, event
);
764 static int nsim_fib_event_nb(struct notifier_block
*nb
, unsigned long event
,
767 struct nsim_fib_data
*data
= container_of(nb
, struct nsim_fib_data
,
769 struct fib_notifier_info
*info
= ptr
;
772 /* IPv6 routes can be added via RAs from softIRQ. */
773 spin_lock_bh(&data
->fib_lock
);
776 case FIB_EVENT_RULE_ADD
:
777 case FIB_EVENT_RULE_DEL
:
778 err
= nsim_fib_rule_event(data
, info
,
779 event
== FIB_EVENT_RULE_ADD
);
782 case FIB_EVENT_ENTRY_REPLACE
:
783 case FIB_EVENT_ENTRY_APPEND
:
784 case FIB_EVENT_ENTRY_DEL
:
785 err
= nsim_fib_event(data
, info
, event
);
789 spin_unlock_bh(&data
->fib_lock
);
791 return notifier_from_errno(err
);
794 static void nsim_fib4_rt_free(struct nsim_fib_rt
*fib_rt
,
795 struct nsim_fib_data
*data
)
797 struct devlink
*devlink
= data
->devlink
;
798 struct nsim_fib4_rt
*fib4_rt
;
800 fib4_rt
= container_of(fib_rt
, struct nsim_fib4_rt
, common
);
801 nsim_fib4_rt_hw_flags_set(devlink_net(devlink
), fib4_rt
, false);
802 nsim_fib_account(&data
->ipv4
.fib
, false, NULL
);
803 nsim_fib4_rt_destroy(fib4_rt
);
806 static void nsim_fib6_rt_free(struct nsim_fib_rt
*fib_rt
,
807 struct nsim_fib_data
*data
)
809 struct nsim_fib6_rt
*fib6_rt
;
811 fib6_rt
= container_of(fib_rt
, struct nsim_fib6_rt
, common
);
812 nsim_fib6_rt_hw_flags_set(fib6_rt
, false);
813 nsim_fib_account(&data
->ipv6
.fib
, false, NULL
);
814 nsim_fib6_rt_destroy(fib6_rt
);
817 static void nsim_fib_rt_free(void *ptr
, void *arg
)
819 struct nsim_fib_rt
*fib_rt
= ptr
;
820 struct nsim_fib_data
*data
= arg
;
822 switch (fib_rt
->key
.family
) {
824 nsim_fib4_rt_free(fib_rt
, data
);
827 nsim_fib6_rt_free(fib_rt
, data
);
834 /* inconsistent dump, trying again */
835 static void nsim_fib_dump_inconsistent(struct notifier_block
*nb
)
837 struct nsim_fib_data
*data
= container_of(nb
, struct nsim_fib_data
,
839 struct nsim_fib_rt
*fib_rt
, *fib_rt_tmp
;
841 /* The notifier block is still not registered, so we do not need to
842 * take any locks here.
844 list_for_each_entry_safe(fib_rt
, fib_rt_tmp
, &data
->fib_rt_list
, list
) {
845 rhashtable_remove_fast(&data
->fib_rt_ht
, &fib_rt
->ht_node
,
846 nsim_fib_rt_ht_params
);
847 nsim_fib_rt_free(fib_rt
, data
);
850 data
->ipv4
.rules
.num
= 0ULL;
851 data
->ipv6
.rules
.num
= 0ULL;
854 static struct nsim_nexthop
*nsim_nexthop_create(struct nsim_fib_data
*data
,
855 struct nh_notifier_info
*info
)
857 struct nsim_nexthop
*nexthop
;
861 nexthop
= kzalloc(sizeof(*nexthop
), GFP_KERNEL
);
865 nexthop
->id
= info
->id
;
867 /* Determine the number of nexthop entries the new nexthop will
876 for (i
= 0; i
< info
->nh_grp
->num_nh
; i
++)
877 occ
+= info
->nh_grp
->nh_entries
[i
].weight
;
884 static void nsim_nexthop_destroy(struct nsim_nexthop
*nexthop
)
889 static int nsim_nexthop_account(struct nsim_fib_data
*data
, u64 occ
,
890 bool add
, struct netlink_ext_ack
*extack
)
895 if (data
->nexthops
.num
+ occ
<= data
->nexthops
.max
) {
896 data
->nexthops
.num
+= occ
;
899 NL_SET_ERR_MSG_MOD(extack
, "Exceeded number of supported nexthops");
902 if (WARN_ON(occ
> data
->nexthops
.num
))
904 data
->nexthops
.num
-= occ
;
910 static int nsim_nexthop_add(struct nsim_fib_data
*data
,
911 struct nsim_nexthop
*nexthop
,
912 struct netlink_ext_ack
*extack
)
914 struct net
*net
= devlink_net(data
->devlink
);
917 err
= nsim_nexthop_account(data
, nexthop
->occ
, true, extack
);
921 err
= rhashtable_insert_fast(&data
->nexthop_ht
, &nexthop
->ht_node
,
922 nsim_nexthop_ht_params
);
924 NL_SET_ERR_MSG_MOD(extack
, "Failed to insert nexthop");
925 goto err_nexthop_dismiss
;
928 nexthop_set_hw_flags(net
, nexthop
->id
, false, true);
933 nsim_nexthop_account(data
, nexthop
->occ
, false, extack
);
937 static int nsim_nexthop_replace(struct nsim_fib_data
*data
,
938 struct nsim_nexthop
*nexthop
,
939 struct nsim_nexthop
*nexthop_old
,
940 struct netlink_ext_ack
*extack
)
942 struct net
*net
= devlink_net(data
->devlink
);
945 err
= nsim_nexthop_account(data
, nexthop
->occ
, true, extack
);
949 err
= rhashtable_replace_fast(&data
->nexthop_ht
,
950 &nexthop_old
->ht_node
, &nexthop
->ht_node
,
951 nsim_nexthop_ht_params
);
953 NL_SET_ERR_MSG_MOD(extack
, "Failed to replace nexthop");
954 goto err_nexthop_dismiss
;
957 nexthop_set_hw_flags(net
, nexthop
->id
, false, true);
958 nsim_nexthop_account(data
, nexthop_old
->occ
, false, extack
);
959 nsim_nexthop_destroy(nexthop_old
);
964 nsim_nexthop_account(data
, nexthop
->occ
, false, extack
);
968 static int nsim_nexthop_insert(struct nsim_fib_data
*data
,
969 struct nh_notifier_info
*info
)
971 struct nsim_nexthop
*nexthop
, *nexthop_old
;
974 nexthop
= nsim_nexthop_create(data
, info
);
978 nexthop_old
= rhashtable_lookup_fast(&data
->nexthop_ht
, &info
->id
,
979 nsim_nexthop_ht_params
);
981 err
= nsim_nexthop_add(data
, nexthop
, info
->extack
);
983 err
= nsim_nexthop_replace(data
, nexthop
, nexthop_old
,
987 nsim_nexthop_destroy(nexthop
);
992 static void nsim_nexthop_remove(struct nsim_fib_data
*data
,
993 struct nh_notifier_info
*info
)
995 struct nsim_nexthop
*nexthop
;
997 nexthop
= rhashtable_lookup_fast(&data
->nexthop_ht
, &info
->id
,
998 nsim_nexthop_ht_params
);
1002 rhashtable_remove_fast(&data
->nexthop_ht
, &nexthop
->ht_node
,
1003 nsim_nexthop_ht_params
);
1004 nsim_nexthop_account(data
, nexthop
->occ
, false, info
->extack
);
1005 nsim_nexthop_destroy(nexthop
);
1008 static int nsim_nexthop_event_nb(struct notifier_block
*nb
, unsigned long event
,
1011 struct nsim_fib_data
*data
= container_of(nb
, struct nsim_fib_data
,
1013 struct nh_notifier_info
*info
= ptr
;
1019 case NEXTHOP_EVENT_REPLACE
:
1020 err
= nsim_nexthop_insert(data
, info
);
1022 case NEXTHOP_EVENT_DEL
:
1023 nsim_nexthop_remove(data
, info
);
1029 return notifier_from_errno(err
);
1032 static void nsim_nexthop_free(void *ptr
, void *arg
)
1034 struct nsim_nexthop
*nexthop
= ptr
;
1035 struct nsim_fib_data
*data
= arg
;
1038 net
= devlink_net(data
->devlink
);
1039 nexthop_set_hw_flags(net
, nexthop
->id
, false, false);
1040 nsim_nexthop_account(data
, nexthop
->occ
, false, NULL
);
1041 nsim_nexthop_destroy(nexthop
);
1044 static u64
nsim_fib_ipv4_resource_occ_get(void *priv
)
1046 struct nsim_fib_data
*data
= priv
;
1048 return nsim_fib_get_val(data
, NSIM_RESOURCE_IPV4_FIB
, false);
1051 static u64
nsim_fib_ipv4_rules_res_occ_get(void *priv
)
1053 struct nsim_fib_data
*data
= priv
;
1055 return nsim_fib_get_val(data
, NSIM_RESOURCE_IPV4_FIB_RULES
, false);
1058 static u64
nsim_fib_ipv6_resource_occ_get(void *priv
)
1060 struct nsim_fib_data
*data
= priv
;
1062 return nsim_fib_get_val(data
, NSIM_RESOURCE_IPV6_FIB
, false);
1065 static u64
nsim_fib_ipv6_rules_res_occ_get(void *priv
)
1067 struct nsim_fib_data
*data
= priv
;
1069 return nsim_fib_get_val(data
, NSIM_RESOURCE_IPV6_FIB_RULES
, false);
1072 static u64
nsim_fib_nexthops_res_occ_get(void *priv
)
1074 struct nsim_fib_data
*data
= priv
;
1076 return nsim_fib_get_val(data
, NSIM_RESOURCE_NEXTHOPS
, false);
1079 static void nsim_fib_set_max_all(struct nsim_fib_data
*data
,
1080 struct devlink
*devlink
)
1082 enum nsim_resource_id res_ids
[] = {
1083 NSIM_RESOURCE_IPV4_FIB
, NSIM_RESOURCE_IPV4_FIB_RULES
,
1084 NSIM_RESOURCE_IPV6_FIB
, NSIM_RESOURCE_IPV6_FIB_RULES
,
1085 NSIM_RESOURCE_NEXTHOPS
,
1089 for (i
= 0; i
< ARRAY_SIZE(res_ids
); i
++) {
1093 err
= devlink_resource_size_get(devlink
, res_ids
[i
], &val
);
1096 nsim_fib_set_max(data
, res_ids
[i
], val
);
1100 struct nsim_fib_data
*nsim_fib_create(struct devlink
*devlink
,
1101 struct netlink_ext_ack
*extack
)
1103 struct nsim_fib_data
*data
;
1106 data
= kzalloc(sizeof(*data
), GFP_KERNEL
);
1108 return ERR_PTR(-ENOMEM
);
1109 data
->devlink
= devlink
;
1111 err
= rhashtable_init(&data
->nexthop_ht
, &nsim_nexthop_ht_params
);
1115 spin_lock_init(&data
->fib_lock
);
1116 INIT_LIST_HEAD(&data
->fib_rt_list
);
1117 err
= rhashtable_init(&data
->fib_rt_ht
, &nsim_fib_rt_ht_params
);
1119 goto err_rhashtable_nexthop_destroy
;
1121 nsim_fib_set_max_all(data
, devlink
);
1123 data
->nexthop_nb
.notifier_call
= nsim_nexthop_event_nb
;
1124 err
= register_nexthop_notifier(devlink_net(devlink
), &data
->nexthop_nb
,
1127 pr_err("Failed to register nexthop notifier\n");
1128 goto err_rhashtable_fib_destroy
;
1131 data
->fib_nb
.notifier_call
= nsim_fib_event_nb
;
1132 err
= register_fib_notifier(devlink_net(devlink
), &data
->fib_nb
,
1133 nsim_fib_dump_inconsistent
, extack
);
1135 pr_err("Failed to register fib notifier\n");
1136 goto err_nexthop_nb_unregister
;
1139 devlink_resource_occ_get_register(devlink
,
1140 NSIM_RESOURCE_IPV4_FIB
,
1141 nsim_fib_ipv4_resource_occ_get
,
1143 devlink_resource_occ_get_register(devlink
,
1144 NSIM_RESOURCE_IPV4_FIB_RULES
,
1145 nsim_fib_ipv4_rules_res_occ_get
,
1147 devlink_resource_occ_get_register(devlink
,
1148 NSIM_RESOURCE_IPV6_FIB
,
1149 nsim_fib_ipv6_resource_occ_get
,
1151 devlink_resource_occ_get_register(devlink
,
1152 NSIM_RESOURCE_IPV6_FIB_RULES
,
1153 nsim_fib_ipv6_rules_res_occ_get
,
1155 devlink_resource_occ_get_register(devlink
,
1156 NSIM_RESOURCE_NEXTHOPS
,
1157 nsim_fib_nexthops_res_occ_get
,
1161 err_nexthop_nb_unregister
:
1162 unregister_nexthop_notifier(devlink_net(devlink
), &data
->nexthop_nb
);
1163 err_rhashtable_fib_destroy
:
1164 rhashtable_free_and_destroy(&data
->fib_rt_ht
, nsim_fib_rt_free
,
1166 err_rhashtable_nexthop_destroy
:
1167 rhashtable_free_and_destroy(&data
->nexthop_ht
, nsim_nexthop_free
,
1171 return ERR_PTR(err
);
1174 void nsim_fib_destroy(struct devlink
*devlink
, struct nsim_fib_data
*data
)
1176 devlink_resource_occ_get_unregister(devlink
,
1177 NSIM_RESOURCE_NEXTHOPS
);
1178 devlink_resource_occ_get_unregister(devlink
,
1179 NSIM_RESOURCE_IPV6_FIB_RULES
);
1180 devlink_resource_occ_get_unregister(devlink
,
1181 NSIM_RESOURCE_IPV6_FIB
);
1182 devlink_resource_occ_get_unregister(devlink
,
1183 NSIM_RESOURCE_IPV4_FIB_RULES
);
1184 devlink_resource_occ_get_unregister(devlink
,
1185 NSIM_RESOURCE_IPV4_FIB
);
1186 unregister_fib_notifier(devlink_net(devlink
), &data
->fib_nb
);
1187 unregister_nexthop_notifier(devlink_net(devlink
), &data
->nexthop_nb
);
1188 rhashtable_free_and_destroy(&data
->fib_rt_ht
, nsim_fib_rt_free
,
1190 rhashtable_free_and_destroy(&data
->nexthop_ht
, nsim_nexthop_free
,
1192 WARN_ON_ONCE(!list_empty(&data
->fib_rt_list
));