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