bus: mhi: core: Add helper API to return number of free TREs
[linux-2.6-microblaze.git] / drivers / net / netdevsim / fib.c
1 /*
2  * Copyright (c) 2018 Cumulus Networks. All rights reserved.
3  * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
4  *
5  * This software is licensed under the GNU General License Version 2,
6  * June 1991 as shown in the file COPYING in the top-level directory of this
7  * source tree.
8  *
9  * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
10  * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
11  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
13  * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
14  * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
15  */
16
17 #include <linux/in6.h>
18 #include <linux/kernel.h>
19 #include <linux/list.h>
20 #include <linux/rhashtable.h>
21 #include <linux/spinlock_types.h>
22 #include <linux/types.h>
23 #include <net/fib_notifier.h>
24 #include <net/ip_fib.h>
25 #include <net/ip6_fib.h>
26 #include <net/fib_rules.h>
27 #include <net/net_namespace.h>
28 #include <net/nexthop.h>
29
30 #include "netdevsim.h"
31
32 struct nsim_fib_entry {
33         u64 max;
34         u64 num;
35 };
36
37 struct nsim_per_fib_data {
38         struct nsim_fib_entry fib;
39         struct nsim_fib_entry rules;
40 };
41
42 struct nsim_fib_data {
43         struct notifier_block fib_nb;
44         struct nsim_per_fib_data ipv4;
45         struct nsim_per_fib_data ipv6;
46         struct nsim_fib_entry nexthops;
47         struct rhashtable fib_rt_ht;
48         struct list_head fib_rt_list;
49         spinlock_t fib_lock;    /* Protects hashtable, list and accounting */
50         struct notifier_block nexthop_nb;
51         struct rhashtable nexthop_ht;
52         struct devlink *devlink;
53 };
54
55 struct nsim_fib_rt_key {
56         unsigned char addr[sizeof(struct in6_addr)];
57         unsigned char prefix_len;
58         int family;
59         u32 tb_id;
60 };
61
62 struct nsim_fib_rt {
63         struct nsim_fib_rt_key key;
64         struct rhash_head ht_node;
65         struct list_head list;  /* Member of fib_rt_list */
66 };
67
68 struct nsim_fib4_rt {
69         struct nsim_fib_rt common;
70         struct fib_info *fi;
71         u8 tos;
72         u8 type;
73 };
74
75 struct nsim_fib6_rt {
76         struct nsim_fib_rt common;
77         struct list_head nh_list;
78         unsigned int nhs;
79 };
80
81 struct nsim_fib6_rt_nh {
82         struct list_head list;  /* Member of nh_list */
83         struct fib6_info *rt;
84 };
85
86 static const struct rhashtable_params nsim_fib_rt_ht_params = {
87         .key_offset = offsetof(struct nsim_fib_rt, key),
88         .head_offset = offsetof(struct nsim_fib_rt, ht_node),
89         .key_len = sizeof(struct nsim_fib_rt_key),
90         .automatic_shrinking = true,
91 };
92
93 struct nsim_nexthop {
94         struct rhash_head ht_node;
95         u64 occ;
96         u32 id;
97 };
98
99 static const struct rhashtable_params nsim_nexthop_ht_params = {
100         .key_offset = offsetof(struct nsim_nexthop, id),
101         .head_offset = offsetof(struct nsim_nexthop, ht_node),
102         .key_len = sizeof(u32),
103         .automatic_shrinking = true,
104 };
105
106 u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
107                      enum nsim_resource_id res_id, bool max)
108 {
109         struct nsim_fib_entry *entry;
110
111         switch (res_id) {
112         case NSIM_RESOURCE_IPV4_FIB:
113                 entry = &fib_data->ipv4.fib;
114                 break;
115         case NSIM_RESOURCE_IPV4_FIB_RULES:
116                 entry = &fib_data->ipv4.rules;
117                 break;
118         case NSIM_RESOURCE_IPV6_FIB:
119                 entry = &fib_data->ipv6.fib;
120                 break;
121         case NSIM_RESOURCE_IPV6_FIB_RULES:
122                 entry = &fib_data->ipv6.rules;
123                 break;
124         case NSIM_RESOURCE_NEXTHOPS:
125                 entry = &fib_data->nexthops;
126                 break;
127         default:
128                 return 0;
129         }
130
131         return max ? entry->max : entry->num;
132 }
133
134 static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
135                              enum nsim_resource_id res_id, u64 val)
136 {
137         struct nsim_fib_entry *entry;
138
139         switch (res_id) {
140         case NSIM_RESOURCE_IPV4_FIB:
141                 entry = &fib_data->ipv4.fib;
142                 break;
143         case NSIM_RESOURCE_IPV4_FIB_RULES:
144                 entry = &fib_data->ipv4.rules;
145                 break;
146         case NSIM_RESOURCE_IPV6_FIB:
147                 entry = &fib_data->ipv6.fib;
148                 break;
149         case NSIM_RESOURCE_IPV6_FIB_RULES:
150                 entry = &fib_data->ipv6.rules;
151                 break;
152         case NSIM_RESOURCE_NEXTHOPS:
153                 entry = &fib_data->nexthops;
154                 break;
155         default:
156                 WARN_ON(1);
157                 return;
158         }
159         entry->max = val;
160 }
161
162 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
163                                  struct netlink_ext_ack *extack)
164 {
165         int err = 0;
166
167         if (add) {
168                 if (entry->num < entry->max) {
169                         entry->num++;
170                 } else {
171                         err = -ENOSPC;
172                         NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
173                 }
174         } else {
175                 entry->num--;
176         }
177
178         return err;
179 }
180
181 static int nsim_fib_rule_event(struct nsim_fib_data *data,
182                                struct fib_notifier_info *info, bool add)
183 {
184         struct netlink_ext_ack *extack = info->extack;
185         int err = 0;
186
187         switch (info->family) {
188         case AF_INET:
189                 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
190                 break;
191         case AF_INET6:
192                 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
193                 break;
194         }
195
196         return err;
197 }
198
199 static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
200                             struct netlink_ext_ack *extack)
201 {
202         int err = 0;
203
204         if (add) {
205                 if (entry->num < entry->max) {
206                         entry->num++;
207                 } else {
208                         err = -ENOSPC;
209                         NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
210                 }
211         } else {
212                 entry->num--;
213         }
214
215         return err;
216 }
217
218 static void nsim_fib_rt_init(struct nsim_fib_data *data,
219                              struct nsim_fib_rt *fib_rt, const void *addr,
220                              size_t addr_len, unsigned int prefix_len,
221                              int family, u32 tb_id)
222 {
223         memcpy(fib_rt->key.addr, addr, addr_len);
224         fib_rt->key.prefix_len = prefix_len;
225         fib_rt->key.family = family;
226         fib_rt->key.tb_id = tb_id;
227         list_add(&fib_rt->list, &data->fib_rt_list);
228 }
229
230 static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
231 {
232         list_del(&fib_rt->list);
233 }
234
235 static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht,
236                                               const void *addr, size_t addr_len,
237                                               unsigned int prefix_len,
238                                               int family, u32 tb_id)
239 {
240         struct nsim_fib_rt_key key;
241
242         memset(&key, 0, sizeof(key));
243         memcpy(key.addr, addr, addr_len);
244         key.prefix_len = prefix_len;
245         key.family = family;
246         key.tb_id = tb_id;
247
248         return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params);
249 }
250
251 static struct nsim_fib4_rt *
252 nsim_fib4_rt_create(struct nsim_fib_data *data,
253                     struct fib_entry_notifier_info *fen_info)
254 {
255         struct nsim_fib4_rt *fib4_rt;
256
257         fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_ATOMIC);
258         if (!fib4_rt)
259                 return NULL;
260
261         nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32),
262                          fen_info->dst_len, AF_INET, fen_info->tb_id);
263
264         fib4_rt->fi = fen_info->fi;
265         fib_info_hold(fib4_rt->fi);
266         fib4_rt->tos = fen_info->tos;
267         fib4_rt->type = fen_info->type;
268
269         return fib4_rt;
270 }
271
272 static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt)
273 {
274         fib_info_put(fib4_rt->fi);
275         nsim_fib_rt_fini(&fib4_rt->common);
276         kfree(fib4_rt);
277 }
278
279 static struct nsim_fib4_rt *
280 nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
281                     const struct fib_entry_notifier_info *fen_info)
282 {
283         struct nsim_fib_rt *fib_rt;
284
285         fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32),
286                                     fen_info->dst_len, AF_INET,
287                                     fen_info->tb_id);
288         if (!fib_rt)
289                 return NULL;
290
291         return container_of(fib_rt, struct nsim_fib4_rt, common);
292 }
293
294 static void nsim_fib4_rt_hw_flags_set(struct net *net,
295                                       const struct nsim_fib4_rt *fib4_rt,
296                                       bool trap)
297 {
298         u32 *p_dst = (u32 *) fib4_rt->common.key.addr;
299         int dst_len = fib4_rt->common.key.prefix_len;
300         struct fib_rt_info fri;
301
302         fri.fi = fib4_rt->fi;
303         fri.tb_id = fib4_rt->common.key.tb_id;
304         fri.dst = cpu_to_be32(*p_dst);
305         fri.dst_len = dst_len;
306         fri.tos = fib4_rt->tos;
307         fri.type = fib4_rt->type;
308         fri.offload = false;
309         fri.trap = trap;
310         fib_alias_hw_flags_set(net, &fri);
311 }
312
313 static int nsim_fib4_rt_add(struct nsim_fib_data *data,
314                             struct nsim_fib4_rt *fib4_rt,
315                             struct netlink_ext_ack *extack)
316 {
317         struct net *net = devlink_net(data->devlink);
318         int err;
319
320         err = nsim_fib_account(&data->ipv4.fib, true, extack);
321         if (err)
322                 return err;
323
324         err = rhashtable_insert_fast(&data->fib_rt_ht,
325                                      &fib4_rt->common.ht_node,
326                                      nsim_fib_rt_ht_params);
327         if (err) {
328                 NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv4 route");
329                 goto err_fib_dismiss;
330         }
331
332         nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
333
334         return 0;
335
336 err_fib_dismiss:
337         nsim_fib_account(&data->ipv4.fib, false, extack);
338         return err;
339 }
340
341 static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
342                                 struct nsim_fib4_rt *fib4_rt,
343                                 struct nsim_fib4_rt *fib4_rt_old,
344                                 struct netlink_ext_ack *extack)
345 {
346         struct net *net = devlink_net(data->devlink);
347         int err;
348
349         /* We are replacing a route, so no need to change the accounting. */
350         err = rhashtable_replace_fast(&data->fib_rt_ht,
351                                       &fib4_rt_old->common.ht_node,
352                                       &fib4_rt->common.ht_node,
353                                       nsim_fib_rt_ht_params);
354         if (err) {
355                 NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv4 route");
356                 return err;
357         }
358
359         nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
360
361         nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
362         nsim_fib4_rt_destroy(fib4_rt_old);
363
364         return 0;
365 }
366
367 static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
368                                struct fib_entry_notifier_info *fen_info)
369 {
370         struct netlink_ext_ack *extack = fen_info->info.extack;
371         struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
372         int err;
373
374         fib4_rt = nsim_fib4_rt_create(data, fen_info);
375         if (!fib4_rt)
376                 return -ENOMEM;
377
378         fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
379         if (!fib4_rt_old)
380                 err = nsim_fib4_rt_add(data, fib4_rt, extack);
381         else
382                 err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old, extack);
383
384         if (err)
385                 nsim_fib4_rt_destroy(fib4_rt);
386
387         return err;
388 }
389
390 static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
391                                 const struct fib_entry_notifier_info *fen_info)
392 {
393         struct netlink_ext_ack *extack = fen_info->info.extack;
394         struct nsim_fib4_rt *fib4_rt;
395
396         fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
397         if (WARN_ON_ONCE(!fib4_rt))
398                 return;
399
400         rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
401                                nsim_fib_rt_ht_params);
402         nsim_fib_account(&data->ipv4.fib, false, extack);
403         nsim_fib4_rt_destroy(fib4_rt);
404 }
405
406 static int nsim_fib4_event(struct nsim_fib_data *data,
407                            struct fib_notifier_info *info,
408                            unsigned long event)
409 {
410         struct fib_entry_notifier_info *fen_info;
411         int err = 0;
412
413         fen_info = container_of(info, struct fib_entry_notifier_info, info);
414
415         switch (event) {
416         case FIB_EVENT_ENTRY_REPLACE:
417                 err = nsim_fib4_rt_insert(data, fen_info);
418                 break;
419         case FIB_EVENT_ENTRY_DEL:
420                 nsim_fib4_rt_remove(data, fen_info);
421                 break;
422         default:
423                 break;
424         }
425
426         return err;
427 }
428
429 static struct nsim_fib6_rt_nh *
430 nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt,
431                      const struct fib6_info *rt)
432 {
433         struct nsim_fib6_rt_nh *fib6_rt_nh;
434
435         list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) {
436                 if (fib6_rt_nh->rt == rt)
437                         return fib6_rt_nh;
438         }
439
440         return NULL;
441 }
442
443 static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt,
444                                struct fib6_info *rt)
445 {
446         struct nsim_fib6_rt_nh *fib6_rt_nh;
447
448         fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_ATOMIC);
449         if (!fib6_rt_nh)
450                 return -ENOMEM;
451
452         fib6_info_hold(rt);
453         fib6_rt_nh->rt = rt;
454         list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
455         fib6_rt->nhs++;
456
457         return 0;
458 }
459
460 static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
461                                 const struct fib6_info *rt)
462 {
463         struct nsim_fib6_rt_nh *fib6_rt_nh;
464
465         fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
466         if (WARN_ON_ONCE(!fib6_rt_nh))
467                 return;
468
469         fib6_rt->nhs--;
470         list_del(&fib6_rt_nh->list);
471 #if IS_ENABLED(CONFIG_IPV6)
472         fib6_info_release(fib6_rt_nh->rt);
473 #endif
474         kfree(fib6_rt_nh);
475 }
476
477 static struct nsim_fib6_rt *
478 nsim_fib6_rt_create(struct nsim_fib_data *data,
479                     struct fib6_entry_notifier_info *fen6_info)
480 {
481         struct fib6_info *iter, *rt = fen6_info->rt;
482         struct nsim_fib6_rt *fib6_rt;
483         int i = 0;
484         int err;
485
486         fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_ATOMIC);
487         if (!fib6_rt)
488                 return ERR_PTR(-ENOMEM);
489
490         nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr,
491                          sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6,
492                          rt->fib6_table->tb6_id);
493
494         /* We consider a multipath IPv6 route as one entry, but it can be made
495          * up from several fib6_info structs (one for each nexthop), so we
496          * add them all to the same list under the entry.
497          */
498         INIT_LIST_HEAD(&fib6_rt->nh_list);
499
500         err = nsim_fib6_rt_nh_add(fib6_rt, rt);
501         if (err)
502                 goto err_fib_rt_fini;
503
504         if (!fen6_info->nsiblings)
505                 return fib6_rt;
506
507         list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
508                 if (i == fen6_info->nsiblings)
509                         break;
510
511                 err = nsim_fib6_rt_nh_add(fib6_rt, iter);
512                 if (err)
513                         goto err_fib6_rt_nh_del;
514                 i++;
515         }
516         WARN_ON_ONCE(i != fen6_info->nsiblings);
517
518         return fib6_rt;
519
520 err_fib6_rt_nh_del:
521         list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
522                                              fib6_siblings)
523                 nsim_fib6_rt_nh_del(fib6_rt, iter);
524         nsim_fib6_rt_nh_del(fib6_rt, rt);
525 err_fib_rt_fini:
526         nsim_fib_rt_fini(&fib6_rt->common);
527         kfree(fib6_rt);
528         return ERR_PTR(err);
529 }
530
531 static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt)
532 {
533         struct nsim_fib6_rt_nh *iter, *tmp;
534
535         list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list)
536                 nsim_fib6_rt_nh_del(fib6_rt, iter->rt);
537         WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list));
538         nsim_fib_rt_fini(&fib6_rt->common);
539         kfree(fib6_rt);
540 }
541
542 static struct nsim_fib6_rt *
543 nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt)
544 {
545         struct nsim_fib_rt *fib_rt;
546
547         fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr,
548                                     sizeof(rt->fib6_dst.addr),
549                                     rt->fib6_dst.plen, AF_INET6,
550                                     rt->fib6_table->tb6_id);
551         if (!fib_rt)
552                 return NULL;
553
554         return container_of(fib_rt, struct nsim_fib6_rt, common);
555 }
556
557 static int nsim_fib6_rt_append(struct nsim_fib_data *data,
558                                struct fib6_entry_notifier_info *fen6_info)
559 {
560         struct fib6_info *iter, *rt = fen6_info->rt;
561         struct nsim_fib6_rt *fib6_rt;
562         int i = 0;
563         int err;
564
565         fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
566         if (WARN_ON_ONCE(!fib6_rt))
567                 return -EINVAL;
568
569         err = nsim_fib6_rt_nh_add(fib6_rt, rt);
570         if (err)
571                 return err;
572         rt->trap = true;
573
574         if (!fen6_info->nsiblings)
575                 return 0;
576
577         list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
578                 if (i == fen6_info->nsiblings)
579                         break;
580
581                 err = nsim_fib6_rt_nh_add(fib6_rt, iter);
582                 if (err)
583                         goto err_fib6_rt_nh_del;
584                 iter->trap = true;
585                 i++;
586         }
587         WARN_ON_ONCE(i != fen6_info->nsiblings);
588
589         return 0;
590
591 err_fib6_rt_nh_del:
592         list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
593                                              fib6_siblings) {
594                 iter->trap = false;
595                 nsim_fib6_rt_nh_del(fib6_rt, iter);
596         }
597         rt->trap = false;
598         nsim_fib6_rt_nh_del(fib6_rt, rt);
599         return err;
600 }
601
602 static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt *fib6_rt,
603                                       bool trap)
604 {
605         struct nsim_fib6_rt_nh *fib6_rt_nh;
606
607         list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
608                 fib6_info_hw_flags_set(fib6_rt_nh->rt, false, trap);
609 }
610
611 static int nsim_fib6_rt_add(struct nsim_fib_data *data,
612                             struct nsim_fib6_rt *fib6_rt,
613                             struct netlink_ext_ack *extack)
614 {
615         int err;
616
617         err = nsim_fib_account(&data->ipv6.fib, true, extack);
618         if (err)
619                 return err;
620
621         err = rhashtable_insert_fast(&data->fib_rt_ht,
622                                      &fib6_rt->common.ht_node,
623                                      nsim_fib_rt_ht_params);
624         if (err) {
625                 NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv6 route");
626                 goto err_fib_dismiss;
627         }
628
629         nsim_fib6_rt_hw_flags_set(fib6_rt, true);
630
631         return 0;
632
633 err_fib_dismiss:
634         nsim_fib_account(&data->ipv6.fib, false, extack);
635         return err;
636 }
637
638 static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
639                                 struct nsim_fib6_rt *fib6_rt,
640                                 struct nsim_fib6_rt *fib6_rt_old,
641                                 struct netlink_ext_ack *extack)
642 {
643         int err;
644
645         /* We are replacing a route, so no need to change the accounting. */
646         err = rhashtable_replace_fast(&data->fib_rt_ht,
647                                       &fib6_rt_old->common.ht_node,
648                                       &fib6_rt->common.ht_node,
649                                       nsim_fib_rt_ht_params);
650         if (err) {
651                 NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv6 route");
652                 return err;
653         }
654
655         nsim_fib6_rt_hw_flags_set(fib6_rt, true);
656
657         nsim_fib6_rt_hw_flags_set(fib6_rt_old, false);
658         nsim_fib6_rt_destroy(fib6_rt_old);
659
660         return 0;
661 }
662
663 static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
664                                struct fib6_entry_notifier_info *fen6_info)
665 {
666         struct netlink_ext_ack *extack = fen6_info->info.extack;
667         struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
668         int err;
669
670         fib6_rt = nsim_fib6_rt_create(data, fen6_info);
671         if (IS_ERR(fib6_rt))
672                 return PTR_ERR(fib6_rt);
673
674         fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
675         if (!fib6_rt_old)
676                 err = nsim_fib6_rt_add(data, fib6_rt, extack);
677         else
678                 err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old, extack);
679
680         if (err)
681                 nsim_fib6_rt_destroy(fib6_rt);
682
683         return err;
684 }
685
686 static void
687 nsim_fib6_rt_remove(struct nsim_fib_data *data,
688                     const struct fib6_entry_notifier_info *fen6_info)
689 {
690         struct netlink_ext_ack *extack = fen6_info->info.extack;
691         struct nsim_fib6_rt *fib6_rt;
692
693         /* Multipath routes are first added to the FIB trie and only then
694          * notified. If we vetoed the addition, we will get a delete
695          * notification for a route we do not have. Therefore, do not warn if
696          * route was not found.
697          */
698         fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
699         if (!fib6_rt)
700                 return;
701
702         /* If not all the nexthops are deleted, then only reduce the nexthop
703          * group.
704          */
705         if (fen6_info->nsiblings + 1 != fib6_rt->nhs) {
706                 nsim_fib6_rt_nh_del(fib6_rt, fen6_info->rt);
707                 return;
708         }
709
710         rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
711                                nsim_fib_rt_ht_params);
712         nsim_fib_account(&data->ipv6.fib, false, extack);
713         nsim_fib6_rt_destroy(fib6_rt);
714 }
715
716 static int nsim_fib6_event(struct nsim_fib_data *data,
717                            struct fib_notifier_info *info,
718                            unsigned long event)
719 {
720         struct fib6_entry_notifier_info *fen6_info;
721         int err = 0;
722
723         fen6_info = container_of(info, struct fib6_entry_notifier_info, info);
724
725         if (fen6_info->rt->fib6_src.plen) {
726                 NL_SET_ERR_MSG_MOD(info->extack, "IPv6 source-specific route is not supported");
727                 return 0;
728         }
729
730         switch (event) {
731         case FIB_EVENT_ENTRY_REPLACE:
732                 err = nsim_fib6_rt_insert(data, fen6_info);
733                 break;
734         case FIB_EVENT_ENTRY_APPEND:
735                 err = nsim_fib6_rt_append(data, fen6_info);
736                 break;
737         case FIB_EVENT_ENTRY_DEL:
738                 nsim_fib6_rt_remove(data, fen6_info);
739                 break;
740         default:
741                 break;
742         }
743
744         return err;
745 }
746
747 static int nsim_fib_event(struct nsim_fib_data *data,
748                           struct fib_notifier_info *info, unsigned long event)
749 {
750         int err = 0;
751
752         switch (info->family) {
753         case AF_INET:
754                 err = nsim_fib4_event(data, info, event);
755                 break;
756         case AF_INET6:
757                 err = nsim_fib6_event(data, info, event);
758                 break;
759         }
760
761         return err;
762 }
763
764 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
765                              void *ptr)
766 {
767         struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
768                                                   fib_nb);
769         struct fib_notifier_info *info = ptr;
770         int err = 0;
771
772         /* IPv6 routes can be added via RAs from softIRQ. */
773         spin_lock_bh(&data->fib_lock);
774
775         switch (event) {
776         case FIB_EVENT_RULE_ADD:
777         case FIB_EVENT_RULE_DEL:
778                 err = nsim_fib_rule_event(data, info,
779                                           event == FIB_EVENT_RULE_ADD);
780                 break;
781
782         case FIB_EVENT_ENTRY_REPLACE:
783         case FIB_EVENT_ENTRY_APPEND:
784         case FIB_EVENT_ENTRY_DEL:
785                 err = nsim_fib_event(data, info, event);
786                 break;
787         }
788
789         spin_unlock_bh(&data->fib_lock);
790
791         return notifier_from_errno(err);
792 }
793
794 static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt,
795                               struct nsim_fib_data *data)
796 {
797         struct devlink *devlink = data->devlink;
798         struct nsim_fib4_rt *fib4_rt;
799
800         fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common);
801         nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false);
802         nsim_fib_account(&data->ipv4.fib, false, NULL);
803         nsim_fib4_rt_destroy(fib4_rt);
804 }
805
806 static void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt,
807                               struct nsim_fib_data *data)
808 {
809         struct nsim_fib6_rt *fib6_rt;
810
811         fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common);
812         nsim_fib6_rt_hw_flags_set(fib6_rt, false);
813         nsim_fib_account(&data->ipv6.fib, false, NULL);
814         nsim_fib6_rt_destroy(fib6_rt);
815 }
816
817 static void nsim_fib_rt_free(void *ptr, void *arg)
818 {
819         struct nsim_fib_rt *fib_rt = ptr;
820         struct nsim_fib_data *data = arg;
821
822         switch (fib_rt->key.family) {
823         case AF_INET:
824                 nsim_fib4_rt_free(fib_rt, data);
825                 break;
826         case AF_INET6:
827                 nsim_fib6_rt_free(fib_rt, data);
828                 break;
829         default:
830                 WARN_ON_ONCE(1);
831         }
832 }
833
834 /* inconsistent dump, trying again */
835 static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
836 {
837         struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
838                                                   fib_nb);
839         struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
840
841         /* The notifier block is still not registered, so we do not need to
842          * take any locks here.
843          */
844         list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
845                 rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
846                                        nsim_fib_rt_ht_params);
847                 nsim_fib_rt_free(fib_rt, data);
848         }
849
850         data->ipv4.rules.num = 0ULL;
851         data->ipv6.rules.num = 0ULL;
852 }
853
854 static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data,
855                                                 struct nh_notifier_info *info)
856 {
857         struct nsim_nexthop *nexthop;
858         u64 occ = 0;
859         int i;
860
861         nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL);
862         if (!nexthop)
863                 return NULL;
864
865         nexthop->id = info->id;
866
867         /* Determine the number of nexthop entries the new nexthop will
868          * occupy.
869          */
870
871         if (!info->is_grp) {
872                 occ = 1;
873                 goto out;
874         }
875
876         for (i = 0; i < info->nh_grp->num_nh; i++)
877                 occ += info->nh_grp->nh_entries[i].weight;
878
879 out:
880         nexthop->occ = occ;
881         return nexthop;
882 }
883
884 static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop)
885 {
886         kfree(nexthop);
887 }
888
889 static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ,
890                                 bool add, struct netlink_ext_ack *extack)
891 {
892         int err = 0;
893
894         if (add) {
895                 if (data->nexthops.num + occ <= data->nexthops.max) {
896                         data->nexthops.num += occ;
897                 } else {
898                         err = -ENOSPC;
899                         NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops");
900                 }
901         } else {
902                 if (WARN_ON(occ > data->nexthops.num))
903                         return -EINVAL;
904                 data->nexthops.num -= occ;
905         }
906
907         return err;
908 }
909
910 static int nsim_nexthop_add(struct nsim_fib_data *data,
911                             struct nsim_nexthop *nexthop,
912                             struct netlink_ext_ack *extack)
913 {
914         struct net *net = devlink_net(data->devlink);
915         int err;
916
917         err = nsim_nexthop_account(data, nexthop->occ, true, extack);
918         if (err)
919                 return err;
920
921         err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node,
922                                      nsim_nexthop_ht_params);
923         if (err) {
924                 NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop");
925                 goto err_nexthop_dismiss;
926         }
927
928         nexthop_set_hw_flags(net, nexthop->id, false, true);
929
930         return 0;
931
932 err_nexthop_dismiss:
933         nsim_nexthop_account(data, nexthop->occ, false, extack);
934         return err;
935 }
936
937 static int nsim_nexthop_replace(struct nsim_fib_data *data,
938                                 struct nsim_nexthop *nexthop,
939                                 struct nsim_nexthop *nexthop_old,
940                                 struct netlink_ext_ack *extack)
941 {
942         struct net *net = devlink_net(data->devlink);
943         int err;
944
945         err = nsim_nexthop_account(data, nexthop->occ, true, extack);
946         if (err)
947                 return err;
948
949         err = rhashtable_replace_fast(&data->nexthop_ht,
950                                       &nexthop_old->ht_node, &nexthop->ht_node,
951                                       nsim_nexthop_ht_params);
952         if (err) {
953                 NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop");
954                 goto err_nexthop_dismiss;
955         }
956
957         nexthop_set_hw_flags(net, nexthop->id, false, true);
958         nsim_nexthop_account(data, nexthop_old->occ, false, extack);
959         nsim_nexthop_destroy(nexthop_old);
960
961         return 0;
962
963 err_nexthop_dismiss:
964         nsim_nexthop_account(data, nexthop->occ, false, extack);
965         return err;
966 }
967
968 static int nsim_nexthop_insert(struct nsim_fib_data *data,
969                                struct nh_notifier_info *info)
970 {
971         struct nsim_nexthop *nexthop, *nexthop_old;
972         int err;
973
974         nexthop = nsim_nexthop_create(data, info);
975         if (!nexthop)
976                 return -ENOMEM;
977
978         nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
979                                              nsim_nexthop_ht_params);
980         if (!nexthop_old)
981                 err = nsim_nexthop_add(data, nexthop, info->extack);
982         else
983                 err = nsim_nexthop_replace(data, nexthop, nexthop_old,
984                                            info->extack);
985
986         if (err)
987                 nsim_nexthop_destroy(nexthop);
988
989         return err;
990 }
991
992 static void nsim_nexthop_remove(struct nsim_fib_data *data,
993                                 struct nh_notifier_info *info)
994 {
995         struct nsim_nexthop *nexthop;
996
997         nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
998                                          nsim_nexthop_ht_params);
999         if (!nexthop)
1000                 return;
1001
1002         rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node,
1003                                nsim_nexthop_ht_params);
1004         nsim_nexthop_account(data, nexthop->occ, false, info->extack);
1005         nsim_nexthop_destroy(nexthop);
1006 }
1007
1008 static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event,
1009                                  void *ptr)
1010 {
1011         struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1012                                                   nexthop_nb);
1013         struct nh_notifier_info *info = ptr;
1014         int err = 0;
1015
1016         ASSERT_RTNL();
1017
1018         switch (event) {
1019         case NEXTHOP_EVENT_REPLACE:
1020                 err = nsim_nexthop_insert(data, info);
1021                 break;
1022         case NEXTHOP_EVENT_DEL:
1023                 nsim_nexthop_remove(data, info);
1024                 break;
1025         default:
1026                 break;
1027         }
1028
1029         return notifier_from_errno(err);
1030 }
1031
1032 static void nsim_nexthop_free(void *ptr, void *arg)
1033 {
1034         struct nsim_nexthop *nexthop = ptr;
1035         struct nsim_fib_data *data = arg;
1036         struct net *net;
1037
1038         net = devlink_net(data->devlink);
1039         nexthop_set_hw_flags(net, nexthop->id, false, false);
1040         nsim_nexthop_account(data, nexthop->occ, false, NULL);
1041         nsim_nexthop_destroy(nexthop);
1042 }
1043
1044 static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
1045 {
1046         struct nsim_fib_data *data = priv;
1047
1048         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
1049 }
1050
1051 static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
1052 {
1053         struct nsim_fib_data *data = priv;
1054
1055         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
1056 }
1057
1058 static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
1059 {
1060         struct nsim_fib_data *data = priv;
1061
1062         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
1063 }
1064
1065 static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
1066 {
1067         struct nsim_fib_data *data = priv;
1068
1069         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
1070 }
1071
1072 static u64 nsim_fib_nexthops_res_occ_get(void *priv)
1073 {
1074         struct nsim_fib_data *data = priv;
1075
1076         return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false);
1077 }
1078
1079 static void nsim_fib_set_max_all(struct nsim_fib_data *data,
1080                                  struct devlink *devlink)
1081 {
1082         enum nsim_resource_id res_ids[] = {
1083                 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
1084                 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES,
1085                 NSIM_RESOURCE_NEXTHOPS,
1086         };
1087         int i;
1088
1089         for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
1090                 int err;
1091                 u64 val;
1092
1093                 err = devlink_resource_size_get(devlink, res_ids[i], &val);
1094                 if (err)
1095                         val = (u64) -1;
1096                 nsim_fib_set_max(data, res_ids[i], val);
1097         }
1098 }
1099
1100 struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
1101                                       struct netlink_ext_ack *extack)
1102 {
1103         struct nsim_fib_data *data;
1104         int err;
1105
1106         data = kzalloc(sizeof(*data), GFP_KERNEL);
1107         if (!data)
1108                 return ERR_PTR(-ENOMEM);
1109         data->devlink = devlink;
1110
1111         err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
1112         if (err)
1113                 goto err_data_free;
1114
1115         spin_lock_init(&data->fib_lock);
1116         INIT_LIST_HEAD(&data->fib_rt_list);
1117         err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
1118         if (err)
1119                 goto err_rhashtable_nexthop_destroy;
1120
1121         nsim_fib_set_max_all(data, devlink);
1122
1123         data->nexthop_nb.notifier_call = nsim_nexthop_event_nb;
1124         err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb,
1125                                         extack);
1126         if (err) {
1127                 pr_err("Failed to register nexthop notifier\n");
1128                 goto err_rhashtable_fib_destroy;
1129         }
1130
1131         data->fib_nb.notifier_call = nsim_fib_event_nb;
1132         err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
1133                                     nsim_fib_dump_inconsistent, extack);
1134         if (err) {
1135                 pr_err("Failed to register fib notifier\n");
1136                 goto err_nexthop_nb_unregister;
1137         }
1138
1139         devlink_resource_occ_get_register(devlink,
1140                                           NSIM_RESOURCE_IPV4_FIB,
1141                                           nsim_fib_ipv4_resource_occ_get,
1142                                           data);
1143         devlink_resource_occ_get_register(devlink,
1144                                           NSIM_RESOURCE_IPV4_FIB_RULES,
1145                                           nsim_fib_ipv4_rules_res_occ_get,
1146                                           data);
1147         devlink_resource_occ_get_register(devlink,
1148                                           NSIM_RESOURCE_IPV6_FIB,
1149                                           nsim_fib_ipv6_resource_occ_get,
1150                                           data);
1151         devlink_resource_occ_get_register(devlink,
1152                                           NSIM_RESOURCE_IPV6_FIB_RULES,
1153                                           nsim_fib_ipv6_rules_res_occ_get,
1154                                           data);
1155         devlink_resource_occ_get_register(devlink,
1156                                           NSIM_RESOURCE_NEXTHOPS,
1157                                           nsim_fib_nexthops_res_occ_get,
1158                                           data);
1159         return data;
1160
1161 err_nexthop_nb_unregister:
1162         unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1163 err_rhashtable_fib_destroy:
1164         rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1165                                     data);
1166 err_rhashtable_nexthop_destroy:
1167         rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1168                                     data);
1169 err_data_free:
1170         kfree(data);
1171         return ERR_PTR(err);
1172 }
1173
1174 void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
1175 {
1176         devlink_resource_occ_get_unregister(devlink,
1177                                             NSIM_RESOURCE_NEXTHOPS);
1178         devlink_resource_occ_get_unregister(devlink,
1179                                             NSIM_RESOURCE_IPV6_FIB_RULES);
1180         devlink_resource_occ_get_unregister(devlink,
1181                                             NSIM_RESOURCE_IPV6_FIB);
1182         devlink_resource_occ_get_unregister(devlink,
1183                                             NSIM_RESOURCE_IPV4_FIB_RULES);
1184         devlink_resource_occ_get_unregister(devlink,
1185                                             NSIM_RESOURCE_IPV4_FIB);
1186         unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
1187         unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1188         rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1189                                     data);
1190         rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1191                                     data);
1192         WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
1193         kfree(data);
1194 }