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