xref: /linux-6.15/drivers/net/netdevsim/fib.c (revision 4ce1f56a)
137923ed6SDavid Ahern /*
237923ed6SDavid Ahern  * Copyright (c) 2018 Cumulus Networks. All rights reserved.
337923ed6SDavid Ahern  * Copyright (c) 2018 David Ahern <[email protected]>
437923ed6SDavid Ahern  *
537923ed6SDavid Ahern  * This software is licensed under the GNU General License Version 2,
637923ed6SDavid Ahern  * June 1991 as shown in the file COPYING in the top-level directory of this
737923ed6SDavid Ahern  * source tree.
837923ed6SDavid Ahern  *
937923ed6SDavid Ahern  * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
1037923ed6SDavid Ahern  * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
1137923ed6SDavid Ahern  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
1237923ed6SDavid Ahern  * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
1337923ed6SDavid Ahern  * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
1437923ed6SDavid Ahern  * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
1537923ed6SDavid Ahern  */
1637923ed6SDavid Ahern 
17c6385c0bSIdo Schimmel #include <linux/bitmap.h>
1848bb9eb4SIdo Schimmel #include <linux/in6.h>
1948bb9eb4SIdo Schimmel #include <linux/kernel.h>
2048bb9eb4SIdo Schimmel #include <linux/list.h>
2148bb9eb4SIdo Schimmel #include <linux/rhashtable.h>
2248bb9eb4SIdo Schimmel #include <linux/spinlock_types.h>
2348bb9eb4SIdo Schimmel #include <linux/types.h>
2437923ed6SDavid Ahern #include <net/fib_notifier.h>
25888ade8fSGuillaume Nault #include <net/inet_dscp.h>
2637923ed6SDavid Ahern #include <net/ip_fib.h>
2737923ed6SDavid Ahern #include <net/ip6_fib.h>
2837923ed6SDavid Ahern #include <net/fib_rules.h>
29a5facc4cSJiri Pirko #include <net/net_namespace.h>
308fa84742SIdo Schimmel #include <net/nexthop.h>
31134c7532SAmit Cohen #include <linux/debugfs.h>
3237923ed6SDavid Ahern 
3337923ed6SDavid Ahern #include "netdevsim.h"
3437923ed6SDavid Ahern 
3537923ed6SDavid Ahern struct nsim_fib_entry {
3637923ed6SDavid Ahern 	u64 max;
379e635a21SAmit Cohen 	atomic64_t num;
3837923ed6SDavid Ahern };
3937923ed6SDavid Ahern 
4037923ed6SDavid Ahern struct nsim_per_fib_data {
4137923ed6SDavid Ahern 	struct nsim_fib_entry fib;
4237923ed6SDavid Ahern 	struct nsim_fib_entry rules;
4337923ed6SDavid Ahern };
4437923ed6SDavid Ahern 
4537923ed6SDavid Ahern struct nsim_fib_data {
46a5facc4cSJiri Pirko 	struct notifier_block fib_nb;
4737923ed6SDavid Ahern 	struct nsim_per_fib_data ipv4;
4837923ed6SDavid Ahern 	struct nsim_per_fib_data ipv6;
4935266255SIdo Schimmel 	struct nsim_fib_entry nexthops;
5048bb9eb4SIdo Schimmel 	struct rhashtable fib_rt_ht;
5148bb9eb4SIdo Schimmel 	struct list_head fib_rt_list;
5286927c9cSPetr Machata 	struct mutex fib_lock; /* Protects FIB HT and list */
538fa84742SIdo Schimmel 	struct notifier_block nexthop_nb;
548fa84742SIdo Schimmel 	struct rhashtable nexthop_ht;
5548bb9eb4SIdo Schimmel 	struct devlink *devlink;
560ae3eb7bSAmit Cohen 	struct work_struct fib_event_work;
57180a6a3eSIdo Schimmel 	struct work_struct fib_flush_work;
580ae3eb7bSAmit Cohen 	struct list_head fib_event_queue;
590ae3eb7bSAmit Cohen 	spinlock_t fib_event_queue_lock; /* Protects fib event queue list */
6086927c9cSPetr Machata 	struct mutex nh_lock; /* Protects NH HT */
61134c7532SAmit Cohen 	struct dentry *ddir;
62134c7532SAmit Cohen 	bool fail_route_offload;
63d8eaa4faSIdo Schimmel 	bool fail_res_nexthop_group_replace;
64d8eaa4faSIdo Schimmel 	bool fail_nexthop_bucket_replace;
65974be75fSIdo Schimmel 	bool fail_route_delete;
6648bb9eb4SIdo Schimmel };
6748bb9eb4SIdo Schimmel 
6848bb9eb4SIdo Schimmel struct nsim_fib_rt_key {
6948bb9eb4SIdo Schimmel 	unsigned char addr[sizeof(struct in6_addr)];
7048bb9eb4SIdo Schimmel 	unsigned char prefix_len;
7148bb9eb4SIdo Schimmel 	int family;
7248bb9eb4SIdo Schimmel 	u32 tb_id;
7348bb9eb4SIdo Schimmel };
7448bb9eb4SIdo Schimmel 
7548bb9eb4SIdo Schimmel struct nsim_fib_rt {
7648bb9eb4SIdo Schimmel 	struct nsim_fib_rt_key key;
7748bb9eb4SIdo Schimmel 	struct rhash_head ht_node;
7848bb9eb4SIdo Schimmel 	struct list_head list;	/* Member of fib_rt_list */
7948bb9eb4SIdo Schimmel };
8048bb9eb4SIdo Schimmel 
8148bb9eb4SIdo Schimmel struct nsim_fib4_rt {
8248bb9eb4SIdo Schimmel 	struct nsim_fib_rt common;
8348bb9eb4SIdo Schimmel 	struct fib_info *fi;
8420bbf32eSGuillaume Nault 	dscp_t dscp;
8548bb9eb4SIdo Schimmel 	u8 type;
8648bb9eb4SIdo Schimmel };
8748bb9eb4SIdo Schimmel 
8848bb9eb4SIdo Schimmel struct nsim_fib6_rt {
8948bb9eb4SIdo Schimmel 	struct nsim_fib_rt common;
9048bb9eb4SIdo Schimmel 	struct list_head nh_list;
9148bb9eb4SIdo Schimmel 	unsigned int nhs;
9248bb9eb4SIdo Schimmel };
9348bb9eb4SIdo Schimmel 
9448bb9eb4SIdo Schimmel struct nsim_fib6_rt_nh {
9548bb9eb4SIdo Schimmel 	struct list_head list;	/* Member of nh_list */
9648bb9eb4SIdo Schimmel 	struct fib6_info *rt;
9748bb9eb4SIdo Schimmel };
9848bb9eb4SIdo Schimmel 
990ae3eb7bSAmit Cohen struct nsim_fib6_event {
1000ae3eb7bSAmit Cohen 	struct fib6_info **rt_arr;
1010ae3eb7bSAmit Cohen 	unsigned int nrt6;
1020ae3eb7bSAmit Cohen };
1030ae3eb7bSAmit Cohen 
1040ae3eb7bSAmit Cohen struct nsim_fib_event {
1050ae3eb7bSAmit Cohen 	struct list_head list; /* node in fib queue */
1060ae3eb7bSAmit Cohen 	union {
1070ae3eb7bSAmit Cohen 		struct fib_entry_notifier_info fen_info;
1080ae3eb7bSAmit Cohen 		struct nsim_fib6_event fib6_event;
1090ae3eb7bSAmit Cohen 	};
1100ae3eb7bSAmit Cohen 	struct nsim_fib_data *data;
1110ae3eb7bSAmit Cohen 	unsigned long event;
1120ae3eb7bSAmit Cohen 	int family;
1130ae3eb7bSAmit Cohen };
1140ae3eb7bSAmit Cohen 
11548bb9eb4SIdo Schimmel static const struct rhashtable_params nsim_fib_rt_ht_params = {
11648bb9eb4SIdo Schimmel 	.key_offset = offsetof(struct nsim_fib_rt, key),
11748bb9eb4SIdo Schimmel 	.head_offset = offsetof(struct nsim_fib_rt, ht_node),
11848bb9eb4SIdo Schimmel 	.key_len = sizeof(struct nsim_fib_rt_key),
11948bb9eb4SIdo Schimmel 	.automatic_shrinking = true,
12037923ed6SDavid Ahern };
12137923ed6SDavid Ahern 
1228fa84742SIdo Schimmel struct nsim_nexthop {
1238fa84742SIdo Schimmel 	struct rhash_head ht_node;
1248fa84742SIdo Schimmel 	u64 occ;
1258fa84742SIdo Schimmel 	u32 id;
126d8eaa4faSIdo Schimmel 	bool is_resilient;
1278fa84742SIdo Schimmel };
1288fa84742SIdo Schimmel 
1298fa84742SIdo Schimmel static const struct rhashtable_params nsim_nexthop_ht_params = {
1308fa84742SIdo Schimmel 	.key_offset = offsetof(struct nsim_nexthop, id),
1318fa84742SIdo Schimmel 	.head_offset = offsetof(struct nsim_nexthop, ht_node),
1328fa84742SIdo Schimmel 	.key_len = sizeof(u32),
1338fa84742SIdo Schimmel 	.automatic_shrinking = true,
1348fa84742SIdo Schimmel };
1358fa84742SIdo Schimmel 
nsim_fib_get_val(struct nsim_fib_data * fib_data,enum nsim_resource_id res_id,bool max)136a5facc4cSJiri Pirko u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
137a5facc4cSJiri Pirko 		     enum nsim_resource_id res_id, bool max)
13837923ed6SDavid Ahern {
13937923ed6SDavid Ahern 	struct nsim_fib_entry *entry;
14037923ed6SDavid Ahern 
14137923ed6SDavid Ahern 	switch (res_id) {
14237923ed6SDavid Ahern 	case NSIM_RESOURCE_IPV4_FIB:
14337923ed6SDavid Ahern 		entry = &fib_data->ipv4.fib;
14437923ed6SDavid Ahern 		break;
14537923ed6SDavid Ahern 	case NSIM_RESOURCE_IPV4_FIB_RULES:
14637923ed6SDavid Ahern 		entry = &fib_data->ipv4.rules;
14737923ed6SDavid Ahern 		break;
14837923ed6SDavid Ahern 	case NSIM_RESOURCE_IPV6_FIB:
14937923ed6SDavid Ahern 		entry = &fib_data->ipv6.fib;
15037923ed6SDavid Ahern 		break;
15137923ed6SDavid Ahern 	case NSIM_RESOURCE_IPV6_FIB_RULES:
15237923ed6SDavid Ahern 		entry = &fib_data->ipv6.rules;
15337923ed6SDavid Ahern 		break;
15435266255SIdo Schimmel 	case NSIM_RESOURCE_NEXTHOPS:
15535266255SIdo Schimmel 		entry = &fib_data->nexthops;
15635266255SIdo Schimmel 		break;
15737923ed6SDavid Ahern 	default:
15837923ed6SDavid Ahern 		return 0;
15937923ed6SDavid Ahern 	}
16037923ed6SDavid Ahern 
1619e635a21SAmit Cohen 	return max ? entry->max : atomic64_read(&entry->num);
16237923ed6SDavid Ahern }
16337923ed6SDavid Ahern 
nsim_fib_set_max(struct nsim_fib_data * fib_data,enum nsim_resource_id res_id,u64 val)16475ba029fSJiri Pirko static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
16575ba029fSJiri Pirko 			     enum nsim_resource_id res_id, u64 val)
16637923ed6SDavid Ahern {
16737923ed6SDavid Ahern 	struct nsim_fib_entry *entry;
16837923ed6SDavid Ahern 
16937923ed6SDavid Ahern 	switch (res_id) {
17037923ed6SDavid Ahern 	case NSIM_RESOURCE_IPV4_FIB:
17137923ed6SDavid Ahern 		entry = &fib_data->ipv4.fib;
17237923ed6SDavid Ahern 		break;
17337923ed6SDavid Ahern 	case NSIM_RESOURCE_IPV4_FIB_RULES:
17437923ed6SDavid Ahern 		entry = &fib_data->ipv4.rules;
17537923ed6SDavid Ahern 		break;
17637923ed6SDavid Ahern 	case NSIM_RESOURCE_IPV6_FIB:
17737923ed6SDavid Ahern 		entry = &fib_data->ipv6.fib;
17837923ed6SDavid Ahern 		break;
17937923ed6SDavid Ahern 	case NSIM_RESOURCE_IPV6_FIB_RULES:
18037923ed6SDavid Ahern 		entry = &fib_data->ipv6.rules;
18137923ed6SDavid Ahern 		break;
18235266255SIdo Schimmel 	case NSIM_RESOURCE_NEXTHOPS:
18335266255SIdo Schimmel 		entry = &fib_data->nexthops;
18435266255SIdo Schimmel 		break;
18537923ed6SDavid Ahern 	default:
18675ba029fSJiri Pirko 		WARN_ON(1);
18775ba029fSJiri Pirko 		return;
18837923ed6SDavid Ahern 	}
18937923ed6SDavid Ahern 	entry->max = val;
1907fa76d77SDavid Ahern }
19137923ed6SDavid Ahern 
nsim_fib_rule_account(struct nsim_fib_entry * entry,bool add,struct netlink_ext_ack * extack)19237923ed6SDavid Ahern static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
19337923ed6SDavid Ahern 				 struct netlink_ext_ack *extack)
19437923ed6SDavid Ahern {
19537923ed6SDavid Ahern 	int err = 0;
19637923ed6SDavid Ahern 
19737923ed6SDavid Ahern 	if (add) {
1989e635a21SAmit Cohen 		if (!atomic64_add_unless(&entry->num, 1, entry->max)) {
19937923ed6SDavid Ahern 			err = -ENOSPC;
20037923ed6SDavid Ahern 			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
20137923ed6SDavid Ahern 		}
20237923ed6SDavid Ahern 	} else {
2039e635a21SAmit Cohen 		atomic64_dec_if_positive(&entry->num);
20437923ed6SDavid Ahern 	}
20537923ed6SDavid Ahern 
20637923ed6SDavid Ahern 	return err;
20737923ed6SDavid Ahern }
20837923ed6SDavid Ahern 
nsim_fib_rule_event(struct nsim_fib_data * data,struct fib_notifier_info * info,bool add)209a5facc4cSJiri Pirko static int nsim_fib_rule_event(struct nsim_fib_data *data,
210a5facc4cSJiri Pirko 			       struct fib_notifier_info *info, bool add)
21137923ed6SDavid Ahern {
21237923ed6SDavid Ahern 	struct netlink_ext_ack *extack = info->extack;
21337923ed6SDavid Ahern 	int err = 0;
21437923ed6SDavid Ahern 
21537923ed6SDavid Ahern 	switch (info->family) {
21637923ed6SDavid Ahern 	case AF_INET:
21737923ed6SDavid Ahern 		err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
21837923ed6SDavid Ahern 		break;
21937923ed6SDavid Ahern 	case AF_INET6:
22037923ed6SDavid Ahern 		err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
22137923ed6SDavid Ahern 		break;
22237923ed6SDavid Ahern 	}
22337923ed6SDavid Ahern 
22437923ed6SDavid Ahern 	return err;
22537923ed6SDavid Ahern }
22637923ed6SDavid Ahern 
nsim_fib_account(struct nsim_fib_entry * entry,bool add)2270ae3eb7bSAmit Cohen static int nsim_fib_account(struct nsim_fib_entry *entry, bool add)
22837923ed6SDavid Ahern {
22937923ed6SDavid Ahern 	int err = 0;
23037923ed6SDavid Ahern 
23137923ed6SDavid Ahern 	if (add) {
2320ae3eb7bSAmit Cohen 		if (!atomic64_add_unless(&entry->num, 1, entry->max))
23337923ed6SDavid Ahern 			err = -ENOSPC;
23437923ed6SDavid Ahern 	} else {
2359e635a21SAmit Cohen 		atomic64_dec_if_positive(&entry->num);
23637923ed6SDavid Ahern 	}
23737923ed6SDavid Ahern 
23837923ed6SDavid Ahern 	return err;
23937923ed6SDavid Ahern }
24037923ed6SDavid Ahern 
nsim_fib_rt_init(struct nsim_fib_data * data,struct nsim_fib_rt * fib_rt,const void * addr,size_t addr_len,unsigned int prefix_len,int family,u32 tb_id)24148bb9eb4SIdo Schimmel static void nsim_fib_rt_init(struct nsim_fib_data *data,
24248bb9eb4SIdo Schimmel 			     struct nsim_fib_rt *fib_rt, const void *addr,
24348bb9eb4SIdo Schimmel 			     size_t addr_len, unsigned int prefix_len,
24448bb9eb4SIdo Schimmel 			     int family, u32 tb_id)
24537923ed6SDavid Ahern {
24648bb9eb4SIdo Schimmel 	memcpy(fib_rt->key.addr, addr, addr_len);
24748bb9eb4SIdo Schimmel 	fib_rt->key.prefix_len = prefix_len;
24848bb9eb4SIdo Schimmel 	fib_rt->key.family = family;
24948bb9eb4SIdo Schimmel 	fib_rt->key.tb_id = tb_id;
25048bb9eb4SIdo Schimmel 	list_add(&fib_rt->list, &data->fib_rt_list);
25148bb9eb4SIdo Schimmel }
25248bb9eb4SIdo Schimmel 
nsim_fib_rt_fini(struct nsim_fib_rt * fib_rt)25348bb9eb4SIdo Schimmel static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
25448bb9eb4SIdo Schimmel {
25548bb9eb4SIdo Schimmel 	list_del(&fib_rt->list);
25648bb9eb4SIdo Schimmel }
25748bb9eb4SIdo Schimmel 
nsim_fib_rt_lookup(struct rhashtable * fib_rt_ht,const void * addr,size_t addr_len,unsigned int prefix_len,int family,u32 tb_id)25848bb9eb4SIdo Schimmel static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht,
25948bb9eb4SIdo Schimmel 					      const void *addr, size_t addr_len,
26048bb9eb4SIdo Schimmel 					      unsigned int prefix_len,
26148bb9eb4SIdo Schimmel 					      int family, u32 tb_id)
26248bb9eb4SIdo Schimmel {
26348bb9eb4SIdo Schimmel 	struct nsim_fib_rt_key key;
26448bb9eb4SIdo Schimmel 
26548bb9eb4SIdo Schimmel 	memset(&key, 0, sizeof(key));
26648bb9eb4SIdo Schimmel 	memcpy(key.addr, addr, addr_len);
26748bb9eb4SIdo Schimmel 	key.prefix_len = prefix_len;
26848bb9eb4SIdo Schimmel 	key.family = family;
26948bb9eb4SIdo Schimmel 	key.tb_id = tb_id;
27048bb9eb4SIdo Schimmel 
27148bb9eb4SIdo Schimmel 	return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params);
27248bb9eb4SIdo Schimmel }
27348bb9eb4SIdo Schimmel 
27448bb9eb4SIdo Schimmel static struct nsim_fib4_rt *
nsim_fib4_rt_create(struct nsim_fib_data * data,struct fib_entry_notifier_info * fen_info)27548bb9eb4SIdo Schimmel nsim_fib4_rt_create(struct nsim_fib_data *data,
27648bb9eb4SIdo Schimmel 		    struct fib_entry_notifier_info *fen_info)
27748bb9eb4SIdo Schimmel {
27848bb9eb4SIdo Schimmel 	struct nsim_fib4_rt *fib4_rt;
27948bb9eb4SIdo Schimmel 
2800ae3eb7bSAmit Cohen 	fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_KERNEL);
28148bb9eb4SIdo Schimmel 	if (!fib4_rt)
28248bb9eb4SIdo Schimmel 		return NULL;
28348bb9eb4SIdo Schimmel 
28448bb9eb4SIdo Schimmel 	nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32),
28548bb9eb4SIdo Schimmel 			 fen_info->dst_len, AF_INET, fen_info->tb_id);
28648bb9eb4SIdo Schimmel 
28748bb9eb4SIdo Schimmel 	fib4_rt->fi = fen_info->fi;
28848bb9eb4SIdo Schimmel 	fib_info_hold(fib4_rt->fi);
28920bbf32eSGuillaume Nault 	fib4_rt->dscp = fen_info->dscp;
29048bb9eb4SIdo Schimmel 	fib4_rt->type = fen_info->type;
29148bb9eb4SIdo Schimmel 
29248bb9eb4SIdo Schimmel 	return fib4_rt;
29348bb9eb4SIdo Schimmel }
29448bb9eb4SIdo Schimmel 
nsim_fib4_rt_destroy(struct nsim_fib4_rt * fib4_rt)29548bb9eb4SIdo Schimmel static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt)
29648bb9eb4SIdo Schimmel {
29748bb9eb4SIdo Schimmel 	fib_info_put(fib4_rt->fi);
29848bb9eb4SIdo Schimmel 	nsim_fib_rt_fini(&fib4_rt->common);
29948bb9eb4SIdo Schimmel 	kfree(fib4_rt);
30048bb9eb4SIdo Schimmel }
30148bb9eb4SIdo Schimmel 
30248bb9eb4SIdo Schimmel static struct nsim_fib4_rt *
nsim_fib4_rt_lookup(struct rhashtable * fib_rt_ht,const struct fib_entry_notifier_info * fen_info)30348bb9eb4SIdo Schimmel nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
30448bb9eb4SIdo Schimmel 		    const struct fib_entry_notifier_info *fen_info)
30548bb9eb4SIdo Schimmel {
30648bb9eb4SIdo Schimmel 	struct nsim_fib_rt *fib_rt;
30748bb9eb4SIdo Schimmel 
30848bb9eb4SIdo Schimmel 	fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32),
30948bb9eb4SIdo Schimmel 				    fen_info->dst_len, AF_INET,
31048bb9eb4SIdo Schimmel 				    fen_info->tb_id);
31148bb9eb4SIdo Schimmel 	if (!fib_rt)
31248bb9eb4SIdo Schimmel 		return NULL;
31348bb9eb4SIdo Schimmel 
31448bb9eb4SIdo Schimmel 	return container_of(fib_rt, struct nsim_fib4_rt, common);
31548bb9eb4SIdo Schimmel }
31648bb9eb4SIdo Schimmel 
317134c7532SAmit Cohen static void
nsim_fib4_rt_offload_failed_flag_set(struct net * net,struct fib_entry_notifier_info * fen_info)318134c7532SAmit Cohen nsim_fib4_rt_offload_failed_flag_set(struct net *net,
319134c7532SAmit Cohen 				     struct fib_entry_notifier_info *fen_info)
320134c7532SAmit Cohen {
321134c7532SAmit Cohen 	u32 *p_dst = (u32 *)&fen_info->dst;
322134c7532SAmit Cohen 	struct fib_rt_info fri;
323134c7532SAmit Cohen 
324134c7532SAmit Cohen 	fri.fi = fen_info->fi;
325134c7532SAmit Cohen 	fri.tb_id = fen_info->tb_id;
326134c7532SAmit Cohen 	fri.dst = cpu_to_be32(*p_dst);
327134c7532SAmit Cohen 	fri.dst_len = fen_info->dst_len;
328568a3f33SGuillaume Nault 	fri.dscp = fen_info->dscp;
329134c7532SAmit Cohen 	fri.type = fen_info->type;
330134c7532SAmit Cohen 	fri.offload = false;
331134c7532SAmit Cohen 	fri.trap = false;
332134c7532SAmit Cohen 	fri.offload_failed = true;
333134c7532SAmit Cohen 	fib_alias_hw_flags_set(net, &fri);
334134c7532SAmit Cohen }
335134c7532SAmit Cohen 
nsim_fib4_rt_hw_flags_set(struct net * net,const struct nsim_fib4_rt * fib4_rt,bool trap)33648bb9eb4SIdo Schimmel static void nsim_fib4_rt_hw_flags_set(struct net *net,
33748bb9eb4SIdo Schimmel 				      const struct nsim_fib4_rt *fib4_rt,
33848bb9eb4SIdo Schimmel 				      bool trap)
33948bb9eb4SIdo Schimmel {
34048bb9eb4SIdo Schimmel 	u32 *p_dst = (u32 *) fib4_rt->common.key.addr;
34148bb9eb4SIdo Schimmel 	int dst_len = fib4_rt->common.key.prefix_len;
34248bb9eb4SIdo Schimmel 	struct fib_rt_info fri;
34348bb9eb4SIdo Schimmel 
34448bb9eb4SIdo Schimmel 	fri.fi = fib4_rt->fi;
34548bb9eb4SIdo Schimmel 	fri.tb_id = fib4_rt->common.key.tb_id;
34648bb9eb4SIdo Schimmel 	fri.dst = cpu_to_be32(*p_dst);
34748bb9eb4SIdo Schimmel 	fri.dst_len = dst_len;
34820bbf32eSGuillaume Nault 	fri.dscp = fib4_rt->dscp;
34948bb9eb4SIdo Schimmel 	fri.type = fib4_rt->type;
35048bb9eb4SIdo Schimmel 	fri.offload = false;
35148bb9eb4SIdo Schimmel 	fri.trap = trap;
35236c5100eSAmit Cohen 	fri.offload_failed = false;
35348bb9eb4SIdo Schimmel 	fib_alias_hw_flags_set(net, &fri);
35448bb9eb4SIdo Schimmel }
35548bb9eb4SIdo Schimmel 
nsim_fib4_rt_add(struct nsim_fib_data * data,struct nsim_fib4_rt * fib4_rt)35648bb9eb4SIdo Schimmel static int nsim_fib4_rt_add(struct nsim_fib_data *data,
3570ae3eb7bSAmit Cohen 			    struct nsim_fib4_rt *fib4_rt)
35848bb9eb4SIdo Schimmel {
35948bb9eb4SIdo Schimmel 	struct net *net = devlink_net(data->devlink);
36048bb9eb4SIdo Schimmel 	int err;
36148bb9eb4SIdo Schimmel 
36248bb9eb4SIdo Schimmel 	err = rhashtable_insert_fast(&data->fib_rt_ht,
36348bb9eb4SIdo Schimmel 				     &fib4_rt->common.ht_node,
36448bb9eb4SIdo Schimmel 				     nsim_fib_rt_ht_params);
3650ae3eb7bSAmit Cohen 	if (err)
36648bb9eb4SIdo Schimmel 		goto err_fib_dismiss;
36748bb9eb4SIdo Schimmel 
3680ae3eb7bSAmit Cohen 	/* Simulate hardware programming latency. */
3690ae3eb7bSAmit Cohen 	msleep(1);
37048bb9eb4SIdo Schimmel 	nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
37148bb9eb4SIdo Schimmel 
37248bb9eb4SIdo Schimmel 	return 0;
37348bb9eb4SIdo Schimmel 
37448bb9eb4SIdo Schimmel err_fib_dismiss:
3750ae3eb7bSAmit Cohen 	/* Drop the accounting that was increased from the notification
3760ae3eb7bSAmit Cohen 	 * context when FIB_EVENT_ENTRY_REPLACE was triggered.
3770ae3eb7bSAmit Cohen 	 */
3780ae3eb7bSAmit Cohen 	nsim_fib_account(&data->ipv4.fib, false);
37948bb9eb4SIdo Schimmel 	return err;
38048bb9eb4SIdo Schimmel }
38148bb9eb4SIdo Schimmel 
nsim_fib4_rt_replace(struct nsim_fib_data * data,struct nsim_fib4_rt * fib4_rt,struct nsim_fib4_rt * fib4_rt_old)38248bb9eb4SIdo Schimmel static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
38348bb9eb4SIdo Schimmel 				struct nsim_fib4_rt *fib4_rt,
3840ae3eb7bSAmit Cohen 				struct nsim_fib4_rt *fib4_rt_old)
38548bb9eb4SIdo Schimmel {
38648bb9eb4SIdo Schimmel 	struct net *net = devlink_net(data->devlink);
38748bb9eb4SIdo Schimmel 	int err;
38848bb9eb4SIdo Schimmel 
3890ae3eb7bSAmit Cohen 	/* We are replacing a route, so need to remove the accounting which
3900ae3eb7bSAmit Cohen 	 * was increased when FIB_EVENT_ENTRY_REPLACE was triggered.
3910ae3eb7bSAmit Cohen 	 */
3920ae3eb7bSAmit Cohen 	err = nsim_fib_account(&data->ipv4.fib, false);
3930ae3eb7bSAmit Cohen 	if (err)
3940ae3eb7bSAmit Cohen 		return err;
39548bb9eb4SIdo Schimmel 	err = rhashtable_replace_fast(&data->fib_rt_ht,
39648bb9eb4SIdo Schimmel 				      &fib4_rt_old->common.ht_node,
39748bb9eb4SIdo Schimmel 				      &fib4_rt->common.ht_node,
39848bb9eb4SIdo Schimmel 				      nsim_fib_rt_ht_params);
3990ae3eb7bSAmit Cohen 	if (err)
40048bb9eb4SIdo Schimmel 		return err;
40148bb9eb4SIdo Schimmel 
4020ae3eb7bSAmit Cohen 	msleep(1);
40348bb9eb4SIdo Schimmel 	nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
40448bb9eb4SIdo Schimmel 
40548bb9eb4SIdo Schimmel 	nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
40648bb9eb4SIdo Schimmel 	nsim_fib4_rt_destroy(fib4_rt_old);
40748bb9eb4SIdo Schimmel 
40848bb9eb4SIdo Schimmel 	return 0;
40948bb9eb4SIdo Schimmel }
41048bb9eb4SIdo Schimmel 
nsim_fib4_rt_insert(struct nsim_fib_data * data,struct fib_entry_notifier_info * fen_info)41148bb9eb4SIdo Schimmel static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
41248bb9eb4SIdo Schimmel 			       struct fib_entry_notifier_info *fen_info)
41348bb9eb4SIdo Schimmel {
41448bb9eb4SIdo Schimmel 	struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
41548bb9eb4SIdo Schimmel 	int err;
41648bb9eb4SIdo Schimmel 
417134c7532SAmit Cohen 	if (data->fail_route_offload) {
418134c7532SAmit Cohen 		/* For testing purposes, user set debugfs fail_route_offload
419134c7532SAmit Cohen 		 * value to true. Simulate hardware programming latency and then
420134c7532SAmit Cohen 		 * fail.
421134c7532SAmit Cohen 		 */
422134c7532SAmit Cohen 		msleep(1);
423134c7532SAmit Cohen 		return -EINVAL;
424134c7532SAmit Cohen 	}
425134c7532SAmit Cohen 
42648bb9eb4SIdo Schimmel 	fib4_rt = nsim_fib4_rt_create(data, fen_info);
42748bb9eb4SIdo Schimmel 	if (!fib4_rt)
42848bb9eb4SIdo Schimmel 		return -ENOMEM;
42948bb9eb4SIdo Schimmel 
43048bb9eb4SIdo Schimmel 	fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
43148bb9eb4SIdo Schimmel 	if (!fib4_rt_old)
4320ae3eb7bSAmit Cohen 		err = nsim_fib4_rt_add(data, fib4_rt);
43348bb9eb4SIdo Schimmel 	else
4340ae3eb7bSAmit Cohen 		err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old);
43548bb9eb4SIdo Schimmel 
43648bb9eb4SIdo Schimmel 	if (err)
43748bb9eb4SIdo Schimmel 		nsim_fib4_rt_destroy(fib4_rt);
43848bb9eb4SIdo Schimmel 
43948bb9eb4SIdo Schimmel 	return err;
44048bb9eb4SIdo Schimmel }
44148bb9eb4SIdo Schimmel 
nsim_fib4_rt_remove(struct nsim_fib_data * data,const struct fib_entry_notifier_info * fen_info)44248bb9eb4SIdo Schimmel static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
44348bb9eb4SIdo Schimmel 				const struct fib_entry_notifier_info *fen_info)
44448bb9eb4SIdo Schimmel {
44548bb9eb4SIdo Schimmel 	struct nsim_fib4_rt *fib4_rt;
44648bb9eb4SIdo Schimmel 
44748bb9eb4SIdo Schimmel 	fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
448484a4dfbSAmit Cohen 	if (!fib4_rt)
44948bb9eb4SIdo Schimmel 		return;
45048bb9eb4SIdo Schimmel 
45148bb9eb4SIdo Schimmel 	rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
45248bb9eb4SIdo Schimmel 			       nsim_fib_rt_ht_params);
45348bb9eb4SIdo Schimmel 	nsim_fib4_rt_destroy(fib4_rt);
45448bb9eb4SIdo Schimmel }
45548bb9eb4SIdo Schimmel 
nsim_fib4_event(struct nsim_fib_data * data,struct fib_entry_notifier_info * fen_info,unsigned long event)45648bb9eb4SIdo Schimmel static int nsim_fib4_event(struct nsim_fib_data *data,
4570ae3eb7bSAmit Cohen 			   struct fib_entry_notifier_info *fen_info,
45848bb9eb4SIdo Schimmel 			   unsigned long event)
45948bb9eb4SIdo Schimmel {
46048bb9eb4SIdo Schimmel 	int err = 0;
46148bb9eb4SIdo Schimmel 
46248bb9eb4SIdo Schimmel 	switch (event) {
46348bb9eb4SIdo Schimmel 	case FIB_EVENT_ENTRY_REPLACE:
46448bb9eb4SIdo Schimmel 		err = nsim_fib4_rt_insert(data, fen_info);
465134c7532SAmit Cohen 		if (err) {
466134c7532SAmit Cohen 			struct net *net = devlink_net(data->devlink);
467134c7532SAmit Cohen 
468134c7532SAmit Cohen 			nsim_fib4_rt_offload_failed_flag_set(net, fen_info);
469134c7532SAmit Cohen 		}
47048bb9eb4SIdo Schimmel 		break;
47148bb9eb4SIdo Schimmel 	case FIB_EVENT_ENTRY_DEL:
47248bb9eb4SIdo Schimmel 		nsim_fib4_rt_remove(data, fen_info);
47348bb9eb4SIdo Schimmel 		break;
47448bb9eb4SIdo Schimmel 	default:
47548bb9eb4SIdo Schimmel 		break;
47648bb9eb4SIdo Schimmel 	}
47748bb9eb4SIdo Schimmel 
47848bb9eb4SIdo Schimmel 	return err;
47948bb9eb4SIdo Schimmel }
48048bb9eb4SIdo Schimmel 
48148bb9eb4SIdo Schimmel static struct nsim_fib6_rt_nh *
nsim_fib6_rt_nh_find(const struct nsim_fib6_rt * fib6_rt,const struct fib6_info * rt)48248bb9eb4SIdo Schimmel nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt,
48348bb9eb4SIdo Schimmel 		     const struct fib6_info *rt)
48448bb9eb4SIdo Schimmel {
48548bb9eb4SIdo Schimmel 	struct nsim_fib6_rt_nh *fib6_rt_nh;
48648bb9eb4SIdo Schimmel 
48748bb9eb4SIdo Schimmel 	list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) {
48848bb9eb4SIdo Schimmel 		if (fib6_rt_nh->rt == rt)
48948bb9eb4SIdo Schimmel 			return fib6_rt_nh;
49048bb9eb4SIdo Schimmel 	}
49148bb9eb4SIdo Schimmel 
49248bb9eb4SIdo Schimmel 	return NULL;
49348bb9eb4SIdo Schimmel }
49448bb9eb4SIdo Schimmel 
nsim_fib6_rt_nh_add(struct nsim_fib6_rt * fib6_rt,struct fib6_info * rt)49548bb9eb4SIdo Schimmel static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt,
49648bb9eb4SIdo Schimmel 			       struct fib6_info *rt)
49748bb9eb4SIdo Schimmel {
49848bb9eb4SIdo Schimmel 	struct nsim_fib6_rt_nh *fib6_rt_nh;
49948bb9eb4SIdo Schimmel 
5000ae3eb7bSAmit Cohen 	fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_KERNEL);
50148bb9eb4SIdo Schimmel 	if (!fib6_rt_nh)
50248bb9eb4SIdo Schimmel 		return -ENOMEM;
50348bb9eb4SIdo Schimmel 
50448bb9eb4SIdo Schimmel 	fib6_info_hold(rt);
50548bb9eb4SIdo Schimmel 	fib6_rt_nh->rt = rt;
50648bb9eb4SIdo Schimmel 	list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
50748bb9eb4SIdo Schimmel 	fib6_rt->nhs++;
50848bb9eb4SIdo Schimmel 
50948bb9eb4SIdo Schimmel 	return 0;
51048bb9eb4SIdo Schimmel }
51148bb9eb4SIdo Schimmel 
5120ae3eb7bSAmit Cohen #if IS_ENABLED(CONFIG_IPV6)
nsim_rt6_release(struct fib6_info * rt)5130ae3eb7bSAmit Cohen static void nsim_rt6_release(struct fib6_info *rt)
5140ae3eb7bSAmit Cohen {
5150ae3eb7bSAmit Cohen 	fib6_info_release(rt);
5160ae3eb7bSAmit Cohen }
5170ae3eb7bSAmit Cohen #else
nsim_rt6_release(struct fib6_info * rt)5180ae3eb7bSAmit Cohen static void nsim_rt6_release(struct fib6_info *rt)
5190ae3eb7bSAmit Cohen {
5200ae3eb7bSAmit Cohen }
5210ae3eb7bSAmit Cohen #endif
5220ae3eb7bSAmit Cohen 
nsim_fib6_rt_nh_del(struct nsim_fib6_rt * fib6_rt,const struct fib6_info * rt)52348bb9eb4SIdo Schimmel static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
52448bb9eb4SIdo Schimmel 				const struct fib6_info *rt)
52548bb9eb4SIdo Schimmel {
52648bb9eb4SIdo Schimmel 	struct nsim_fib6_rt_nh *fib6_rt_nh;
52748bb9eb4SIdo Schimmel 
52848bb9eb4SIdo Schimmel 	fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
529484a4dfbSAmit Cohen 	if (!fib6_rt_nh)
53048bb9eb4SIdo Schimmel 		return;
53148bb9eb4SIdo Schimmel 
53248bb9eb4SIdo Schimmel 	fib6_rt->nhs--;
53348bb9eb4SIdo Schimmel 	list_del(&fib6_rt_nh->list);
5340ae3eb7bSAmit Cohen 	nsim_rt6_release(fib6_rt_nh->rt);
53548bb9eb4SIdo Schimmel 	kfree(fib6_rt_nh);
53648bb9eb4SIdo Schimmel }
53748bb9eb4SIdo Schimmel 
53848bb9eb4SIdo Schimmel static struct nsim_fib6_rt *
nsim_fib6_rt_create(struct nsim_fib_data * data,struct fib6_info ** rt_arr,unsigned int nrt6)53948bb9eb4SIdo Schimmel nsim_fib6_rt_create(struct nsim_fib_data *data,
5400ae3eb7bSAmit Cohen 		    struct fib6_info **rt_arr, unsigned int nrt6)
54148bb9eb4SIdo Schimmel {
5420ae3eb7bSAmit Cohen 	struct fib6_info *rt = rt_arr[0];
54348bb9eb4SIdo Schimmel 	struct nsim_fib6_rt *fib6_rt;
54448bb9eb4SIdo Schimmel 	int i = 0;
54548bb9eb4SIdo Schimmel 	int err;
54648bb9eb4SIdo Schimmel 
5470ae3eb7bSAmit Cohen 	fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_KERNEL);
54848bb9eb4SIdo Schimmel 	if (!fib6_rt)
54941cdc741SEric Dumazet 		return ERR_PTR(-ENOMEM);
55048bb9eb4SIdo Schimmel 
55148bb9eb4SIdo Schimmel 	nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr,
55248bb9eb4SIdo Schimmel 			 sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6,
55348bb9eb4SIdo Schimmel 			 rt->fib6_table->tb6_id);
55448bb9eb4SIdo Schimmel 
55548bb9eb4SIdo Schimmel 	/* We consider a multipath IPv6 route as one entry, but it can be made
55648bb9eb4SIdo Schimmel 	 * up from several fib6_info structs (one for each nexthop), so we
55748bb9eb4SIdo Schimmel 	 * add them all to the same list under the entry.
55848bb9eb4SIdo Schimmel 	 */
55948bb9eb4SIdo Schimmel 	INIT_LIST_HEAD(&fib6_rt->nh_list);
56048bb9eb4SIdo Schimmel 
5610ae3eb7bSAmit Cohen 	for (i = 0; i < nrt6; i++) {
5620ae3eb7bSAmit Cohen 		err = nsim_fib6_rt_nh_add(fib6_rt, rt_arr[i]);
56348bb9eb4SIdo Schimmel 		if (err)
56448bb9eb4SIdo Schimmel 			goto err_fib6_rt_nh_del;
56548bb9eb4SIdo Schimmel 	}
56648bb9eb4SIdo Schimmel 
56748bb9eb4SIdo Schimmel 	return fib6_rt;
56848bb9eb4SIdo Schimmel 
56948bb9eb4SIdo Schimmel err_fib6_rt_nh_del:
5700ae3eb7bSAmit Cohen 	for (i--; i >= 0; i--) {
5710ae3eb7bSAmit Cohen 		nsim_fib6_rt_nh_del(fib6_rt, rt_arr[i]);
572be107538SQiheng Lin 	}
57348bb9eb4SIdo Schimmel 	nsim_fib_rt_fini(&fib6_rt->common);
57448bb9eb4SIdo Schimmel 	kfree(fib6_rt);
57548bb9eb4SIdo Schimmel 	return ERR_PTR(err);
57648bb9eb4SIdo Schimmel }
57748bb9eb4SIdo Schimmel 
nsim_fib6_rt_destroy(struct nsim_fib6_rt * fib6_rt)57848bb9eb4SIdo Schimmel static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt)
57948bb9eb4SIdo Schimmel {
58048bb9eb4SIdo Schimmel 	struct nsim_fib6_rt_nh *iter, *tmp;
58148bb9eb4SIdo Schimmel 
58248bb9eb4SIdo Schimmel 	list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list)
58348bb9eb4SIdo Schimmel 		nsim_fib6_rt_nh_del(fib6_rt, iter->rt);
58448bb9eb4SIdo Schimmel 	WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list));
58548bb9eb4SIdo Schimmel 	nsim_fib_rt_fini(&fib6_rt->common);
58648bb9eb4SIdo Schimmel 	kfree(fib6_rt);
58748bb9eb4SIdo Schimmel }
58848bb9eb4SIdo Schimmel 
58948bb9eb4SIdo Schimmel static struct nsim_fib6_rt *
nsim_fib6_rt_lookup(struct rhashtable * fib_rt_ht,const struct fib6_info * rt)59048bb9eb4SIdo Schimmel nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt)
59148bb9eb4SIdo Schimmel {
59248bb9eb4SIdo Schimmel 	struct nsim_fib_rt *fib_rt;
59348bb9eb4SIdo Schimmel 
59448bb9eb4SIdo Schimmel 	fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr,
59548bb9eb4SIdo Schimmel 				    sizeof(rt->fib6_dst.addr),
59648bb9eb4SIdo Schimmel 				    rt->fib6_dst.plen, AF_INET6,
59748bb9eb4SIdo Schimmel 				    rt->fib6_table->tb6_id);
59848bb9eb4SIdo Schimmel 	if (!fib_rt)
59948bb9eb4SIdo Schimmel 		return NULL;
60048bb9eb4SIdo Schimmel 
60148bb9eb4SIdo Schimmel 	return container_of(fib_rt, struct nsim_fib6_rt, common);
60248bb9eb4SIdo Schimmel }
60348bb9eb4SIdo Schimmel 
nsim_fib6_rt_append(struct nsim_fib_data * data,struct nsim_fib6_event * fib6_event)60448bb9eb4SIdo Schimmel static int nsim_fib6_rt_append(struct nsim_fib_data *data,
6050ae3eb7bSAmit Cohen 			       struct nsim_fib6_event *fib6_event)
60648bb9eb4SIdo Schimmel {
6070ae3eb7bSAmit Cohen 	struct fib6_info *rt = fib6_event->rt_arr[0];
60848bb9eb4SIdo Schimmel 	struct nsim_fib6_rt *fib6_rt;
6090ae3eb7bSAmit Cohen 	int i, err;
61048bb9eb4SIdo Schimmel 
611134c7532SAmit Cohen 	if (data->fail_route_offload) {
612134c7532SAmit Cohen 		/* For testing purposes, user set debugfs fail_route_offload
613134c7532SAmit Cohen 		 * value to true. Simulate hardware programming latency and then
614134c7532SAmit Cohen 		 * fail.
615134c7532SAmit Cohen 		 */
616134c7532SAmit Cohen 		msleep(1);
617134c7532SAmit Cohen 		return -EINVAL;
618134c7532SAmit Cohen 	}
619134c7532SAmit Cohen 
62048bb9eb4SIdo Schimmel 	fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
621484a4dfbSAmit Cohen 	if (!fib6_rt)
62248bb9eb4SIdo Schimmel 		return -EINVAL;
62348bb9eb4SIdo Schimmel 
6240ae3eb7bSAmit Cohen 	for (i = 0; i < fib6_event->nrt6; i++) {
6250ae3eb7bSAmit Cohen 		err = nsim_fib6_rt_nh_add(fib6_rt, fib6_event->rt_arr[i]);
62648bb9eb4SIdo Schimmel 		if (err)
62748bb9eb4SIdo Schimmel 			goto err_fib6_rt_nh_del;
6280ae3eb7bSAmit Cohen 
629d95d6320SEric Dumazet 		WRITE_ONCE(fib6_event->rt_arr[i]->trap, true);
63048bb9eb4SIdo Schimmel 	}
63148bb9eb4SIdo Schimmel 
63248bb9eb4SIdo Schimmel 	return 0;
63348bb9eb4SIdo Schimmel 
63448bb9eb4SIdo Schimmel err_fib6_rt_nh_del:
6350ae3eb7bSAmit Cohen 	for (i--; i >= 0; i--) {
636d95d6320SEric Dumazet 		WRITE_ONCE(fib6_event->rt_arr[i]->trap, false);
6370ae3eb7bSAmit Cohen 		nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
63848bb9eb4SIdo Schimmel 	}
63948bb9eb4SIdo Schimmel 	return err;
64048bb9eb4SIdo Schimmel }
64148bb9eb4SIdo Schimmel 
642efc42879SAmit Cohen #if IS_ENABLED(CONFIG_IPV6)
nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data * data,struct fib6_info ** rt_arr,unsigned int nrt6)643134c7532SAmit Cohen static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
644134c7532SAmit Cohen 						 struct fib6_info **rt_arr,
645134c7532SAmit Cohen 						 unsigned int nrt6)
646134c7532SAmit Cohen 
647134c7532SAmit Cohen {
648134c7532SAmit Cohen 	struct net *net = devlink_net(data->devlink);
649134c7532SAmit Cohen 	int i;
650134c7532SAmit Cohen 
651134c7532SAmit Cohen 	for (i = 0; i < nrt6; i++)
652134c7532SAmit Cohen 		fib6_info_hw_flags_set(net, rt_arr[i], false, false, true);
653134c7532SAmit Cohen }
654134c7532SAmit Cohen #else
nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data * data,struct fib6_info ** rt_arr,unsigned int nrt6)655134c7532SAmit Cohen static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
656134c7532SAmit Cohen 						 struct fib6_info **rt_arr,
657134c7532SAmit Cohen 						 unsigned int nrt6)
658134c7532SAmit Cohen {
659134c7532SAmit Cohen }
660134c7532SAmit Cohen #endif
661134c7532SAmit Cohen 
662134c7532SAmit Cohen #if IS_ENABLED(CONFIG_IPV6)
nsim_fib6_rt_hw_flags_set(struct nsim_fib_data * data,const struct nsim_fib6_rt * fib6_rt,bool trap)663fbaca8f8SAmit Cohen static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
664fbaca8f8SAmit Cohen 				      const struct nsim_fib6_rt *fib6_rt,
66548bb9eb4SIdo Schimmel 				      bool trap)
66648bb9eb4SIdo Schimmel {
667fbaca8f8SAmit Cohen 	struct net *net = devlink_net(data->devlink);
66848bb9eb4SIdo Schimmel 	struct nsim_fib6_rt_nh *fib6_rt_nh;
66948bb9eb4SIdo Schimmel 
67048bb9eb4SIdo Schimmel 	list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
6710c5fcf9eSAmit Cohen 		fib6_info_hw_flags_set(net, fib6_rt_nh->rt, false, trap, false);
67248bb9eb4SIdo Schimmel }
673efc42879SAmit Cohen #else
nsim_fib6_rt_hw_flags_set(struct nsim_fib_data * data,const struct nsim_fib6_rt * fib6_rt,bool trap)674efc42879SAmit Cohen static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
675efc42879SAmit Cohen 				      const struct nsim_fib6_rt *fib6_rt,
676efc42879SAmit Cohen 				      bool trap)
677efc42879SAmit Cohen {
678efc42879SAmit Cohen }
679efc42879SAmit Cohen #endif
68048bb9eb4SIdo Schimmel 
nsim_fib6_rt_add(struct nsim_fib_data * data,struct nsim_fib6_rt * fib6_rt)68148bb9eb4SIdo Schimmel static int nsim_fib6_rt_add(struct nsim_fib_data *data,
6820ae3eb7bSAmit Cohen 			    struct nsim_fib6_rt *fib6_rt)
68348bb9eb4SIdo Schimmel {
68448bb9eb4SIdo Schimmel 	int err;
68548bb9eb4SIdo Schimmel 
68648bb9eb4SIdo Schimmel 	err = rhashtable_insert_fast(&data->fib_rt_ht,
68748bb9eb4SIdo Schimmel 				     &fib6_rt->common.ht_node,
68848bb9eb4SIdo Schimmel 				     nsim_fib_rt_ht_params);
68948bb9eb4SIdo Schimmel 
6900ae3eb7bSAmit Cohen 	if (err)
6910ae3eb7bSAmit Cohen 		goto err_fib_dismiss;
6920ae3eb7bSAmit Cohen 
6930ae3eb7bSAmit Cohen 	msleep(1);
694fbaca8f8SAmit Cohen 	nsim_fib6_rt_hw_flags_set(data, fib6_rt, true);
69548bb9eb4SIdo Schimmel 
69648bb9eb4SIdo Schimmel 	return 0;
69748bb9eb4SIdo Schimmel 
69848bb9eb4SIdo Schimmel err_fib_dismiss:
6990ae3eb7bSAmit Cohen 	/* Drop the accounting that was increased from the notification
7000ae3eb7bSAmit Cohen 	 * context when FIB_EVENT_ENTRY_REPLACE was triggered.
7010ae3eb7bSAmit Cohen 	 */
7020ae3eb7bSAmit Cohen 	nsim_fib_account(&data->ipv6.fib, false);
70348bb9eb4SIdo Schimmel 	return err;
70448bb9eb4SIdo Schimmel }
70548bb9eb4SIdo Schimmel 
nsim_fib6_rt_replace(struct nsim_fib_data * data,struct nsim_fib6_rt * fib6_rt,struct nsim_fib6_rt * fib6_rt_old)70648bb9eb4SIdo Schimmel static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
70748bb9eb4SIdo Schimmel 				struct nsim_fib6_rt *fib6_rt,
7080ae3eb7bSAmit Cohen 				struct nsim_fib6_rt *fib6_rt_old)
70948bb9eb4SIdo Schimmel {
71048bb9eb4SIdo Schimmel 	int err;
71148bb9eb4SIdo Schimmel 
7120ae3eb7bSAmit Cohen 	/* We are replacing a route, so need to remove the accounting which
7130ae3eb7bSAmit Cohen 	 * was increased when FIB_EVENT_ENTRY_REPLACE was triggered.
7140ae3eb7bSAmit Cohen 	 */
7150ae3eb7bSAmit Cohen 	err = nsim_fib_account(&data->ipv6.fib, false);
7160ae3eb7bSAmit Cohen 	if (err)
7170ae3eb7bSAmit Cohen 		return err;
7180ae3eb7bSAmit Cohen 
71948bb9eb4SIdo Schimmel 	err = rhashtable_replace_fast(&data->fib_rt_ht,
72048bb9eb4SIdo Schimmel 				      &fib6_rt_old->common.ht_node,
72148bb9eb4SIdo Schimmel 				      &fib6_rt->common.ht_node,
72248bb9eb4SIdo Schimmel 				      nsim_fib_rt_ht_params);
72348bb9eb4SIdo Schimmel 
7240ae3eb7bSAmit Cohen 	if (err)
7250ae3eb7bSAmit Cohen 		return err;
7260ae3eb7bSAmit Cohen 
7270ae3eb7bSAmit Cohen 	msleep(1);
728fbaca8f8SAmit Cohen 	nsim_fib6_rt_hw_flags_set(data, fib6_rt, true);
72948bb9eb4SIdo Schimmel 
730fbaca8f8SAmit Cohen 	nsim_fib6_rt_hw_flags_set(data, fib6_rt_old, false);
73148bb9eb4SIdo Schimmel 	nsim_fib6_rt_destroy(fib6_rt_old);
73248bb9eb4SIdo Schimmel 
73348bb9eb4SIdo Schimmel 	return 0;
73448bb9eb4SIdo Schimmel }
73548bb9eb4SIdo Schimmel 
nsim_fib6_rt_insert(struct nsim_fib_data * data,struct nsim_fib6_event * fib6_event)73648bb9eb4SIdo Schimmel static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
7370ae3eb7bSAmit Cohen 			       struct nsim_fib6_event *fib6_event)
73848bb9eb4SIdo Schimmel {
7390ae3eb7bSAmit Cohen 	struct fib6_info *rt = fib6_event->rt_arr[0];
74048bb9eb4SIdo Schimmel 	struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
74148bb9eb4SIdo Schimmel 	int err;
74248bb9eb4SIdo Schimmel 
743134c7532SAmit Cohen 	if (data->fail_route_offload) {
744134c7532SAmit Cohen 		/* For testing purposes, user set debugfs fail_route_offload
745134c7532SAmit Cohen 		 * value to true. Simulate hardware programming latency and then
746134c7532SAmit Cohen 		 * fail.
747134c7532SAmit Cohen 		 */
748134c7532SAmit Cohen 		msleep(1);
749134c7532SAmit Cohen 		return -EINVAL;
750134c7532SAmit Cohen 	}
751134c7532SAmit Cohen 
7520ae3eb7bSAmit Cohen 	fib6_rt = nsim_fib6_rt_create(data, fib6_event->rt_arr,
7530ae3eb7bSAmit Cohen 				      fib6_event->nrt6);
75441cdc741SEric Dumazet 	if (IS_ERR(fib6_rt))
75541cdc741SEric Dumazet 		return PTR_ERR(fib6_rt);
75648bb9eb4SIdo Schimmel 
7570ae3eb7bSAmit Cohen 	fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
75848bb9eb4SIdo Schimmel 	if (!fib6_rt_old)
7590ae3eb7bSAmit Cohen 		err = nsim_fib6_rt_add(data, fib6_rt);
76048bb9eb4SIdo Schimmel 	else
7610ae3eb7bSAmit Cohen 		err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old);
76248bb9eb4SIdo Schimmel 
76348bb9eb4SIdo Schimmel 	if (err)
76448bb9eb4SIdo Schimmel 		nsim_fib6_rt_destroy(fib6_rt);
76548bb9eb4SIdo Schimmel 
76648bb9eb4SIdo Schimmel 	return err;
76748bb9eb4SIdo Schimmel }
76848bb9eb4SIdo Schimmel 
nsim_fib6_rt_remove(struct nsim_fib_data * data,struct nsim_fib6_event * fib6_event)7690ae3eb7bSAmit Cohen static void nsim_fib6_rt_remove(struct nsim_fib_data *data,
7700ae3eb7bSAmit Cohen 				struct nsim_fib6_event *fib6_event)
77148bb9eb4SIdo Schimmel {
7720ae3eb7bSAmit Cohen 	struct fib6_info *rt = fib6_event->rt_arr[0];
77348bb9eb4SIdo Schimmel 	struct nsim_fib6_rt *fib6_rt;
7740ae3eb7bSAmit Cohen 	int i;
77548bb9eb4SIdo Schimmel 
77648bb9eb4SIdo Schimmel 	/* Multipath routes are first added to the FIB trie and only then
77748bb9eb4SIdo Schimmel 	 * notified. If we vetoed the addition, we will get a delete
77848bb9eb4SIdo Schimmel 	 * notification for a route we do not have. Therefore, do not warn if
77948bb9eb4SIdo Schimmel 	 * route was not found.
78048bb9eb4SIdo Schimmel 	 */
7810ae3eb7bSAmit Cohen 	fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
78248bb9eb4SIdo Schimmel 	if (!fib6_rt)
78348bb9eb4SIdo Schimmel 		return;
78448bb9eb4SIdo Schimmel 
78548bb9eb4SIdo Schimmel 	/* If not all the nexthops are deleted, then only reduce the nexthop
78648bb9eb4SIdo Schimmel 	 * group.
78748bb9eb4SIdo Schimmel 	 */
7880ae3eb7bSAmit Cohen 	if (fib6_event->nrt6 != fib6_rt->nhs) {
7890ae3eb7bSAmit Cohen 		for (i = 0; i < fib6_event->nrt6; i++)
7900ae3eb7bSAmit Cohen 			nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
79148bb9eb4SIdo Schimmel 		return;
79248bb9eb4SIdo Schimmel 	}
79348bb9eb4SIdo Schimmel 
79448bb9eb4SIdo Schimmel 	rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
79548bb9eb4SIdo Schimmel 			       nsim_fib_rt_ht_params);
79648bb9eb4SIdo Schimmel 	nsim_fib6_rt_destroy(fib6_rt);
79748bb9eb4SIdo Schimmel }
79848bb9eb4SIdo Schimmel 
nsim_fib6_event_init(struct nsim_fib6_event * fib6_event,struct fib6_entry_notifier_info * fen6_info)7990ae3eb7bSAmit Cohen static int nsim_fib6_event_init(struct nsim_fib6_event *fib6_event,
8000ae3eb7bSAmit Cohen 				struct fib6_entry_notifier_info *fen6_info)
80148bb9eb4SIdo Schimmel {
8020ae3eb7bSAmit Cohen 	struct fib6_info *rt = fen6_info->rt;
8030ae3eb7bSAmit Cohen 	struct fib6_info **rt_arr;
8040ae3eb7bSAmit Cohen 	struct fib6_info *iter;
8050ae3eb7bSAmit Cohen 	unsigned int nrt6;
8060ae3eb7bSAmit Cohen 	int i = 0;
80748bb9eb4SIdo Schimmel 
8080ae3eb7bSAmit Cohen 	nrt6 = fen6_info->nsiblings + 1;
80948bb9eb4SIdo Schimmel 
8100ae3eb7bSAmit Cohen 	rt_arr = kcalloc(nrt6, sizeof(struct fib6_info *), GFP_ATOMIC);
8110ae3eb7bSAmit Cohen 	if (!rt_arr)
8120ae3eb7bSAmit Cohen 		return -ENOMEM;
8130ae3eb7bSAmit Cohen 
8140ae3eb7bSAmit Cohen 	fib6_event->rt_arr = rt_arr;
8150ae3eb7bSAmit Cohen 	fib6_event->nrt6 = nrt6;
8160ae3eb7bSAmit Cohen 
8170ae3eb7bSAmit Cohen 	rt_arr[0] = rt;
8180ae3eb7bSAmit Cohen 	fib6_info_hold(rt);
8190ae3eb7bSAmit Cohen 
8200ae3eb7bSAmit Cohen 	if (!fen6_info->nsiblings)
8210ae3eb7bSAmit Cohen 		return 0;
8220ae3eb7bSAmit Cohen 
8230ae3eb7bSAmit Cohen 	list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
8240ae3eb7bSAmit Cohen 		if (i == fen6_info->nsiblings)
8250ae3eb7bSAmit Cohen 			break;
8260ae3eb7bSAmit Cohen 
8270ae3eb7bSAmit Cohen 		rt_arr[i + 1] = iter;
8280ae3eb7bSAmit Cohen 		fib6_info_hold(iter);
8290ae3eb7bSAmit Cohen 		i++;
8300ae3eb7bSAmit Cohen 	}
8310ae3eb7bSAmit Cohen 	WARN_ON_ONCE(i != fen6_info->nsiblings);
8320ae3eb7bSAmit Cohen 
83348bb9eb4SIdo Schimmel 	return 0;
83448bb9eb4SIdo Schimmel }
83548bb9eb4SIdo Schimmel 
nsim_fib6_event_fini(struct nsim_fib6_event * fib6_event)8360ae3eb7bSAmit Cohen static void nsim_fib6_event_fini(struct nsim_fib6_event *fib6_event)
8370ae3eb7bSAmit Cohen {
8380ae3eb7bSAmit Cohen 	int i;
8390ae3eb7bSAmit Cohen 
8400ae3eb7bSAmit Cohen 	for (i = 0; i < fib6_event->nrt6; i++)
8410ae3eb7bSAmit Cohen 		nsim_rt6_release(fib6_event->rt_arr[i]);
8420ae3eb7bSAmit Cohen 	kfree(fib6_event->rt_arr);
8430ae3eb7bSAmit Cohen }
8440ae3eb7bSAmit Cohen 
nsim_fib6_event(struct nsim_fib_data * data,struct nsim_fib6_event * fib6_event,unsigned long event)8450ae3eb7bSAmit Cohen static int nsim_fib6_event(struct nsim_fib_data *data,
8460ae3eb7bSAmit Cohen 			   struct nsim_fib6_event *fib6_event,
8470ae3eb7bSAmit Cohen 			   unsigned long event)
8480ae3eb7bSAmit Cohen {
849134c7532SAmit Cohen 	int err;
8500ae3eb7bSAmit Cohen 
8510ae3eb7bSAmit Cohen 	if (fib6_event->rt_arr[0]->fib6_src.plen)
8520ae3eb7bSAmit Cohen 		return 0;
8530ae3eb7bSAmit Cohen 
85448bb9eb4SIdo Schimmel 	switch (event) {
85548bb9eb4SIdo Schimmel 	case FIB_EVENT_ENTRY_REPLACE:
8560ae3eb7bSAmit Cohen 		err = nsim_fib6_rt_insert(data, fib6_event);
857134c7532SAmit Cohen 		if (err)
858134c7532SAmit Cohen 			goto err_rt_offload_failed_flag_set;
85948bb9eb4SIdo Schimmel 		break;
86048bb9eb4SIdo Schimmel 	case FIB_EVENT_ENTRY_APPEND:
8610ae3eb7bSAmit Cohen 		err = nsim_fib6_rt_append(data, fib6_event);
862134c7532SAmit Cohen 		if (err)
863134c7532SAmit Cohen 			goto err_rt_offload_failed_flag_set;
86448bb9eb4SIdo Schimmel 		break;
86548bb9eb4SIdo Schimmel 	case FIB_EVENT_ENTRY_DEL:
8660ae3eb7bSAmit Cohen 		nsim_fib6_rt_remove(data, fib6_event);
86748bb9eb4SIdo Schimmel 		break;
86848bb9eb4SIdo Schimmel 	default:
86948bb9eb4SIdo Schimmel 		break;
87048bb9eb4SIdo Schimmel 	}
87148bb9eb4SIdo Schimmel 
872134c7532SAmit Cohen 	return 0;
873134c7532SAmit Cohen 
874134c7532SAmit Cohen err_rt_offload_failed_flag_set:
875134c7532SAmit Cohen 	nsim_fib6_rt_offload_failed_flag_set(data, fib6_event->rt_arr,
876134c7532SAmit Cohen 					     fib6_event->nrt6);
87748bb9eb4SIdo Schimmel 	return err;
87848bb9eb4SIdo Schimmel }
87948bb9eb4SIdo Schimmel 
nsim_fib_event(struct nsim_fib_event * fib_event)880c53d21afSJiapeng Chong static void nsim_fib_event(struct nsim_fib_event *fib_event)
88148bb9eb4SIdo Schimmel {
8820ae3eb7bSAmit Cohen 	switch (fib_event->family) {
88337923ed6SDavid Ahern 	case AF_INET:
8840ae3eb7bSAmit Cohen 		nsim_fib4_event(fib_event->data, &fib_event->fen_info,
8850ae3eb7bSAmit Cohen 				fib_event->event);
8860ae3eb7bSAmit Cohen 		fib_info_put(fib_event->fen_info.fi);
88737923ed6SDavid Ahern 		break;
88837923ed6SDavid Ahern 	case AF_INET6:
8890ae3eb7bSAmit Cohen 		nsim_fib6_event(fib_event->data, &fib_event->fib6_event,
8900ae3eb7bSAmit Cohen 				fib_event->event);
8910ae3eb7bSAmit Cohen 		nsim_fib6_event_fini(&fib_event->fib6_event);
89237923ed6SDavid Ahern 		break;
89337923ed6SDavid Ahern 	}
89437923ed6SDavid Ahern }
89537923ed6SDavid Ahern 
nsim_fib4_prepare_event(struct fib_notifier_info * info,struct nsim_fib_event * fib_event,unsigned long event)8960ae3eb7bSAmit Cohen static int nsim_fib4_prepare_event(struct fib_notifier_info *info,
8970ae3eb7bSAmit Cohen 				   struct nsim_fib_event *fib_event,
8980ae3eb7bSAmit Cohen 				   unsigned long event)
8990ae3eb7bSAmit Cohen {
9000ae3eb7bSAmit Cohen 	struct nsim_fib_data *data = fib_event->data;
9010ae3eb7bSAmit Cohen 	struct fib_entry_notifier_info *fen_info;
9020ae3eb7bSAmit Cohen 	struct netlink_ext_ack *extack;
9030ae3eb7bSAmit Cohen 	int err = 0;
9040ae3eb7bSAmit Cohen 
9050ae3eb7bSAmit Cohen 	fen_info = container_of(info, struct fib_entry_notifier_info,
9060ae3eb7bSAmit Cohen 				info);
9070ae3eb7bSAmit Cohen 	fib_event->fen_info = *fen_info;
9080ae3eb7bSAmit Cohen 	extack = info->extack;
9090ae3eb7bSAmit Cohen 
9100ae3eb7bSAmit Cohen 	switch (event) {
9110ae3eb7bSAmit Cohen 	case FIB_EVENT_ENTRY_REPLACE:
9120ae3eb7bSAmit Cohen 		err = nsim_fib_account(&data->ipv4.fib, true);
9130ae3eb7bSAmit Cohen 		if (err) {
9140ae3eb7bSAmit Cohen 			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
9150ae3eb7bSAmit Cohen 			return err;
9160ae3eb7bSAmit Cohen 		}
9170ae3eb7bSAmit Cohen 		break;
9180ae3eb7bSAmit Cohen 	case FIB_EVENT_ENTRY_DEL:
919974be75fSIdo Schimmel 		if (data->fail_route_delete) {
920974be75fSIdo Schimmel 			NL_SET_ERR_MSG_MOD(extack, "Failed to process route deletion");
921974be75fSIdo Schimmel 			return -EINVAL;
922974be75fSIdo Schimmel 		}
9230ae3eb7bSAmit Cohen 		nsim_fib_account(&data->ipv4.fib, false);
9240ae3eb7bSAmit Cohen 		break;
9250ae3eb7bSAmit Cohen 	}
9260ae3eb7bSAmit Cohen 
9270ae3eb7bSAmit Cohen 	/* Take reference on fib_info to prevent it from being
9280ae3eb7bSAmit Cohen 	 * freed while event is queued. Release it afterwards.
9290ae3eb7bSAmit Cohen 	 */
9300ae3eb7bSAmit Cohen 	fib_info_hold(fib_event->fen_info.fi);
9310ae3eb7bSAmit Cohen 
9320ae3eb7bSAmit Cohen 	return 0;
9330ae3eb7bSAmit Cohen }
9340ae3eb7bSAmit Cohen 
nsim_fib6_prepare_event(struct fib_notifier_info * info,struct nsim_fib_event * fib_event,unsigned long event)9350ae3eb7bSAmit Cohen static int nsim_fib6_prepare_event(struct fib_notifier_info *info,
9360ae3eb7bSAmit Cohen 				   struct nsim_fib_event *fib_event,
9370ae3eb7bSAmit Cohen 				   unsigned long event)
9380ae3eb7bSAmit Cohen {
9390ae3eb7bSAmit Cohen 	struct nsim_fib_data *data = fib_event->data;
9400ae3eb7bSAmit Cohen 	struct fib6_entry_notifier_info *fen6_info;
9410ae3eb7bSAmit Cohen 	struct netlink_ext_ack *extack;
9420ae3eb7bSAmit Cohen 	int err = 0;
9430ae3eb7bSAmit Cohen 
9440ae3eb7bSAmit Cohen 	fen6_info = container_of(info, struct fib6_entry_notifier_info,
9450ae3eb7bSAmit Cohen 				 info);
9460ae3eb7bSAmit Cohen 
9470ae3eb7bSAmit Cohen 	err = nsim_fib6_event_init(&fib_event->fib6_event, fen6_info);
9480ae3eb7bSAmit Cohen 	if (err)
9490ae3eb7bSAmit Cohen 		return err;
9500ae3eb7bSAmit Cohen 
9510ae3eb7bSAmit Cohen 	extack = info->extack;
9520ae3eb7bSAmit Cohen 	switch (event) {
9530ae3eb7bSAmit Cohen 	case FIB_EVENT_ENTRY_REPLACE:
9540ae3eb7bSAmit Cohen 		err = nsim_fib_account(&data->ipv6.fib, true);
9550ae3eb7bSAmit Cohen 		if (err) {
9560ae3eb7bSAmit Cohen 			NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
9570ae3eb7bSAmit Cohen 			goto err_fib6_event_fini;
9580ae3eb7bSAmit Cohen 		}
9590ae3eb7bSAmit Cohen 		break;
9600ae3eb7bSAmit Cohen 	case FIB_EVENT_ENTRY_DEL:
961974be75fSIdo Schimmel 		if (data->fail_route_delete) {
962974be75fSIdo Schimmel 			err = -EINVAL;
963974be75fSIdo Schimmel 			NL_SET_ERR_MSG_MOD(extack, "Failed to process route deletion");
964974be75fSIdo Schimmel 			goto err_fib6_event_fini;
965974be75fSIdo Schimmel 		}
9660ae3eb7bSAmit Cohen 		nsim_fib_account(&data->ipv6.fib, false);
9670ae3eb7bSAmit Cohen 		break;
9680ae3eb7bSAmit Cohen 	}
9690ae3eb7bSAmit Cohen 
9700ae3eb7bSAmit Cohen 	return 0;
9710ae3eb7bSAmit Cohen 
9720ae3eb7bSAmit Cohen err_fib6_event_fini:
9730ae3eb7bSAmit Cohen 	nsim_fib6_event_fini(&fib_event->fib6_event);
9740ae3eb7bSAmit Cohen 	return err;
9750ae3eb7bSAmit Cohen }
9760ae3eb7bSAmit Cohen 
nsim_fib_event_schedule_work(struct nsim_fib_data * data,struct fib_notifier_info * info,unsigned long event)9770ae3eb7bSAmit Cohen static int nsim_fib_event_schedule_work(struct nsim_fib_data *data,
9780ae3eb7bSAmit Cohen 					struct fib_notifier_info *info,
9790ae3eb7bSAmit Cohen 					unsigned long event)
9800ae3eb7bSAmit Cohen {
9810ae3eb7bSAmit Cohen 	struct nsim_fib_event *fib_event;
9820ae3eb7bSAmit Cohen 	int err;
9830ae3eb7bSAmit Cohen 
9840ae3eb7bSAmit Cohen 	if (info->family != AF_INET && info->family != AF_INET6)
9850ae3eb7bSAmit Cohen 		/* netdevsim does not support 'RTNL_FAMILY_IP6MR' and
9860ae3eb7bSAmit Cohen 		 * 'RTNL_FAMILY_IPMR' and should ignore them.
9870ae3eb7bSAmit Cohen 		 */
9880ae3eb7bSAmit Cohen 		return NOTIFY_DONE;
9890ae3eb7bSAmit Cohen 
9900ae3eb7bSAmit Cohen 	fib_event = kzalloc(sizeof(*fib_event), GFP_ATOMIC);
9910ae3eb7bSAmit Cohen 	if (!fib_event)
992180a6a3eSIdo Schimmel 		goto err_fib_event_alloc;
9930ae3eb7bSAmit Cohen 
9940ae3eb7bSAmit Cohen 	fib_event->data = data;
9950ae3eb7bSAmit Cohen 	fib_event->event = event;
9960ae3eb7bSAmit Cohen 	fib_event->family = info->family;
9970ae3eb7bSAmit Cohen 
9980ae3eb7bSAmit Cohen 	switch (info->family) {
9990ae3eb7bSAmit Cohen 	case AF_INET:
10000ae3eb7bSAmit Cohen 		err = nsim_fib4_prepare_event(info, fib_event, event);
10010ae3eb7bSAmit Cohen 		break;
10020ae3eb7bSAmit Cohen 	case AF_INET6:
10030ae3eb7bSAmit Cohen 		err = nsim_fib6_prepare_event(info, fib_event, event);
10040ae3eb7bSAmit Cohen 		break;
10050ae3eb7bSAmit Cohen 	}
10060ae3eb7bSAmit Cohen 
10070ae3eb7bSAmit Cohen 	if (err)
10080ae3eb7bSAmit Cohen 		goto err_fib_prepare_event;
10090ae3eb7bSAmit Cohen 
10100ae3eb7bSAmit Cohen 	/* Enqueue the event and trigger the work */
10110ae3eb7bSAmit Cohen 	spin_lock_bh(&data->fib_event_queue_lock);
10120ae3eb7bSAmit Cohen 	list_add_tail(&fib_event->list, &data->fib_event_queue);
10130ae3eb7bSAmit Cohen 	spin_unlock_bh(&data->fib_event_queue_lock);
10140ae3eb7bSAmit Cohen 	schedule_work(&data->fib_event_work);
10150ae3eb7bSAmit Cohen 
10160ae3eb7bSAmit Cohen 	return NOTIFY_DONE;
10170ae3eb7bSAmit Cohen 
10180ae3eb7bSAmit Cohen err_fib_prepare_event:
10190ae3eb7bSAmit Cohen 	kfree(fib_event);
1020180a6a3eSIdo Schimmel err_fib_event_alloc:
1021180a6a3eSIdo Schimmel 	if (event == FIB_EVENT_ENTRY_DEL)
1022180a6a3eSIdo Schimmel 		schedule_work(&data->fib_flush_work);
10230ae3eb7bSAmit Cohen 	return NOTIFY_BAD;
10240ae3eb7bSAmit Cohen }
10250ae3eb7bSAmit Cohen 
nsim_fib_event_nb(struct notifier_block * nb,unsigned long event,void * ptr)102637923ed6SDavid Ahern static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event,
102737923ed6SDavid Ahern 			     void *ptr)
102837923ed6SDavid Ahern {
1029a5facc4cSJiri Pirko 	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1030a5facc4cSJiri Pirko 						  fib_nb);
103137923ed6SDavid Ahern 	struct fib_notifier_info *info = ptr;
10320ae3eb7bSAmit Cohen 	int err;
103337923ed6SDavid Ahern 
103437923ed6SDavid Ahern 	switch (event) {
1035df561f66SGustavo A. R. Silva 	case FIB_EVENT_RULE_ADD:
103637923ed6SDavid Ahern 	case FIB_EVENT_RULE_DEL:
1037a5facc4cSJiri Pirko 		err = nsim_fib_rule_event(data, info,
1038a5facc4cSJiri Pirko 					  event == FIB_EVENT_RULE_ADD);
10390ae3eb7bSAmit Cohen 		return notifier_from_errno(err);
1040df561f66SGustavo A. R. Silva 	case FIB_EVENT_ENTRY_REPLACE:
1041df561f66SGustavo A. R. Silva 	case FIB_EVENT_ENTRY_APPEND:
104237923ed6SDavid Ahern 	case FIB_EVENT_ENTRY_DEL:
10430ae3eb7bSAmit Cohen 		return nsim_fib_event_schedule_work(data, info, event);
104437923ed6SDavid Ahern 	}
104537923ed6SDavid Ahern 
10460ae3eb7bSAmit Cohen 	return NOTIFY_DONE;
104737923ed6SDavid Ahern }
104837923ed6SDavid Ahern 
nsim_fib4_rt_free(struct nsim_fib_rt * fib_rt,struct nsim_fib_data * data)104948bb9eb4SIdo Schimmel static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt,
105048bb9eb4SIdo Schimmel 			      struct nsim_fib_data *data)
105148bb9eb4SIdo Schimmel {
105248bb9eb4SIdo Schimmel 	struct devlink *devlink = data->devlink;
105348bb9eb4SIdo Schimmel 	struct nsim_fib4_rt *fib4_rt;
105448bb9eb4SIdo Schimmel 
105548bb9eb4SIdo Schimmel 	fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common);
105648bb9eb4SIdo Schimmel 	nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false);
10570ae3eb7bSAmit Cohen 	nsim_fib_account(&data->ipv4.fib, false);
105848bb9eb4SIdo Schimmel 	nsim_fib4_rt_destroy(fib4_rt);
105948bb9eb4SIdo Schimmel }
106048bb9eb4SIdo Schimmel 
nsim_fib6_rt_free(struct nsim_fib_rt * fib_rt,struct nsim_fib_data * data)106148bb9eb4SIdo Schimmel static void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt,
106248bb9eb4SIdo Schimmel 			      struct nsim_fib_data *data)
106348bb9eb4SIdo Schimmel {
106448bb9eb4SIdo Schimmel 	struct nsim_fib6_rt *fib6_rt;
106548bb9eb4SIdo Schimmel 
106648bb9eb4SIdo Schimmel 	fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common);
1067fbaca8f8SAmit Cohen 	nsim_fib6_rt_hw_flags_set(data, fib6_rt, false);
10680ae3eb7bSAmit Cohen 	nsim_fib_account(&data->ipv6.fib, false);
106948bb9eb4SIdo Schimmel 	nsim_fib6_rt_destroy(fib6_rt);
107048bb9eb4SIdo Schimmel }
107148bb9eb4SIdo Schimmel 
nsim_fib_rt_free(void * ptr,void * arg)107248bb9eb4SIdo Schimmel static void nsim_fib_rt_free(void *ptr, void *arg)
107348bb9eb4SIdo Schimmel {
107448bb9eb4SIdo Schimmel 	struct nsim_fib_rt *fib_rt = ptr;
107548bb9eb4SIdo Schimmel 	struct nsim_fib_data *data = arg;
107648bb9eb4SIdo Schimmel 
107748bb9eb4SIdo Schimmel 	switch (fib_rt->key.family) {
107848bb9eb4SIdo Schimmel 	case AF_INET:
107948bb9eb4SIdo Schimmel 		nsim_fib4_rt_free(fib_rt, data);
108048bb9eb4SIdo Schimmel 		break;
108148bb9eb4SIdo Schimmel 	case AF_INET6:
108248bb9eb4SIdo Schimmel 		nsim_fib6_rt_free(fib_rt, data);
108348bb9eb4SIdo Schimmel 		break;
108448bb9eb4SIdo Schimmel 	default:
108548bb9eb4SIdo Schimmel 		WARN_ON_ONCE(1);
108648bb9eb4SIdo Schimmel 	}
108748bb9eb4SIdo Schimmel }
108848bb9eb4SIdo Schimmel 
108937923ed6SDavid Ahern /* inconsistent dump, trying again */
nsim_fib_dump_inconsistent(struct notifier_block * nb)109037923ed6SDavid Ahern static void nsim_fib_dump_inconsistent(struct notifier_block *nb)
109137923ed6SDavid Ahern {
1092a5facc4cSJiri Pirko 	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
1093a5facc4cSJiri Pirko 						  fib_nb);
109448bb9eb4SIdo Schimmel 	struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
109537923ed6SDavid Ahern 
10960ae3eb7bSAmit Cohen 	/* Flush the work to make sure there is no race with notifications. */
10970ae3eb7bSAmit Cohen 	flush_work(&data->fib_event_work);
10980ae3eb7bSAmit Cohen 
109948bb9eb4SIdo Schimmel 	/* The notifier block is still not registered, so we do not need to
110048bb9eb4SIdo Schimmel 	 * take any locks here.
110148bb9eb4SIdo Schimmel 	 */
110248bb9eb4SIdo Schimmel 	list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
110348bb9eb4SIdo Schimmel 		rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
110448bb9eb4SIdo Schimmel 				       nsim_fib_rt_ht_params);
110548bb9eb4SIdo Schimmel 		nsim_fib_rt_free(fib_rt, data);
110648bb9eb4SIdo Schimmel 	}
110748bb9eb4SIdo Schimmel 
11089e635a21SAmit Cohen 	atomic64_set(&data->ipv4.rules.num, 0ULL);
11099e635a21SAmit Cohen 	atomic64_set(&data->ipv6.rules.num, 0ULL);
111037923ed6SDavid Ahern }
1111a5facc4cSJiri Pirko 
nsim_nexthop_create(struct nsim_fib_data * data,struct nh_notifier_info * info)11128fa84742SIdo Schimmel static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data,
11138fa84742SIdo Schimmel 						struct nh_notifier_info *info)
11148fa84742SIdo Schimmel {
11158fa84742SIdo Schimmel 	struct nsim_nexthop *nexthop;
11168fa84742SIdo Schimmel 	u64 occ = 0;
11178fa84742SIdo Schimmel 	int i;
11188fa84742SIdo Schimmel 
11198fa84742SIdo Schimmel 	nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL);
11208fa84742SIdo Schimmel 	if (!nexthop)
112109ad6becSIdo Schimmel 		return ERR_PTR(-ENOMEM);
11228fa84742SIdo Schimmel 
11238fa84742SIdo Schimmel 	nexthop->id = info->id;
11248fa84742SIdo Schimmel 
11258fa84742SIdo Schimmel 	/* Determine the number of nexthop entries the new nexthop will
11268fa84742SIdo Schimmel 	 * occupy.
11278fa84742SIdo Schimmel 	 */
11288fa84742SIdo Schimmel 
112909ad6becSIdo Schimmel 	switch (info->type) {
113009ad6becSIdo Schimmel 	case NH_NOTIFIER_INFO_TYPE_SINGLE:
11318fa84742SIdo Schimmel 		occ = 1;
113209ad6becSIdo Schimmel 		break;
113309ad6becSIdo Schimmel 	case NH_NOTIFIER_INFO_TYPE_GRP:
11348fa84742SIdo Schimmel 		for (i = 0; i < info->nh_grp->num_nh; i++)
11358fa84742SIdo Schimmel 			occ += info->nh_grp->nh_entries[i].weight;
113609ad6becSIdo Schimmel 		break;
1137d8eaa4faSIdo Schimmel 	case NH_NOTIFIER_INFO_TYPE_RES_TABLE:
1138d8eaa4faSIdo Schimmel 		occ = info->nh_res_table->num_nh_buckets;
1139d8eaa4faSIdo Schimmel 		nexthop->is_resilient = true;
1140d8eaa4faSIdo Schimmel 		break;
114109ad6becSIdo Schimmel 	default:
114209ad6becSIdo Schimmel 		NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type");
114309ad6becSIdo Schimmel 		kfree(nexthop);
114409ad6becSIdo Schimmel 		return ERR_PTR(-EOPNOTSUPP);
114509ad6becSIdo Schimmel 	}
11468fa84742SIdo Schimmel 
11478fa84742SIdo Schimmel 	nexthop->occ = occ;
11488fa84742SIdo Schimmel 	return nexthop;
11498fa84742SIdo Schimmel }
11508fa84742SIdo Schimmel 
nsim_nexthop_destroy(struct nsim_nexthop * nexthop)11518fa84742SIdo Schimmel static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop)
11528fa84742SIdo Schimmel {
11538fa84742SIdo Schimmel 	kfree(nexthop);
11548fa84742SIdo Schimmel }
11558fa84742SIdo Schimmel 
nsim_nexthop_account(struct nsim_fib_data * data,u64 occ,bool add,struct netlink_ext_ack * extack)11568fa84742SIdo Schimmel static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ,
11578fa84742SIdo Schimmel 				bool add, struct netlink_ext_ack *extack)
11588fa84742SIdo Schimmel {
11599e635a21SAmit Cohen 	int i, err = 0;
11608fa84742SIdo Schimmel 
11618fa84742SIdo Schimmel 	if (add) {
11629e635a21SAmit Cohen 		for (i = 0; i < occ; i++)
11639e635a21SAmit Cohen 			if (!atomic64_add_unless(&data->nexthops.num, 1,
11649e635a21SAmit Cohen 						 data->nexthops.max)) {
11658fa84742SIdo Schimmel 				err = -ENOSPC;
11668fa84742SIdo Schimmel 				NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops");
11679e635a21SAmit Cohen 				goto err_num_decrease;
11688fa84742SIdo Schimmel 			}
11698fa84742SIdo Schimmel 	} else {
11709e635a21SAmit Cohen 		if (WARN_ON(occ > atomic64_read(&data->nexthops.num)))
11718fa84742SIdo Schimmel 			return -EINVAL;
11729e635a21SAmit Cohen 		atomic64_sub(occ, &data->nexthops.num);
11738fa84742SIdo Schimmel 	}
11748fa84742SIdo Schimmel 
11758fa84742SIdo Schimmel 	return err;
11769e635a21SAmit Cohen 
11779e635a21SAmit Cohen err_num_decrease:
11789e635a21SAmit Cohen 	atomic64_sub(i, &data->nexthops.num);
11799e635a21SAmit Cohen 	return err;
11809e635a21SAmit Cohen 
11818fa84742SIdo Schimmel }
11828fa84742SIdo Schimmel 
nsim_nexthop_hw_flags_set(struct net * net,const struct nsim_nexthop * nexthop,bool trap)118340ff8371SIdo Schimmel static void nsim_nexthop_hw_flags_set(struct net *net,
118440ff8371SIdo Schimmel 				      const struct nsim_nexthop *nexthop,
118540ff8371SIdo Schimmel 				      bool trap)
118640ff8371SIdo Schimmel {
1187d8eaa4faSIdo Schimmel 	int i;
1188d8eaa4faSIdo Schimmel 
118940ff8371SIdo Schimmel 	nexthop_set_hw_flags(net, nexthop->id, false, trap);
1190d8eaa4faSIdo Schimmel 
1191d8eaa4faSIdo Schimmel 	if (!nexthop->is_resilient)
1192d8eaa4faSIdo Schimmel 		return;
1193d8eaa4faSIdo Schimmel 
1194d8eaa4faSIdo Schimmel 	for (i = 0; i < nexthop->occ; i++)
1195d8eaa4faSIdo Schimmel 		nexthop_bucket_set_hw_flags(net, nexthop->id, i, false, trap);
119640ff8371SIdo Schimmel }
119740ff8371SIdo Schimmel 
nsim_nexthop_add(struct nsim_fib_data * data,struct nsim_nexthop * nexthop,struct netlink_ext_ack * extack)11988fa84742SIdo Schimmel static int nsim_nexthop_add(struct nsim_fib_data *data,
11998fa84742SIdo Schimmel 			    struct nsim_nexthop *nexthop,
12008fa84742SIdo Schimmel 			    struct netlink_ext_ack *extack)
12018fa84742SIdo Schimmel {
12028fa84742SIdo Schimmel 	struct net *net = devlink_net(data->devlink);
12038fa84742SIdo Schimmel 	int err;
12048fa84742SIdo Schimmel 
12058fa84742SIdo Schimmel 	err = nsim_nexthop_account(data, nexthop->occ, true, extack);
12068fa84742SIdo Schimmel 	if (err)
12078fa84742SIdo Schimmel 		return err;
12088fa84742SIdo Schimmel 
12098fa84742SIdo Schimmel 	err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node,
12108fa84742SIdo Schimmel 				     nsim_nexthop_ht_params);
12118fa84742SIdo Schimmel 	if (err) {
12128fa84742SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop");
12138fa84742SIdo Schimmel 		goto err_nexthop_dismiss;
12148fa84742SIdo Schimmel 	}
12158fa84742SIdo Schimmel 
121640ff8371SIdo Schimmel 	nsim_nexthop_hw_flags_set(net, nexthop, true);
12178fa84742SIdo Schimmel 
12188fa84742SIdo Schimmel 	return 0;
12198fa84742SIdo Schimmel 
12208fa84742SIdo Schimmel err_nexthop_dismiss:
12218fa84742SIdo Schimmel 	nsim_nexthop_account(data, nexthop->occ, false, extack);
12228fa84742SIdo Schimmel 	return err;
12238fa84742SIdo Schimmel }
12248fa84742SIdo Schimmel 
nsim_nexthop_replace(struct nsim_fib_data * data,struct nsim_nexthop * nexthop,struct nsim_nexthop * nexthop_old,struct netlink_ext_ack * extack)12258fa84742SIdo Schimmel static int nsim_nexthop_replace(struct nsim_fib_data *data,
12268fa84742SIdo Schimmel 				struct nsim_nexthop *nexthop,
12278fa84742SIdo Schimmel 				struct nsim_nexthop *nexthop_old,
12288fa84742SIdo Schimmel 				struct netlink_ext_ack *extack)
12298fa84742SIdo Schimmel {
12308fa84742SIdo Schimmel 	struct net *net = devlink_net(data->devlink);
12318fa84742SIdo Schimmel 	int err;
12328fa84742SIdo Schimmel 
12338fa84742SIdo Schimmel 	err = nsim_nexthop_account(data, nexthop->occ, true, extack);
12348fa84742SIdo Schimmel 	if (err)
12358fa84742SIdo Schimmel 		return err;
12368fa84742SIdo Schimmel 
12378fa84742SIdo Schimmel 	err = rhashtable_replace_fast(&data->nexthop_ht,
12388fa84742SIdo Schimmel 				      &nexthop_old->ht_node, &nexthop->ht_node,
12398fa84742SIdo Schimmel 				      nsim_nexthop_ht_params);
12408fa84742SIdo Schimmel 	if (err) {
12418fa84742SIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop");
12428fa84742SIdo Schimmel 		goto err_nexthop_dismiss;
12438fa84742SIdo Schimmel 	}
12448fa84742SIdo Schimmel 
124540ff8371SIdo Schimmel 	nsim_nexthop_hw_flags_set(net, nexthop, true);
12468fa84742SIdo Schimmel 	nsim_nexthop_account(data, nexthop_old->occ, false, extack);
12478fa84742SIdo Schimmel 	nsim_nexthop_destroy(nexthop_old);
12488fa84742SIdo Schimmel 
12498fa84742SIdo Schimmel 	return 0;
12508fa84742SIdo Schimmel 
12518fa84742SIdo Schimmel err_nexthop_dismiss:
12528fa84742SIdo Schimmel 	nsim_nexthop_account(data, nexthop->occ, false, extack);
12538fa84742SIdo Schimmel 	return err;
12548fa84742SIdo Schimmel }
12558fa84742SIdo Schimmel 
nsim_nexthop_insert(struct nsim_fib_data * data,struct nh_notifier_info * info)12568fa84742SIdo Schimmel static int nsim_nexthop_insert(struct nsim_fib_data *data,
12578fa84742SIdo Schimmel 			       struct nh_notifier_info *info)
12588fa84742SIdo Schimmel {
12598fa84742SIdo Schimmel 	struct nsim_nexthop *nexthop, *nexthop_old;
12608fa84742SIdo Schimmel 	int err;
12618fa84742SIdo Schimmel 
12628fa84742SIdo Schimmel 	nexthop = nsim_nexthop_create(data, info);
126309ad6becSIdo Schimmel 	if (IS_ERR(nexthop))
126409ad6becSIdo Schimmel 		return PTR_ERR(nexthop);
12658fa84742SIdo Schimmel 
12668fa84742SIdo Schimmel 	nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
12678fa84742SIdo Schimmel 					     nsim_nexthop_ht_params);
12688fa84742SIdo Schimmel 	if (!nexthop_old)
12698fa84742SIdo Schimmel 		err = nsim_nexthop_add(data, nexthop, info->extack);
12708fa84742SIdo Schimmel 	else
12718fa84742SIdo Schimmel 		err = nsim_nexthop_replace(data, nexthop, nexthop_old,
12728fa84742SIdo Schimmel 					   info->extack);
12738fa84742SIdo Schimmel 
12748fa84742SIdo Schimmel 	if (err)
12758fa84742SIdo Schimmel 		nsim_nexthop_destroy(nexthop);
12768fa84742SIdo Schimmel 
12778fa84742SIdo Schimmel 	return err;
12788fa84742SIdo Schimmel }
12798fa84742SIdo Schimmel 
nsim_nexthop_remove(struct nsim_fib_data * data,struct nh_notifier_info * info)12808fa84742SIdo Schimmel static void nsim_nexthop_remove(struct nsim_fib_data *data,
12818fa84742SIdo Schimmel 				struct nh_notifier_info *info)
12828fa84742SIdo Schimmel {
12838fa84742SIdo Schimmel 	struct nsim_nexthop *nexthop;
12848fa84742SIdo Schimmel 
12858fa84742SIdo Schimmel 	nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
12868fa84742SIdo Schimmel 					 nsim_nexthop_ht_params);
12878fa84742SIdo Schimmel 	if (!nexthop)
12888fa84742SIdo Schimmel 		return;
12898fa84742SIdo Schimmel 
12908fa84742SIdo Schimmel 	rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node,
12918fa84742SIdo Schimmel 			       nsim_nexthop_ht_params);
12928fa84742SIdo Schimmel 	nsim_nexthop_account(data, nexthop->occ, false, info->extack);
12938fa84742SIdo Schimmel 	nsim_nexthop_destroy(nexthop);
12948fa84742SIdo Schimmel }
12958fa84742SIdo Schimmel 
nsim_nexthop_res_table_pre_replace(struct nsim_fib_data * data,struct nh_notifier_info * info)1296d8eaa4faSIdo Schimmel static int nsim_nexthop_res_table_pre_replace(struct nsim_fib_data *data,
1297d8eaa4faSIdo Schimmel 					      struct nh_notifier_info *info)
1298d8eaa4faSIdo Schimmel {
1299d8eaa4faSIdo Schimmel 	if (data->fail_res_nexthop_group_replace) {
1300d8eaa4faSIdo Schimmel 		NL_SET_ERR_MSG_MOD(info->extack, "Failed to replace a resilient nexthop group");
1301d8eaa4faSIdo Schimmel 		return -EINVAL;
1302d8eaa4faSIdo Schimmel 	}
1303d8eaa4faSIdo Schimmel 
1304d8eaa4faSIdo Schimmel 	return 0;
1305d8eaa4faSIdo Schimmel }
1306d8eaa4faSIdo Schimmel 
nsim_nexthop_bucket_replace(struct nsim_fib_data * data,struct nh_notifier_info * info)1307d8eaa4faSIdo Schimmel static int nsim_nexthop_bucket_replace(struct nsim_fib_data *data,
1308d8eaa4faSIdo Schimmel 				       struct nh_notifier_info *info)
1309d8eaa4faSIdo Schimmel {
1310d8eaa4faSIdo Schimmel 	if (data->fail_nexthop_bucket_replace) {
1311d8eaa4faSIdo Schimmel 		NL_SET_ERR_MSG_MOD(info->extack, "Failed to replace nexthop bucket");
1312d8eaa4faSIdo Schimmel 		return -EINVAL;
1313d8eaa4faSIdo Schimmel 	}
1314d8eaa4faSIdo Schimmel 
1315d8eaa4faSIdo Schimmel 	nexthop_bucket_set_hw_flags(info->net, info->id,
1316d8eaa4faSIdo Schimmel 				    info->nh_res_bucket->bucket_index,
1317d8eaa4faSIdo Schimmel 				    false, true);
1318d8eaa4faSIdo Schimmel 
1319d8eaa4faSIdo Schimmel 	return 0;
1320d8eaa4faSIdo Schimmel }
1321d8eaa4faSIdo Schimmel 
nsim_nexthop_event_nb(struct notifier_block * nb,unsigned long event,void * ptr)13228fa84742SIdo Schimmel static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event,
13238fa84742SIdo Schimmel 				 void *ptr)
13248fa84742SIdo Schimmel {
13258fa84742SIdo Schimmel 	struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data,
13268fa84742SIdo Schimmel 						  nexthop_nb);
13278fa84742SIdo Schimmel 	struct nh_notifier_info *info = ptr;
13288fa84742SIdo Schimmel 	int err = 0;
13298fa84742SIdo Schimmel 
133086927c9cSPetr Machata 	mutex_lock(&data->nh_lock);
13318fa84742SIdo Schimmel 	switch (event) {
13328fa84742SIdo Schimmel 	case NEXTHOP_EVENT_REPLACE:
13338fa84742SIdo Schimmel 		err = nsim_nexthop_insert(data, info);
13348fa84742SIdo Schimmel 		break;
13358fa84742SIdo Schimmel 	case NEXTHOP_EVENT_DEL:
13368fa84742SIdo Schimmel 		nsim_nexthop_remove(data, info);
13378fa84742SIdo Schimmel 		break;
1338d8eaa4faSIdo Schimmel 	case NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE:
1339d8eaa4faSIdo Schimmel 		err = nsim_nexthop_res_table_pre_replace(data, info);
1340d8eaa4faSIdo Schimmel 		break;
1341d8eaa4faSIdo Schimmel 	case NEXTHOP_EVENT_BUCKET_REPLACE:
1342d8eaa4faSIdo Schimmel 		err = nsim_nexthop_bucket_replace(data, info);
1343d8eaa4faSIdo Schimmel 		break;
13448fa84742SIdo Schimmel 	default:
13458fa84742SIdo Schimmel 		break;
13468fa84742SIdo Schimmel 	}
13478fa84742SIdo Schimmel 
134886927c9cSPetr Machata 	mutex_unlock(&data->nh_lock);
13498fa84742SIdo Schimmel 	return notifier_from_errno(err);
13508fa84742SIdo Schimmel }
13518fa84742SIdo Schimmel 
nsim_nexthop_free(void * ptr,void * arg)13528fa84742SIdo Schimmel static void nsim_nexthop_free(void *ptr, void *arg)
13538fa84742SIdo Schimmel {
13548fa84742SIdo Schimmel 	struct nsim_nexthop *nexthop = ptr;
13558fa84742SIdo Schimmel 	struct nsim_fib_data *data = arg;
13568fa84742SIdo Schimmel 	struct net *net;
13578fa84742SIdo Schimmel 
13588fa84742SIdo Schimmel 	net = devlink_net(data->devlink);
135940ff8371SIdo Schimmel 	nsim_nexthop_hw_flags_set(net, nexthop, false);
13608fa84742SIdo Schimmel 	nsim_nexthop_account(data, nexthop->occ, false, NULL);
13618fa84742SIdo Schimmel 	nsim_nexthop_destroy(nexthop);
13628fa84742SIdo Schimmel }
13638fa84742SIdo Schimmel 
nsim_nexthop_bucket_activity_write(struct file * file,const char __user * user_buf,size_t size,loff_t * ppos)1364c6385c0bSIdo Schimmel static ssize_t nsim_nexthop_bucket_activity_write(struct file *file,
1365c6385c0bSIdo Schimmel 						  const char __user *user_buf,
1366c6385c0bSIdo Schimmel 						  size_t size, loff_t *ppos)
1367c6385c0bSIdo Schimmel {
1368c6385c0bSIdo Schimmel 	struct nsim_fib_data *data = file->private_data;
1369c6385c0bSIdo Schimmel 	struct net *net = devlink_net(data->devlink);
1370c6385c0bSIdo Schimmel 	struct nsim_nexthop *nexthop;
1371c6385c0bSIdo Schimmel 	unsigned long *activity;
1372c6385c0bSIdo Schimmel 	loff_t pos = *ppos;
1373c6385c0bSIdo Schimmel 	u16 bucket_index;
1374c6385c0bSIdo Schimmel 	char buf[128];
1375c6385c0bSIdo Schimmel 	int err = 0;
1376c6385c0bSIdo Schimmel 	u32 nhid;
1377c6385c0bSIdo Schimmel 
1378c6385c0bSIdo Schimmel 	if (pos != 0)
1379c6385c0bSIdo Schimmel 		return -EINVAL;
1380*4ce1f56aSZichen Xie 	if (size > sizeof(buf) - 1)
1381c6385c0bSIdo Schimmel 		return -EINVAL;
1382c6385c0bSIdo Schimmel 	if (copy_from_user(buf, user_buf, size))
1383c6385c0bSIdo Schimmel 		return -EFAULT;
1384*4ce1f56aSZichen Xie 	buf[size] = 0;
1385*4ce1f56aSZichen Xie 
1386c6385c0bSIdo Schimmel 	if (sscanf(buf, "%u %hu", &nhid, &bucket_index) != 2)
1387c6385c0bSIdo Schimmel 		return -EINVAL;
1388c6385c0bSIdo Schimmel 
1389c6385c0bSIdo Schimmel 	rtnl_lock();
1390c6385c0bSIdo Schimmel 
1391c6385c0bSIdo Schimmel 	nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &nhid,
1392c6385c0bSIdo Schimmel 					 nsim_nexthop_ht_params);
1393c6385c0bSIdo Schimmel 	if (!nexthop || !nexthop->is_resilient ||
1394c6385c0bSIdo Schimmel 	    bucket_index >= nexthop->occ) {
1395c6385c0bSIdo Schimmel 		err = -EINVAL;
1396c6385c0bSIdo Schimmel 		goto out;
1397c6385c0bSIdo Schimmel 	}
1398c6385c0bSIdo Schimmel 
1399c6385c0bSIdo Schimmel 	activity = bitmap_zalloc(nexthop->occ, GFP_KERNEL);
1400c6385c0bSIdo Schimmel 	if (!activity) {
1401c6385c0bSIdo Schimmel 		err = -ENOMEM;
1402c6385c0bSIdo Schimmel 		goto out;
1403c6385c0bSIdo Schimmel 	}
1404c6385c0bSIdo Schimmel 
1405c6385c0bSIdo Schimmel 	bitmap_set(activity, bucket_index, 1);
1406c6385c0bSIdo Schimmel 	nexthop_res_grp_activity_update(net, nhid, nexthop->occ, activity);
1407c6385c0bSIdo Schimmel 	bitmap_free(activity);
1408c6385c0bSIdo Schimmel 
1409c6385c0bSIdo Schimmel out:
1410c6385c0bSIdo Schimmel 	rtnl_unlock();
1411c6385c0bSIdo Schimmel 
1412c6385c0bSIdo Schimmel 	*ppos = size;
1413c6385c0bSIdo Schimmel 	return err ?: size;
1414c6385c0bSIdo Schimmel }
1415c6385c0bSIdo Schimmel 
1416c6385c0bSIdo Schimmel static const struct file_operations nsim_nexthop_bucket_activity_fops = {
1417c6385c0bSIdo Schimmel 	.open = simple_open,
1418c6385c0bSIdo Schimmel 	.write = nsim_nexthop_bucket_activity_write,
1419c6385c0bSIdo Schimmel 	.owner = THIS_MODULE,
1420c6385c0bSIdo Schimmel };
1421c6385c0bSIdo Schimmel 
nsim_fib_ipv4_resource_occ_get(void * priv)1422a5facc4cSJiri Pirko static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
1423a5facc4cSJiri Pirko {
1424a5facc4cSJiri Pirko 	struct nsim_fib_data *data = priv;
1425a5facc4cSJiri Pirko 
1426a5facc4cSJiri Pirko 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
142759c84b9fSDavid Ahern }
142837923ed6SDavid Ahern 
nsim_fib_ipv4_rules_res_occ_get(void * priv)1429a5facc4cSJiri Pirko static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
143037923ed6SDavid Ahern {
1431a5facc4cSJiri Pirko 	struct nsim_fib_data *data = priv;
1432a5facc4cSJiri Pirko 
1433a5facc4cSJiri Pirko 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
1434a5facc4cSJiri Pirko }
1435a5facc4cSJiri Pirko 
nsim_fib_ipv6_resource_occ_get(void * priv)1436a5facc4cSJiri Pirko static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
1437a5facc4cSJiri Pirko {
1438a5facc4cSJiri Pirko 	struct nsim_fib_data *data = priv;
1439a5facc4cSJiri Pirko 
1440a5facc4cSJiri Pirko 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
1441a5facc4cSJiri Pirko }
1442a5facc4cSJiri Pirko 
nsim_fib_ipv6_rules_res_occ_get(void * priv)1443a5facc4cSJiri Pirko static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
1444a5facc4cSJiri Pirko {
1445a5facc4cSJiri Pirko 	struct nsim_fib_data *data = priv;
1446a5facc4cSJiri Pirko 
1447a5facc4cSJiri Pirko 	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
1448a5facc4cSJiri Pirko }
1449a5facc4cSJiri Pirko 
nsim_fib_nexthops_res_occ_get(void * priv)145035266255SIdo Schimmel static u64 nsim_fib_nexthops_res_occ_get(void *priv)
145135266255SIdo Schimmel {
145235266255SIdo Schimmel 	struct nsim_fib_data *data = priv;
145335266255SIdo Schimmel 
145435266255SIdo Schimmel 	return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false);
145535266255SIdo Schimmel }
145635266255SIdo Schimmel 
nsim_fib_set_max_all(struct nsim_fib_data * data,struct devlink * devlink)145775ba029fSJiri Pirko static void nsim_fib_set_max_all(struct nsim_fib_data *data,
145875ba029fSJiri Pirko 				 struct devlink *devlink)
145975ba029fSJiri Pirko {
1460f36c82acSColin Ian King 	static const enum nsim_resource_id res_ids[] = {
146175ba029fSJiri Pirko 		NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
146235266255SIdo Schimmel 		NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES,
146335266255SIdo Schimmel 		NSIM_RESOURCE_NEXTHOPS,
146475ba029fSJiri Pirko 	};
146575ba029fSJiri Pirko 	int i;
146675ba029fSJiri Pirko 
146775ba029fSJiri Pirko 	for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
146875ba029fSJiri Pirko 		int err;
146975ba029fSJiri Pirko 		u64 val;
147075ba029fSJiri Pirko 
1471012ec02aSJiri Pirko 		err = devl_resource_size_get(devlink, res_ids[i], &val);
147275ba029fSJiri Pirko 		if (err)
147375ba029fSJiri Pirko 			val = (u64) -1;
147475ba029fSJiri Pirko 		nsim_fib_set_max(data, res_ids[i], val);
147575ba029fSJiri Pirko 	}
147675ba029fSJiri Pirko }
147775ba029fSJiri Pirko 
nsim_fib_event_work(struct work_struct * work)14780ae3eb7bSAmit Cohen static void nsim_fib_event_work(struct work_struct *work)
14790ae3eb7bSAmit Cohen {
14800ae3eb7bSAmit Cohen 	struct nsim_fib_data *data = container_of(work, struct nsim_fib_data,
14810ae3eb7bSAmit Cohen 						  fib_event_work);
14820ae3eb7bSAmit Cohen 	struct nsim_fib_event *fib_event, *next_fib_event;
14830ae3eb7bSAmit Cohen 
14840ae3eb7bSAmit Cohen 	LIST_HEAD(fib_event_queue);
14850ae3eb7bSAmit Cohen 
14860ae3eb7bSAmit Cohen 	spin_lock_bh(&data->fib_event_queue_lock);
14870ae3eb7bSAmit Cohen 	list_splice_init(&data->fib_event_queue, &fib_event_queue);
14880ae3eb7bSAmit Cohen 	spin_unlock_bh(&data->fib_event_queue_lock);
14890ae3eb7bSAmit Cohen 
14900ae3eb7bSAmit Cohen 	mutex_lock(&data->fib_lock);
14910ae3eb7bSAmit Cohen 	list_for_each_entry_safe(fib_event, next_fib_event, &fib_event_queue,
14920ae3eb7bSAmit Cohen 				 list) {
14930ae3eb7bSAmit Cohen 		nsim_fib_event(fib_event);
14940ae3eb7bSAmit Cohen 		list_del(&fib_event->list);
14950ae3eb7bSAmit Cohen 		kfree(fib_event);
14960ae3eb7bSAmit Cohen 		cond_resched();
14970ae3eb7bSAmit Cohen 	}
14980ae3eb7bSAmit Cohen 	mutex_unlock(&data->fib_lock);
14990ae3eb7bSAmit Cohen }
15000ae3eb7bSAmit Cohen 
nsim_fib_flush_work(struct work_struct * work)1501180a6a3eSIdo Schimmel static void nsim_fib_flush_work(struct work_struct *work)
1502180a6a3eSIdo Schimmel {
1503180a6a3eSIdo Schimmel 	struct nsim_fib_data *data = container_of(work, struct nsim_fib_data,
1504180a6a3eSIdo Schimmel 						  fib_flush_work);
1505180a6a3eSIdo Schimmel 	struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
1506180a6a3eSIdo Schimmel 
1507180a6a3eSIdo Schimmel 	/* Process pending work. */
1508180a6a3eSIdo Schimmel 	flush_work(&data->fib_event_work);
1509180a6a3eSIdo Schimmel 
1510180a6a3eSIdo Schimmel 	mutex_lock(&data->fib_lock);
1511180a6a3eSIdo Schimmel 	list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
1512180a6a3eSIdo Schimmel 		rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
1513180a6a3eSIdo Schimmel 				       nsim_fib_rt_ht_params);
1514180a6a3eSIdo Schimmel 		nsim_fib_rt_free(fib_rt, data);
1515180a6a3eSIdo Schimmel 	}
1516180a6a3eSIdo Schimmel 	mutex_unlock(&data->fib_lock);
1517180a6a3eSIdo Schimmel }
1518180a6a3eSIdo Schimmel 
1519134c7532SAmit Cohen static int
nsim_fib_debugfs_init(struct nsim_fib_data * data,struct nsim_dev * nsim_dev)1520134c7532SAmit Cohen nsim_fib_debugfs_init(struct nsim_fib_data *data, struct nsim_dev *nsim_dev)
1521134c7532SAmit Cohen {
1522134c7532SAmit Cohen 	data->ddir = debugfs_create_dir("fib", nsim_dev->ddir);
1523134c7532SAmit Cohen 	if (IS_ERR(data->ddir))
1524134c7532SAmit Cohen 		return PTR_ERR(data->ddir);
1525134c7532SAmit Cohen 
1526134c7532SAmit Cohen 	data->fail_route_offload = false;
1527134c7532SAmit Cohen 	debugfs_create_bool("fail_route_offload", 0600, data->ddir,
1528134c7532SAmit Cohen 			    &data->fail_route_offload);
1529d8eaa4faSIdo Schimmel 
1530d8eaa4faSIdo Schimmel 	data->fail_res_nexthop_group_replace = false;
1531d8eaa4faSIdo Schimmel 	debugfs_create_bool("fail_res_nexthop_group_replace", 0600, data->ddir,
1532d8eaa4faSIdo Schimmel 			    &data->fail_res_nexthop_group_replace);
1533d8eaa4faSIdo Schimmel 
1534d8eaa4faSIdo Schimmel 	data->fail_nexthop_bucket_replace = false;
1535d8eaa4faSIdo Schimmel 	debugfs_create_bool("fail_nexthop_bucket_replace", 0600, data->ddir,
1536d8eaa4faSIdo Schimmel 			    &data->fail_nexthop_bucket_replace);
1537c6385c0bSIdo Schimmel 
1538c6385c0bSIdo Schimmel 	debugfs_create_file("nexthop_bucket_activity", 0200, data->ddir,
1539c6385c0bSIdo Schimmel 			    data, &nsim_nexthop_bucket_activity_fops);
1540974be75fSIdo Schimmel 
1541974be75fSIdo Schimmel 	data->fail_route_delete = false;
1542974be75fSIdo Schimmel 	debugfs_create_bool("fail_route_delete", 0600, data->ddir,
1543974be75fSIdo Schimmel 			    &data->fail_route_delete);
1544134c7532SAmit Cohen 	return 0;
1545134c7532SAmit Cohen }
1546134c7532SAmit Cohen 
nsim_fib_debugfs_exit(struct nsim_fib_data * data)1547134c7532SAmit Cohen static void nsim_fib_debugfs_exit(struct nsim_fib_data *data)
1548134c7532SAmit Cohen {
1549134c7532SAmit Cohen 	debugfs_remove_recursive(data->ddir);
1550134c7532SAmit Cohen }
1551134c7532SAmit Cohen 
nsim_fib_create(struct devlink * devlink,struct netlink_ext_ack * extack)155275ba029fSJiri Pirko struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
155375ba029fSJiri Pirko 				      struct netlink_ext_ack *extack)
1554a5facc4cSJiri Pirko {
1555a5facc4cSJiri Pirko 	struct nsim_fib_data *data;
1556134c7532SAmit Cohen 	struct nsim_dev *nsim_dev;
1557a5facc4cSJiri Pirko 	int err;
1558a5facc4cSJiri Pirko 
1559a5facc4cSJiri Pirko 	data = kzalloc(sizeof(*data), GFP_KERNEL);
1560a5facc4cSJiri Pirko 	if (!data)
1561a5facc4cSJiri Pirko 		return ERR_PTR(-ENOMEM);
156248bb9eb4SIdo Schimmel 	data->devlink = devlink;
156348bb9eb4SIdo Schimmel 
1564134c7532SAmit Cohen 	nsim_dev = devlink_priv(devlink);
1565134c7532SAmit Cohen 	err = nsim_fib_debugfs_init(data, nsim_dev);
15668fa84742SIdo Schimmel 	if (err)
15678fa84742SIdo Schimmel 		goto err_data_free;
15688fa84742SIdo Schimmel 
156986927c9cSPetr Machata 	mutex_init(&data->nh_lock);
1570134c7532SAmit Cohen 	err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
1571134c7532SAmit Cohen 	if (err)
1572134c7532SAmit Cohen 		goto err_debugfs_exit;
1573134c7532SAmit Cohen 
15740ae3eb7bSAmit Cohen 	mutex_init(&data->fib_lock);
157548bb9eb4SIdo Schimmel 	INIT_LIST_HEAD(&data->fib_rt_list);
157648bb9eb4SIdo Schimmel 	err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
157748bb9eb4SIdo Schimmel 	if (err)
15788fa84742SIdo Schimmel 		goto err_rhashtable_nexthop_destroy;
157937923ed6SDavid Ahern 
15800ae3eb7bSAmit Cohen 	INIT_WORK(&data->fib_event_work, nsim_fib_event_work);
1581180a6a3eSIdo Schimmel 	INIT_WORK(&data->fib_flush_work, nsim_fib_flush_work);
15820ae3eb7bSAmit Cohen 	INIT_LIST_HEAD(&data->fib_event_queue);
15830ae3eb7bSAmit Cohen 	spin_lock_init(&data->fib_event_queue_lock);
15840ae3eb7bSAmit Cohen 
158575ba029fSJiri Pirko 	nsim_fib_set_max_all(data, devlink);
158637923ed6SDavid Ahern 
15878fa84742SIdo Schimmel 	data->nexthop_nb.notifier_call = nsim_nexthop_event_nb;
15888fa84742SIdo Schimmel 	err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb,
15898fa84742SIdo Schimmel 					extack);
15908fa84742SIdo Schimmel 	if (err) {
15918fa84742SIdo Schimmel 		pr_err("Failed to register nexthop notifier\n");
15928fa84742SIdo Schimmel 		goto err_rhashtable_fib_destroy;
15938fa84742SIdo Schimmel 	}
15948fa84742SIdo Schimmel 
1595a5facc4cSJiri Pirko 	data->fib_nb.notifier_call = nsim_fib_event_nb;
15964f174bbcSJiri Pirko 	err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
159775ba029fSJiri Pirko 				    nsim_fib_dump_inconsistent, extack);
1598a5facc4cSJiri Pirko 	if (err) {
159937923ed6SDavid Ahern 		pr_err("Failed to register fib notifier\n");
16008fa84742SIdo Schimmel 		goto err_nexthop_nb_unregister;
160137923ed6SDavid Ahern 	}
160237923ed6SDavid Ahern 
1603012ec02aSJiri Pirko 	devl_resource_occ_get_register(devlink,
1604a5facc4cSJiri Pirko 				       NSIM_RESOURCE_IPV4_FIB,
1605a5facc4cSJiri Pirko 				       nsim_fib_ipv4_resource_occ_get,
1606a5facc4cSJiri Pirko 				       data);
1607012ec02aSJiri Pirko 	devl_resource_occ_get_register(devlink,
1608a5facc4cSJiri Pirko 				       NSIM_RESOURCE_IPV4_FIB_RULES,
1609a5facc4cSJiri Pirko 				       nsim_fib_ipv4_rules_res_occ_get,
1610a5facc4cSJiri Pirko 				       data);
1611012ec02aSJiri Pirko 	devl_resource_occ_get_register(devlink,
1612a5facc4cSJiri Pirko 				       NSIM_RESOURCE_IPV6_FIB,
1613a5facc4cSJiri Pirko 				       nsim_fib_ipv6_resource_occ_get,
1614a5facc4cSJiri Pirko 				       data);
1615012ec02aSJiri Pirko 	devl_resource_occ_get_register(devlink,
1616a5facc4cSJiri Pirko 				       NSIM_RESOURCE_IPV6_FIB_RULES,
1617a5facc4cSJiri Pirko 				       nsim_fib_ipv6_rules_res_occ_get,
1618a5facc4cSJiri Pirko 				       data);
1619012ec02aSJiri Pirko 	devl_resource_occ_get_register(devlink,
162035266255SIdo Schimmel 				       NSIM_RESOURCE_NEXTHOPS,
162135266255SIdo Schimmel 				       nsim_fib_nexthops_res_occ_get,
162235266255SIdo Schimmel 				       data);
1623a5facc4cSJiri Pirko 	return data;
1624a5facc4cSJiri Pirko 
16258fa84742SIdo Schimmel err_nexthop_nb_unregister:
16268fa84742SIdo Schimmel 	unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
16278fa84742SIdo Schimmel err_rhashtable_fib_destroy:
1628180a6a3eSIdo Schimmel 	cancel_work_sync(&data->fib_flush_work);
16290ae3eb7bSAmit Cohen 	flush_work(&data->fib_event_work);
163048bb9eb4SIdo Schimmel 	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
163148bb9eb4SIdo Schimmel 				    data);
16328fa84742SIdo Schimmel err_rhashtable_nexthop_destroy:
16338fa84742SIdo Schimmel 	rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
16348fa84742SIdo Schimmel 				    data);
16350ae3eb7bSAmit Cohen 	mutex_destroy(&data->fib_lock);
1636134c7532SAmit Cohen err_debugfs_exit:
163786927c9cSPetr Machata 	mutex_destroy(&data->nh_lock);
1638134c7532SAmit Cohen 	nsim_fib_debugfs_exit(data);
163948bb9eb4SIdo Schimmel err_data_free:
1640a5facc4cSJiri Pirko 	kfree(data);
1641a5facc4cSJiri Pirko 	return ERR_PTR(err);
1642a5facc4cSJiri Pirko }
1643a5facc4cSJiri Pirko 
nsim_fib_destroy(struct devlink * devlink,struct nsim_fib_data * data)1644a5facc4cSJiri Pirko void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
1645a5facc4cSJiri Pirko {
1646012ec02aSJiri Pirko 	devl_resource_occ_get_unregister(devlink,
164735266255SIdo Schimmel 					 NSIM_RESOURCE_NEXTHOPS);
1648012ec02aSJiri Pirko 	devl_resource_occ_get_unregister(devlink,
1649a5facc4cSJiri Pirko 					 NSIM_RESOURCE_IPV6_FIB_RULES);
1650012ec02aSJiri Pirko 	devl_resource_occ_get_unregister(devlink,
1651a5facc4cSJiri Pirko 					 NSIM_RESOURCE_IPV6_FIB);
1652012ec02aSJiri Pirko 	devl_resource_occ_get_unregister(devlink,
1653a5facc4cSJiri Pirko 					 NSIM_RESOURCE_IPV4_FIB_RULES);
1654012ec02aSJiri Pirko 	devl_resource_occ_get_unregister(devlink,
1655a5facc4cSJiri Pirko 					 NSIM_RESOURCE_IPV4_FIB);
16564f174bbcSJiri Pirko 	unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
16578fa84742SIdo Schimmel 	unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1658180a6a3eSIdo Schimmel 	cancel_work_sync(&data->fib_flush_work);
16590ae3eb7bSAmit Cohen 	flush_work(&data->fib_event_work);
166048bb9eb4SIdo Schimmel 	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
166148bb9eb4SIdo Schimmel 				    data);
16628fa84742SIdo Schimmel 	rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
16638fa84742SIdo Schimmel 				    data);
16640ae3eb7bSAmit Cohen 	WARN_ON_ONCE(!list_empty(&data->fib_event_queue));
166548bb9eb4SIdo Schimmel 	WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
16660ae3eb7bSAmit Cohen 	mutex_destroy(&data->fib_lock);
166786927c9cSPetr Machata 	mutex_destroy(&data->nh_lock);
1668134c7532SAmit Cohen 	nsim_fib_debugfs_exit(data);
1669a5facc4cSJiri Pirko 	kfree(data);
167037923ed6SDavid Ahern }
1671