soundwire: sysfs: add slave status and device number before probe
[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
29 #include "netdevsim.h"
30
31 struct nsim_fib_entry {
32         u64 max;
33         u64 num;
34 };
35
36 struct nsim_per_fib_data {
37         struct nsim_fib_entry fib;
38         struct nsim_fib_entry rules;
39 };
40
41 struct nsim_fib_data {
42         struct notifier_block fib_nb;
43         struct nsim_per_fib_data ipv4;
44         struct nsim_per_fib_data ipv6;
45         struct rhashtable fib_rt_ht;
46         struct list_head fib_rt_list;
47         spinlock_t fib_lock;    /* Protects hashtable, list and accounting */
48         struct devlink *devlink;
49 };
50
51 struct nsim_fib_rt_key {
52         unsigned char addr[sizeof(struct in6_addr)];
53         unsigned char prefix_len;
54         int family;
55         u32 tb_id;
56 };
57
58 struct nsim_fib_rt {
59         struct nsim_fib_rt_key key;
60         struct rhash_head ht_node;
61         struct list_head list;  /* Member of fib_rt_list */
62 };
63
64 struct nsim_fib4_rt {
65         struct nsim_fib_rt common;
66         struct fib_info *fi;
67         u8 tos;
68         u8 type;
69 };
70
71 struct nsim_fib6_rt {
72         struct nsim_fib_rt common;
73         struct list_head nh_list;
74         unsigned int nhs;
75 };
76
77 struct nsim_fib6_rt_nh {
78         struct list_head list;  /* Member of nh_list */
79         struct fib6_info *rt;
80 };
81
82 static const struct rhashtable_params nsim_fib_rt_ht_params = {
83         .key_offset = offsetof(struct nsim_fib_rt, key),
84         .head_offset = offsetof(struct nsim_fib_rt, ht_node),
85         .key_len = sizeof(struct nsim_fib_rt_key),
86         .automatic_shrinking = true,
87 };
88
89 u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
90                      enum nsim_resource_id res_id, bool max)
91 {
92         struct nsim_fib_entry *entry;
93
94         switch (res_id) {
95         case NSIM_RESOURCE_IPV4_FIB:
96                 entry = &fib_data->ipv4.fib;
97                 break;
98         case NSIM_RESOURCE_IPV4_FIB_RULES:
99                 entry = &fib_data->ipv4.rules;
100                 break;
101         case NSIM_RESOURCE_IPV6_FIB:
102                 entry = &fib_data->ipv6.fib;
103                 break;
104         case NSIM_RESOURCE_IPV6_FIB_RULES:
105                 entry = &fib_data->ipv6.rules;
106                 break;
107         default:
108                 return 0;
109         }
110
111         return max ? entry->max : entry->num;
112 }
113
114 static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
115                              enum nsim_resource_id res_id, u64 val)
116 {
117         struct nsim_fib_entry *entry;
118
119         switch (res_id) {
120         case NSIM_RESOURCE_IPV4_FIB:
121                 entry = &fib_data->ipv4.fib;
122                 break;
123         case NSIM_RESOURCE_IPV4_FIB_RULES:
124                 entry = &fib_data->ipv4.rules;
125                 break;
126         case NSIM_RESOURCE_IPV6_FIB:
127                 entry = &fib_data->ipv6.fib;
128                 break;
129         case NSIM_RESOURCE_IPV6_FIB_RULES:
130                 entry = &fib_data->ipv6.rules;
131                 break;
132         default:
133                 WARN_ON(1);
134                 return;
135         }
136         entry->max = val;
137 }
138
139 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
140                                  struct netlink_ext_ack *extack)
141 {
142         int err = 0;
143
144         if (add) {
145                 if (entry->num < entry->max) {
146                         entry->num++;
147                 } else {
148                         err = -ENOSPC;
149                         NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
150                 }
151         } else {
152                 entry->num--;
153         }
154
155         return err;
156 }
157
158 static int nsim_fib_rule_event(struct nsim_fib_data *data,
159                                struct fib_notifier_info *info, bool add)
160 {
161         struct netlink_ext_ack *extack = info->extack;
162         int err = 0;
163
164         switch (info->family) {
165         case AF_INET:
166                 err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
167                 break;
168         case AF_INET6:
169                 err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
170                 break;
171         }
172
173         return err;
174 }
175
176 static int nsim_fib_account(struct nsim_fib_entry *entry, bool add,
177                             struct netlink_ext_ack *extack)
178 {
179         int err = 0;
180
181         if (add) {
182                 if (entry->num < entry->max) {
183                         entry->num++;
184                 } else {
185                         err = -ENOSPC;
186                         NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
187                 }
188         } else {
189                 entry->num--;
190         }
191
192         return err;
193 }
194
195 static void nsim_fib_rt_init(struct nsim_fib_data *data,
196                              struct nsim_fib_rt *fib_rt, const void *addr,
197                              size_t addr_len, unsigned int prefix_len,
198                              int family, u32 tb_id)
199 {
200         memcpy(fib_rt->key.addr, addr, addr_len);
201         fib_rt->key.prefix_len = prefix_len;
202         fib_rt->key.family = family;
203         fib_rt->key.tb_id = tb_id;
204         list_add(&fib_rt->list, &data->fib_rt_list);
205 }
206
207 static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
208 {
209         list_del(&fib_rt->list);
210 }
211
212 static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht,
213                                               const void *addr, size_t addr_len,
214                                               unsigned int prefix_len,
215                                               int family, u32 tb_id)
216 {
217         struct nsim_fib_rt_key key;
218
219         memset(&key, 0, sizeof(key));
220         memcpy(key.addr, addr, addr_len);
221         key.prefix_len = prefix_len;
222         key.family = family;
223         key.tb_id = tb_id;
224
225         return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params);
226 }
227
228 static struct nsim_fib4_rt *
229 nsim_fib4_rt_create(struct nsim_fib_data *data,
230                     struct fib_entry_notifier_info *fen_info)
231 {
232         struct nsim_fib4_rt *fib4_rt;
233
234         fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_ATOMIC);
235         if (!fib4_rt)
236                 return NULL;
237
238         nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32),
239                          fen_info->dst_len, AF_INET, fen_info->tb_id);
240
241         fib4_rt->fi = fen_info->fi;
242         fib_info_hold(fib4_rt->fi);
243         fib4_rt->tos = fen_info->tos;
244         fib4_rt->type = fen_info->type;
245
246         return fib4_rt;
247 }
248
249 static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt)
250 {
251         fib_info_put(fib4_rt->fi);
252         nsim_fib_rt_fini(&fib4_rt->common);
253         kfree(fib4_rt);
254 }
255
256 static struct nsim_fib4_rt *
257 nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
258                     const struct fib_entry_notifier_info *fen_info)
259 {
260         struct nsim_fib_rt *fib_rt;
261
262         fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32),
263                                     fen_info->dst_len, AF_INET,
264                                     fen_info->tb_id);
265         if (!fib_rt)
266                 return NULL;
267
268         return container_of(fib_rt, struct nsim_fib4_rt, common);
269 }
270
271 static void nsim_fib4_rt_hw_flags_set(struct net *net,
272                                       const struct nsim_fib4_rt *fib4_rt,
273                                       bool trap)
274 {
275         u32 *p_dst = (u32 *) fib4_rt->common.key.addr;
276         int dst_len = fib4_rt->common.key.prefix_len;
277         struct fib_rt_info fri;
278
279         fri.fi = fib4_rt->fi;
280         fri.tb_id = fib4_rt->common.key.tb_id;
281         fri.dst = cpu_to_be32(*p_dst);
282         fri.dst_len = dst_len;
283         fri.tos = fib4_rt->tos;
284         fri.type = fib4_rt->type;
285         fri.offload = false;
286         fri.trap = trap;
287         fib_alias_hw_flags_set(net, &fri);
288 }
289
290 static int nsim_fib4_rt_add(struct nsim_fib_data *data,
291                             struct nsim_fib4_rt *fib4_rt,
292                             struct netlink_ext_ack *extack)
293 {
294         struct net *net = devlink_net(data->devlink);
295         int err;
296
297         err = nsim_fib_account(&data->ipv4.fib, true, extack);
298         if (err)
299                 return err;
300
301         err = rhashtable_insert_fast(&data->fib_rt_ht,
302                                      &fib4_rt->common.ht_node,
303                                      nsim_fib_rt_ht_params);
304         if (err) {
305                 NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv4 route");
306                 goto err_fib_dismiss;
307         }
308
309         nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
310
311         return 0;
312
313 err_fib_dismiss:
314         nsim_fib_account(&data->ipv4.fib, false, extack);
315         return err;
316 }
317
318 static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
319                                 struct nsim_fib4_rt *fib4_rt,
320                                 struct nsim_fib4_rt *fib4_rt_old,
321                                 struct netlink_ext_ack *extack)
322 {
323         struct net *net = devlink_net(data->devlink);
324         int err;
325
326         /* We are replacing a route, so no need to change the accounting. */
327         err = rhashtable_replace_fast(&data->fib_rt_ht,
328                                       &fib4_rt_old->common.ht_node,
329                                       &fib4_rt->common.ht_node,
330                                       nsim_fib_rt_ht_params);
331         if (err) {
332                 NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv4 route");
333                 return err;
334         }
335
336         nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
337
338         nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
339         nsim_fib4_rt_destroy(fib4_rt_old);
340
341         return 0;
342 }
343
344 static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
345                                struct fib_entry_notifier_info *fen_info)
346 {
347         struct netlink_ext_ack *extack = fen_info->info.extack;
348         struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
349         int err;
350
351         fib4_rt = nsim_fib4_rt_create(data, fen_info);
352         if (!fib4_rt)
353                 return -ENOMEM;
354
355         fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
356         if (!fib4_rt_old)
357                 err = nsim_fib4_rt_add(data, fib4_rt, extack);
358         else
359                 err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old, extack);
360
361         if (err)
362                 nsim_fib4_rt_destroy(fib4_rt);
363
364         return err;
365 }
366
367 static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
368                                 const 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;
372
373         fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
374         if (WARN_ON_ONCE(!fib4_rt))
375                 return;
376
377         rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
378                                nsim_fib_rt_ht_params);
379         nsim_fib_account(&data->ipv4.fib, false, extack);
380         nsim_fib4_rt_destroy(fib4_rt);
381 }
382
383 static int nsim_fib4_event(struct nsim_fib_data *data,
384                            struct fib_notifier_info *info,
385                            unsigned long event)
386 {
387         struct fib_entry_notifier_info *fen_info;
388         int err = 0;
389
390         fen_info = container_of(info, struct fib_entry_notifier_info, info);
391
392         if (fen_info->fi->nh) {
393                 NL_SET_ERR_MSG_MOD(info->extack, "IPv4 route with nexthop objects is not supported");
394                 return 0;
395         }
396
397         switch (event) {
398         case FIB_EVENT_ENTRY_REPLACE:
399                 err = nsim_fib4_rt_insert(data, fen_info);
400                 break;
401         case FIB_EVENT_ENTRY_DEL:
402                 nsim_fib4_rt_remove(data, fen_info);
403                 break;
404         default:
405                 break;
406         }
407
408         return err;
409 }
410
411 static struct nsim_fib6_rt_nh *
412 nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt,
413                      const struct fib6_info *rt)
414 {
415         struct nsim_fib6_rt_nh *fib6_rt_nh;
416
417         list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) {
418                 if (fib6_rt_nh->rt == rt)
419                         return fib6_rt_nh;
420         }
421
422         return NULL;
423 }
424
425 static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt,
426                                struct fib6_info *rt)
427 {
428         struct nsim_fib6_rt_nh *fib6_rt_nh;
429
430         fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_ATOMIC);
431         if (!fib6_rt_nh)
432                 return -ENOMEM;
433
434         fib6_info_hold(rt);
435         fib6_rt_nh->rt = rt;
436         list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
437         fib6_rt->nhs++;
438
439         return 0;
440 }
441
442 static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
443                                 const struct fib6_info *rt)
444 {
445         struct nsim_fib6_rt_nh *fib6_rt_nh;
446
447         fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
448         if (WARN_ON_ONCE(!fib6_rt_nh))
449                 return;
450
451         fib6_rt->nhs--;
452         list_del(&fib6_rt_nh->list);
453 #if IS_ENABLED(CONFIG_IPV6)
454         fib6_info_release(fib6_rt_nh->rt);
455 #endif
456         kfree(fib6_rt_nh);
457 }
458
459 static struct nsim_fib6_rt *
460 nsim_fib6_rt_create(struct nsim_fib_data *data,
461                     struct fib6_entry_notifier_info *fen6_info)
462 {
463         struct fib6_info *iter, *rt = fen6_info->rt;
464         struct nsim_fib6_rt *fib6_rt;
465         int i = 0;
466         int err;
467
468         fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_ATOMIC);
469         if (!fib6_rt)
470                 return ERR_PTR(-ENOMEM);
471
472         nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr,
473                          sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6,
474                          rt->fib6_table->tb6_id);
475
476         /* We consider a multipath IPv6 route as one entry, but it can be made
477          * up from several fib6_info structs (one for each nexthop), so we
478          * add them all to the same list under the entry.
479          */
480         INIT_LIST_HEAD(&fib6_rt->nh_list);
481
482         err = nsim_fib6_rt_nh_add(fib6_rt, rt);
483         if (err)
484                 goto err_fib_rt_fini;
485
486         if (!fen6_info->nsiblings)
487                 return fib6_rt;
488
489         list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
490                 if (i == fen6_info->nsiblings)
491                         break;
492
493                 err = nsim_fib6_rt_nh_add(fib6_rt, iter);
494                 if (err)
495                         goto err_fib6_rt_nh_del;
496                 i++;
497         }
498         WARN_ON_ONCE(i != fen6_info->nsiblings);
499
500         return fib6_rt;
501
502 err_fib6_rt_nh_del:
503         list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
504                                              fib6_siblings)
505                 nsim_fib6_rt_nh_del(fib6_rt, iter);
506         nsim_fib6_rt_nh_del(fib6_rt, rt);
507 err_fib_rt_fini:
508         nsim_fib_rt_fini(&fib6_rt->common);
509         kfree(fib6_rt);
510         return ERR_PTR(err);
511 }
512
513 static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt)
514 {
515         struct nsim_fib6_rt_nh *iter, *tmp;
516
517         list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list)
518                 nsim_fib6_rt_nh_del(fib6_rt, iter->rt);
519         WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list));
520         nsim_fib_rt_fini(&fib6_rt->common);
521         kfree(fib6_rt);
522 }
523
524 static struct nsim_fib6_rt *
525 nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt)
526 {
527         struct nsim_fib_rt *fib_rt;
528
529         fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr,
530                                     sizeof(rt->fib6_dst.addr),
531                                     rt->fib6_dst.plen, AF_INET6,
532                                     rt->fib6_table->tb6_id);
533         if (!fib_rt)
534                 return NULL;
535
536         return container_of(fib_rt, struct nsim_fib6_rt, common);
537 }
538
539 static int nsim_fib6_rt_append(struct nsim_fib_data *data,
540                                struct fib6_entry_notifier_info *fen6_info)
541 {
542         struct fib6_info *iter, *rt = fen6_info->rt;
543         struct nsim_fib6_rt *fib6_rt;
544         int i = 0;
545         int err;
546
547         fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
548         if (WARN_ON_ONCE(!fib6_rt))
549                 return -EINVAL;
550
551         err = nsim_fib6_rt_nh_add(fib6_rt, rt);
552         if (err)
553                 return err;
554         rt->trap = true;
555
556         if (!fen6_info->nsiblings)
557                 return 0;
558
559         list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
560                 if (i == fen6_info->nsiblings)
561                         break;
562
563                 err = nsim_fib6_rt_nh_add(fib6_rt, iter);
564                 if (err)
565                         goto err_fib6_rt_nh_del;
566                 iter->trap = true;
567                 i++;
568         }
569         WARN_ON_ONCE(i != fen6_info->nsiblings);
570
571         return 0;
572
573 err_fib6_rt_nh_del:
574         list_for_each_entry_continue_reverse(iter, &rt->fib6_siblings,
575                                              fib6_siblings) {
576                 iter->trap = false;
577                 nsim_fib6_rt_nh_del(fib6_rt, iter);
578         }
579         rt->trap = false;
580         nsim_fib6_rt_nh_del(fib6_rt, rt);
581         return err;
582 }
583
584 static void nsim_fib6_rt_hw_flags_set(const struct nsim_fib6_rt *fib6_rt,
585                                       bool trap)
586 {
587         struct nsim_fib6_rt_nh *fib6_rt_nh;
588
589         list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
590                 fib6_info_hw_flags_set(fib6_rt_nh->rt, false, trap);
591 }
592
593 static int nsim_fib6_rt_add(struct nsim_fib_data *data,
594                             struct nsim_fib6_rt *fib6_rt,
595                             struct netlink_ext_ack *extack)
596 {
597         int err;
598
599         err = nsim_fib_account(&data->ipv6.fib, true, extack);
600         if (err)
601                 return err;
602
603         err = rhashtable_insert_fast(&data->fib_rt_ht,
604                                      &fib6_rt->common.ht_node,
605                                      nsim_fib_rt_ht_params);
606         if (err) {
607                 NL_SET_ERR_MSG_MOD(extack, "Failed to insert IPv6 route");
608                 goto err_fib_dismiss;
609         }
610
611         nsim_fib6_rt_hw_flags_set(fib6_rt, true);
612
613         return 0;
614
615 err_fib_dismiss:
616         nsim_fib_account(&data->ipv6.fib, false, extack);
617         return err;
618 }
619
620 static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
621                                 struct nsim_fib6_rt *fib6_rt,
622                                 struct nsim_fib6_rt *fib6_rt_old,
623                                 struct netlink_ext_ack *extack)
624 {
625         int err;
626
627         /* We are replacing a route, so no need to change the accounting. */
628         err = rhashtable_replace_fast(&data->fib_rt_ht,
629                                       &fib6_rt_old->common.ht_node,
630                                       &fib6_rt->common.ht_node,
631                                       nsim_fib_rt_ht_params);
632         if (err) {
633                 NL_SET_ERR_MSG_MOD(extack, "Failed to replace IPv6 route");
634                 return err;
635         }
636
637         nsim_fib6_rt_hw_flags_set(fib6_rt, true);
638
639         nsim_fib6_rt_hw_flags_set(fib6_rt_old, false);
640         nsim_fib6_rt_destroy(fib6_rt_old);
641
642         return 0;
643 }
644
645 static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
646                                struct fib6_entry_notifier_info *fen6_info)
647 {
648         struct netlink_ext_ack *extack = fen6_info->info.extack;
649         struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
650         int err;
651
652         fib6_rt = nsim_fib6_rt_create(data, fen6_info);
653         if (IS_ERR(fib6_rt))
654                 return PTR_ERR(fib6_rt);
655
656         fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
657         if (!fib6_rt_old)
658                 err = nsim_fib6_rt_add(data, fib6_rt, extack);
659         else
660                 err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old, extack);
661
662         if (err)
663                 nsim_fib6_rt_destroy(fib6_rt);
664
665         return err;
666 }
667
668 static void
669 nsim_fib6_rt_remove(struct nsim_fib_data *data,
670                     const struct fib6_entry_notifier_info *fen6_info)
671 {
672         struct netlink_ext_ack *extack = fen6_info->info.extack;
673         struct nsim_fib6_rt *fib6_rt;
674
675         /* Multipath routes are first added to the FIB trie and only then
676          * notified. If we vetoed the addition, we will get a delete
677          * notification for a route we do not have. Therefore, do not warn if
678          * route was not found.
679          */
680         fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, fen6_info->rt);
681         if (!fib6_rt)
682                 return;
683
684         /* If not all the nexthops are deleted, then only reduce the nexthop
685          * group.
686          */
687         if (fen6_info->nsiblings + 1 != fib6_rt->nhs) {
688                 nsim_fib6_rt_nh_del(fib6_rt, fen6_info->rt);
689                 return;
690         }
691
692         rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
693                                nsim_fib_rt_ht_params);
694         nsim_fib_account(&data->ipv6.fib, false, extack);
695         nsim_fib6_rt_destroy(fib6_rt);
696 }
697
698 static int nsim_fib6_event(struct nsim_fib_data *data,
699                            struct fib_notifier_info *info,
700                            unsigned long event)
701 {
702         struct fib6_entry_notifier_info *fen6_info;
703         int err = 0;
704
705         fen6_info = container_of(info, struct fib6_entry_notifier_info, info);
706
707         if (fen6_info->rt->nh) {
708                 NL_SET_ERR_MSG_MOD(info->extack, "IPv6 route with nexthop objects is not supported");
709                 return 0;
710         }
711
712         if (fen6_info->rt->fib6_src.plen) {
713                 NL_SET_ERR_MSG_MOD(info->extack, "IPv6 source-specific route is not supported");
714                 return 0;
715         }
716
717         switch (event) {
718         case FIB_EVENT_ENTRY_REPLACE:
719                 err = nsim_fib6_rt_insert(data, fen6_info);
720                 break;
721         case FIB_EVENT_ENTRY_APPEND:
722                 err = nsim_fib6_rt_append(data, fen6_info);
723                 break;
724         case FIB_EVENT_ENTRY_DEL:
725                 nsim_fib6_rt_remove(data, fen6_info);
726                 break;
727         default:
728                 break;
729         }
730
731         return err;
732 }
733
734 static int nsim_fib_event(struct nsim_fib_data *data,
735                           struct fib_notifier_info *info, unsigned long event)
736 {
737         int err = 0;
738
739         switch (info->family) {
740         case AF_INET:
741                 err = nsim_fib4_event(data, info, event);
742                 break;
743         case AF_INET6:
744                 err = nsim_fib6_event(data, info, event);
745                 break;
746         }
747
748         return err;
749 }
750
751 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
752                              void *ptr)
753 {
754         struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
755                                                   fib_nb);
756         struct fib_notifier_info *info = ptr;
757         int err = 0;
758
759         /* IPv6 routes can be added via RAs from softIRQ. */
760         spin_lock_bh(&data->fib_lock);
761
762         switch (event) {
763         case FIB_EVENT_RULE_ADD: /* fall through */
764         case FIB_EVENT_RULE_DEL:
765                 err = nsim_fib_rule_event(data, info,
766                                           event == FIB_EVENT_RULE_ADD);
767                 break;
768
769         case FIB_EVENT_ENTRY_REPLACE:  /* fall through */
770         case FIB_EVENT_ENTRY_APPEND:  /* fall through */
771         case FIB_EVENT_ENTRY_DEL:
772                 err = nsim_fib_event(data, info, event);
773                 break;
774         }
775
776         spin_unlock_bh(&data->fib_lock);
777
778         return notifier_from_errno(err);
779 }
780
781 static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt,
782                               struct nsim_fib_data *data)
783 {
784         struct devlink *devlink = data->devlink;
785         struct nsim_fib4_rt *fib4_rt;
786
787         fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common);
788         nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false);
789         nsim_fib_account(&data->ipv4.fib, false, NULL);
790         nsim_fib4_rt_destroy(fib4_rt);
791 }
792
793 static void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt,
794                               struct nsim_fib_data *data)
795 {
796         struct nsim_fib6_rt *fib6_rt;
797
798         fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common);
799         nsim_fib6_rt_hw_flags_set(fib6_rt, false);
800         nsim_fib_account(&data->ipv6.fib, false, NULL);
801         nsim_fib6_rt_destroy(fib6_rt);
802 }
803
804 static void nsim_fib_rt_free(void *ptr, void *arg)
805 {
806         struct nsim_fib_rt *fib_rt = ptr;
807         struct nsim_fib_data *data = arg;
808
809         switch (fib_rt->key.family) {
810         case AF_INET:
811                 nsim_fib4_rt_free(fib_rt, data);
812                 break;
813         case AF_INET6:
814                 nsim_fib6_rt_free(fib_rt, data);
815                 break;
816         default:
817                 WARN_ON_ONCE(1);
818         }
819 }
820
821 /* inconsistent dump, trying again */
822 static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
823 {
824         struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
825                                                   fib_nb);
826         struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
827
828         /* The notifier block is still not registered, so we do not need to
829          * take any locks here.
830          */
831         list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
832                 rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
833                                        nsim_fib_rt_ht_params);
834                 nsim_fib_rt_free(fib_rt, data);
835         }
836
837         data->ipv4.rules.num = 0ULL;
838         data->ipv6.rules.num = 0ULL;
839 }
840
841 static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
842 {
843         struct nsim_fib_data *data = priv;
844
845         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
846 }
847
848 static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
849 {
850         struct nsim_fib_data *data = priv;
851
852         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
853 }
854
855 static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
856 {
857         struct nsim_fib_data *data = priv;
858
859         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
860 }
861
862 static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
863 {
864         struct nsim_fib_data *data = priv;
865
866         return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
867 }
868
869 static void nsim_fib_set_max_all(struct nsim_fib_data *data,
870                                  struct devlink *devlink)
871 {
872         enum nsim_resource_id res_ids[] = {
873                 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
874                 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
875         };
876         int i;
877
878         for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
879                 int err;
880                 u64 val;
881
882                 err = devlink_resource_size_get(devlink, res_ids[i], &val);
883                 if (err)
884                         val = (u64) -1;
885                 nsim_fib_set_max(data, res_ids[i], val);
886         }
887 }
888
889 struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
890                                       struct netlink_ext_ack *extack)
891 {
892         struct nsim_fib_data *data;
893         int err;
894
895         data = kzalloc(sizeof(*data), GFP_KERNEL);
896         if (!data)
897                 return ERR_PTR(-ENOMEM);
898         data->devlink = devlink;
899
900         spin_lock_init(&data->fib_lock);
901         INIT_LIST_HEAD(&data->fib_rt_list);
902         err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
903         if (err)
904                 goto err_data_free;
905
906         nsim_fib_set_max_all(data, devlink);
907
908         data->fib_nb.notifier_call = nsim_fib_event_nb;
909         err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
910                                     nsim_fib_dump_inconsistent, extack);
911         if (err) {
912                 pr_err("Failed to register fib notifier\n");
913                 goto err_rhashtable_destroy;
914         }
915
916         devlink_resource_occ_get_register(devlink,
917                                           NSIM_RESOURCE_IPV4_FIB,
918                                           nsim_fib_ipv4_resource_occ_get,
919                                           data);
920         devlink_resource_occ_get_register(devlink,
921                                           NSIM_RESOURCE_IPV4_FIB_RULES,
922                                           nsim_fib_ipv4_rules_res_occ_get,
923                                           data);
924         devlink_resource_occ_get_register(devlink,
925                                           NSIM_RESOURCE_IPV6_FIB,
926                                           nsim_fib_ipv6_resource_occ_get,
927                                           data);
928         devlink_resource_occ_get_register(devlink,
929                                           NSIM_RESOURCE_IPV6_FIB_RULES,
930                                           nsim_fib_ipv6_rules_res_occ_get,
931                                           data);
932         return data;
933
934 err_rhashtable_destroy:
935         rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
936                                     data);
937 err_data_free:
938         kfree(data);
939         return ERR_PTR(err);
940 }
941
942 void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
943 {
944         devlink_resource_occ_get_unregister(devlink,
945                                             NSIM_RESOURCE_IPV6_FIB_RULES);
946         devlink_resource_occ_get_unregister(devlink,
947                                             NSIM_RESOURCE_IPV6_FIB);
948         devlink_resource_occ_get_unregister(devlink,
949                                             NSIM_RESOURCE_IPV4_FIB_RULES);
950         devlink_resource_occ_get_unregister(devlink,
951                                             NSIM_RESOURCE_IPV4_FIB);
952         unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
953         rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
954                                     data);
955         WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
956         kfree(data);
957 }