Linux Audio

Check our new training course

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