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