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