xref: /openbmc/linux/drivers/net/netdevsim/fib.c (revision 6197e5b7)
1 /*
2  * Copyright (c) 2018 Cumulus Networks. All rights reserved.
3  * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
4  *
5  * This software is licensed under the GNU General License Version 2,
6  * June 1991 as shown in the file COPYING in the top-level directory of this
7  * source tree.
8  *
9  * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
10  * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
11  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
13  * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
14  * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
15  */
16 
17 #include <linux/in6.h>
18 #include <linux/kernel.h>
19 #include <linux/list.h>
20 #include <linux/rhashtable.h>
21 #include <linux/spinlock_types.h>
22 #include <linux/types.h>
23 #include <net/fib_notifier.h>
24 #include <net/ip_fib.h>
25 #include <net/ip6_fib.h>
26 #include <net/fib_rules.h>
27 #include <net/net_namespace.h>
28 #include <net/nexthop.h>
29 #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 hashtable 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 dentry *ddir;
58 	bool fail_route_offload;
59 };
60 
61 struct nsim_fib_rt_key {
62 	unsigned char addr[sizeof(struct in6_addr)];
63 	unsigned char prefix_len;
64 	int family;
65 	u32 tb_id;
66 };
67 
68 struct nsim_fib_rt {
69 	struct nsim_fib_rt_key key;
70 	struct rhash_head ht_node;
71 	struct list_head list;	/* Member of fib_rt_list */
72 };
73 
74 struct nsim_fib4_rt {
75 	struct nsim_fib_rt common;
76 	struct fib_info *fi;
77 	u8 tos;
78 	u8 type;
79 };
80 
81 struct nsim_fib6_rt {
82 	struct nsim_fib_rt common;
83 	struct list_head nh_list;
84 	unsigned int nhs;
85 };
86 
87 struct nsim_fib6_rt_nh {
88 	struct list_head list;	/* Member of nh_list */
89 	struct fib6_info *rt;
90 };
91 
92 struct nsim_fib6_event {
93 	struct fib6_info **rt_arr;
94 	unsigned int nrt6;
95 };
96 
97 struct nsim_fib_event {
98 	struct list_head list; /* node in fib queue */
99 	union {
100 		struct fib_entry_notifier_info fen_info;
101 		struct nsim_fib6_event fib6_event;
102 	};
103 	struct nsim_fib_data *data;
104 	unsigned long event;
105 	int family;
106 };
107 
108 static const struct rhashtable_params nsim_fib_rt_ht_params = {
109 	.key_offset = offsetof(struct nsim_fib_rt, key),
110 	.head_offset = offsetof(struct nsim_fib_rt, ht_node),
111 	.key_len = sizeof(struct nsim_fib_rt_key),
112 	.automatic_shrinking = true,
113 };
114 
115 struct nsim_nexthop {
116 	struct rhash_head ht_node;
117 	u64 occ;
118 	u32 id;
119 };
120 
121 static const struct rhashtable_params nsim_nexthop_ht_params = {
122 	.key_offset = offsetof(struct nsim_nexthop, id),
123 	.head_offset = offsetof(struct nsim_nexthop, ht_node),
124 	.key_len = sizeof(u32),
125 	.automatic_shrinking = true,
126 };
127 
128 u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
129 		     enum nsim_resource_id res_id, bool max)
130 {
131 	struct nsim_fib_entry *entry;
132 
133 	switch (res_id) {
134 	case NSIM_RESOURCE_IPV4_FIB:
135 		entry = &fib_data->ipv4.fib;
136 		break;
137 	case NSIM_RESOURCE_IPV4_FIB_RULES:
138 		entry = &fib_data->ipv4.rules;
139 		break;
140 	case NSIM_RESOURCE_IPV6_FIB:
141 		entry = &fib_data->ipv6.fib;
142 		break;
143 	case NSIM_RESOURCE_IPV6_FIB_RULES:
144 		entry = &fib_data->ipv6.rules;
145 		break;
146 	case NSIM_RESOURCE_NEXTHOPS:
147 		entry = &fib_data->nexthops;
148 		break;
149 	default:
150 		return 0;
151 	}
152 
153 	return max ? entry->max : atomic64_read(&entry->num);
154 }
155 
156 static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
157 			     enum nsim_resource_id res_id, u64 val)
158 {
159 	struct nsim_fib_entry *entry;
160 
161 	switch (res_id) {
162 	case NSIM_RESOURCE_IPV4_FIB:
163 		entry = &fib_data->ipv4.fib;
164 		break;
165 	case NSIM_RESOURCE_IPV4_FIB_RULES:
166 		entry = &fib_data->ipv4.rules;
167 		break;
168 	case NSIM_RESOURCE_IPV6_FIB:
169 		entry = &fib_data->ipv6.fib;
170 		break;
171 	case NSIM_RESOURCE_IPV6_FIB_RULES:
172 		entry = &fib_data->ipv6.rules;
173 		break;
174 	case NSIM_RESOURCE_NEXTHOPS:
175 		entry = &fib_data->nexthops;
176 		break;
177 	default:
178 		WARN_ON(1);
179 		return;
180 	}
181 	entry->max = val;
182 }
183 
184 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
185 				 struct netlink_ext_ack *extack)
186 {
187 	int err = 0;
188 
189 	if (add) {
190 		if (!atomic64_add_unless(&entry->num, 1, entry->max)) {
191 			err = -ENOSPC;
192 			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
193 		}
194 	} else {
195 		atomic64_dec_if_positive(&entry->num);
196 	}
197 
198 	return err;
199 }
200 
201 static int nsim_fib_rule_event(struct nsim_fib_data *data,
202 			       struct fib_notifier_info *info, bool add)
203 {
204 	struct netlink_ext_ack *extack = info->extack;
205 	int err = 0;
206 
207 	switch (info->family) {
208 	case AF_INET:
209 		err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
210 		break;
211 	case AF_INET6:
212 		err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
213 		break;
214 	}
215 
216 	return err;
217 }
218 
219 static int nsim_fib_account(struct nsim_fib_entry *entry, bool add)
220 {
221 	int err = 0;
222 
223 	if (add) {
224 		if (!atomic64_add_unless(&entry->num, 1, entry->max))
225 			err = -ENOSPC;
226 	} else {
227 		atomic64_dec_if_positive(&entry->num);
228 	}
229 
230 	return err;
231 }
232 
233 static void nsim_fib_rt_init(struct nsim_fib_data *data,
234 			     struct nsim_fib_rt *fib_rt, const void *addr,
235 			     size_t addr_len, unsigned int prefix_len,
236 			     int family, u32 tb_id)
237 {
238 	memcpy(fib_rt->key.addr, addr, addr_len);
239 	fib_rt->key.prefix_len = prefix_len;
240 	fib_rt->key.family = family;
241 	fib_rt->key.tb_id = tb_id;
242 	list_add(&fib_rt->list, &data->fib_rt_list);
243 }
244 
245 static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
246 {
247 	list_del(&fib_rt->list);
248 }
249 
250 static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht,
251 					      const void *addr, size_t addr_len,
252 					      unsigned int prefix_len,
253 					      int family, u32 tb_id)
254 {
255 	struct nsim_fib_rt_key key;
256 
257 	memset(&key, 0, sizeof(key));
258 	memcpy(key.addr, addr, addr_len);
259 	key.prefix_len = prefix_len;
260 	key.family = family;
261 	key.tb_id = tb_id;
262 
263 	return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params);
264 }
265 
266 static struct nsim_fib4_rt *
267 nsim_fib4_rt_create(struct nsim_fib_data *data,
268 		    struct fib_entry_notifier_info *fen_info)
269 {
270 	struct nsim_fib4_rt *fib4_rt;
271 
272 	fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_KERNEL);
273 	if (!fib4_rt)
274 		return NULL;
275 
276 	nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32),
277 			 fen_info->dst_len, AF_INET, fen_info->tb_id);
278 
279 	fib4_rt->fi = fen_info->fi;
280 	fib_info_hold(fib4_rt->fi);
281 	fib4_rt->tos = fen_info->tos;
282 	fib4_rt->type = fen_info->type;
283 
284 	return fib4_rt;
285 }
286 
287 static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt)
288 {
289 	fib_info_put(fib4_rt->fi);
290 	nsim_fib_rt_fini(&fib4_rt->common);
291 	kfree(fib4_rt);
292 }
293 
294 static struct nsim_fib4_rt *
295 nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
296 		    const struct fib_entry_notifier_info *fen_info)
297 {
298 	struct nsim_fib_rt *fib_rt;
299 
300 	fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32),
301 				    fen_info->dst_len, AF_INET,
302 				    fen_info->tb_id);
303 	if (!fib_rt)
304 		return NULL;
305 
306 	return container_of(fib_rt, struct nsim_fib4_rt, common);
307 }
308 
309 static void
310 nsim_fib4_rt_offload_failed_flag_set(struct net *net,
311 				     struct fib_entry_notifier_info *fen_info)
312 {
313 	u32 *p_dst = (u32 *)&fen_info->dst;
314 	struct fib_rt_info fri;
315 
316 	fri.fi = fen_info->fi;
317 	fri.tb_id = fen_info->tb_id;
318 	fri.dst = cpu_to_be32(*p_dst);
319 	fri.dst_len = fen_info->dst_len;
320 	fri.tos = fen_info->tos;
321 	fri.type = fen_info->type;
322 	fri.offload = false;
323 	fri.trap = false;
324 	fri.offload_failed = true;
325 	fib_alias_hw_flags_set(net, &fri);
326 }
327 
328 static void nsim_fib4_rt_hw_flags_set(struct net *net,
329 				      const struct nsim_fib4_rt *fib4_rt,
330 				      bool trap)
331 {
332 	u32 *p_dst = (u32 *) fib4_rt->common.key.addr;
333 	int dst_len = fib4_rt->common.key.prefix_len;
334 	struct fib_rt_info fri;
335 
336 	fri.fi = fib4_rt->fi;
337 	fri.tb_id = fib4_rt->common.key.tb_id;
338 	fri.dst = cpu_to_be32(*p_dst);
339 	fri.dst_len = dst_len;
340 	fri.tos = fib4_rt->tos;
341 	fri.type = fib4_rt->type;
342 	fri.offload = false;
343 	fri.trap = trap;
344 	fri.offload_failed = false;
345 	fib_alias_hw_flags_set(net, &fri);
346 }
347 
348 static int nsim_fib4_rt_add(struct nsim_fib_data *data,
349 			    struct nsim_fib4_rt *fib4_rt)
350 {
351 	struct net *net = devlink_net(data->devlink);
352 	int err;
353 
354 	err = rhashtable_insert_fast(&data->fib_rt_ht,
355 				     &fib4_rt->common.ht_node,
356 				     nsim_fib_rt_ht_params);
357 	if (err)
358 		goto err_fib_dismiss;
359 
360 	/* Simulate hardware programming latency. */
361 	msleep(1);
362 	nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
363 
364 	return 0;
365 
366 err_fib_dismiss:
367 	/* Drop the accounting that was increased from the notification
368 	 * context when FIB_EVENT_ENTRY_REPLACE was triggered.
369 	 */
370 	nsim_fib_account(&data->ipv4.fib, false);
371 	return err;
372 }
373 
374 static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
375 				struct nsim_fib4_rt *fib4_rt,
376 				struct nsim_fib4_rt *fib4_rt_old)
377 {
378 	struct net *net = devlink_net(data->devlink);
379 	int err;
380 
381 	/* We are replacing a route, so need to remove the accounting which
382 	 * was increased when FIB_EVENT_ENTRY_REPLACE was triggered.
383 	 */
384 	err = nsim_fib_account(&data->ipv4.fib, false);
385 	if (err)
386 		return err;
387 	err = rhashtable_replace_fast(&data->fib_rt_ht,
388 				      &fib4_rt_old->common.ht_node,
389 				      &fib4_rt->common.ht_node,
390 				      nsim_fib_rt_ht_params);
391 	if (err)
392 		return err;
393 
394 	msleep(1);
395 	nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
396 
397 	nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
398 	nsim_fib4_rt_destroy(fib4_rt_old);
399 
400 	return 0;
401 }
402 
403 static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
404 			       struct fib_entry_notifier_info *fen_info)
405 {
406 	struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
407 	int err;
408 
409 	if (data->fail_route_offload) {
410 		/* For testing purposes, user set debugfs fail_route_offload
411 		 * value to true. Simulate hardware programming latency and then
412 		 * fail.
413 		 */
414 		msleep(1);
415 		return -EINVAL;
416 	}
417 
418 	fib4_rt = nsim_fib4_rt_create(data, fen_info);
419 	if (!fib4_rt)
420 		return -ENOMEM;
421 
422 	fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
423 	if (!fib4_rt_old)
424 		err = nsim_fib4_rt_add(data, fib4_rt);
425 	else
426 		err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old);
427 
428 	if (err)
429 		nsim_fib4_rt_destroy(fib4_rt);
430 
431 	return err;
432 }
433 
434 static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
435 				const struct fib_entry_notifier_info *fen_info)
436 {
437 	struct nsim_fib4_rt *fib4_rt;
438 
439 	fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
440 	if (!fib4_rt)
441 		return;
442 
443 	rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
444 			       nsim_fib_rt_ht_params);
445 	nsim_fib4_rt_destroy(fib4_rt);
446 }
447 
448 static int nsim_fib4_event(struct nsim_fib_data *data,
449 			   struct fib_entry_notifier_info *fen_info,
450 			   unsigned long event)
451 {
452 	int err = 0;
453 
454 	switch (event) {
455 	case FIB_EVENT_ENTRY_REPLACE:
456 		err = nsim_fib4_rt_insert(data, fen_info);
457 		if (err) {
458 			struct net *net = devlink_net(data->devlink);
459 
460 			nsim_fib4_rt_offload_failed_flag_set(net, fen_info);
461 		}
462 		break;
463 	case FIB_EVENT_ENTRY_DEL:
464 		nsim_fib4_rt_remove(data, fen_info);
465 		break;
466 	default:
467 		break;
468 	}
469 
470 	return err;
471 }
472 
473 static struct nsim_fib6_rt_nh *
474 nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt,
475 		     const struct fib6_info *rt)
476 {
477 	struct nsim_fib6_rt_nh *fib6_rt_nh;
478 
479 	list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) {
480 		if (fib6_rt_nh->rt == rt)
481 			return fib6_rt_nh;
482 	}
483 
484 	return NULL;
485 }
486 
487 static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt,
488 			       struct fib6_info *rt)
489 {
490 	struct nsim_fib6_rt_nh *fib6_rt_nh;
491 
492 	fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_KERNEL);
493 	if (!fib6_rt_nh)
494 		return -ENOMEM;
495 
496 	fib6_info_hold(rt);
497 	fib6_rt_nh->rt = rt;
498 	list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
499 	fib6_rt->nhs++;
500 
501 	return 0;
502 }
503 
504 #if IS_ENABLED(CONFIG_IPV6)
505 static void nsim_rt6_release(struct fib6_info *rt)
506 {
507 	fib6_info_release(rt);
508 }
509 #else
510 static void nsim_rt6_release(struct fib6_info *rt)
511 {
512 }
513 #endif
514 
515 static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
516 				const struct fib6_info *rt)
517 {
518 	struct nsim_fib6_rt_nh *fib6_rt_nh;
519 
520 	fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
521 	if (!fib6_rt_nh)
522 		return;
523 
524 	fib6_rt->nhs--;
525 	list_del(&fib6_rt_nh->list);
526 	nsim_rt6_release(fib6_rt_nh->rt);
527 	kfree(fib6_rt_nh);
528 }
529 
530 static struct nsim_fib6_rt *
531 nsim_fib6_rt_create(struct nsim_fib_data *data,
532 		    struct fib6_info **rt_arr, unsigned int nrt6)
533 {
534 	struct fib6_info *rt = rt_arr[0];
535 	struct nsim_fib6_rt *fib6_rt;
536 	int i = 0;
537 	int err;
538 
539 	fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_KERNEL);
540 	if (!fib6_rt)
541 		return ERR_PTR(-ENOMEM);
542 
543 	nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr,
544 			 sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6,
545 			 rt->fib6_table->tb6_id);
546 
547 	/* We consider a multipath IPv6 route as one entry, but it can be made
548 	 * up from several fib6_info structs (one for each nexthop), so we
549 	 * add them all to the same list under the entry.
550 	 */
551 	INIT_LIST_HEAD(&fib6_rt->nh_list);
552 
553 	for (i = 0; i < nrt6; i++) {
554 		err = nsim_fib6_rt_nh_add(fib6_rt, rt_arr[i]);
555 		if (err)
556 			goto err_fib6_rt_nh_del;
557 	}
558 
559 	return fib6_rt;
560 
561 err_fib6_rt_nh_del:
562 	for (i--; i >= 0; i--) {
563 		nsim_fib6_rt_nh_del(fib6_rt, rt_arr[i]);
564 	};
565 	nsim_fib_rt_fini(&fib6_rt->common);
566 	kfree(fib6_rt);
567 	return ERR_PTR(err);
568 }
569 
570 static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt)
571 {
572 	struct nsim_fib6_rt_nh *iter, *tmp;
573 
574 	list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list)
575 		nsim_fib6_rt_nh_del(fib6_rt, iter->rt);
576 	WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list));
577 	nsim_fib_rt_fini(&fib6_rt->common);
578 	kfree(fib6_rt);
579 }
580 
581 static struct nsim_fib6_rt *
582 nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt)
583 {
584 	struct nsim_fib_rt *fib_rt;
585 
586 	fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr,
587 				    sizeof(rt->fib6_dst.addr),
588 				    rt->fib6_dst.plen, AF_INET6,
589 				    rt->fib6_table->tb6_id);
590 	if (!fib_rt)
591 		return NULL;
592 
593 	return container_of(fib_rt, struct nsim_fib6_rt, common);
594 }
595 
596 static int nsim_fib6_rt_append(struct nsim_fib_data *data,
597 			       struct nsim_fib6_event *fib6_event)
598 {
599 	struct fib6_info *rt = fib6_event->rt_arr[0];
600 	struct nsim_fib6_rt *fib6_rt;
601 	int i, err;
602 
603 	if (data->fail_route_offload) {
604 		/* For testing purposes, user set debugfs fail_route_offload
605 		 * value to true. Simulate hardware programming latency and then
606 		 * fail.
607 		 */
608 		msleep(1);
609 		return -EINVAL;
610 	}
611 
612 	fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
613 	if (!fib6_rt)
614 		return -EINVAL;
615 
616 	for (i = 0; i < fib6_event->nrt6; i++) {
617 		err = nsim_fib6_rt_nh_add(fib6_rt, fib6_event->rt_arr[i]);
618 		if (err)
619 			goto err_fib6_rt_nh_del;
620 
621 		fib6_event->rt_arr[i]->trap = true;
622 	}
623 
624 	return 0;
625 
626 err_fib6_rt_nh_del:
627 	for (i--; i >= 0; i--) {
628 		fib6_event->rt_arr[i]->trap = false;
629 		nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
630 	}
631 	return err;
632 }
633 
634 #if IS_ENABLED(CONFIG_IPV6)
635 static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
636 						 struct fib6_info **rt_arr,
637 						 unsigned int nrt6)
638 
639 {
640 	struct net *net = devlink_net(data->devlink);
641 	int i;
642 
643 	for (i = 0; i < nrt6; i++)
644 		fib6_info_hw_flags_set(net, rt_arr[i], false, false, true);
645 }
646 #else
647 static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
648 						 struct fib6_info **rt_arr,
649 						 unsigned int nrt6)
650 {
651 }
652 #endif
653 
654 #if IS_ENABLED(CONFIG_IPV6)
655 static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
656 				      const struct nsim_fib6_rt *fib6_rt,
657 				      bool trap)
658 {
659 	struct net *net = devlink_net(data->devlink);
660 	struct nsim_fib6_rt_nh *fib6_rt_nh;
661 
662 	list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
663 		fib6_info_hw_flags_set(net, fib6_rt_nh->rt, false, trap, false);
664 }
665 #else
666 static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
667 				      const struct nsim_fib6_rt *fib6_rt,
668 				      bool trap)
669 {
670 }
671 #endif
672 
673 static int nsim_fib6_rt_add(struct nsim_fib_data *data,
674 			    struct nsim_fib6_rt *fib6_rt)
675 {
676 	int err;
677 
678 	err = rhashtable_insert_fast(&data->fib_rt_ht,
679 				     &fib6_rt->common.ht_node,
680 				     nsim_fib_rt_ht_params);
681 
682 	if (err)
683 		goto err_fib_dismiss;
684 
685 	msleep(1);
686 	nsim_fib6_rt_hw_flags_set(data, fib6_rt, true);
687 
688 	return 0;
689 
690 err_fib_dismiss:
691 	/* Drop the accounting that was increased from the notification
692 	 * context when FIB_EVENT_ENTRY_REPLACE was triggered.
693 	 */
694 	nsim_fib_account(&data->ipv6.fib, false);
695 	return err;
696 }
697 
698 static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
699 				struct nsim_fib6_rt *fib6_rt,
700 				struct nsim_fib6_rt *fib6_rt_old)
701 {
702 	int err;
703 
704 	/* We are replacing a route, so need to remove the accounting which
705 	 * was increased when FIB_EVENT_ENTRY_REPLACE was triggered.
706 	 */
707 	err = nsim_fib_account(&data->ipv6.fib, false);
708 	if (err)
709 		return err;
710 
711 	err = rhashtable_replace_fast(&data->fib_rt_ht,
712 				      &fib6_rt_old->common.ht_node,
713 				      &fib6_rt->common.ht_node,
714 				      nsim_fib_rt_ht_params);
715 
716 	if (err)
717 		return err;
718 
719 	msleep(1);
720 	nsim_fib6_rt_hw_flags_set(data, fib6_rt, true);
721 
722 	nsim_fib6_rt_hw_flags_set(data, fib6_rt_old, false);
723 	nsim_fib6_rt_destroy(fib6_rt_old);
724 
725 	return 0;
726 }
727 
728 static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
729 			       struct nsim_fib6_event *fib6_event)
730 {
731 	struct fib6_info *rt = fib6_event->rt_arr[0];
732 	struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
733 	int err;
734 
735 	if (data->fail_route_offload) {
736 		/* For testing purposes, user set debugfs fail_route_offload
737 		 * value to true. Simulate hardware programming latency and then
738 		 * fail.
739 		 */
740 		msleep(1);
741 		return -EINVAL;
742 	}
743 
744 	fib6_rt = nsim_fib6_rt_create(data, fib6_event->rt_arr,
745 				      fib6_event->nrt6);
746 	if (IS_ERR(fib6_rt))
747 		return PTR_ERR(fib6_rt);
748 
749 	fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
750 	if (!fib6_rt_old)
751 		err = nsim_fib6_rt_add(data, fib6_rt);
752 	else
753 		err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old);
754 
755 	if (err)
756 		nsim_fib6_rt_destroy(fib6_rt);
757 
758 	return err;
759 }
760 
761 static void nsim_fib6_rt_remove(struct nsim_fib_data *data,
762 				struct nsim_fib6_event *fib6_event)
763 {
764 	struct fib6_info *rt = fib6_event->rt_arr[0];
765 	struct nsim_fib6_rt *fib6_rt;
766 	int i;
767 
768 	/* Multipath routes are first added to the FIB trie and only then
769 	 * notified. If we vetoed the addition, we will get a delete
770 	 * notification for a route we do not have. Therefore, do not warn if
771 	 * route was not found.
772 	 */
773 	fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
774 	if (!fib6_rt)
775 		return;
776 
777 	/* If not all the nexthops are deleted, then only reduce the nexthop
778 	 * group.
779 	 */
780 	if (fib6_event->nrt6 != fib6_rt->nhs) {
781 		for (i = 0; i < fib6_event->nrt6; i++)
782 			nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
783 		return;
784 	}
785 
786 	rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
787 			       nsim_fib_rt_ht_params);
788 	nsim_fib6_rt_destroy(fib6_rt);
789 }
790 
791 static int nsim_fib6_event_init(struct nsim_fib6_event *fib6_event,
792 				struct fib6_entry_notifier_info *fen6_info)
793 {
794 	struct fib6_info *rt = fen6_info->rt;
795 	struct fib6_info **rt_arr;
796 	struct fib6_info *iter;
797 	unsigned int nrt6;
798 	int i = 0;
799 
800 	nrt6 = fen6_info->nsiblings + 1;
801 
802 	rt_arr = kcalloc(nrt6, sizeof(struct fib6_info *), GFP_ATOMIC);
803 	if (!rt_arr)
804 		return -ENOMEM;
805 
806 	fib6_event->rt_arr = rt_arr;
807 	fib6_event->nrt6 = nrt6;
808 
809 	rt_arr[0] = rt;
810 	fib6_info_hold(rt);
811 
812 	if (!fen6_info->nsiblings)
813 		return 0;
814 
815 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
816 		if (i == fen6_info->nsiblings)
817 			break;
818 
819 		rt_arr[i + 1] = iter;
820 		fib6_info_hold(iter);
821 		i++;
822 	}
823 	WARN_ON_ONCE(i != fen6_info->nsiblings);
824 
825 	return 0;
826 }
827 
828 static void nsim_fib6_event_fini(struct nsim_fib6_event *fib6_event)
829 {
830 	int i;
831 
832 	for (i = 0; i < fib6_event->nrt6; i++)
833 		nsim_rt6_release(fib6_event->rt_arr[i]);
834 	kfree(fib6_event->rt_arr);
835 }
836 
837 static int nsim_fib6_event(struct nsim_fib_data *data,
838 			   struct nsim_fib6_event *fib6_event,
839 			   unsigned long event)
840 {
841 	int err;
842 
843 	if (fib6_event->rt_arr[0]->fib6_src.plen)
844 		return 0;
845 
846 	switch (event) {
847 	case FIB_EVENT_ENTRY_REPLACE:
848 		err = nsim_fib6_rt_insert(data, fib6_event);
849 		if (err)
850 			goto err_rt_offload_failed_flag_set;
851 		break;
852 	case FIB_EVENT_ENTRY_APPEND:
853 		err = nsim_fib6_rt_append(data, fib6_event);
854 		if (err)
855 			goto err_rt_offload_failed_flag_set;
856 		break;
857 	case FIB_EVENT_ENTRY_DEL:
858 		nsim_fib6_rt_remove(data, fib6_event);
859 		break;
860 	default:
861 		break;
862 	}
863 
864 	return 0;
865 
866 err_rt_offload_failed_flag_set:
867 	nsim_fib6_rt_offload_failed_flag_set(data, fib6_event->rt_arr,
868 					     fib6_event->nrt6);
869 	return err;
870 }
871 
872 static int nsim_fib_event(struct nsim_fib_event *fib_event)
873 {
874 	int err = 0;
875 
876 	switch (fib_event->family) {
877 	case AF_INET:
878 		nsim_fib4_event(fib_event->data, &fib_event->fen_info,
879 				fib_event->event);
880 		fib_info_put(fib_event->fen_info.fi);
881 		break;
882 	case AF_INET6:
883 		nsim_fib6_event(fib_event->data, &fib_event->fib6_event,
884 				fib_event->event);
885 		nsim_fib6_event_fini(&fib_event->fib6_event);
886 		break;
887 	}
888 
889 	return err;
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 	default:
1122 		NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type");
1123 		kfree(nexthop);
1124 		return ERR_PTR(-EOPNOTSUPP);
1125 	}
1126 
1127 	nexthop->occ = occ;
1128 	return nexthop;
1129 }
1130 
1131 static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop)
1132 {
1133 	kfree(nexthop);
1134 }
1135 
1136 static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ,
1137 				bool add, struct netlink_ext_ack *extack)
1138 {
1139 	int i, err = 0;
1140 
1141 	if (add) {
1142 		for (i = 0; i < occ; i++)
1143 			if (!atomic64_add_unless(&data->nexthops.num, 1,
1144 						 data->nexthops.max)) {
1145 				err = -ENOSPC;
1146 				NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops");
1147 				goto err_num_decrease;
1148 			}
1149 	} else {
1150 		if (WARN_ON(occ > atomic64_read(&data->nexthops.num)))
1151 			return -EINVAL;
1152 		atomic64_sub(occ, &data->nexthops.num);
1153 	}
1154 
1155 	return err;
1156 
1157 err_num_decrease:
1158 	atomic64_sub(i, &data->nexthops.num);
1159 	return err;
1160 
1161 }
1162 
1163 static int nsim_nexthop_add(struct nsim_fib_data *data,
1164 			    struct nsim_nexthop *nexthop,
1165 			    struct netlink_ext_ack *extack)
1166 {
1167 	struct net *net = devlink_net(data->devlink);
1168 	int err;
1169 
1170 	err = nsim_nexthop_account(data, nexthop->occ, true, extack);
1171 	if (err)
1172 		return err;
1173 
1174 	err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node,
1175 				     nsim_nexthop_ht_params);
1176 	if (err) {
1177 		NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop");
1178 		goto err_nexthop_dismiss;
1179 	}
1180 
1181 	nexthop_set_hw_flags(net, nexthop->id, false, true);
1182 
1183 	return 0;
1184 
1185 err_nexthop_dismiss:
1186 	nsim_nexthop_account(data, nexthop->occ, false, extack);
1187 	return err;
1188 }
1189 
1190 static int nsim_nexthop_replace(struct nsim_fib_data *data,
1191 				struct nsim_nexthop *nexthop,
1192 				struct nsim_nexthop *nexthop_old,
1193 				struct netlink_ext_ack *extack)
1194 {
1195 	struct net *net = devlink_net(data->devlink);
1196 	int err;
1197 
1198 	err = nsim_nexthop_account(data, nexthop->occ, true, extack);
1199 	if (err)
1200 		return err;
1201 
1202 	err = rhashtable_replace_fast(&data->nexthop_ht,
1203 				      &nexthop_old->ht_node, &nexthop->ht_node,
1204 				      nsim_nexthop_ht_params);
1205 	if (err) {
1206 		NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop");
1207 		goto err_nexthop_dismiss;
1208 	}
1209 
1210 	nexthop_set_hw_flags(net, nexthop->id, false, true);
1211 	nsim_nexthop_account(data, nexthop_old->occ, false, extack);
1212 	nsim_nexthop_destroy(nexthop_old);
1213 
1214 	return 0;
1215 
1216 err_nexthop_dismiss:
1217 	nsim_nexthop_account(data, nexthop->occ, false, extack);
1218 	return err;
1219 }
1220 
1221 static int nsim_nexthop_insert(struct nsim_fib_data *data,
1222 			       struct nh_notifier_info *info)
1223 {
1224 	struct nsim_nexthop *nexthop, *nexthop_old;
1225 	int err;
1226 
1227 	nexthop = nsim_nexthop_create(data, info);
1228 	if (IS_ERR(nexthop))
1229 		return PTR_ERR(nexthop);
1230 
1231 	nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
1232 					     nsim_nexthop_ht_params);
1233 	if (!nexthop_old)
1234 		err = nsim_nexthop_add(data, nexthop, info->extack);
1235 	else
1236 		err = nsim_nexthop_replace(data, nexthop, nexthop_old,
1237 					   info->extack);
1238 
1239 	if (err)
1240 		nsim_nexthop_destroy(nexthop);
1241 
1242 	return err;
1243 }
1244 
1245 static void nsim_nexthop_remove(struct nsim_fib_data *data,
1246 				struct nh_notifier_info *info)
1247 {
1248 	struct nsim_nexthop *nexthop;
1249 
1250 	nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
1251 					 nsim_nexthop_ht_params);
1252 	if (!nexthop)
1253 		return;
1254 
1255 	rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node,
1256 			       nsim_nexthop_ht_params);
1257 	nsim_nexthop_account(data, nexthop->occ, false, info->extack);
1258 	nsim_nexthop_destroy(nexthop);
1259 }
1260 
1261 static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event,
1262 				 void *ptr)
1263 {
1264 	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1265 						  nexthop_nb);
1266 	struct nh_notifier_info *info = ptr;
1267 	int err = 0;
1268 
1269 	ASSERT_RTNL();
1270 
1271 	switch (event) {
1272 	case NEXTHOP_EVENT_REPLACE:
1273 		err = nsim_nexthop_insert(data, info);
1274 		break;
1275 	case NEXTHOP_EVENT_DEL:
1276 		nsim_nexthop_remove(data, info);
1277 		break;
1278 	default:
1279 		break;
1280 	}
1281 
1282 	return notifier_from_errno(err);
1283 }
1284 
1285 static void nsim_nexthop_free(void *ptr, void *arg)
1286 {
1287 	struct nsim_nexthop *nexthop = ptr;
1288 	struct nsim_fib_data *data = arg;
1289 	struct net *net;
1290 
1291 	net = devlink_net(data->devlink);
1292 	nexthop_set_hw_flags(net, nexthop->id, false, false);
1293 	nsim_nexthop_account(data, nexthop->occ, false, NULL);
1294 	nsim_nexthop_destroy(nexthop);
1295 }
1296 
1297 static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
1298 {
1299 	struct nsim_fib_data *data = priv;
1300 
1301 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
1302 }
1303 
1304 static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
1305 {
1306 	struct nsim_fib_data *data = priv;
1307 
1308 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
1309 }
1310 
1311 static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
1312 {
1313 	struct nsim_fib_data *data = priv;
1314 
1315 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
1316 }
1317 
1318 static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
1319 {
1320 	struct nsim_fib_data *data = priv;
1321 
1322 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
1323 }
1324 
1325 static u64 nsim_fib_nexthops_res_occ_get(void *priv)
1326 {
1327 	struct nsim_fib_data *data = priv;
1328 
1329 	return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false);
1330 }
1331 
1332 static void nsim_fib_set_max_all(struct nsim_fib_data *data,
1333 				 struct devlink *devlink)
1334 {
1335 	enum nsim_resource_id res_ids[] = {
1336 		NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
1337 		NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES,
1338 		NSIM_RESOURCE_NEXTHOPS,
1339 	};
1340 	int i;
1341 
1342 	for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
1343 		int err;
1344 		u64 val;
1345 
1346 		err = devlink_resource_size_get(devlink, res_ids[i], &val);
1347 		if (err)
1348 			val = (u64) -1;
1349 		nsim_fib_set_max(data, res_ids[i], val);
1350 	}
1351 }
1352 
1353 static void nsim_fib_event_work(struct work_struct *work)
1354 {
1355 	struct nsim_fib_data *data = container_of(work, struct nsim_fib_data,
1356 						  fib_event_work);
1357 	struct nsim_fib_event *fib_event, *next_fib_event;
1358 
1359 	LIST_HEAD(fib_event_queue);
1360 
1361 	spin_lock_bh(&data->fib_event_queue_lock);
1362 	list_splice_init(&data->fib_event_queue, &fib_event_queue);
1363 	spin_unlock_bh(&data->fib_event_queue_lock);
1364 
1365 	mutex_lock(&data->fib_lock);
1366 	list_for_each_entry_safe(fib_event, next_fib_event, &fib_event_queue,
1367 				 list) {
1368 		nsim_fib_event(fib_event);
1369 		list_del(&fib_event->list);
1370 		kfree(fib_event);
1371 		cond_resched();
1372 	}
1373 	mutex_unlock(&data->fib_lock);
1374 }
1375 
1376 static int
1377 nsim_fib_debugfs_init(struct nsim_fib_data *data, struct nsim_dev *nsim_dev)
1378 {
1379 	data->ddir = debugfs_create_dir("fib", nsim_dev->ddir);
1380 	if (IS_ERR(data->ddir))
1381 		return PTR_ERR(data->ddir);
1382 
1383 	data->fail_route_offload = false;
1384 	debugfs_create_bool("fail_route_offload", 0600, data->ddir,
1385 			    &data->fail_route_offload);
1386 	return 0;
1387 }
1388 
1389 static void nsim_fib_debugfs_exit(struct nsim_fib_data *data)
1390 {
1391 	debugfs_remove_recursive(data->ddir);
1392 }
1393 
1394 struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
1395 				      struct netlink_ext_ack *extack)
1396 {
1397 	struct nsim_fib_data *data;
1398 	struct nsim_dev *nsim_dev;
1399 	int err;
1400 
1401 	data = kzalloc(sizeof(*data), GFP_KERNEL);
1402 	if (!data)
1403 		return ERR_PTR(-ENOMEM);
1404 	data->devlink = devlink;
1405 
1406 	nsim_dev = devlink_priv(devlink);
1407 	err = nsim_fib_debugfs_init(data, nsim_dev);
1408 	if (err)
1409 		goto err_data_free;
1410 
1411 	err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
1412 	if (err)
1413 		goto err_debugfs_exit;
1414 
1415 	mutex_init(&data->fib_lock);
1416 	INIT_LIST_HEAD(&data->fib_rt_list);
1417 	err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
1418 	if (err)
1419 		goto err_rhashtable_nexthop_destroy;
1420 
1421 	INIT_WORK(&data->fib_event_work, nsim_fib_event_work);
1422 	INIT_LIST_HEAD(&data->fib_event_queue);
1423 	spin_lock_init(&data->fib_event_queue_lock);
1424 
1425 	nsim_fib_set_max_all(data, devlink);
1426 
1427 	data->nexthop_nb.notifier_call = nsim_nexthop_event_nb;
1428 	err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb,
1429 					extack);
1430 	if (err) {
1431 		pr_err("Failed to register nexthop notifier\n");
1432 		goto err_rhashtable_fib_destroy;
1433 	}
1434 
1435 	data->fib_nb.notifier_call = nsim_fib_event_nb;
1436 	err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
1437 				    nsim_fib_dump_inconsistent, extack);
1438 	if (err) {
1439 		pr_err("Failed to register fib notifier\n");
1440 		goto err_nexthop_nb_unregister;
1441 	}
1442 
1443 	devlink_resource_occ_get_register(devlink,
1444 					  NSIM_RESOURCE_IPV4_FIB,
1445 					  nsim_fib_ipv4_resource_occ_get,
1446 					  data);
1447 	devlink_resource_occ_get_register(devlink,
1448 					  NSIM_RESOURCE_IPV4_FIB_RULES,
1449 					  nsim_fib_ipv4_rules_res_occ_get,
1450 					  data);
1451 	devlink_resource_occ_get_register(devlink,
1452 					  NSIM_RESOURCE_IPV6_FIB,
1453 					  nsim_fib_ipv6_resource_occ_get,
1454 					  data);
1455 	devlink_resource_occ_get_register(devlink,
1456 					  NSIM_RESOURCE_IPV6_FIB_RULES,
1457 					  nsim_fib_ipv6_rules_res_occ_get,
1458 					  data);
1459 	devlink_resource_occ_get_register(devlink,
1460 					  NSIM_RESOURCE_NEXTHOPS,
1461 					  nsim_fib_nexthops_res_occ_get,
1462 					  data);
1463 	return data;
1464 
1465 err_nexthop_nb_unregister:
1466 	unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1467 err_rhashtable_fib_destroy:
1468 	flush_work(&data->fib_event_work);
1469 	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1470 				    data);
1471 err_rhashtable_nexthop_destroy:
1472 	rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1473 				    data);
1474 	mutex_destroy(&data->fib_lock);
1475 err_debugfs_exit:
1476 	nsim_fib_debugfs_exit(data);
1477 err_data_free:
1478 	kfree(data);
1479 	return ERR_PTR(err);
1480 }
1481 
1482 void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
1483 {
1484 	devlink_resource_occ_get_unregister(devlink,
1485 					    NSIM_RESOURCE_NEXTHOPS);
1486 	devlink_resource_occ_get_unregister(devlink,
1487 					    NSIM_RESOURCE_IPV6_FIB_RULES);
1488 	devlink_resource_occ_get_unregister(devlink,
1489 					    NSIM_RESOURCE_IPV6_FIB);
1490 	devlink_resource_occ_get_unregister(devlink,
1491 					    NSIM_RESOURCE_IPV4_FIB_RULES);
1492 	devlink_resource_occ_get_unregister(devlink,
1493 					    NSIM_RESOURCE_IPV4_FIB);
1494 	unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
1495 	unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1496 	flush_work(&data->fib_event_work);
1497 	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1498 				    data);
1499 	rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1500 				    data);
1501 	WARN_ON_ONCE(!list_empty(&data->fib_event_queue));
1502 	WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
1503 	mutex_destroy(&data->fib_lock);
1504 	nsim_fib_debugfs_exit(data);
1505 	kfree(data);
1506 }
1507