162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2018 Cumulus Networks. All rights reserved. 362306a36Sopenharmony_ci * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This software is licensed under the GNU General License Version 2, 662306a36Sopenharmony_ci * June 1991 as shown in the file COPYING in the top-level directory of this 762306a36Sopenharmony_ci * source tree. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" 1062306a36Sopenharmony_ci * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 1162306a36Sopenharmony_ci * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 1262306a36Sopenharmony_ci * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 1362306a36Sopenharmony_ci * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 1462306a36Sopenharmony_ci * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/bitmap.h> 1862306a36Sopenharmony_ci#include <linux/in6.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/list.h> 2162306a36Sopenharmony_ci#include <linux/rhashtable.h> 2262306a36Sopenharmony_ci#include <linux/spinlock_types.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci#include <net/fib_notifier.h> 2562306a36Sopenharmony_ci#include <net/inet_dscp.h> 2662306a36Sopenharmony_ci#include <net/ip_fib.h> 2762306a36Sopenharmony_ci#include <net/ip6_fib.h> 2862306a36Sopenharmony_ci#include <net/fib_rules.h> 2962306a36Sopenharmony_ci#include <net/net_namespace.h> 3062306a36Sopenharmony_ci#include <net/nexthop.h> 3162306a36Sopenharmony_ci#include <linux/debugfs.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "netdevsim.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct nsim_fib_entry { 3662306a36Sopenharmony_ci u64 max; 3762306a36Sopenharmony_ci atomic64_t num; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct nsim_per_fib_data { 4162306a36Sopenharmony_ci struct nsim_fib_entry fib; 4262306a36Sopenharmony_ci struct nsim_fib_entry rules; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct nsim_fib_data { 4662306a36Sopenharmony_ci struct notifier_block fib_nb; 4762306a36Sopenharmony_ci struct nsim_per_fib_data ipv4; 4862306a36Sopenharmony_ci struct nsim_per_fib_data ipv6; 4962306a36Sopenharmony_ci struct nsim_fib_entry nexthops; 5062306a36Sopenharmony_ci struct rhashtable fib_rt_ht; 5162306a36Sopenharmony_ci struct list_head fib_rt_list; 5262306a36Sopenharmony_ci struct mutex fib_lock; /* Protects FIB HT and list */ 5362306a36Sopenharmony_ci struct notifier_block nexthop_nb; 5462306a36Sopenharmony_ci struct rhashtable nexthop_ht; 5562306a36Sopenharmony_ci struct devlink *devlink; 5662306a36Sopenharmony_ci struct work_struct fib_event_work; 5762306a36Sopenharmony_ci struct work_struct fib_flush_work; 5862306a36Sopenharmony_ci struct list_head fib_event_queue; 5962306a36Sopenharmony_ci spinlock_t fib_event_queue_lock; /* Protects fib event queue list */ 6062306a36Sopenharmony_ci struct mutex nh_lock; /* Protects NH HT */ 6162306a36Sopenharmony_ci struct dentry *ddir; 6262306a36Sopenharmony_ci bool fail_route_offload; 6362306a36Sopenharmony_ci bool fail_res_nexthop_group_replace; 6462306a36Sopenharmony_ci bool fail_nexthop_bucket_replace; 6562306a36Sopenharmony_ci bool fail_route_delete; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct nsim_fib_rt_key { 6962306a36Sopenharmony_ci unsigned char addr[sizeof(struct in6_addr)]; 7062306a36Sopenharmony_ci unsigned char prefix_len; 7162306a36Sopenharmony_ci int family; 7262306a36Sopenharmony_ci u32 tb_id; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct nsim_fib_rt { 7662306a36Sopenharmony_ci struct nsim_fib_rt_key key; 7762306a36Sopenharmony_ci struct rhash_head ht_node; 7862306a36Sopenharmony_ci struct list_head list; /* Member of fib_rt_list */ 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistruct nsim_fib4_rt { 8262306a36Sopenharmony_ci struct nsim_fib_rt common; 8362306a36Sopenharmony_ci struct fib_info *fi; 8462306a36Sopenharmony_ci dscp_t dscp; 8562306a36Sopenharmony_ci u8 type; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistruct nsim_fib6_rt { 8962306a36Sopenharmony_ci struct nsim_fib_rt common; 9062306a36Sopenharmony_ci struct list_head nh_list; 9162306a36Sopenharmony_ci unsigned int nhs; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistruct nsim_fib6_rt_nh { 9562306a36Sopenharmony_ci struct list_head list; /* Member of nh_list */ 9662306a36Sopenharmony_ci struct fib6_info *rt; 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct nsim_fib6_event { 10062306a36Sopenharmony_ci struct fib6_info **rt_arr; 10162306a36Sopenharmony_ci unsigned int nrt6; 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistruct nsim_fib_event { 10562306a36Sopenharmony_ci struct list_head list; /* node in fib queue */ 10662306a36Sopenharmony_ci union { 10762306a36Sopenharmony_ci struct fib_entry_notifier_info fen_info; 10862306a36Sopenharmony_ci struct nsim_fib6_event fib6_event; 10962306a36Sopenharmony_ci }; 11062306a36Sopenharmony_ci struct nsim_fib_data *data; 11162306a36Sopenharmony_ci unsigned long event; 11262306a36Sopenharmony_ci int family; 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const struct rhashtable_params nsim_fib_rt_ht_params = { 11662306a36Sopenharmony_ci .key_offset = offsetof(struct nsim_fib_rt, key), 11762306a36Sopenharmony_ci .head_offset = offsetof(struct nsim_fib_rt, ht_node), 11862306a36Sopenharmony_ci .key_len = sizeof(struct nsim_fib_rt_key), 11962306a36Sopenharmony_ci .automatic_shrinking = true, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistruct nsim_nexthop { 12362306a36Sopenharmony_ci struct rhash_head ht_node; 12462306a36Sopenharmony_ci u64 occ; 12562306a36Sopenharmony_ci u32 id; 12662306a36Sopenharmony_ci bool is_resilient; 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const struct rhashtable_params nsim_nexthop_ht_params = { 13062306a36Sopenharmony_ci .key_offset = offsetof(struct nsim_nexthop, id), 13162306a36Sopenharmony_ci .head_offset = offsetof(struct nsim_nexthop, ht_node), 13262306a36Sopenharmony_ci .key_len = sizeof(u32), 13362306a36Sopenharmony_ci .automatic_shrinking = true, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciu64 nsim_fib_get_val(struct nsim_fib_data *fib_data, 13762306a36Sopenharmony_ci enum nsim_resource_id res_id, bool max) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct nsim_fib_entry *entry; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci switch (res_id) { 14262306a36Sopenharmony_ci case NSIM_RESOURCE_IPV4_FIB: 14362306a36Sopenharmony_ci entry = &fib_data->ipv4.fib; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci case NSIM_RESOURCE_IPV4_FIB_RULES: 14662306a36Sopenharmony_ci entry = &fib_data->ipv4.rules; 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci case NSIM_RESOURCE_IPV6_FIB: 14962306a36Sopenharmony_ci entry = &fib_data->ipv6.fib; 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci case NSIM_RESOURCE_IPV6_FIB_RULES: 15262306a36Sopenharmony_ci entry = &fib_data->ipv6.rules; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci case NSIM_RESOURCE_NEXTHOPS: 15562306a36Sopenharmony_ci entry = &fib_data->nexthops; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci default: 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return max ? entry->max : atomic64_read(&entry->num); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void nsim_fib_set_max(struct nsim_fib_data *fib_data, 16562306a36Sopenharmony_ci enum nsim_resource_id res_id, u64 val) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct nsim_fib_entry *entry; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci switch (res_id) { 17062306a36Sopenharmony_ci case NSIM_RESOURCE_IPV4_FIB: 17162306a36Sopenharmony_ci entry = &fib_data->ipv4.fib; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci case NSIM_RESOURCE_IPV4_FIB_RULES: 17462306a36Sopenharmony_ci entry = &fib_data->ipv4.rules; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci case NSIM_RESOURCE_IPV6_FIB: 17762306a36Sopenharmony_ci entry = &fib_data->ipv6.fib; 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci case NSIM_RESOURCE_IPV6_FIB_RULES: 18062306a36Sopenharmony_ci entry = &fib_data->ipv6.rules; 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci case NSIM_RESOURCE_NEXTHOPS: 18362306a36Sopenharmony_ci entry = &fib_data->nexthops; 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci default: 18662306a36Sopenharmony_ci WARN_ON(1); 18762306a36Sopenharmony_ci return; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci entry->max = val; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add, 19362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci int err = 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (add) { 19862306a36Sopenharmony_ci if (!atomic64_add_unless(&entry->num, 1, entry->max)) { 19962306a36Sopenharmony_ci err = -ENOSPC; 20062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries"); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } else { 20362306a36Sopenharmony_ci atomic64_dec_if_positive(&entry->num); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return err; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int nsim_fib_rule_event(struct nsim_fib_data *data, 21062306a36Sopenharmony_ci struct fib_notifier_info *info, bool add) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct netlink_ext_ack *extack = info->extack; 21362306a36Sopenharmony_ci int err = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci switch (info->family) { 21662306a36Sopenharmony_ci case AF_INET: 21762306a36Sopenharmony_ci err = nsim_fib_rule_account(&data->ipv4.rules, add, extack); 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci case AF_INET6: 22062306a36Sopenharmony_ci err = nsim_fib_rule_account(&data->ipv6.rules, add, extack); 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return err; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int nsim_fib_account(struct nsim_fib_entry *entry, bool add) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci int err = 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (add) { 23262306a36Sopenharmony_ci if (!atomic64_add_unless(&entry->num, 1, entry->max)) 23362306a36Sopenharmony_ci err = -ENOSPC; 23462306a36Sopenharmony_ci } else { 23562306a36Sopenharmony_ci atomic64_dec_if_positive(&entry->num); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return err; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void nsim_fib_rt_init(struct nsim_fib_data *data, 24262306a36Sopenharmony_ci struct nsim_fib_rt *fib_rt, const void *addr, 24362306a36Sopenharmony_ci size_t addr_len, unsigned int prefix_len, 24462306a36Sopenharmony_ci int family, u32 tb_id) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci memcpy(fib_rt->key.addr, addr, addr_len); 24762306a36Sopenharmony_ci fib_rt->key.prefix_len = prefix_len; 24862306a36Sopenharmony_ci fib_rt->key.family = family; 24962306a36Sopenharmony_ci fib_rt->key.tb_id = tb_id; 25062306a36Sopenharmony_ci list_add(&fib_rt->list, &data->fib_rt_list); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci list_del(&fib_rt->list); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht, 25962306a36Sopenharmony_ci const void *addr, size_t addr_len, 26062306a36Sopenharmony_ci unsigned int prefix_len, 26162306a36Sopenharmony_ci int family, u32 tb_id) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct nsim_fib_rt_key key; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci memset(&key, 0, sizeof(key)); 26662306a36Sopenharmony_ci memcpy(key.addr, addr, addr_len); 26762306a36Sopenharmony_ci key.prefix_len = prefix_len; 26862306a36Sopenharmony_ci key.family = family; 26962306a36Sopenharmony_ci key.tb_id = tb_id; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic struct nsim_fib4_rt * 27562306a36Sopenharmony_cinsim_fib4_rt_create(struct nsim_fib_data *data, 27662306a36Sopenharmony_ci struct fib_entry_notifier_info *fen_info) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct nsim_fib4_rt *fib4_rt; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_KERNEL); 28162306a36Sopenharmony_ci if (!fib4_rt) 28262306a36Sopenharmony_ci return NULL; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32), 28562306a36Sopenharmony_ci fen_info->dst_len, AF_INET, fen_info->tb_id); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci fib4_rt->fi = fen_info->fi; 28862306a36Sopenharmony_ci fib_info_hold(fib4_rt->fi); 28962306a36Sopenharmony_ci fib4_rt->dscp = fen_info->dscp; 29062306a36Sopenharmony_ci fib4_rt->type = fen_info->type; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return fib4_rt; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci fib_info_put(fib4_rt->fi); 29862306a36Sopenharmony_ci nsim_fib_rt_fini(&fib4_rt->common); 29962306a36Sopenharmony_ci kfree(fib4_rt); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic struct nsim_fib4_rt * 30362306a36Sopenharmony_cinsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht, 30462306a36Sopenharmony_ci const struct fib_entry_notifier_info *fen_info) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct nsim_fib_rt *fib_rt; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32), 30962306a36Sopenharmony_ci fen_info->dst_len, AF_INET, 31062306a36Sopenharmony_ci fen_info->tb_id); 31162306a36Sopenharmony_ci if (!fib_rt) 31262306a36Sopenharmony_ci return NULL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return container_of(fib_rt, struct nsim_fib4_rt, common); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void 31862306a36Sopenharmony_cinsim_fib4_rt_offload_failed_flag_set(struct net *net, 31962306a36Sopenharmony_ci struct fib_entry_notifier_info *fen_info) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci u32 *p_dst = (u32 *)&fen_info->dst; 32262306a36Sopenharmony_ci struct fib_rt_info fri; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci fri.fi = fen_info->fi; 32562306a36Sopenharmony_ci fri.tb_id = fen_info->tb_id; 32662306a36Sopenharmony_ci fri.dst = cpu_to_be32(*p_dst); 32762306a36Sopenharmony_ci fri.dst_len = fen_info->dst_len; 32862306a36Sopenharmony_ci fri.dscp = fen_info->dscp; 32962306a36Sopenharmony_ci fri.type = fen_info->type; 33062306a36Sopenharmony_ci fri.offload = false; 33162306a36Sopenharmony_ci fri.trap = false; 33262306a36Sopenharmony_ci fri.offload_failed = true; 33362306a36Sopenharmony_ci fib_alias_hw_flags_set(net, &fri); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void nsim_fib4_rt_hw_flags_set(struct net *net, 33762306a36Sopenharmony_ci const struct nsim_fib4_rt *fib4_rt, 33862306a36Sopenharmony_ci bool trap) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci u32 *p_dst = (u32 *) fib4_rt->common.key.addr; 34162306a36Sopenharmony_ci int dst_len = fib4_rt->common.key.prefix_len; 34262306a36Sopenharmony_ci struct fib_rt_info fri; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci fri.fi = fib4_rt->fi; 34562306a36Sopenharmony_ci fri.tb_id = fib4_rt->common.key.tb_id; 34662306a36Sopenharmony_ci fri.dst = cpu_to_be32(*p_dst); 34762306a36Sopenharmony_ci fri.dst_len = dst_len; 34862306a36Sopenharmony_ci fri.dscp = fib4_rt->dscp; 34962306a36Sopenharmony_ci fri.type = fib4_rt->type; 35062306a36Sopenharmony_ci fri.offload = false; 35162306a36Sopenharmony_ci fri.trap = trap; 35262306a36Sopenharmony_ci fri.offload_failed = false; 35362306a36Sopenharmony_ci fib_alias_hw_flags_set(net, &fri); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int nsim_fib4_rt_add(struct nsim_fib_data *data, 35762306a36Sopenharmony_ci struct nsim_fib4_rt *fib4_rt) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct net *net = devlink_net(data->devlink); 36062306a36Sopenharmony_ci int err; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci err = rhashtable_insert_fast(&data->fib_rt_ht, 36362306a36Sopenharmony_ci &fib4_rt->common.ht_node, 36462306a36Sopenharmony_ci nsim_fib_rt_ht_params); 36562306a36Sopenharmony_ci if (err) 36662306a36Sopenharmony_ci goto err_fib_dismiss; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Simulate hardware programming latency. */ 36962306a36Sopenharmony_ci msleep(1); 37062306a36Sopenharmony_ci nsim_fib4_rt_hw_flags_set(net, fib4_rt, true); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cierr_fib_dismiss: 37562306a36Sopenharmony_ci /* Drop the accounting that was increased from the notification 37662306a36Sopenharmony_ci * context when FIB_EVENT_ENTRY_REPLACE was triggered. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci nsim_fib_account(&data->ipv4.fib, false); 37962306a36Sopenharmony_ci return err; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int nsim_fib4_rt_replace(struct nsim_fib_data *data, 38362306a36Sopenharmony_ci struct nsim_fib4_rt *fib4_rt, 38462306a36Sopenharmony_ci struct nsim_fib4_rt *fib4_rt_old) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct net *net = devlink_net(data->devlink); 38762306a36Sopenharmony_ci int err; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* We are replacing a route, so need to remove the accounting which 39062306a36Sopenharmony_ci * was increased when FIB_EVENT_ENTRY_REPLACE was triggered. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci err = nsim_fib_account(&data->ipv4.fib, false); 39362306a36Sopenharmony_ci if (err) 39462306a36Sopenharmony_ci return err; 39562306a36Sopenharmony_ci err = rhashtable_replace_fast(&data->fib_rt_ht, 39662306a36Sopenharmony_ci &fib4_rt_old->common.ht_node, 39762306a36Sopenharmony_ci &fib4_rt->common.ht_node, 39862306a36Sopenharmony_ci nsim_fib_rt_ht_params); 39962306a36Sopenharmony_ci if (err) 40062306a36Sopenharmony_ci return err; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci msleep(1); 40362306a36Sopenharmony_ci nsim_fib4_rt_hw_flags_set(net, fib4_rt, true); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false); 40662306a36Sopenharmony_ci nsim_fib4_rt_destroy(fib4_rt_old); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int nsim_fib4_rt_insert(struct nsim_fib_data *data, 41262306a36Sopenharmony_ci struct fib_entry_notifier_info *fen_info) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct nsim_fib4_rt *fib4_rt, *fib4_rt_old; 41562306a36Sopenharmony_ci int err; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (data->fail_route_offload) { 41862306a36Sopenharmony_ci /* For testing purposes, user set debugfs fail_route_offload 41962306a36Sopenharmony_ci * value to true. Simulate hardware programming latency and then 42062306a36Sopenharmony_ci * fail. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci msleep(1); 42362306a36Sopenharmony_ci return -EINVAL; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci fib4_rt = nsim_fib4_rt_create(data, fen_info); 42762306a36Sopenharmony_ci if (!fib4_rt) 42862306a36Sopenharmony_ci return -ENOMEM; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info); 43162306a36Sopenharmony_ci if (!fib4_rt_old) 43262306a36Sopenharmony_ci err = nsim_fib4_rt_add(data, fib4_rt); 43362306a36Sopenharmony_ci else 43462306a36Sopenharmony_ci err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (err) 43762306a36Sopenharmony_ci nsim_fib4_rt_destroy(fib4_rt); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return err; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic void nsim_fib4_rt_remove(struct nsim_fib_data *data, 44362306a36Sopenharmony_ci const struct fib_entry_notifier_info *fen_info) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct nsim_fib4_rt *fib4_rt; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info); 44862306a36Sopenharmony_ci if (!fib4_rt) 44962306a36Sopenharmony_ci return; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node, 45262306a36Sopenharmony_ci nsim_fib_rt_ht_params); 45362306a36Sopenharmony_ci nsim_fib4_rt_destroy(fib4_rt); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int nsim_fib4_event(struct nsim_fib_data *data, 45762306a36Sopenharmony_ci struct fib_entry_notifier_info *fen_info, 45862306a36Sopenharmony_ci unsigned long event) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci int err = 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci switch (event) { 46362306a36Sopenharmony_ci case FIB_EVENT_ENTRY_REPLACE: 46462306a36Sopenharmony_ci err = nsim_fib4_rt_insert(data, fen_info); 46562306a36Sopenharmony_ci if (err) { 46662306a36Sopenharmony_ci struct net *net = devlink_net(data->devlink); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci nsim_fib4_rt_offload_failed_flag_set(net, fen_info); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci case FIB_EVENT_ENTRY_DEL: 47262306a36Sopenharmony_ci nsim_fib4_rt_remove(data, fen_info); 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci default: 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return err; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic struct nsim_fib6_rt_nh * 48262306a36Sopenharmony_cinsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt, 48362306a36Sopenharmony_ci const struct fib6_info *rt) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct nsim_fib6_rt_nh *fib6_rt_nh; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) { 48862306a36Sopenharmony_ci if (fib6_rt_nh->rt == rt) 48962306a36Sopenharmony_ci return fib6_rt_nh; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return NULL; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt, 49662306a36Sopenharmony_ci struct fib6_info *rt) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct nsim_fib6_rt_nh *fib6_rt_nh; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_KERNEL); 50162306a36Sopenharmony_ci if (!fib6_rt_nh) 50262306a36Sopenharmony_ci return -ENOMEM; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci fib6_info_hold(rt); 50562306a36Sopenharmony_ci fib6_rt_nh->rt = rt; 50662306a36Sopenharmony_ci list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list); 50762306a36Sopenharmony_ci fib6_rt->nhs++; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 51362306a36Sopenharmony_cistatic void nsim_rt6_release(struct fib6_info *rt) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci fib6_info_release(rt); 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci#else 51862306a36Sopenharmony_cistatic void nsim_rt6_release(struct fib6_info *rt) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci#endif 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt, 52462306a36Sopenharmony_ci const struct fib6_info *rt) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct nsim_fib6_rt_nh *fib6_rt_nh; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt); 52962306a36Sopenharmony_ci if (!fib6_rt_nh) 53062306a36Sopenharmony_ci return; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci fib6_rt->nhs--; 53362306a36Sopenharmony_ci list_del(&fib6_rt_nh->list); 53462306a36Sopenharmony_ci nsim_rt6_release(fib6_rt_nh->rt); 53562306a36Sopenharmony_ci kfree(fib6_rt_nh); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic struct nsim_fib6_rt * 53962306a36Sopenharmony_cinsim_fib6_rt_create(struct nsim_fib_data *data, 54062306a36Sopenharmony_ci struct fib6_info **rt_arr, unsigned int nrt6) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct fib6_info *rt = rt_arr[0]; 54362306a36Sopenharmony_ci struct nsim_fib6_rt *fib6_rt; 54462306a36Sopenharmony_ci int i = 0; 54562306a36Sopenharmony_ci int err; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_KERNEL); 54862306a36Sopenharmony_ci if (!fib6_rt) 54962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr, 55262306a36Sopenharmony_ci sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6, 55362306a36Sopenharmony_ci rt->fib6_table->tb6_id); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* We consider a multipath IPv6 route as one entry, but it can be made 55662306a36Sopenharmony_ci * up from several fib6_info structs (one for each nexthop), so we 55762306a36Sopenharmony_ci * add them all to the same list under the entry. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci INIT_LIST_HEAD(&fib6_rt->nh_list); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci for (i = 0; i < nrt6; i++) { 56262306a36Sopenharmony_ci err = nsim_fib6_rt_nh_add(fib6_rt, rt_arr[i]); 56362306a36Sopenharmony_ci if (err) 56462306a36Sopenharmony_ci goto err_fib6_rt_nh_del; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return fib6_rt; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cierr_fib6_rt_nh_del: 57062306a36Sopenharmony_ci for (i--; i >= 0; i--) { 57162306a36Sopenharmony_ci nsim_fib6_rt_nh_del(fib6_rt, rt_arr[i]); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci nsim_fib_rt_fini(&fib6_rt->common); 57462306a36Sopenharmony_ci kfree(fib6_rt); 57562306a36Sopenharmony_ci return ERR_PTR(err); 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct nsim_fib6_rt_nh *iter, *tmp; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list) 58362306a36Sopenharmony_ci nsim_fib6_rt_nh_del(fib6_rt, iter->rt); 58462306a36Sopenharmony_ci WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list)); 58562306a36Sopenharmony_ci nsim_fib_rt_fini(&fib6_rt->common); 58662306a36Sopenharmony_ci kfree(fib6_rt); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic struct nsim_fib6_rt * 59062306a36Sopenharmony_cinsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct nsim_fib_rt *fib_rt; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr, 59562306a36Sopenharmony_ci sizeof(rt->fib6_dst.addr), 59662306a36Sopenharmony_ci rt->fib6_dst.plen, AF_INET6, 59762306a36Sopenharmony_ci rt->fib6_table->tb6_id); 59862306a36Sopenharmony_ci if (!fib_rt) 59962306a36Sopenharmony_ci return NULL; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return container_of(fib_rt, struct nsim_fib6_rt, common); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic int nsim_fib6_rt_append(struct nsim_fib_data *data, 60562306a36Sopenharmony_ci struct nsim_fib6_event *fib6_event) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct fib6_info *rt = fib6_event->rt_arr[0]; 60862306a36Sopenharmony_ci struct nsim_fib6_rt *fib6_rt; 60962306a36Sopenharmony_ci int i, err; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (data->fail_route_offload) { 61262306a36Sopenharmony_ci /* For testing purposes, user set debugfs fail_route_offload 61362306a36Sopenharmony_ci * value to true. Simulate hardware programming latency and then 61462306a36Sopenharmony_ci * fail. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci msleep(1); 61762306a36Sopenharmony_ci return -EINVAL; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt); 62162306a36Sopenharmony_ci if (!fib6_rt) 62262306a36Sopenharmony_ci return -EINVAL; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci for (i = 0; i < fib6_event->nrt6; i++) { 62562306a36Sopenharmony_ci err = nsim_fib6_rt_nh_add(fib6_rt, fib6_event->rt_arr[i]); 62662306a36Sopenharmony_ci if (err) 62762306a36Sopenharmony_ci goto err_fib6_rt_nh_del; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci WRITE_ONCE(fib6_event->rt_arr[i]->trap, true); 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cierr_fib6_rt_nh_del: 63562306a36Sopenharmony_ci for (i--; i >= 0; i--) { 63662306a36Sopenharmony_ci WRITE_ONCE(fib6_event->rt_arr[i]->trap, false); 63762306a36Sopenharmony_ci nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci return err; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 64362306a36Sopenharmony_cistatic void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data, 64462306a36Sopenharmony_ci struct fib6_info **rt_arr, 64562306a36Sopenharmony_ci unsigned int nrt6) 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct net *net = devlink_net(data->devlink); 64962306a36Sopenharmony_ci int i; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci for (i = 0; i < nrt6; i++) 65262306a36Sopenharmony_ci fib6_info_hw_flags_set(net, rt_arr[i], false, false, true); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci#else 65562306a36Sopenharmony_cistatic void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data, 65662306a36Sopenharmony_ci struct fib6_info **rt_arr, 65762306a36Sopenharmony_ci unsigned int nrt6) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci#endif 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 66362306a36Sopenharmony_cistatic void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data, 66462306a36Sopenharmony_ci const struct nsim_fib6_rt *fib6_rt, 66562306a36Sopenharmony_ci bool trap) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct net *net = devlink_net(data->devlink); 66862306a36Sopenharmony_ci struct nsim_fib6_rt_nh *fib6_rt_nh; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) 67162306a36Sopenharmony_ci fib6_info_hw_flags_set(net, fib6_rt_nh->rt, false, trap, false); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci#else 67462306a36Sopenharmony_cistatic void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data, 67562306a36Sopenharmony_ci const struct nsim_fib6_rt *fib6_rt, 67662306a36Sopenharmony_ci bool trap) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci#endif 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic int nsim_fib6_rt_add(struct nsim_fib_data *data, 68262306a36Sopenharmony_ci struct nsim_fib6_rt *fib6_rt) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci int err; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci err = rhashtable_insert_fast(&data->fib_rt_ht, 68762306a36Sopenharmony_ci &fib6_rt->common.ht_node, 68862306a36Sopenharmony_ci nsim_fib_rt_ht_params); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (err) 69162306a36Sopenharmony_ci goto err_fib_dismiss; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci msleep(1); 69462306a36Sopenharmony_ci nsim_fib6_rt_hw_flags_set(data, fib6_rt, true); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return 0; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cierr_fib_dismiss: 69962306a36Sopenharmony_ci /* Drop the accounting that was increased from the notification 70062306a36Sopenharmony_ci * context when FIB_EVENT_ENTRY_REPLACE was triggered. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci nsim_fib_account(&data->ipv6.fib, false); 70362306a36Sopenharmony_ci return err; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int nsim_fib6_rt_replace(struct nsim_fib_data *data, 70762306a36Sopenharmony_ci struct nsim_fib6_rt *fib6_rt, 70862306a36Sopenharmony_ci struct nsim_fib6_rt *fib6_rt_old) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci int err; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* We are replacing a route, so need to remove the accounting which 71362306a36Sopenharmony_ci * was increased when FIB_EVENT_ENTRY_REPLACE was triggered. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci err = nsim_fib_account(&data->ipv6.fib, false); 71662306a36Sopenharmony_ci if (err) 71762306a36Sopenharmony_ci return err; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci err = rhashtable_replace_fast(&data->fib_rt_ht, 72062306a36Sopenharmony_ci &fib6_rt_old->common.ht_node, 72162306a36Sopenharmony_ci &fib6_rt->common.ht_node, 72262306a36Sopenharmony_ci nsim_fib_rt_ht_params); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (err) 72562306a36Sopenharmony_ci return err; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci msleep(1); 72862306a36Sopenharmony_ci nsim_fib6_rt_hw_flags_set(data, fib6_rt, true); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci nsim_fib6_rt_hw_flags_set(data, fib6_rt_old, false); 73162306a36Sopenharmony_ci nsim_fib6_rt_destroy(fib6_rt_old); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int nsim_fib6_rt_insert(struct nsim_fib_data *data, 73762306a36Sopenharmony_ci struct nsim_fib6_event *fib6_event) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct fib6_info *rt = fib6_event->rt_arr[0]; 74062306a36Sopenharmony_ci struct nsim_fib6_rt *fib6_rt, *fib6_rt_old; 74162306a36Sopenharmony_ci int err; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (data->fail_route_offload) { 74462306a36Sopenharmony_ci /* For testing purposes, user set debugfs fail_route_offload 74562306a36Sopenharmony_ci * value to true. Simulate hardware programming latency and then 74662306a36Sopenharmony_ci * fail. 74762306a36Sopenharmony_ci */ 74862306a36Sopenharmony_ci msleep(1); 74962306a36Sopenharmony_ci return -EINVAL; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci fib6_rt = nsim_fib6_rt_create(data, fib6_event->rt_arr, 75362306a36Sopenharmony_ci fib6_event->nrt6); 75462306a36Sopenharmony_ci if (IS_ERR(fib6_rt)) 75562306a36Sopenharmony_ci return PTR_ERR(fib6_rt); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt); 75862306a36Sopenharmony_ci if (!fib6_rt_old) 75962306a36Sopenharmony_ci err = nsim_fib6_rt_add(data, fib6_rt); 76062306a36Sopenharmony_ci else 76162306a36Sopenharmony_ci err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci if (err) 76462306a36Sopenharmony_ci nsim_fib6_rt_destroy(fib6_rt); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return err; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic void nsim_fib6_rt_remove(struct nsim_fib_data *data, 77062306a36Sopenharmony_ci struct nsim_fib6_event *fib6_event) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct fib6_info *rt = fib6_event->rt_arr[0]; 77362306a36Sopenharmony_ci struct nsim_fib6_rt *fib6_rt; 77462306a36Sopenharmony_ci int i; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci /* Multipath routes are first added to the FIB trie and only then 77762306a36Sopenharmony_ci * notified. If we vetoed the addition, we will get a delete 77862306a36Sopenharmony_ci * notification for a route we do not have. Therefore, do not warn if 77962306a36Sopenharmony_ci * route was not found. 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ci fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt); 78262306a36Sopenharmony_ci if (!fib6_rt) 78362306a36Sopenharmony_ci return; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* If not all the nexthops are deleted, then only reduce the nexthop 78662306a36Sopenharmony_ci * group. 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci if (fib6_event->nrt6 != fib6_rt->nhs) { 78962306a36Sopenharmony_ci for (i = 0; i < fib6_event->nrt6; i++) 79062306a36Sopenharmony_ci nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]); 79162306a36Sopenharmony_ci return; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node, 79562306a36Sopenharmony_ci nsim_fib_rt_ht_params); 79662306a36Sopenharmony_ci nsim_fib6_rt_destroy(fib6_rt); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic int nsim_fib6_event_init(struct nsim_fib6_event *fib6_event, 80062306a36Sopenharmony_ci struct fib6_entry_notifier_info *fen6_info) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct fib6_info *rt = fen6_info->rt; 80362306a36Sopenharmony_ci struct fib6_info **rt_arr; 80462306a36Sopenharmony_ci struct fib6_info *iter; 80562306a36Sopenharmony_ci unsigned int nrt6; 80662306a36Sopenharmony_ci int i = 0; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci nrt6 = fen6_info->nsiblings + 1; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci rt_arr = kcalloc(nrt6, sizeof(struct fib6_info *), GFP_ATOMIC); 81162306a36Sopenharmony_ci if (!rt_arr) 81262306a36Sopenharmony_ci return -ENOMEM; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci fib6_event->rt_arr = rt_arr; 81562306a36Sopenharmony_ci fib6_event->nrt6 = nrt6; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci rt_arr[0] = rt; 81862306a36Sopenharmony_ci fib6_info_hold(rt); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (!fen6_info->nsiblings) 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { 82462306a36Sopenharmony_ci if (i == fen6_info->nsiblings) 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci rt_arr[i + 1] = iter; 82862306a36Sopenharmony_ci fib6_info_hold(iter); 82962306a36Sopenharmony_ci i++; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci WARN_ON_ONCE(i != fen6_info->nsiblings); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci return 0; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic void nsim_fib6_event_fini(struct nsim_fib6_event *fib6_event) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci int i; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci for (i = 0; i < fib6_event->nrt6; i++) 84162306a36Sopenharmony_ci nsim_rt6_release(fib6_event->rt_arr[i]); 84262306a36Sopenharmony_ci kfree(fib6_event->rt_arr); 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic int nsim_fib6_event(struct nsim_fib_data *data, 84662306a36Sopenharmony_ci struct nsim_fib6_event *fib6_event, 84762306a36Sopenharmony_ci unsigned long event) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci int err; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (fib6_event->rt_arr[0]->fib6_src.plen) 85262306a36Sopenharmony_ci return 0; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci switch (event) { 85562306a36Sopenharmony_ci case FIB_EVENT_ENTRY_REPLACE: 85662306a36Sopenharmony_ci err = nsim_fib6_rt_insert(data, fib6_event); 85762306a36Sopenharmony_ci if (err) 85862306a36Sopenharmony_ci goto err_rt_offload_failed_flag_set; 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci case FIB_EVENT_ENTRY_APPEND: 86162306a36Sopenharmony_ci err = nsim_fib6_rt_append(data, fib6_event); 86262306a36Sopenharmony_ci if (err) 86362306a36Sopenharmony_ci goto err_rt_offload_failed_flag_set; 86462306a36Sopenharmony_ci break; 86562306a36Sopenharmony_ci case FIB_EVENT_ENTRY_DEL: 86662306a36Sopenharmony_ci nsim_fib6_rt_remove(data, fib6_event); 86762306a36Sopenharmony_ci break; 86862306a36Sopenharmony_ci default: 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci return 0; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cierr_rt_offload_failed_flag_set: 87562306a36Sopenharmony_ci nsim_fib6_rt_offload_failed_flag_set(data, fib6_event->rt_arr, 87662306a36Sopenharmony_ci fib6_event->nrt6); 87762306a36Sopenharmony_ci return err; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic void nsim_fib_event(struct nsim_fib_event *fib_event) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci switch (fib_event->family) { 88362306a36Sopenharmony_ci case AF_INET: 88462306a36Sopenharmony_ci nsim_fib4_event(fib_event->data, &fib_event->fen_info, 88562306a36Sopenharmony_ci fib_event->event); 88662306a36Sopenharmony_ci fib_info_put(fib_event->fen_info.fi); 88762306a36Sopenharmony_ci break; 88862306a36Sopenharmony_ci case AF_INET6: 88962306a36Sopenharmony_ci nsim_fib6_event(fib_event->data, &fib_event->fib6_event, 89062306a36Sopenharmony_ci fib_event->event); 89162306a36Sopenharmony_ci nsim_fib6_event_fini(&fib_event->fib6_event); 89262306a36Sopenharmony_ci break; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic int nsim_fib4_prepare_event(struct fib_notifier_info *info, 89762306a36Sopenharmony_ci struct nsim_fib_event *fib_event, 89862306a36Sopenharmony_ci unsigned long event) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct nsim_fib_data *data = fib_event->data; 90162306a36Sopenharmony_ci struct fib_entry_notifier_info *fen_info; 90262306a36Sopenharmony_ci struct netlink_ext_ack *extack; 90362306a36Sopenharmony_ci int err = 0; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci fen_info = container_of(info, struct fib_entry_notifier_info, 90662306a36Sopenharmony_ci info); 90762306a36Sopenharmony_ci fib_event->fen_info = *fen_info; 90862306a36Sopenharmony_ci extack = info->extack; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci switch (event) { 91162306a36Sopenharmony_ci case FIB_EVENT_ENTRY_REPLACE: 91262306a36Sopenharmony_ci err = nsim_fib_account(&data->ipv4.fib, true); 91362306a36Sopenharmony_ci if (err) { 91462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries"); 91562306a36Sopenharmony_ci return err; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci break; 91862306a36Sopenharmony_ci case FIB_EVENT_ENTRY_DEL: 91962306a36Sopenharmony_ci if (data->fail_route_delete) { 92062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to process route deletion"); 92162306a36Sopenharmony_ci return -EINVAL; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci nsim_fib_account(&data->ipv4.fib, false); 92462306a36Sopenharmony_ci break; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* Take reference on fib_info to prevent it from being 92862306a36Sopenharmony_ci * freed while event is queued. Release it afterwards. 92962306a36Sopenharmony_ci */ 93062306a36Sopenharmony_ci fib_info_hold(fib_event->fen_info.fi); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci return 0; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic int nsim_fib6_prepare_event(struct fib_notifier_info *info, 93662306a36Sopenharmony_ci struct nsim_fib_event *fib_event, 93762306a36Sopenharmony_ci unsigned long event) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci struct nsim_fib_data *data = fib_event->data; 94062306a36Sopenharmony_ci struct fib6_entry_notifier_info *fen6_info; 94162306a36Sopenharmony_ci struct netlink_ext_ack *extack; 94262306a36Sopenharmony_ci int err = 0; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci fen6_info = container_of(info, struct fib6_entry_notifier_info, 94562306a36Sopenharmony_ci info); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci err = nsim_fib6_event_init(&fib_event->fib6_event, fen6_info); 94862306a36Sopenharmony_ci if (err) 94962306a36Sopenharmony_ci return err; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci extack = info->extack; 95262306a36Sopenharmony_ci switch (event) { 95362306a36Sopenharmony_ci case FIB_EVENT_ENTRY_REPLACE: 95462306a36Sopenharmony_ci err = nsim_fib_account(&data->ipv6.fib, true); 95562306a36Sopenharmony_ci if (err) { 95662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries"); 95762306a36Sopenharmony_ci goto err_fib6_event_fini; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci break; 96062306a36Sopenharmony_ci case FIB_EVENT_ENTRY_DEL: 96162306a36Sopenharmony_ci if (data->fail_route_delete) { 96262306a36Sopenharmony_ci err = -EINVAL; 96362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to process route deletion"); 96462306a36Sopenharmony_ci goto err_fib6_event_fini; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci nsim_fib_account(&data->ipv6.fib, false); 96762306a36Sopenharmony_ci break; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return 0; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cierr_fib6_event_fini: 97362306a36Sopenharmony_ci nsim_fib6_event_fini(&fib_event->fib6_event); 97462306a36Sopenharmony_ci return err; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic int nsim_fib_event_schedule_work(struct nsim_fib_data *data, 97862306a36Sopenharmony_ci struct fib_notifier_info *info, 97962306a36Sopenharmony_ci unsigned long event) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct nsim_fib_event *fib_event; 98262306a36Sopenharmony_ci int err; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (info->family != AF_INET && info->family != AF_INET6) 98562306a36Sopenharmony_ci /* netdevsim does not support 'RTNL_FAMILY_IP6MR' and 98662306a36Sopenharmony_ci * 'RTNL_FAMILY_IPMR' and should ignore them. 98762306a36Sopenharmony_ci */ 98862306a36Sopenharmony_ci return NOTIFY_DONE; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci fib_event = kzalloc(sizeof(*fib_event), GFP_ATOMIC); 99162306a36Sopenharmony_ci if (!fib_event) 99262306a36Sopenharmony_ci goto err_fib_event_alloc; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci fib_event->data = data; 99562306a36Sopenharmony_ci fib_event->event = event; 99662306a36Sopenharmony_ci fib_event->family = info->family; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci switch (info->family) { 99962306a36Sopenharmony_ci case AF_INET: 100062306a36Sopenharmony_ci err = nsim_fib4_prepare_event(info, fib_event, event); 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci case AF_INET6: 100362306a36Sopenharmony_ci err = nsim_fib6_prepare_event(info, fib_event, event); 100462306a36Sopenharmony_ci break; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (err) 100862306a36Sopenharmony_ci goto err_fib_prepare_event; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* Enqueue the event and trigger the work */ 101162306a36Sopenharmony_ci spin_lock_bh(&data->fib_event_queue_lock); 101262306a36Sopenharmony_ci list_add_tail(&fib_event->list, &data->fib_event_queue); 101362306a36Sopenharmony_ci spin_unlock_bh(&data->fib_event_queue_lock); 101462306a36Sopenharmony_ci schedule_work(&data->fib_event_work); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci return NOTIFY_DONE; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cierr_fib_prepare_event: 101962306a36Sopenharmony_ci kfree(fib_event); 102062306a36Sopenharmony_cierr_fib_event_alloc: 102162306a36Sopenharmony_ci if (event == FIB_EVENT_ENTRY_DEL) 102262306a36Sopenharmony_ci schedule_work(&data->fib_flush_work); 102362306a36Sopenharmony_ci return NOTIFY_BAD; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event, 102762306a36Sopenharmony_ci void *ptr) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, 103062306a36Sopenharmony_ci fib_nb); 103162306a36Sopenharmony_ci struct fib_notifier_info *info = ptr; 103262306a36Sopenharmony_ci int err; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci switch (event) { 103562306a36Sopenharmony_ci case FIB_EVENT_RULE_ADD: 103662306a36Sopenharmony_ci case FIB_EVENT_RULE_DEL: 103762306a36Sopenharmony_ci err = nsim_fib_rule_event(data, info, 103862306a36Sopenharmony_ci event == FIB_EVENT_RULE_ADD); 103962306a36Sopenharmony_ci return notifier_from_errno(err); 104062306a36Sopenharmony_ci case FIB_EVENT_ENTRY_REPLACE: 104162306a36Sopenharmony_ci case FIB_EVENT_ENTRY_APPEND: 104262306a36Sopenharmony_ci case FIB_EVENT_ENTRY_DEL: 104362306a36Sopenharmony_ci return nsim_fib_event_schedule_work(data, info, event); 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci return NOTIFY_DONE; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt, 105062306a36Sopenharmony_ci struct nsim_fib_data *data) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct devlink *devlink = data->devlink; 105362306a36Sopenharmony_ci struct nsim_fib4_rt *fib4_rt; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common); 105662306a36Sopenharmony_ci nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false); 105762306a36Sopenharmony_ci nsim_fib_account(&data->ipv4.fib, false); 105862306a36Sopenharmony_ci nsim_fib4_rt_destroy(fib4_rt); 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt, 106262306a36Sopenharmony_ci struct nsim_fib_data *data) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct nsim_fib6_rt *fib6_rt; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common); 106762306a36Sopenharmony_ci nsim_fib6_rt_hw_flags_set(data, fib6_rt, false); 106862306a36Sopenharmony_ci nsim_fib_account(&data->ipv6.fib, false); 106962306a36Sopenharmony_ci nsim_fib6_rt_destroy(fib6_rt); 107062306a36Sopenharmony_ci} 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic void nsim_fib_rt_free(void *ptr, void *arg) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci struct nsim_fib_rt *fib_rt = ptr; 107562306a36Sopenharmony_ci struct nsim_fib_data *data = arg; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci switch (fib_rt->key.family) { 107862306a36Sopenharmony_ci case AF_INET: 107962306a36Sopenharmony_ci nsim_fib4_rt_free(fib_rt, data); 108062306a36Sopenharmony_ci break; 108162306a36Sopenharmony_ci case AF_INET6: 108262306a36Sopenharmony_ci nsim_fib6_rt_free(fib_rt, data); 108362306a36Sopenharmony_ci break; 108462306a36Sopenharmony_ci default: 108562306a36Sopenharmony_ci WARN_ON_ONCE(1); 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci/* inconsistent dump, trying again */ 109062306a36Sopenharmony_cistatic void nsim_fib_dump_inconsistent(struct notifier_block *nb) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, 109362306a36Sopenharmony_ci fib_nb); 109462306a36Sopenharmony_ci struct nsim_fib_rt *fib_rt, *fib_rt_tmp; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci /* Flush the work to make sure there is no race with notifications. */ 109762306a36Sopenharmony_ci flush_work(&data->fib_event_work); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci /* The notifier block is still not registered, so we do not need to 110062306a36Sopenharmony_ci * take any locks here. 110162306a36Sopenharmony_ci */ 110262306a36Sopenharmony_ci list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) { 110362306a36Sopenharmony_ci rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node, 110462306a36Sopenharmony_ci nsim_fib_rt_ht_params); 110562306a36Sopenharmony_ci nsim_fib_rt_free(fib_rt, data); 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci atomic64_set(&data->ipv4.rules.num, 0ULL); 110962306a36Sopenharmony_ci atomic64_set(&data->ipv6.rules.num, 0ULL); 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data, 111362306a36Sopenharmony_ci struct nh_notifier_info *info) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct nsim_nexthop *nexthop; 111662306a36Sopenharmony_ci u64 occ = 0; 111762306a36Sopenharmony_ci int i; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL); 112062306a36Sopenharmony_ci if (!nexthop) 112162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci nexthop->id = info->id; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci /* Determine the number of nexthop entries the new nexthop will 112662306a36Sopenharmony_ci * occupy. 112762306a36Sopenharmony_ci */ 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci switch (info->type) { 113062306a36Sopenharmony_ci case NH_NOTIFIER_INFO_TYPE_SINGLE: 113162306a36Sopenharmony_ci occ = 1; 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci case NH_NOTIFIER_INFO_TYPE_GRP: 113462306a36Sopenharmony_ci for (i = 0; i < info->nh_grp->num_nh; i++) 113562306a36Sopenharmony_ci occ += info->nh_grp->nh_entries[i].weight; 113662306a36Sopenharmony_ci break; 113762306a36Sopenharmony_ci case NH_NOTIFIER_INFO_TYPE_RES_TABLE: 113862306a36Sopenharmony_ci occ = info->nh_res_table->num_nh_buckets; 113962306a36Sopenharmony_ci nexthop->is_resilient = true; 114062306a36Sopenharmony_ci break; 114162306a36Sopenharmony_ci default: 114262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type"); 114362306a36Sopenharmony_ci kfree(nexthop); 114462306a36Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci nexthop->occ = occ; 114862306a36Sopenharmony_ci return nexthop; 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_cistatic void nsim_nexthop_destroy(struct nsim_nexthop *nexthop) 115262306a36Sopenharmony_ci{ 115362306a36Sopenharmony_ci kfree(nexthop); 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_cistatic int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ, 115762306a36Sopenharmony_ci bool add, struct netlink_ext_ack *extack) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci int i, err = 0; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (add) { 116262306a36Sopenharmony_ci for (i = 0; i < occ; i++) 116362306a36Sopenharmony_ci if (!atomic64_add_unless(&data->nexthops.num, 1, 116462306a36Sopenharmony_ci data->nexthops.max)) { 116562306a36Sopenharmony_ci err = -ENOSPC; 116662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops"); 116762306a36Sopenharmony_ci goto err_num_decrease; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci } else { 117062306a36Sopenharmony_ci if (WARN_ON(occ > atomic64_read(&data->nexthops.num))) 117162306a36Sopenharmony_ci return -EINVAL; 117262306a36Sopenharmony_ci atomic64_sub(occ, &data->nexthops.num); 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci return err; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cierr_num_decrease: 117862306a36Sopenharmony_ci atomic64_sub(i, &data->nexthops.num); 117962306a36Sopenharmony_ci return err; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic void nsim_nexthop_hw_flags_set(struct net *net, 118462306a36Sopenharmony_ci const struct nsim_nexthop *nexthop, 118562306a36Sopenharmony_ci bool trap) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci int i; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci nexthop_set_hw_flags(net, nexthop->id, false, trap); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci if (!nexthop->is_resilient) 119262306a36Sopenharmony_ci return; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci for (i = 0; i < nexthop->occ; i++) 119562306a36Sopenharmony_ci nexthop_bucket_set_hw_flags(net, nexthop->id, i, false, trap); 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic int nsim_nexthop_add(struct nsim_fib_data *data, 119962306a36Sopenharmony_ci struct nsim_nexthop *nexthop, 120062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci struct net *net = devlink_net(data->devlink); 120362306a36Sopenharmony_ci int err; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci err = nsim_nexthop_account(data, nexthop->occ, true, extack); 120662306a36Sopenharmony_ci if (err) 120762306a36Sopenharmony_ci return err; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node, 121062306a36Sopenharmony_ci nsim_nexthop_ht_params); 121162306a36Sopenharmony_ci if (err) { 121262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop"); 121362306a36Sopenharmony_ci goto err_nexthop_dismiss; 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci nsim_nexthop_hw_flags_set(net, nexthop, true); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci return 0; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_cierr_nexthop_dismiss: 122162306a36Sopenharmony_ci nsim_nexthop_account(data, nexthop->occ, false, extack); 122262306a36Sopenharmony_ci return err; 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cistatic int nsim_nexthop_replace(struct nsim_fib_data *data, 122662306a36Sopenharmony_ci struct nsim_nexthop *nexthop, 122762306a36Sopenharmony_ci struct nsim_nexthop *nexthop_old, 122862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 122962306a36Sopenharmony_ci{ 123062306a36Sopenharmony_ci struct net *net = devlink_net(data->devlink); 123162306a36Sopenharmony_ci int err; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci err = nsim_nexthop_account(data, nexthop->occ, true, extack); 123462306a36Sopenharmony_ci if (err) 123562306a36Sopenharmony_ci return err; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci err = rhashtable_replace_fast(&data->nexthop_ht, 123862306a36Sopenharmony_ci &nexthop_old->ht_node, &nexthop->ht_node, 123962306a36Sopenharmony_ci nsim_nexthop_ht_params); 124062306a36Sopenharmony_ci if (err) { 124162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop"); 124262306a36Sopenharmony_ci goto err_nexthop_dismiss; 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci nsim_nexthop_hw_flags_set(net, nexthop, true); 124662306a36Sopenharmony_ci nsim_nexthop_account(data, nexthop_old->occ, false, extack); 124762306a36Sopenharmony_ci nsim_nexthop_destroy(nexthop_old); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci return 0; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cierr_nexthop_dismiss: 125262306a36Sopenharmony_ci nsim_nexthop_account(data, nexthop->occ, false, extack); 125362306a36Sopenharmony_ci return err; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic int nsim_nexthop_insert(struct nsim_fib_data *data, 125762306a36Sopenharmony_ci struct nh_notifier_info *info) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci struct nsim_nexthop *nexthop, *nexthop_old; 126062306a36Sopenharmony_ci int err; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci nexthop = nsim_nexthop_create(data, info); 126362306a36Sopenharmony_ci if (IS_ERR(nexthop)) 126462306a36Sopenharmony_ci return PTR_ERR(nexthop); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id, 126762306a36Sopenharmony_ci nsim_nexthop_ht_params); 126862306a36Sopenharmony_ci if (!nexthop_old) 126962306a36Sopenharmony_ci err = nsim_nexthop_add(data, nexthop, info->extack); 127062306a36Sopenharmony_ci else 127162306a36Sopenharmony_ci err = nsim_nexthop_replace(data, nexthop, nexthop_old, 127262306a36Sopenharmony_ci info->extack); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (err) 127562306a36Sopenharmony_ci nsim_nexthop_destroy(nexthop); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci return err; 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cistatic void nsim_nexthop_remove(struct nsim_fib_data *data, 128162306a36Sopenharmony_ci struct nh_notifier_info *info) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct nsim_nexthop *nexthop; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id, 128662306a36Sopenharmony_ci nsim_nexthop_ht_params); 128762306a36Sopenharmony_ci if (!nexthop) 128862306a36Sopenharmony_ci return; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node, 129162306a36Sopenharmony_ci nsim_nexthop_ht_params); 129262306a36Sopenharmony_ci nsim_nexthop_account(data, nexthop->occ, false, info->extack); 129362306a36Sopenharmony_ci nsim_nexthop_destroy(nexthop); 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic int nsim_nexthop_res_table_pre_replace(struct nsim_fib_data *data, 129762306a36Sopenharmony_ci struct nh_notifier_info *info) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci if (data->fail_res_nexthop_group_replace) { 130062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(info->extack, "Failed to replace a resilient nexthop group"); 130162306a36Sopenharmony_ci return -EINVAL; 130262306a36Sopenharmony_ci } 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci return 0; 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic int nsim_nexthop_bucket_replace(struct nsim_fib_data *data, 130862306a36Sopenharmony_ci struct nh_notifier_info *info) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci if (data->fail_nexthop_bucket_replace) { 131162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(info->extack, "Failed to replace nexthop bucket"); 131262306a36Sopenharmony_ci return -EINVAL; 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci nexthop_bucket_set_hw_flags(info->net, info->id, 131662306a36Sopenharmony_ci info->nh_res_bucket->bucket_index, 131762306a36Sopenharmony_ci false, true); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci return 0; 132062306a36Sopenharmony_ci} 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_cistatic int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event, 132362306a36Sopenharmony_ci void *ptr) 132462306a36Sopenharmony_ci{ 132562306a36Sopenharmony_ci struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, 132662306a36Sopenharmony_ci nexthop_nb); 132762306a36Sopenharmony_ci struct nh_notifier_info *info = ptr; 132862306a36Sopenharmony_ci int err = 0; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci mutex_lock(&data->nh_lock); 133162306a36Sopenharmony_ci switch (event) { 133262306a36Sopenharmony_ci case NEXTHOP_EVENT_REPLACE: 133362306a36Sopenharmony_ci err = nsim_nexthop_insert(data, info); 133462306a36Sopenharmony_ci break; 133562306a36Sopenharmony_ci case NEXTHOP_EVENT_DEL: 133662306a36Sopenharmony_ci nsim_nexthop_remove(data, info); 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci case NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE: 133962306a36Sopenharmony_ci err = nsim_nexthop_res_table_pre_replace(data, info); 134062306a36Sopenharmony_ci break; 134162306a36Sopenharmony_ci case NEXTHOP_EVENT_BUCKET_REPLACE: 134262306a36Sopenharmony_ci err = nsim_nexthop_bucket_replace(data, info); 134362306a36Sopenharmony_ci break; 134462306a36Sopenharmony_ci default: 134562306a36Sopenharmony_ci break; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci mutex_unlock(&data->nh_lock); 134962306a36Sopenharmony_ci return notifier_from_errno(err); 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic void nsim_nexthop_free(void *ptr, void *arg) 135362306a36Sopenharmony_ci{ 135462306a36Sopenharmony_ci struct nsim_nexthop *nexthop = ptr; 135562306a36Sopenharmony_ci struct nsim_fib_data *data = arg; 135662306a36Sopenharmony_ci struct net *net; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci net = devlink_net(data->devlink); 135962306a36Sopenharmony_ci nsim_nexthop_hw_flags_set(net, nexthop, false); 136062306a36Sopenharmony_ci nsim_nexthop_account(data, nexthop->occ, false, NULL); 136162306a36Sopenharmony_ci nsim_nexthop_destroy(nexthop); 136262306a36Sopenharmony_ci} 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_cistatic ssize_t nsim_nexthop_bucket_activity_write(struct file *file, 136562306a36Sopenharmony_ci const char __user *user_buf, 136662306a36Sopenharmony_ci size_t size, loff_t *ppos) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci struct nsim_fib_data *data = file->private_data; 136962306a36Sopenharmony_ci struct net *net = devlink_net(data->devlink); 137062306a36Sopenharmony_ci struct nsim_nexthop *nexthop; 137162306a36Sopenharmony_ci unsigned long *activity; 137262306a36Sopenharmony_ci loff_t pos = *ppos; 137362306a36Sopenharmony_ci u16 bucket_index; 137462306a36Sopenharmony_ci char buf[128]; 137562306a36Sopenharmony_ci int err = 0; 137662306a36Sopenharmony_ci u32 nhid; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (pos != 0) 137962306a36Sopenharmony_ci return -EINVAL; 138062306a36Sopenharmony_ci if (size > sizeof(buf)) 138162306a36Sopenharmony_ci return -EINVAL; 138262306a36Sopenharmony_ci if (copy_from_user(buf, user_buf, size)) 138362306a36Sopenharmony_ci return -EFAULT; 138462306a36Sopenharmony_ci if (sscanf(buf, "%u %hu", &nhid, &bucket_index) != 2) 138562306a36Sopenharmony_ci return -EINVAL; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci rtnl_lock(); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &nhid, 139062306a36Sopenharmony_ci nsim_nexthop_ht_params); 139162306a36Sopenharmony_ci if (!nexthop || !nexthop->is_resilient || 139262306a36Sopenharmony_ci bucket_index >= nexthop->occ) { 139362306a36Sopenharmony_ci err = -EINVAL; 139462306a36Sopenharmony_ci goto out; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci activity = bitmap_zalloc(nexthop->occ, GFP_KERNEL); 139862306a36Sopenharmony_ci if (!activity) { 139962306a36Sopenharmony_ci err = -ENOMEM; 140062306a36Sopenharmony_ci goto out; 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci bitmap_set(activity, bucket_index, 1); 140462306a36Sopenharmony_ci nexthop_res_grp_activity_update(net, nhid, nexthop->occ, activity); 140562306a36Sopenharmony_ci bitmap_free(activity); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ciout: 140862306a36Sopenharmony_ci rtnl_unlock(); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci *ppos = size; 141162306a36Sopenharmony_ci return err ?: size; 141262306a36Sopenharmony_ci} 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_cistatic const struct file_operations nsim_nexthop_bucket_activity_fops = { 141562306a36Sopenharmony_ci .open = simple_open, 141662306a36Sopenharmony_ci .write = nsim_nexthop_bucket_activity_write, 141762306a36Sopenharmony_ci .llseek = no_llseek, 141862306a36Sopenharmony_ci .owner = THIS_MODULE, 141962306a36Sopenharmony_ci}; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_cistatic u64 nsim_fib_ipv4_resource_occ_get(void *priv) 142262306a36Sopenharmony_ci{ 142362306a36Sopenharmony_ci struct nsim_fib_data *data = priv; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false); 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cistatic u64 nsim_fib_ipv4_rules_res_occ_get(void *priv) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci struct nsim_fib_data *data = priv; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false); 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_cistatic u64 nsim_fib_ipv6_resource_occ_get(void *priv) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci struct nsim_fib_data *data = priv; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false); 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic u64 nsim_fib_ipv6_rules_res_occ_get(void *priv) 144362306a36Sopenharmony_ci{ 144462306a36Sopenharmony_ci struct nsim_fib_data *data = priv; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false); 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_cistatic u64 nsim_fib_nexthops_res_occ_get(void *priv) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci struct nsim_fib_data *data = priv; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false); 145462306a36Sopenharmony_ci} 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_cistatic void nsim_fib_set_max_all(struct nsim_fib_data *data, 145762306a36Sopenharmony_ci struct devlink *devlink) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci static const enum nsim_resource_id res_ids[] = { 146062306a36Sopenharmony_ci NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES, 146162306a36Sopenharmony_ci NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES, 146262306a36Sopenharmony_ci NSIM_RESOURCE_NEXTHOPS, 146362306a36Sopenharmony_ci }; 146462306a36Sopenharmony_ci int i; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(res_ids); i++) { 146762306a36Sopenharmony_ci int err; 146862306a36Sopenharmony_ci u64 val; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci err = devl_resource_size_get(devlink, res_ids[i], &val); 147162306a36Sopenharmony_ci if (err) 147262306a36Sopenharmony_ci val = (u64) -1; 147362306a36Sopenharmony_ci nsim_fib_set_max(data, res_ids[i], val); 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci} 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic void nsim_fib_event_work(struct work_struct *work) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci struct nsim_fib_data *data = container_of(work, struct nsim_fib_data, 148062306a36Sopenharmony_ci fib_event_work); 148162306a36Sopenharmony_ci struct nsim_fib_event *fib_event, *next_fib_event; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci LIST_HEAD(fib_event_queue); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci spin_lock_bh(&data->fib_event_queue_lock); 148662306a36Sopenharmony_ci list_splice_init(&data->fib_event_queue, &fib_event_queue); 148762306a36Sopenharmony_ci spin_unlock_bh(&data->fib_event_queue_lock); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci mutex_lock(&data->fib_lock); 149062306a36Sopenharmony_ci list_for_each_entry_safe(fib_event, next_fib_event, &fib_event_queue, 149162306a36Sopenharmony_ci list) { 149262306a36Sopenharmony_ci nsim_fib_event(fib_event); 149362306a36Sopenharmony_ci list_del(&fib_event->list); 149462306a36Sopenharmony_ci kfree(fib_event); 149562306a36Sopenharmony_ci cond_resched(); 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci mutex_unlock(&data->fib_lock); 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic void nsim_fib_flush_work(struct work_struct *work) 150162306a36Sopenharmony_ci{ 150262306a36Sopenharmony_ci struct nsim_fib_data *data = container_of(work, struct nsim_fib_data, 150362306a36Sopenharmony_ci fib_flush_work); 150462306a36Sopenharmony_ci struct nsim_fib_rt *fib_rt, *fib_rt_tmp; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci /* Process pending work. */ 150762306a36Sopenharmony_ci flush_work(&data->fib_event_work); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci mutex_lock(&data->fib_lock); 151062306a36Sopenharmony_ci list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) { 151162306a36Sopenharmony_ci rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node, 151262306a36Sopenharmony_ci nsim_fib_rt_ht_params); 151362306a36Sopenharmony_ci nsim_fib_rt_free(fib_rt, data); 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci mutex_unlock(&data->fib_lock); 151662306a36Sopenharmony_ci} 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_cistatic int 151962306a36Sopenharmony_cinsim_fib_debugfs_init(struct nsim_fib_data *data, struct nsim_dev *nsim_dev) 152062306a36Sopenharmony_ci{ 152162306a36Sopenharmony_ci data->ddir = debugfs_create_dir("fib", nsim_dev->ddir); 152262306a36Sopenharmony_ci if (IS_ERR(data->ddir)) 152362306a36Sopenharmony_ci return PTR_ERR(data->ddir); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci data->fail_route_offload = false; 152662306a36Sopenharmony_ci debugfs_create_bool("fail_route_offload", 0600, data->ddir, 152762306a36Sopenharmony_ci &data->fail_route_offload); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci data->fail_res_nexthop_group_replace = false; 153062306a36Sopenharmony_ci debugfs_create_bool("fail_res_nexthop_group_replace", 0600, data->ddir, 153162306a36Sopenharmony_ci &data->fail_res_nexthop_group_replace); 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci data->fail_nexthop_bucket_replace = false; 153462306a36Sopenharmony_ci debugfs_create_bool("fail_nexthop_bucket_replace", 0600, data->ddir, 153562306a36Sopenharmony_ci &data->fail_nexthop_bucket_replace); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci debugfs_create_file("nexthop_bucket_activity", 0200, data->ddir, 153862306a36Sopenharmony_ci data, &nsim_nexthop_bucket_activity_fops); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci data->fail_route_delete = false; 154162306a36Sopenharmony_ci debugfs_create_bool("fail_route_delete", 0600, data->ddir, 154262306a36Sopenharmony_ci &data->fail_route_delete); 154362306a36Sopenharmony_ci return 0; 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_cistatic void nsim_fib_debugfs_exit(struct nsim_fib_data *data) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci debugfs_remove_recursive(data->ddir); 154962306a36Sopenharmony_ci} 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_cistruct nsim_fib_data *nsim_fib_create(struct devlink *devlink, 155262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 155362306a36Sopenharmony_ci{ 155462306a36Sopenharmony_ci struct nsim_fib_data *data; 155562306a36Sopenharmony_ci struct nsim_dev *nsim_dev; 155662306a36Sopenharmony_ci int err; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 155962306a36Sopenharmony_ci if (!data) 156062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 156162306a36Sopenharmony_ci data->devlink = devlink; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci nsim_dev = devlink_priv(devlink); 156462306a36Sopenharmony_ci err = nsim_fib_debugfs_init(data, nsim_dev); 156562306a36Sopenharmony_ci if (err) 156662306a36Sopenharmony_ci goto err_data_free; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci mutex_init(&data->nh_lock); 156962306a36Sopenharmony_ci err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params); 157062306a36Sopenharmony_ci if (err) 157162306a36Sopenharmony_ci goto err_debugfs_exit; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci mutex_init(&data->fib_lock); 157462306a36Sopenharmony_ci INIT_LIST_HEAD(&data->fib_rt_list); 157562306a36Sopenharmony_ci err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params); 157662306a36Sopenharmony_ci if (err) 157762306a36Sopenharmony_ci goto err_rhashtable_nexthop_destroy; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci INIT_WORK(&data->fib_event_work, nsim_fib_event_work); 158062306a36Sopenharmony_ci INIT_WORK(&data->fib_flush_work, nsim_fib_flush_work); 158162306a36Sopenharmony_ci INIT_LIST_HEAD(&data->fib_event_queue); 158262306a36Sopenharmony_ci spin_lock_init(&data->fib_event_queue_lock); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci nsim_fib_set_max_all(data, devlink); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci data->nexthop_nb.notifier_call = nsim_nexthop_event_nb; 158762306a36Sopenharmony_ci err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb, 158862306a36Sopenharmony_ci extack); 158962306a36Sopenharmony_ci if (err) { 159062306a36Sopenharmony_ci pr_err("Failed to register nexthop notifier\n"); 159162306a36Sopenharmony_ci goto err_rhashtable_fib_destroy; 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci data->fib_nb.notifier_call = nsim_fib_event_nb; 159562306a36Sopenharmony_ci err = register_fib_notifier(devlink_net(devlink), &data->fib_nb, 159662306a36Sopenharmony_ci nsim_fib_dump_inconsistent, extack); 159762306a36Sopenharmony_ci if (err) { 159862306a36Sopenharmony_ci pr_err("Failed to register fib notifier\n"); 159962306a36Sopenharmony_ci goto err_nexthop_nb_unregister; 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci devl_resource_occ_get_register(devlink, 160362306a36Sopenharmony_ci NSIM_RESOURCE_IPV4_FIB, 160462306a36Sopenharmony_ci nsim_fib_ipv4_resource_occ_get, 160562306a36Sopenharmony_ci data); 160662306a36Sopenharmony_ci devl_resource_occ_get_register(devlink, 160762306a36Sopenharmony_ci NSIM_RESOURCE_IPV4_FIB_RULES, 160862306a36Sopenharmony_ci nsim_fib_ipv4_rules_res_occ_get, 160962306a36Sopenharmony_ci data); 161062306a36Sopenharmony_ci devl_resource_occ_get_register(devlink, 161162306a36Sopenharmony_ci NSIM_RESOURCE_IPV6_FIB, 161262306a36Sopenharmony_ci nsim_fib_ipv6_resource_occ_get, 161362306a36Sopenharmony_ci data); 161462306a36Sopenharmony_ci devl_resource_occ_get_register(devlink, 161562306a36Sopenharmony_ci NSIM_RESOURCE_IPV6_FIB_RULES, 161662306a36Sopenharmony_ci nsim_fib_ipv6_rules_res_occ_get, 161762306a36Sopenharmony_ci data); 161862306a36Sopenharmony_ci devl_resource_occ_get_register(devlink, 161962306a36Sopenharmony_ci NSIM_RESOURCE_NEXTHOPS, 162062306a36Sopenharmony_ci nsim_fib_nexthops_res_occ_get, 162162306a36Sopenharmony_ci data); 162262306a36Sopenharmony_ci return data; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_cierr_nexthop_nb_unregister: 162562306a36Sopenharmony_ci unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); 162662306a36Sopenharmony_cierr_rhashtable_fib_destroy: 162762306a36Sopenharmony_ci cancel_work_sync(&data->fib_flush_work); 162862306a36Sopenharmony_ci flush_work(&data->fib_event_work); 162962306a36Sopenharmony_ci rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free, 163062306a36Sopenharmony_ci data); 163162306a36Sopenharmony_cierr_rhashtable_nexthop_destroy: 163262306a36Sopenharmony_ci rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, 163362306a36Sopenharmony_ci data); 163462306a36Sopenharmony_ci mutex_destroy(&data->fib_lock); 163562306a36Sopenharmony_cierr_debugfs_exit: 163662306a36Sopenharmony_ci mutex_destroy(&data->nh_lock); 163762306a36Sopenharmony_ci nsim_fib_debugfs_exit(data); 163862306a36Sopenharmony_cierr_data_free: 163962306a36Sopenharmony_ci kfree(data); 164062306a36Sopenharmony_ci return ERR_PTR(err); 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_civoid nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data) 164462306a36Sopenharmony_ci{ 164562306a36Sopenharmony_ci devl_resource_occ_get_unregister(devlink, 164662306a36Sopenharmony_ci NSIM_RESOURCE_NEXTHOPS); 164762306a36Sopenharmony_ci devl_resource_occ_get_unregister(devlink, 164862306a36Sopenharmony_ci NSIM_RESOURCE_IPV6_FIB_RULES); 164962306a36Sopenharmony_ci devl_resource_occ_get_unregister(devlink, 165062306a36Sopenharmony_ci NSIM_RESOURCE_IPV6_FIB); 165162306a36Sopenharmony_ci devl_resource_occ_get_unregister(devlink, 165262306a36Sopenharmony_ci NSIM_RESOURCE_IPV4_FIB_RULES); 165362306a36Sopenharmony_ci devl_resource_occ_get_unregister(devlink, 165462306a36Sopenharmony_ci NSIM_RESOURCE_IPV4_FIB); 165562306a36Sopenharmony_ci unregister_fib_notifier(devlink_net(devlink), &data->fib_nb); 165662306a36Sopenharmony_ci unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); 165762306a36Sopenharmony_ci cancel_work_sync(&data->fib_flush_work); 165862306a36Sopenharmony_ci flush_work(&data->fib_event_work); 165962306a36Sopenharmony_ci rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free, 166062306a36Sopenharmony_ci data); 166162306a36Sopenharmony_ci rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, 166262306a36Sopenharmony_ci data); 166362306a36Sopenharmony_ci WARN_ON_ONCE(!list_empty(&data->fib_event_queue)); 166462306a36Sopenharmony_ci WARN_ON_ONCE(!list_empty(&data->fib_rt_list)); 166562306a36Sopenharmony_ci mutex_destroy(&data->fib_lock); 166662306a36Sopenharmony_ci mutex_destroy(&data->nh_lock); 166762306a36Sopenharmony_ci nsim_fib_debugfs_exit(data); 166862306a36Sopenharmony_ci kfree(data); 166962306a36Sopenharmony_ci} 1670