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