dm writecache: add cond_resched to loop in persistent_memory_claim()
[linux/fpc-iii.git] / drivers / net / netdevsim / fib.c
blobf32d56ac3e80faaa9e30b6b6aea8cc1b49a6f947
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>
29 #include "netdevsim.h"
31 struct nsim_fib_entry {
32 u64 max;
33 u64 num;
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;
54 int family;
55 u32 tb_id;
58 struct nsim_fib_rt {
59 struct nsim_fib_rt_key key;
60 struct rhash_head ht_node;
61 struct list_head list; /* Member of fib_rt_list */
64 struct nsim_fib4_rt {
65 struct nsim_fib_rt common;
66 struct fib_info *fi;
67 u8 tos;
68 u8 type;
71 struct nsim_fib6_rt {
72 struct nsim_fib_rt common;
73 struct list_head nh_list;
74 unsigned int nhs;
77 struct nsim_fib6_rt_nh {
78 struct list_head list; /* Member of nh_list */
79 struct fib6_info *rt;
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;
94 switch (res_id) {
95 case NSIM_RESOURCE_IPV4_FIB:
96 entry = &fib_data->ipv4.fib;
97 break;
98 case NSIM_RESOURCE_IPV4_FIB_RULES:
99 entry = &fib_data->ipv4.rules;
100 break;
101 case NSIM_RESOURCE_IPV6_FIB:
102 entry = &fib_data->ipv6.fib;
103 break;
104 case NSIM_RESOURCE_IPV6_FIB_RULES:
105 entry = &fib_data->ipv6.rules;
106 break;
107 default:
108 return 0;
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;
119 switch (res_id) {
120 case NSIM_RESOURCE_IPV4_FIB:
121 entry = &fib_data->ipv4.fib;
122 break;
123 case NSIM_RESOURCE_IPV4_FIB_RULES:
124 entry = &fib_data->ipv4.rules;
125 break;
126 case NSIM_RESOURCE_IPV6_FIB:
127 entry = &fib_data->ipv6.fib;
128 break;
129 case NSIM_RESOURCE_IPV6_FIB_RULES:
130 entry = &fib_data->ipv6.rules;
131 break;
132 default:
133 WARN_ON(1);
134 return;
136 entry->max = val;
139 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
140 struct netlink_ext_ack *extack)
142 int err = 0;
144 if (add) {
145 if (entry->num < entry->max) {
146 entry->num++;
147 } else {
148 err = -ENOSPC;
149 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
151 } else {
152 entry->num--;
155 return err;
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;
162 int err = 0;
164 switch (info->family) {
165 case AF_INET:
166 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
167 break;
168 case AF_INET6:
169 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
170 break;
173 return err;
176 static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
177 struct netlink_ext_ack *extack)
179 int err = 0;
181 if (add) {
182 if (entry->num < entry->max) {
183 entry->num++;
184 } else {
185 err = -ENOSPC;
186 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
188 } else {
189 entry->num--;
192 return err;
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;
222 key.family = family;
223 key.tb_id = tb_id;
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);
235 if (!fib4_rt)
236 return NULL;
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;
246 return fib4_rt;
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);
253 kfree(fib4_rt);
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,
264 fen_info->tb_id);
265 if (!fib_rt)
266 return NULL;
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,
273 bool trap)
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;
285 fri.offload = false;
286 fri.trap = trap;
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);
295 int err;
297 err = nsim_fib_account(&data->ipv4.fib, true, extack);
298 if (err)
299 return err;
301 err = rhashtable_insert_fast(&data->fib_rt_ht,
302 &fib4_rt->common.ht_node,
303 nsim_fib_rt_ht_params);
304 if (err) {
305 NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv4 route");
306 goto err_fib_dismiss;
309 nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
311 return 0;
313 err_fib_dismiss:
314 nsim_fib_account(&data->ipv4.fib, false, extack);
315 return err;
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);
324 int err;
326 /* We are replacing a route, so no need to change the accounting. */
327 err = rhashtable_replace_fast(&data->fib_rt_ht,
328 &fib4_rt_old->common.ht_node,
329 &fib4_rt->common.ht_node,
330 nsim_fib_rt_ht_params);
331 if (err) {
332 NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv4 route");
333 return err;
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);
341 return 0;
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;
349 int err;
351 fib4_rt = nsim_fib4_rt_create(data, fen_info);
352 if (!fib4_rt)
353 return -ENOMEM;
355 fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
356 if (!fib4_rt_old)
357 err = nsim_fib4_rt_add(data, fib4_rt, extack);
358 else
359 err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old, extack);
361 if (err)
362 nsim_fib4_rt_destroy(fib4_rt);
364 return err;
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))
375 return;
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,
385 unsigned long event)
387 struct fib_entry_notifier_info *fen_info;
388 int err = 0;
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");
394 return 0;
397 switch (event) {
398 case FIB_EVENT_ENTRY_REPLACE:
399 err = nsim_fib4_rt_insert(data, fen_info);
400 break;
401 case FIB_EVENT_ENTRY_DEL:
402 nsim_fib4_rt_remove(data, fen_info);
403 break;
404 default:
405 break;
408 return err;
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)
419 return fib6_rt_nh;
422 return NULL;
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);
431 if (!fib6_rt_nh)
432 return -ENOMEM;
434 fib6_info_hold(rt);
435 fib6_rt_nh->rt = rt;
436 list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
437 fib6_rt->nhs++;
439 return 0;
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))
449 return;
451 fib6_rt->nhs--;
452 list_del(&fib6_rt_nh->list);
453 #if IS_ENABLED(CONFIG_IPV6)
454 fib6_info_release(fib6_rt_nh->rt);
455 #endif
456 kfree(fib6_rt_nh);
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;
465 int i = 0;
466 int err;
468 fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_ATOMIC);
469 if (!fib6_rt)
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);
483 if (err)
484 goto err_fib_rt_fini;
486 if (!fen6_info->nsiblings)
487 return fib6_rt;
489 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
490 if (i == fen6_info->nsiblings)
491 break;
493 err = nsim_fib6_rt_nh_add(fib6_rt, iter);
494 if (err)
495 goto err_fib6_rt_nh_del;
496 i++;
498 WARN_ON_ONCE(i != fen6_info->nsiblings);
500 return fib6_rt;
502 err_fib6_rt_nh_del:
503 list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
504 fib6_siblings)
505 nsim_fib6_rt_nh_del(fib6_rt, iter);
506 nsim_fib6_rt_nh_del(fib6_rt, rt);
507 err_fib_rt_fini:
508 nsim_fib_rt_fini(&fib6_rt->common);
509 kfree(fib6_rt);
510 return ERR_PTR(err);
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);
521 kfree(fib6_rt);
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);
533 if (!fib_rt)
534 return NULL;
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;
544 int i = 0;
545 int err;
547 fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
548 if (WARN_ON_ONCE(!fib6_rt))
549 return -EINVAL;
551 err = nsim_fib6_rt_nh_add(fib6_rt, rt);
552 if (err)
553 return err;
554 rt->trap = true;
556 if (!fen6_info->nsiblings)
557 return 0;
559 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
560 if (i == fen6_info->nsiblings)
561 break;
563 err = nsim_fib6_rt_nh_add(fib6_rt, iter);
564 if (err)
565 goto err_fib6_rt_nh_del;
566 iter->trap = true;
567 i++;
569 WARN_ON_ONCE(i != fen6_info->nsiblings);
571 return 0;
573 err_fib6_rt_nh_del:
574 list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
575 fib6_siblings) {
576 iter->trap = false;
577 nsim_fib6_rt_nh_del(fib6_rt, iter);
579 rt->trap = false;
580 nsim_fib6_rt_nh_del(fib6_rt, rt);
581 return err;
584 static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt *fib6_rt,
585 bool trap)
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)
597 int err;
599 err = nsim_fib_account(&data->ipv6.fib, true, extack);
600 if (err)
601 return err;
603 err = rhashtable_insert_fast(&data->fib_rt_ht,
604 &fib6_rt->common.ht_node,
605 nsim_fib_rt_ht_params);
606 if (err) {
607 NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv6 route");
608 goto err_fib_dismiss;
611 nsim_fib6_rt_hw_flags_set(fib6_rt, true);
613 return 0;
615 err_fib_dismiss:
616 nsim_fib_account(&data->ipv6.fib, false, extack);
617 return err;
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)
625 int err;
627 /* We are replacing a route, so no need to change the accounting. */
628 err = rhashtable_replace_fast(&data->fib_rt_ht,
629 &fib6_rt_old->common.ht_node,
630 &fib6_rt->common.ht_node,
631 nsim_fib_rt_ht_params);
632 if (err) {
633 NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv6 route");
634 return err;
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);
642 return 0;
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;
650 int err;
652 fib6_rt = nsim_fib6_rt_create(data, fen6_info);
653 if (IS_ERR(fib6_rt))
654 return PTR_ERR(fib6_rt);
656 fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
657 if (!fib6_rt_old)
658 err = nsim_fib6_rt_add(data, fib6_rt, extack);
659 else
660 err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old, extack);
662 if (err)
663 nsim_fib6_rt_destroy(fib6_rt);
665 return err;
668 static void
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);
681 if (!fib6_rt)
682 return;
684 /* If not all the nexthops are deleted, then only reduce the nexthop
685 * group.
687 if (fen6_info->nsiblings + 1 != fib6_rt->nhs) {
688 nsim_fib6_rt_nh_del(fib6_rt, fen6_info->rt);
689 return;
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,
700 unsigned long event)
702 struct fib6_entry_notifier_info *fen6_info;
703 int err = 0;
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");
709 return 0;
712 if (fen6_info->rt->fib6_src.plen) {
713 NL_SET_ERR_MSG_MOD(info->extack, "IPv6 source-specific route is not supported");
714 return 0;
717 switch (event) {
718 case FIB_EVENT_ENTRY_REPLACE:
719 err = nsim_fib6_rt_insert(data, fen6_info);
720 break;
721 case FIB_EVENT_ENTRY_APPEND:
722 err = nsim_fib6_rt_append(data, fen6_info);
723 break;
724 case FIB_EVENT_ENTRY_DEL:
725 nsim_fib6_rt_remove(data, fen6_info);
726 break;
727 default:
728 break;
731 return err;
734 static int nsim_fib_event(struct nsim_fib_data *data,
735 struct fib_notifier_info *info, unsigned long event)
737 int err = 0;
739 switch (info->family) {
740 case AF_INET:
741 err = nsim_fib4_event(data, info, event);
742 break;
743 case AF_INET6:
744 err = nsim_fib6_event(data, info, event);
745 break;
748 return err;
751 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
752 void *ptr)
754 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
755 fib_nb);
756 struct fib_notifier_info *info = ptr;
757 int err = 0;
759 /* IPv6 routes can be added via RAs from softIRQ. */
760 spin_lock_bh(&data->fib_lock);
762 switch (event) {
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);
767 break;
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);
773 break;
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) {
810 case AF_INET:
811 nsim_fib4_rt_free(fib_rt, data);
812 break;
813 case AF_INET6:
814 nsim_fib6_rt_free(fib_rt, data);
815 break;
816 default:
817 WARN_ON_ONCE(1);
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,
825 fib_nb);
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
876 int i;
878 for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
879 int err;
880 u64 val;
882 err = devlink_resource_size_get(devlink, res_ids[i], &val);
883 if (err)
884 val = (u64) -1;
885 nsim_fib_set_max(data, res_ids[i], val);
889 struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
890 struct netlink_ext_ack *extack)
892 struct nsim_fib_data *data;
893 int err;
895 data = kzalloc(sizeof(*data), GFP_KERNEL);
896 if (!data)
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);
903 if (err)
904 goto err_data_free;
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);
911 if (err) {
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,
919 data);
920 devlink_resource_occ_get_register(devlink,
921 NSIM_RESOURCE_IPV4_FIB_RULES,
922 nsim_fib_ipv4_rules_res_occ_get,
923 data);
924 devlink_resource_occ_get_register(devlink,
925 NSIM_RESOURCE_IPV6_FIB,
926 nsim_fib_ipv6_resource_occ_get,
927 data);
928 devlink_resource_occ_get_register(devlink,
929 NSIM_RESOURCE_IPV6_FIB_RULES,
930 nsim_fib_ipv6_rules_res_occ_get,
931 data);
932 return data;
934 err_rhashtable_destroy:
935 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
936 data);
937 err_data_free:
938 kfree(data);
939 return ERR_PTR(err);
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,
954 data);
955 WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
956 kfree(data);