WIP FPC-III support
[linux/fpc-iii.git] / drivers / net / netdevsim / fib.c
blob45d8a7790bd5b049437de2458ed034a60f471e39
1 /*
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
7 * source tree.
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 {
33 u64 max;
34 u64 num;
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;
58 int family;
59 u32 tb_id;
62 struct nsim_fib_rt {
63 struct nsim_fib_rt_key key;
64 struct rhash_head ht_node;
65 struct list_head list; /* Member of fib_rt_list */
68 struct nsim_fib4_rt {
69 struct nsim_fib_rt common;
70 struct fib_info *fi;
71 u8 tos;
72 u8 type;
75 struct nsim_fib6_rt {
76 struct nsim_fib_rt common;
77 struct list_head nh_list;
78 unsigned int nhs;
81 struct nsim_fib6_rt_nh {
82 struct list_head list; /* Member of nh_list */
83 struct fib6_info *rt;
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,
93 struct nsim_nexthop {
94 struct rhash_head ht_node;
95 u64 occ;
96 u32 id;
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;
111 switch (res_id) {
112 case NSIM_RESOURCE_IPV4_FIB:
113 entry = &fib_data->ipv4.fib;
114 break;
115 case NSIM_RESOURCE_IPV4_FIB_RULES:
116 entry = &fib_data->ipv4.rules;
117 break;
118 case NSIM_RESOURCE_IPV6_FIB:
119 entry = &fib_data->ipv6.fib;
120 break;
121 case NSIM_RESOURCE_IPV6_FIB_RULES:
122 entry = &fib_data->ipv6.rules;
123 break;
124 case NSIM_RESOURCE_NEXTHOPS:
125 entry = &fib_data->nexthops;
126 break;
127 default:
128 return 0;
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;
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 WARN_ON(1);
157 return;
159 entry->max = val;
162 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
163 struct netlink_ext_ack *extack)
165 int err = 0;
167 if (add) {
168 if (entry->num < entry->max) {
169 entry->num++;
170 } else {
171 err = -ENOSPC;
172 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
174 } else {
175 entry->num--;
178 return err;
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;
185 int err = 0;
187 switch (info->family) {
188 case AF_INET:
189 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
190 break;
191 case AF_INET6:
192 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
193 break;
196 return err;
199 static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
200 struct netlink_ext_ack *extack)
202 int err = 0;
204 if (add) {
205 if (entry->num < entry->max) {
206 entry->num++;
207 } else {
208 err = -ENOSPC;
209 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
211 } else {
212 entry->num--;
215 return err;
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;
245 key.family = family;
246 key.tb_id = tb_id;
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);
258 if (!fib4_rt)
259 return NULL;
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;
269 return fib4_rt;
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);
276 kfree(fib4_rt);
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,
287 fen_info->tb_id);
288 if (!fib_rt)
289 return NULL;
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,
296 bool trap)
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;
308 fri.offload = false;
309 fri.trap = trap;
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);
318 int err;
320 err = nsim_fib_account(&data->ipv4.fib, true, extack);
321 if (err)
322 return err;
324 err = rhashtable_insert_fast(&data->fib_rt_ht,
325 &fib4_rt->common.ht_node,
326 nsim_fib_rt_ht_params);
327 if (err) {
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);
334 return 0;
336 err_fib_dismiss:
337 nsim_fib_account(&data->ipv4.fib, false, extack);
338 return err;
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);
347 int err;
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);
354 if (err) {
355 NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv4 route");
356 return err;
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);
364 return 0;
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;
372 int err;
374 fib4_rt = nsim_fib4_rt_create(data, fen_info);
375 if (!fib4_rt)
376 return -ENOMEM;
378 fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
379 if (!fib4_rt_old)
380 err = nsim_fib4_rt_add(data, fib4_rt, extack);
381 else
382 err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old, extack);
384 if (err)
385 nsim_fib4_rt_destroy(fib4_rt);
387 return err;
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))
398 return;
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,
408 unsigned long event)
410 struct fib_entry_notifier_info *fen_info;
411 int err = 0;
413 fen_info = container_of(info, struct fib_entry_notifier_info, info);
415 switch (event) {
416 case FIB_EVENT_ENTRY_REPLACE:
417 err = nsim_fib4_rt_insert(data, fen_info);
418 break;
419 case FIB_EVENT_ENTRY_DEL:
420 nsim_fib4_rt_remove(data, fen_info);
421 break;
422 default:
423 break;
426 return err;
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)
437 return fib6_rt_nh;
440 return NULL;
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);
449 if (!fib6_rt_nh)
450 return -ENOMEM;
452 fib6_info_hold(rt);
453 fib6_rt_nh->rt = rt;
454 list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
455 fib6_rt->nhs++;
457 return 0;
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))
467 return;
469 fib6_rt->nhs--;
470 list_del(&fib6_rt_nh->list);
471 #if IS_ENABLED(CONFIG_IPV6)
472 fib6_info_release(fib6_rt_nh->rt);
473 #endif
474 kfree(fib6_rt_nh);
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;
483 int i = 0;
484 int err;
486 fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_ATOMIC);
487 if (!fib6_rt)
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);
501 if (err)
502 goto err_fib_rt_fini;
504 if (!fen6_info->nsiblings)
505 return fib6_rt;
507 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
508 if (i == fen6_info->nsiblings)
509 break;
511 err = nsim_fib6_rt_nh_add(fib6_rt, iter);
512 if (err)
513 goto err_fib6_rt_nh_del;
514 i++;
516 WARN_ON_ONCE(i != fen6_info->nsiblings);
518 return fib6_rt;
520 err_fib6_rt_nh_del:
521 list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
522 fib6_siblings)
523 nsim_fib6_rt_nh_del(fib6_rt, iter);
524 nsim_fib6_rt_nh_del(fib6_rt, rt);
525 err_fib_rt_fini:
526 nsim_fib_rt_fini(&fib6_rt->common);
527 kfree(fib6_rt);
528 return ERR_PTR(err);
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);
539 kfree(fib6_rt);
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);
551 if (!fib_rt)
552 return NULL;
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;
562 int i = 0;
563 int err;
565 fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
566 if (WARN_ON_ONCE(!fib6_rt))
567 return -EINVAL;
569 err = nsim_fib6_rt_nh_add(fib6_rt, rt);
570 if (err)
571 return err;
572 rt->trap = true;
574 if (!fen6_info->nsiblings)
575 return 0;
577 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
578 if (i == fen6_info->nsiblings)
579 break;
581 err = nsim_fib6_rt_nh_add(fib6_rt, iter);
582 if (err)
583 goto err_fib6_rt_nh_del;
584 iter->trap = true;
585 i++;
587 WARN_ON_ONCE(i != fen6_info->nsiblings);
589 return 0;
591 err_fib6_rt_nh_del:
592 list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
593 fib6_siblings) {
594 iter->trap = false;
595 nsim_fib6_rt_nh_del(fib6_rt, iter);
597 rt->trap = false;
598 nsim_fib6_rt_nh_del(fib6_rt, rt);
599 return err;
602 static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt *fib6_rt,
603 bool trap)
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)
615 int err;
617 err = nsim_fib_account(&data->ipv6.fib, true, extack);
618 if (err)
619 return err;
621 err = rhashtable_insert_fast(&data->fib_rt_ht,
622 &fib6_rt->common.ht_node,
623 nsim_fib_rt_ht_params);
624 if (err) {
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);
631 return 0;
633 err_fib_dismiss:
634 nsim_fib_account(&data->ipv6.fib, false, extack);
635 return err;
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)
643 int err;
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);
650 if (err) {
651 NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv6 route");
652 return err;
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);
660 return 0;
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;
668 int err;
670 fib6_rt = nsim_fib6_rt_create(data, fen6_info);
671 if (IS_ERR(fib6_rt))
672 return PTR_ERR(fib6_rt);
674 fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
675 if (!fib6_rt_old)
676 err = nsim_fib6_rt_add(data, fib6_rt, extack);
677 else
678 err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old, extack);
680 if (err)
681 nsim_fib6_rt_destroy(fib6_rt);
683 return err;
686 static void
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);
699 if (!fib6_rt)
700 return;
702 /* If not all the nexthops are deleted, then only reduce the nexthop
703 * group.
705 if (fen6_info->nsiblings + 1 != fib6_rt->nhs) {
706 nsim_fib6_rt_nh_del(fib6_rt, fen6_info->rt);
707 return;
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,
718 unsigned long event)
720 struct fib6_entry_notifier_info *fen6_info;
721 int err = 0;
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");
727 return 0;
730 switch (event) {
731 case FIB_EVENT_ENTRY_REPLACE:
732 err = nsim_fib6_rt_insert(data, fen6_info);
733 break;
734 case FIB_EVENT_ENTRY_APPEND:
735 err = nsim_fib6_rt_append(data, fen6_info);
736 break;
737 case FIB_EVENT_ENTRY_DEL:
738 nsim_fib6_rt_remove(data, fen6_info);
739 break;
740 default:
741 break;
744 return err;
747 static int nsim_fib_event(struct nsim_fib_data *data,
748 struct fib_notifier_info *info, unsigned long event)
750 int err = 0;
752 switch (info->family) {
753 case AF_INET:
754 err = nsim_fib4_event(data, info, event);
755 break;
756 case AF_INET6:
757 err = nsim_fib6_event(data, info, event);
758 break;
761 return err;
764 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
765 void *ptr)
767 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
768 fib_nb);
769 struct fib_notifier_info *info = ptr;
770 int err = 0;
772 /* IPv6 routes can be added via RAs from softIRQ. */
773 spin_lock_bh(&data->fib_lock);
775 switch (event) {
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);
780 break;
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);
786 break;
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) {
823 case AF_INET:
824 nsim_fib4_rt_free(fib_rt, data);
825 break;
826 case AF_INET6:
827 nsim_fib6_rt_free(fib_rt, data);
828 break;
829 default:
830 WARN_ON_ONCE(1);
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,
838 fib_nb);
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;
858 u64 occ = 0;
859 int i;
861 nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL);
862 if (!nexthop)
863 return NULL;
865 nexthop->id = info->id;
867 /* Determine the number of nexthop entries the new nexthop will
868 * occupy.
871 if (!info->is_grp) {
872 occ = 1;
873 goto out;
876 for (i = 0; i < info->nh_grp->num_nh; i++)
877 occ += info->nh_grp->nh_entries[i].weight;
879 out:
880 nexthop->occ = occ;
881 return nexthop;
884 static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop)
886 kfree(nexthop);
889 static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ,
890 bool add, struct netlink_ext_ack *extack)
892 int err = 0;
894 if (add) {
895 if (data->nexthops.num + occ <= data->nexthops.max) {
896 data->nexthops.num += occ;
897 } else {
898 err = -ENOSPC;
899 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops");
901 } else {
902 if (WARN_ON(occ > data->nexthops.num))
903 return -EINVAL;
904 data->nexthops.num -= occ;
907 return err;
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);
915 int err;
917 err = nsim_nexthop_account(data, nexthop->occ, true, extack);
918 if (err)
919 return err;
921 err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node,
922 nsim_nexthop_ht_params);
923 if (err) {
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);
930 return 0;
932 err_nexthop_dismiss:
933 nsim_nexthop_account(data, nexthop->occ, false, extack);
934 return err;
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);
943 int err;
945 err = nsim_nexthop_account(data, nexthop->occ, true, extack);
946 if (err)
947 return err;
949 err = rhashtable_replace_fast(&data->nexthop_ht,
950 &nexthop_old->ht_node, &nexthop->ht_node,
951 nsim_nexthop_ht_params);
952 if (err) {
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);
961 return 0;
963 err_nexthop_dismiss:
964 nsim_nexthop_account(data, nexthop->occ, false, extack);
965 return err;
968 static int nsim_nexthop_insert(struct nsim_fib_data *data,
969 struct nh_notifier_info *info)
971 struct nsim_nexthop *nexthop, *nexthop_old;
972 int err;
974 nexthop = nsim_nexthop_create(data, info);
975 if (!nexthop)
976 return -ENOMEM;
978 nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
979 nsim_nexthop_ht_params);
980 if (!nexthop_old)
981 err = nsim_nexthop_add(data, nexthop, info->extack);
982 else
983 err = nsim_nexthop_replace(data, nexthop, nexthop_old,
984 info->extack);
986 if (err)
987 nsim_nexthop_destroy(nexthop);
989 return err;
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);
999 if (!nexthop)
1000 return;
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,
1009 void *ptr)
1011 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1012 nexthop_nb);
1013 struct nh_notifier_info *info = ptr;
1014 int err = 0;
1016 ASSERT_RTNL();
1018 switch (event) {
1019 case NEXTHOP_EVENT_REPLACE:
1020 err = nsim_nexthop_insert(data, info);
1021 break;
1022 case NEXTHOP_EVENT_DEL:
1023 nsim_nexthop_remove(data, info);
1024 break;
1025 default:
1026 break;
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;
1036 struct net *net;
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,
1087 int i;
1089 for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
1090 int err;
1091 u64 val;
1093 err = devlink_resource_size_get(devlink, res_ids[i], &val);
1094 if (err)
1095 val = (u64) -1;
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;
1104 int err;
1106 data = kzalloc(sizeof(*data), GFP_KERNEL);
1107 if (!data)
1108 return ERR_PTR(-ENOMEM);
1109 data->devlink = devlink;
1111 err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
1112 if (err)
1113 goto err_data_free;
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);
1118 if (err)
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,
1125 extack);
1126 if (err) {
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);
1134 if (err) {
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,
1142 data);
1143 devlink_resource_occ_get_register(devlink,
1144 NSIM_RESOURCE_IPV4_FIB_RULES,
1145 nsim_fib_ipv4_rules_res_occ_get,
1146 data);
1147 devlink_resource_occ_get_register(devlink,
1148 NSIM_RESOURCE_IPV6_FIB,
1149 nsim_fib_ipv6_resource_occ_get,
1150 data);
1151 devlink_resource_occ_get_register(devlink,
1152 NSIM_RESOURCE_IPV6_FIB_RULES,
1153 nsim_fib_ipv6_rules_res_occ_get,
1154 data);
1155 devlink_resource_occ_get_register(devlink,
1156 NSIM_RESOURCE_NEXTHOPS,
1157 nsim_fib_nexthops_res_occ_get,
1158 data);
1159 return data;
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,
1165 data);
1166 err_rhashtable_nexthop_destroy:
1167 rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1168 data);
1169 err_data_free:
1170 kfree(data);
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,
1189 data);
1190 rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1191 data);
1192 WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
1193 kfree(data);