Linux Audio

Check our new training course

Loading...
v6.8
   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/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 void
 318nsim_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 notification
 376	 * 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 which
 390	 * 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_offload
 419		 * value to true. Simulate hardware programming latency and then
 420		 * 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	else
 434		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#else
 518static void nsim_rt6_release(struct fib6_info *rt)
 519{
 520}
 521#endif
 522
 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 made
 556	 * up from several fib6_info structs (one for each nexthop), so we
 557	 * 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_offload
 613		 * value to true. Simulate hardware programming latency and then
 614		 * 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#else
 655static 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#endif
 661
 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#else
 674static 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#endif
 680
 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 notification
 700	 * 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 which
 713	 * 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_offload
 745		 * value to true. Simulate hardware programming latency and then
 746		 * 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	else
 761		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 then
 777	 * notified. If we vetoed the addition, we will get a delete
 778	 * notification for a route we do not have. Therefore, do not warn if
 779	 * 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 nexthop
 786	 * 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 being
 928	 * 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' and
 986		 * '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 to
1100	 * 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 will
1126	 * 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	else
1271		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))
1381		return -EINVAL;
1382	if (copy_from_user(buf, user_buf, size))
1383		return -EFAULT;
1384	if (sscanf(buf, "%u %hu", &nhid, &bucket_index) != 2)
1385		return -EINVAL;
1386
1387	rtnl_lock();
1388
1389	nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &nhid,
1390					 nsim_nexthop_ht_params);
1391	if (!nexthop || !nexthop->is_resilient ||
1392	    bucket_index >= nexthop->occ) {
1393		err = -EINVAL;
1394		goto out;
1395	}
1396
1397	activity = bitmap_zalloc(nexthop->occ, GFP_KERNEL);
1398	if (!activity) {
1399		err = -ENOMEM;
1400		goto out;
1401	}
1402
1403	bitmap_set(activity, bucket_index, 1);
1404	nexthop_res_grp_activity_update(net, nhid, nexthop->occ, activity);
1405	bitmap_free(activity);
1406
1407out:
1408	rtnl_unlock();
1409
1410	*ppos = size;
1411	return err ?: size;
1412}
1413
1414static const struct file_operations nsim_nexthop_bucket_activity_fops = {
1415	.open = simple_open,
1416	.write = nsim_nexthop_bucket_activity_write,
1417	.llseek = no_llseek,
1418	.owner = THIS_MODULE,
1419};
1420
1421static u64 nsim_fib_ipv4_resource_occ_get(void *priv)
1422{
1423	struct nsim_fib_data *data = priv;
1424
1425	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false);
1426}
1427
1428static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv)
1429{
1430	struct nsim_fib_data *data = priv;
1431
1432	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false);
1433}
1434
1435static u64 nsim_fib_ipv6_resource_occ_get(void *priv)
1436{
1437	struct nsim_fib_data *data = priv;
1438
1439	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false);
1440}
1441
1442static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv)
1443{
1444	struct nsim_fib_data *data = priv;
1445
1446	return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false);
1447}
1448
1449static u64 nsim_fib_nexthops_res_occ_get(void *priv)
1450{
1451	struct nsim_fib_data *data = priv;
1452
1453	return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false);
1454}
1455
1456static void nsim_fib_set_max_all(struct nsim_fib_data *data,
1457				 struct devlink *devlink)
1458{
1459	static const enum nsim_resource_id res_ids[] = {
1460		NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
1461		NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES,
1462		NSIM_RESOURCE_NEXTHOPS,
1463	};
1464	int i;
1465
1466	for (i = 0; i < ARRAY_SIZE(res_ids); i++) {
1467		int err;
1468		u64 val;
1469
1470		err = devl_resource_size_get(devlink, res_ids[i], &val);
1471		if (err)
1472			val = (u64) -1;
1473		nsim_fib_set_max(data, res_ids[i], val);
1474	}
1475}
1476
1477static void nsim_fib_event_work(struct work_struct *work)
1478{
1479	struct nsim_fib_data *data = container_of(work, struct nsim_fib_data,
1480						  fib_event_work);
1481	struct nsim_fib_event *fib_event, *next_fib_event;
1482
1483	LIST_HEAD(fib_event_queue);
1484
1485	spin_lock_bh(&data->fib_event_queue_lock);
1486	list_splice_init(&data->fib_event_queue, &fib_event_queue);
1487	spin_unlock_bh(&data->fib_event_queue_lock);
1488
1489	mutex_lock(&data->fib_lock);
1490	list_for_each_entry_safe(fib_event, next_fib_event, &fib_event_queue,
1491				 list) {
1492		nsim_fib_event(fib_event);
1493		list_del(&fib_event->list);
1494		kfree(fib_event);
1495		cond_resched();
1496	}
1497	mutex_unlock(&data->fib_lock);
1498}
1499
1500static void nsim_fib_flush_work(struct work_struct *work)
1501{
1502	struct nsim_fib_data *data = container_of(work, struct nsim_fib_data,
1503						  fib_flush_work);
1504	struct nsim_fib_rt *fib_rt, *fib_rt_tmp;
1505
1506	/* Process pending work. */
1507	flush_work(&data->fib_event_work);
1508
1509	mutex_lock(&data->fib_lock);
1510	list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) {
1511		rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node,
1512				       nsim_fib_rt_ht_params);
1513		nsim_fib_rt_free(fib_rt, data);
1514	}
1515	mutex_unlock(&data->fib_lock);
1516}
1517
1518static int
1519nsim_fib_debugfs_init(struct nsim_fib_data *data, struct nsim_dev *nsim_dev)
1520{
1521	data->ddir = debugfs_create_dir("fib", nsim_dev->ddir);
1522	if (IS_ERR(data->ddir))
1523		return PTR_ERR(data->ddir);
1524
1525	data->fail_route_offload = false;
1526	debugfs_create_bool("fail_route_offload", 0600, data->ddir,
1527			    &data->fail_route_offload);
1528
1529	data->fail_res_nexthop_group_replace = false;
1530	debugfs_create_bool("fail_res_nexthop_group_replace", 0600, data->ddir,
1531			    &data->fail_res_nexthop_group_replace);
1532
1533	data->fail_nexthop_bucket_replace = false;
1534	debugfs_create_bool("fail_nexthop_bucket_replace", 0600, data->ddir,
1535			    &data->fail_nexthop_bucket_replace);
1536
1537	debugfs_create_file("nexthop_bucket_activity", 0200, data->ddir,
1538			    data, &nsim_nexthop_bucket_activity_fops);
1539
1540	data->fail_route_delete = false;
1541	debugfs_create_bool("fail_route_delete", 0600, data->ddir,
1542			    &data->fail_route_delete);
1543	return 0;
1544}
1545
1546static void nsim_fib_debugfs_exit(struct nsim_fib_data *data)
1547{
1548	debugfs_remove_recursive(data->ddir);
1549}
1550
1551struct nsim_fib_data *nsim_fib_create(struct devlink *devlink,
1552				      struct netlink_ext_ack *extack)
1553{
1554	struct nsim_fib_data *data;
1555	struct nsim_dev *nsim_dev;
1556	int err;
1557
1558	data = kzalloc(sizeof(*data), GFP_KERNEL);
1559	if (!data)
1560		return ERR_PTR(-ENOMEM);
1561	data->devlink = devlink;
1562
1563	nsim_dev = devlink_priv(devlink);
1564	err = nsim_fib_debugfs_init(data, nsim_dev);
1565	if (err)
1566		goto err_data_free;
1567
1568	mutex_init(&data->nh_lock);
1569	err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params);
1570	if (err)
1571		goto err_debugfs_exit;
1572
1573	mutex_init(&data->fib_lock);
1574	INIT_LIST_HEAD(&data->fib_rt_list);
1575	err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params);
1576	if (err)
1577		goto err_rhashtable_nexthop_destroy;
1578
1579	INIT_WORK(&data->fib_event_work, nsim_fib_event_work);
1580	INIT_WORK(&data->fib_flush_work, nsim_fib_flush_work);
1581	INIT_LIST_HEAD(&data->fib_event_queue);
1582	spin_lock_init(&data->fib_event_queue_lock);
1583
1584	nsim_fib_set_max_all(data, devlink);
1585
1586	data->nexthop_nb.notifier_call = nsim_nexthop_event_nb;
1587	err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb,
1588					extack);
1589	if (err) {
1590		pr_err("Failed to register nexthop notifier\n");
1591		goto err_rhashtable_fib_destroy;
1592	}
1593
1594	data->fib_nb.notifier_call = nsim_fib_event_nb;
1595	err = register_fib_notifier(devlink_net(devlink), &data->fib_nb,
1596				    nsim_fib_dump_inconsistent, extack);
1597	if (err) {
1598		pr_err("Failed to register fib notifier\n");
1599		goto err_nexthop_nb_unregister;
1600	}
1601
1602	devl_resource_occ_get_register(devlink,
1603				       NSIM_RESOURCE_IPV4_FIB,
1604				       nsim_fib_ipv4_resource_occ_get,
1605				       data);
1606	devl_resource_occ_get_register(devlink,
1607				       NSIM_RESOURCE_IPV4_FIB_RULES,
1608				       nsim_fib_ipv4_rules_res_occ_get,
1609				       data);
1610	devl_resource_occ_get_register(devlink,
1611				       NSIM_RESOURCE_IPV6_FIB,
1612				       nsim_fib_ipv6_resource_occ_get,
1613				       data);
1614	devl_resource_occ_get_register(devlink,
1615				       NSIM_RESOURCE_IPV6_FIB_RULES,
1616				       nsim_fib_ipv6_rules_res_occ_get,
1617				       data);
1618	devl_resource_occ_get_register(devlink,
1619				       NSIM_RESOURCE_NEXTHOPS,
1620				       nsim_fib_nexthops_res_occ_get,
1621				       data);
1622	return data;
1623
1624err_nexthop_nb_unregister:
1625	unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1626err_rhashtable_fib_destroy:
1627	cancel_work_sync(&data->fib_flush_work);
1628	flush_work(&data->fib_event_work);
1629	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1630				    data);
1631err_rhashtable_nexthop_destroy:
1632	rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1633				    data);
1634	mutex_destroy(&data->fib_lock);
1635err_debugfs_exit:
1636	mutex_destroy(&data->nh_lock);
1637	nsim_fib_debugfs_exit(data);
1638err_data_free:
1639	kfree(data);
1640	return ERR_PTR(err);
1641}
1642
1643void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data)
1644{
1645	devl_resource_occ_get_unregister(devlink,
1646					 NSIM_RESOURCE_NEXTHOPS);
1647	devl_resource_occ_get_unregister(devlink,
1648					 NSIM_RESOURCE_IPV6_FIB_RULES);
1649	devl_resource_occ_get_unregister(devlink,
1650					 NSIM_RESOURCE_IPV6_FIB);
1651	devl_resource_occ_get_unregister(devlink,
1652					 NSIM_RESOURCE_IPV4_FIB_RULES);
1653	devl_resource_occ_get_unregister(devlink,
1654					 NSIM_RESOURCE_IPV4_FIB);
1655	unregister_fib_notifier(devlink_net(devlink), &data->fib_nb);
1656	unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1657	cancel_work_sync(&data->fib_flush_work);
1658	flush_work(&data->fib_event_work);
1659	rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free,
1660				    data);
1661	rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1662				    data);
1663	WARN_ON_ONCE(!list_empty(&data->fib_event_queue));
1664	WARN_ON_ONCE(!list_empty(&data->fib_rt_list));
1665	mutex_destroy(&data->fib_lock);
1666	mutex_destroy(&data->nh_lock);
1667	nsim_fib_debugfs_exit(data);
1668	kfree(data);
1669}
v5.14.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}