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