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