brintos

brintos / linux-shallow public Read only

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