Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2018 Cumulus Networks. All rights reserved.
0003  * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
0004  *
0005  * This software is licensed under the GNU General License Version 2,
0006  * June 1991 as shown in the file COPYING in the top-level directory of this
0007  * source tree.
0008  *
0009  * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
0010  * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
0011  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
0012  * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
0013  * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
0014  * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
0015  */
0016 
0017 #include <linux/bitmap.h>
0018 #include <linux/in6.h>
0019 #include <linux/kernel.h>
0020 #include <linux/list.h>
0021 #include <linux/rhashtable.h>
0022 #include <linux/spinlock_types.h>
0023 #include <linux/types.h>
0024 #include <net/fib_notifier.h>
0025 #include <net/inet_dscp.h>
0026 #include <net/ip_fib.h>
0027 #include <net/ip6_fib.h>
0028 #include <net/fib_rules.h>
0029 #include <net/net_namespace.h>
0030 #include <net/nexthop.h>
0031 #include <linux/debugfs.h>
0032 
0033 #include "netdevsim.h"
0034 
0035 struct nsim_fib_entry {
0036     u64 max;
0037     atomic64_t num;
0038 };
0039 
0040 struct nsim_per_fib_data {
0041     struct nsim_fib_entry fib;
0042     struct nsim_fib_entry rules;
0043 };
0044 
0045 struct nsim_fib_data {
0046     struct notifier_block fib_nb;
0047     struct nsim_per_fib_data ipv4;
0048     struct nsim_per_fib_data ipv6;
0049     struct nsim_fib_entry nexthops;
0050     struct rhashtable fib_rt_ht;
0051     struct list_head fib_rt_list;
0052     struct mutex fib_lock; /* Protects FIB HT and list */
0053     struct notifier_block nexthop_nb;
0054     struct rhashtable nexthop_ht;
0055     struct devlink *devlink;
0056     struct work_struct fib_event_work;
0057     struct work_struct fib_flush_work;
0058     struct list_head fib_event_queue;
0059     spinlock_t fib_event_queue_lock; /* Protects fib event queue list */
0060     struct mutex nh_lock; /* Protects NH HT */
0061     struct dentry *ddir;
0062     bool fail_route_offload;
0063     bool fail_res_nexthop_group_replace;
0064     bool fail_nexthop_bucket_replace;
0065     bool fail_route_delete;
0066 };
0067 
0068 struct nsim_fib_rt_key {
0069     unsigned char addr[sizeof(struct in6_addr)];
0070     unsigned char prefix_len;
0071     int family;
0072     u32 tb_id;
0073 };
0074 
0075 struct nsim_fib_rt {
0076     struct nsim_fib_rt_key key;
0077     struct rhash_head ht_node;
0078     struct list_head list;  /* Member of fib_rt_list */
0079 };
0080 
0081 struct nsim_fib4_rt {
0082     struct nsim_fib_rt common;
0083     struct fib_info *fi;
0084     dscp_t dscp;
0085     u8 type;
0086 };
0087 
0088 struct nsim_fib6_rt {
0089     struct nsim_fib_rt common;
0090     struct list_head nh_list;
0091     unsigned int nhs;
0092 };
0093 
0094 struct nsim_fib6_rt_nh {
0095     struct list_head list;  /* Member of nh_list */
0096     struct fib6_info *rt;
0097 };
0098 
0099 struct nsim_fib6_event {
0100     struct fib6_info **rt_arr;
0101     unsigned int nrt6;
0102 };
0103 
0104 struct nsim_fib_event {
0105     struct list_head list; /* node in fib queue */
0106     union {
0107         struct fib_entry_notifier_info fen_info;
0108         struct nsim_fib6_event fib6_event;
0109     };
0110     struct nsim_fib_data *data;
0111     unsigned long event;
0112     int family;
0113 };
0114 
0115 static const struct rhashtable_params nsim_fib_rt_ht_params = {
0116     .key_offset = offsetof(struct nsim_fib_rt, key),
0117     .head_offset = offsetof(struct nsim_fib_rt, ht_node),
0118     .key_len = sizeof(struct nsim_fib_rt_key),
0119     .automatic_shrinking = true,
0120 };
0121 
0122 struct nsim_nexthop {
0123     struct rhash_head ht_node;
0124     u64 occ;
0125     u32 id;
0126     bool is_resilient;
0127 };
0128 
0129 static const struct rhashtable_params nsim_nexthop_ht_params = {
0130     .key_offset = offsetof(struct nsim_nexthop, id),
0131     .head_offset = offsetof(struct nsim_nexthop, ht_node),
0132     .key_len = sizeof(u32),
0133     .automatic_shrinking = true,
0134 };
0135 
0136 u64 nsim_fib_get_val(struct nsim_fib_data *fib_data,
0137              enum nsim_resource_id res_id, bool max)
0138 {
0139     struct nsim_fib_entry *entry;
0140 
0141     switch (res_id) {
0142     case NSIM_RESOURCE_IPV4_FIB:
0143         entry = &fib_data->ipv4.fib;
0144         break;
0145     case NSIM_RESOURCE_IPV4_FIB_RULES:
0146         entry = &fib_data->ipv4.rules;
0147         break;
0148     case NSIM_RESOURCE_IPV6_FIB:
0149         entry = &fib_data->ipv6.fib;
0150         break;
0151     case NSIM_RESOURCE_IPV6_FIB_RULES:
0152         entry = &fib_data->ipv6.rules;
0153         break;
0154     case NSIM_RESOURCE_NEXTHOPS:
0155         entry = &fib_data->nexthops;
0156         break;
0157     default:
0158         return 0;
0159     }
0160 
0161     return max ? entry->max : atomic64_read(&entry->num);
0162 }
0163 
0164 static void nsim_fib_set_max(struct nsim_fib_data *fib_data,
0165                  enum nsim_resource_id res_id, u64 val)
0166 {
0167     struct nsim_fib_entry *entry;
0168 
0169     switch (res_id) {
0170     case NSIM_RESOURCE_IPV4_FIB:
0171         entry = &fib_data->ipv4.fib;
0172         break;
0173     case NSIM_RESOURCE_IPV4_FIB_RULES:
0174         entry = &fib_data->ipv4.rules;
0175         break;
0176     case NSIM_RESOURCE_IPV6_FIB:
0177         entry = &fib_data->ipv6.fib;
0178         break;
0179     case NSIM_RESOURCE_IPV6_FIB_RULES:
0180         entry = &fib_data->ipv6.rules;
0181         break;
0182     case NSIM_RESOURCE_NEXTHOPS:
0183         entry = &fib_data->nexthops;
0184         break;
0185     default:
0186         WARN_ON(1);
0187         return;
0188     }
0189     entry->max = val;
0190 }
0191 
0192 static int nsim_fib_rule_account(struct nsim_fib_entry *entry, bool add,
0193                  struct netlink_ext_ack *extack)
0194 {
0195     int err = 0;
0196 
0197     if (add) {
0198         if (!atomic64_add_unless(&entry->num, 1, entry->max)) {
0199             err = -ENOSPC;
0200             NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib rule entries");
0201         }
0202     } else {
0203         atomic64_dec_if_positive(&entry->num);
0204     }
0205 
0206     return err;
0207 }
0208 
0209 static int nsim_fib_rule_event(struct nsim_fib_data *data,
0210                    struct fib_notifier_info *info, bool add)
0211 {
0212     struct netlink_ext_ack *extack = info->extack;
0213     int err = 0;
0214 
0215     switch (info->family) {
0216     case AF_INET:
0217         err = nsim_fib_rule_account(&data->ipv4.rules, add, extack);
0218         break;
0219     case AF_INET6:
0220         err = nsim_fib_rule_account(&data->ipv6.rules, add, extack);
0221         break;
0222     }
0223 
0224     return err;
0225 }
0226 
0227 static int nsim_fib_account(struct nsim_fib_entry *entry, bool add)
0228 {
0229     int err = 0;
0230 
0231     if (add) {
0232         if (!atomic64_add_unless(&entry->num, 1, entry->max))
0233             err = -ENOSPC;
0234     } else {
0235         atomic64_dec_if_positive(&entry->num);
0236     }
0237 
0238     return err;
0239 }
0240 
0241 static void nsim_fib_rt_init(struct nsim_fib_data *data,
0242                  struct nsim_fib_rt *fib_rt, const void *addr,
0243                  size_t addr_len, unsigned int prefix_len,
0244                  int family, u32 tb_id)
0245 {
0246     memcpy(fib_rt->key.addr, addr, addr_len);
0247     fib_rt->key.prefix_len = prefix_len;
0248     fib_rt->key.family = family;
0249     fib_rt->key.tb_id = tb_id;
0250     list_add(&fib_rt->list, &data->fib_rt_list);
0251 }
0252 
0253 static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
0254 {
0255     list_del(&fib_rt->list);
0256 }
0257 
0258 static struct nsim_fib_rt *nsim_fib_rt_lookup(struct rhashtable *fib_rt_ht,
0259                           const void *addr, size_t addr_len,
0260                           unsigned int prefix_len,
0261                           int family, u32 tb_id)
0262 {
0263     struct nsim_fib_rt_key key;
0264 
0265     memset(&key, 0, sizeof(key));
0266     memcpy(key.addr, addr, addr_len);
0267     key.prefix_len = prefix_len;
0268     key.family = family;
0269     key.tb_id = tb_id;
0270 
0271     return rhashtable_lookup_fast(fib_rt_ht, &key, nsim_fib_rt_ht_params);
0272 }
0273 
0274 static struct nsim_fib4_rt *
0275 nsim_fib4_rt_create(struct nsim_fib_data *data,
0276             struct fib_entry_notifier_info *fen_info)
0277 {
0278     struct nsim_fib4_rt *fib4_rt;
0279 
0280     fib4_rt = kzalloc(sizeof(*fib4_rt), GFP_KERNEL);
0281     if (!fib4_rt)
0282         return NULL;
0283 
0284     nsim_fib_rt_init(data, &fib4_rt->common, &fen_info->dst, sizeof(u32),
0285              fen_info->dst_len, AF_INET, fen_info->tb_id);
0286 
0287     fib4_rt->fi = fen_info->fi;
0288     fib_info_hold(fib4_rt->fi);
0289     fib4_rt->dscp = fen_info->dscp;
0290     fib4_rt->type = fen_info->type;
0291 
0292     return fib4_rt;
0293 }
0294 
0295 static void nsim_fib4_rt_destroy(struct nsim_fib4_rt *fib4_rt)
0296 {
0297     fib_info_put(fib4_rt->fi);
0298     nsim_fib_rt_fini(&fib4_rt->common);
0299     kfree(fib4_rt);
0300 }
0301 
0302 static struct nsim_fib4_rt *
0303 nsim_fib4_rt_lookup(struct rhashtable *fib_rt_ht,
0304             const struct fib_entry_notifier_info *fen_info)
0305 {
0306     struct nsim_fib_rt *fib_rt;
0307 
0308     fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &fen_info->dst, sizeof(u32),
0309                     fen_info->dst_len, AF_INET,
0310                     fen_info->tb_id);
0311     if (!fib_rt)
0312         return NULL;
0313 
0314     return container_of(fib_rt, struct nsim_fib4_rt, common);
0315 }
0316 
0317 static void
0318 nsim_fib4_rt_offload_failed_flag_set(struct net *net,
0319                      struct fib_entry_notifier_info *fen_info)
0320 {
0321     u32 *p_dst = (u32 *)&fen_info->dst;
0322     struct fib_rt_info fri;
0323 
0324     fri.fi = fen_info->fi;
0325     fri.tb_id = fen_info->tb_id;
0326     fri.dst = cpu_to_be32(*p_dst);
0327     fri.dst_len = fen_info->dst_len;
0328     fri.dscp = fen_info->dscp;
0329     fri.type = fen_info->type;
0330     fri.offload = false;
0331     fri.trap = false;
0332     fri.offload_failed = true;
0333     fib_alias_hw_flags_set(net, &fri);
0334 }
0335 
0336 static void nsim_fib4_rt_hw_flags_set(struct net *net,
0337                       const struct nsim_fib4_rt *fib4_rt,
0338                       bool trap)
0339 {
0340     u32 *p_dst = (u32 *) fib4_rt->common.key.addr;
0341     int dst_len = fib4_rt->common.key.prefix_len;
0342     struct fib_rt_info fri;
0343 
0344     fri.fi = fib4_rt->fi;
0345     fri.tb_id = fib4_rt->common.key.tb_id;
0346     fri.dst = cpu_to_be32(*p_dst);
0347     fri.dst_len = dst_len;
0348     fri.dscp = fib4_rt->dscp;
0349     fri.type = fib4_rt->type;
0350     fri.offload = false;
0351     fri.trap = trap;
0352     fri.offload_failed = false;
0353     fib_alias_hw_flags_set(net, &fri);
0354 }
0355 
0356 static int nsim_fib4_rt_add(struct nsim_fib_data *data,
0357                 struct nsim_fib4_rt *fib4_rt)
0358 {
0359     struct net *net = devlink_net(data->devlink);
0360     int err;
0361 
0362     err = rhashtable_insert_fast(&data->fib_rt_ht,
0363                      &fib4_rt->common.ht_node,
0364                      nsim_fib_rt_ht_params);
0365     if (err)
0366         goto err_fib_dismiss;
0367 
0368     /* Simulate hardware programming latency. */
0369     msleep(1);
0370     nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
0371 
0372     return 0;
0373 
0374 err_fib_dismiss:
0375     /* Drop the accounting that was increased from the notification
0376      * context when FIB_EVENT_ENTRY_REPLACE was triggered.
0377      */
0378     nsim_fib_account(&data->ipv4.fib, false);
0379     return err;
0380 }
0381 
0382 static int nsim_fib4_rt_replace(struct nsim_fib_data *data,
0383                 struct nsim_fib4_rt *fib4_rt,
0384                 struct nsim_fib4_rt *fib4_rt_old)
0385 {
0386     struct net *net = devlink_net(data->devlink);
0387     int err;
0388 
0389     /* We are replacing a route, so need to remove the accounting which
0390      * was increased when FIB_EVENT_ENTRY_REPLACE was triggered.
0391      */
0392     err = nsim_fib_account(&data->ipv4.fib, false);
0393     if (err)
0394         return err;
0395     err = rhashtable_replace_fast(&data->fib_rt_ht,
0396                       &fib4_rt_old->common.ht_node,
0397                       &fib4_rt->common.ht_node,
0398                       nsim_fib_rt_ht_params);
0399     if (err)
0400         return err;
0401 
0402     msleep(1);
0403     nsim_fib4_rt_hw_flags_set(net, fib4_rt, true);
0404 
0405     nsim_fib4_rt_hw_flags_set(net, fib4_rt_old, false);
0406     nsim_fib4_rt_destroy(fib4_rt_old);
0407 
0408     return 0;
0409 }
0410 
0411 static int nsim_fib4_rt_insert(struct nsim_fib_data *data,
0412                    struct fib_entry_notifier_info *fen_info)
0413 {
0414     struct nsim_fib4_rt *fib4_rt, *fib4_rt_old;
0415     int err;
0416 
0417     if (data->fail_route_offload) {
0418         /* For testing purposes, user set debugfs fail_route_offload
0419          * value to true. Simulate hardware programming latency and then
0420          * fail.
0421          */
0422         msleep(1);
0423         return -EINVAL;
0424     }
0425 
0426     fib4_rt = nsim_fib4_rt_create(data, fen_info);
0427     if (!fib4_rt)
0428         return -ENOMEM;
0429 
0430     fib4_rt_old = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
0431     if (!fib4_rt_old)
0432         err = nsim_fib4_rt_add(data, fib4_rt);
0433     else
0434         err = nsim_fib4_rt_replace(data, fib4_rt, fib4_rt_old);
0435 
0436     if (err)
0437         nsim_fib4_rt_destroy(fib4_rt);
0438 
0439     return err;
0440 }
0441 
0442 static void nsim_fib4_rt_remove(struct nsim_fib_data *data,
0443                 const struct fib_entry_notifier_info *fen_info)
0444 {
0445     struct nsim_fib4_rt *fib4_rt;
0446 
0447     fib4_rt = nsim_fib4_rt_lookup(&data->fib_rt_ht, fen_info);
0448     if (!fib4_rt)
0449         return;
0450 
0451     rhashtable_remove_fast(&data->fib_rt_ht, &fib4_rt->common.ht_node,
0452                    nsim_fib_rt_ht_params);
0453     nsim_fib4_rt_destroy(fib4_rt);
0454 }
0455 
0456 static int nsim_fib4_event(struct nsim_fib_data *data,
0457                struct fib_entry_notifier_info *fen_info,
0458                unsigned long event)
0459 {
0460     int err = 0;
0461 
0462     switch (event) {
0463     case FIB_EVENT_ENTRY_REPLACE:
0464         err = nsim_fib4_rt_insert(data, fen_info);
0465         if (err) {
0466             struct net *net = devlink_net(data->devlink);
0467 
0468             nsim_fib4_rt_offload_failed_flag_set(net, fen_info);
0469         }
0470         break;
0471     case FIB_EVENT_ENTRY_DEL:
0472         nsim_fib4_rt_remove(data, fen_info);
0473         break;
0474     default:
0475         break;
0476     }
0477 
0478     return err;
0479 }
0480 
0481 static struct nsim_fib6_rt_nh *
0482 nsim_fib6_rt_nh_find(const struct nsim_fib6_rt *fib6_rt,
0483              const struct fib6_info *rt)
0484 {
0485     struct nsim_fib6_rt_nh *fib6_rt_nh;
0486 
0487     list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) {
0488         if (fib6_rt_nh->rt == rt)
0489             return fib6_rt_nh;
0490     }
0491 
0492     return NULL;
0493 }
0494 
0495 static int nsim_fib6_rt_nh_add(struct nsim_fib6_rt *fib6_rt,
0496                    struct fib6_info *rt)
0497 {
0498     struct nsim_fib6_rt_nh *fib6_rt_nh;
0499 
0500     fib6_rt_nh = kzalloc(sizeof(*fib6_rt_nh), GFP_KERNEL);
0501     if (!fib6_rt_nh)
0502         return -ENOMEM;
0503 
0504     fib6_info_hold(rt);
0505     fib6_rt_nh->rt = rt;
0506     list_add_tail(&fib6_rt_nh->list, &fib6_rt->nh_list);
0507     fib6_rt->nhs++;
0508 
0509     return 0;
0510 }
0511 
0512 #if IS_ENABLED(CONFIG_IPV6)
0513 static void nsim_rt6_release(struct fib6_info *rt)
0514 {
0515     fib6_info_release(rt);
0516 }
0517 #else
0518 static void nsim_rt6_release(struct fib6_info *rt)
0519 {
0520 }
0521 #endif
0522 
0523 static void nsim_fib6_rt_nh_del(struct nsim_fib6_rt *fib6_rt,
0524                 const struct fib6_info *rt)
0525 {
0526     struct nsim_fib6_rt_nh *fib6_rt_nh;
0527 
0528     fib6_rt_nh = nsim_fib6_rt_nh_find(fib6_rt, rt);
0529     if (!fib6_rt_nh)
0530         return;
0531 
0532     fib6_rt->nhs--;
0533     list_del(&fib6_rt_nh->list);
0534     nsim_rt6_release(fib6_rt_nh->rt);
0535     kfree(fib6_rt_nh);
0536 }
0537 
0538 static struct nsim_fib6_rt *
0539 nsim_fib6_rt_create(struct nsim_fib_data *data,
0540             struct fib6_info **rt_arr, unsigned int nrt6)
0541 {
0542     struct fib6_info *rt = rt_arr[0];
0543     struct nsim_fib6_rt *fib6_rt;
0544     int i = 0;
0545     int err;
0546 
0547     fib6_rt = kzalloc(sizeof(*fib6_rt), GFP_KERNEL);
0548     if (!fib6_rt)
0549         return ERR_PTR(-ENOMEM);
0550 
0551     nsim_fib_rt_init(data, &fib6_rt->common, &rt->fib6_dst.addr,
0552              sizeof(rt->fib6_dst.addr), rt->fib6_dst.plen, AF_INET6,
0553              rt->fib6_table->tb6_id);
0554 
0555     /* We consider a multipath IPv6 route as one entry, but it can be made
0556      * up from several fib6_info structs (one for each nexthop), so we
0557      * add them all to the same list under the entry.
0558      */
0559     INIT_LIST_HEAD(&fib6_rt->nh_list);
0560 
0561     for (i = 0; i < nrt6; i++) {
0562         err = nsim_fib6_rt_nh_add(fib6_rt, rt_arr[i]);
0563         if (err)
0564             goto err_fib6_rt_nh_del;
0565     }
0566 
0567     return fib6_rt;
0568 
0569 err_fib6_rt_nh_del:
0570     for (i--; i >= 0; i--) {
0571         nsim_fib6_rt_nh_del(fib6_rt, rt_arr[i]);
0572     }
0573     nsim_fib_rt_fini(&fib6_rt->common);
0574     kfree(fib6_rt);
0575     return ERR_PTR(err);
0576 }
0577 
0578 static void nsim_fib6_rt_destroy(struct nsim_fib6_rt *fib6_rt)
0579 {
0580     struct nsim_fib6_rt_nh *iter, *tmp;
0581 
0582     list_for_each_entry_safe(iter, tmp, &fib6_rt->nh_list, list)
0583         nsim_fib6_rt_nh_del(fib6_rt, iter->rt);
0584     WARN_ON_ONCE(!list_empty(&fib6_rt->nh_list));
0585     nsim_fib_rt_fini(&fib6_rt->common);
0586     kfree(fib6_rt);
0587 }
0588 
0589 static struct nsim_fib6_rt *
0590 nsim_fib6_rt_lookup(struct rhashtable *fib_rt_ht, const struct fib6_info *rt)
0591 {
0592     struct nsim_fib_rt *fib_rt;
0593 
0594     fib_rt = nsim_fib_rt_lookup(fib_rt_ht, &rt->fib6_dst.addr,
0595                     sizeof(rt->fib6_dst.addr),
0596                     rt->fib6_dst.plen, AF_INET6,
0597                     rt->fib6_table->tb6_id);
0598     if (!fib_rt)
0599         return NULL;
0600 
0601     return container_of(fib_rt, struct nsim_fib6_rt, common);
0602 }
0603 
0604 static int nsim_fib6_rt_append(struct nsim_fib_data *data,
0605                    struct nsim_fib6_event *fib6_event)
0606 {
0607     struct fib6_info *rt = fib6_event->rt_arr[0];
0608     struct nsim_fib6_rt *fib6_rt;
0609     int i, err;
0610 
0611     if (data->fail_route_offload) {
0612         /* For testing purposes, user set debugfs fail_route_offload
0613          * value to true. Simulate hardware programming latency and then
0614          * fail.
0615          */
0616         msleep(1);
0617         return -EINVAL;
0618     }
0619 
0620     fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
0621     if (!fib6_rt)
0622         return -EINVAL;
0623 
0624     for (i = 0; i < fib6_event->nrt6; i++) {
0625         err = nsim_fib6_rt_nh_add(fib6_rt, fib6_event->rt_arr[i]);
0626         if (err)
0627             goto err_fib6_rt_nh_del;
0628 
0629         WRITE_ONCE(fib6_event->rt_arr[i]->trap, true);
0630     }
0631 
0632     return 0;
0633 
0634 err_fib6_rt_nh_del:
0635     for (i--; i >= 0; i--) {
0636         WRITE_ONCE(fib6_event->rt_arr[i]->trap, false);
0637         nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
0638     }
0639     return err;
0640 }
0641 
0642 #if IS_ENABLED(CONFIG_IPV6)
0643 static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
0644                          struct fib6_info **rt_arr,
0645                          unsigned int nrt6)
0646 
0647 {
0648     struct net *net = devlink_net(data->devlink);
0649     int i;
0650 
0651     for (i = 0; i < nrt6; i++)
0652         fib6_info_hw_flags_set(net, rt_arr[i], false, false, true);
0653 }
0654 #else
0655 static void nsim_fib6_rt_offload_failed_flag_set(struct nsim_fib_data *data,
0656                          struct fib6_info **rt_arr,
0657                          unsigned int nrt6)
0658 {
0659 }
0660 #endif
0661 
0662 #if IS_ENABLED(CONFIG_IPV6)
0663 static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
0664                       const struct nsim_fib6_rt *fib6_rt,
0665                       bool trap)
0666 {
0667     struct net *net = devlink_net(data->devlink);
0668     struct nsim_fib6_rt_nh *fib6_rt_nh;
0669 
0670     list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list)
0671         fib6_info_hw_flags_set(net, fib6_rt_nh->rt, false, trap, false);
0672 }
0673 #else
0674 static void nsim_fib6_rt_hw_flags_set(struct nsim_fib_data *data,
0675                       const struct nsim_fib6_rt *fib6_rt,
0676                       bool trap)
0677 {
0678 }
0679 #endif
0680 
0681 static int nsim_fib6_rt_add(struct nsim_fib_data *data,
0682                 struct nsim_fib6_rt *fib6_rt)
0683 {
0684     int err;
0685 
0686     err = rhashtable_insert_fast(&data->fib_rt_ht,
0687                      &fib6_rt->common.ht_node,
0688                      nsim_fib_rt_ht_params);
0689 
0690     if (err)
0691         goto err_fib_dismiss;
0692 
0693     msleep(1);
0694     nsim_fib6_rt_hw_flags_set(data, fib6_rt, true);
0695 
0696     return 0;
0697 
0698 err_fib_dismiss:
0699     /* Drop the accounting that was increased from the notification
0700      * context when FIB_EVENT_ENTRY_REPLACE was triggered.
0701      */
0702     nsim_fib_account(&data->ipv6.fib, false);
0703     return err;
0704 }
0705 
0706 static int nsim_fib6_rt_replace(struct nsim_fib_data *data,
0707                 struct nsim_fib6_rt *fib6_rt,
0708                 struct nsim_fib6_rt *fib6_rt_old)
0709 {
0710     int err;
0711 
0712     /* We are replacing a route, so need to remove the accounting which
0713      * was increased when FIB_EVENT_ENTRY_REPLACE was triggered.
0714      */
0715     err = nsim_fib_account(&data->ipv6.fib, false);
0716     if (err)
0717         return err;
0718 
0719     err = rhashtable_replace_fast(&data->fib_rt_ht,
0720                       &fib6_rt_old->common.ht_node,
0721                       &fib6_rt->common.ht_node,
0722                       nsim_fib_rt_ht_params);
0723 
0724     if (err)
0725         return err;
0726 
0727     msleep(1);
0728     nsim_fib6_rt_hw_flags_set(data, fib6_rt, true);
0729 
0730     nsim_fib6_rt_hw_flags_set(data, fib6_rt_old, false);
0731     nsim_fib6_rt_destroy(fib6_rt_old);
0732 
0733     return 0;
0734 }
0735 
0736 static int nsim_fib6_rt_insert(struct nsim_fib_data *data,
0737                    struct nsim_fib6_event *fib6_event)
0738 {
0739     struct fib6_info *rt = fib6_event->rt_arr[0];
0740     struct nsim_fib6_rt *fib6_rt, *fib6_rt_old;
0741     int err;
0742 
0743     if (data->fail_route_offload) {
0744         /* For testing purposes, user set debugfs fail_route_offload
0745          * value to true. Simulate hardware programming latency and then
0746          * fail.
0747          */
0748         msleep(1);
0749         return -EINVAL;
0750     }
0751 
0752     fib6_rt = nsim_fib6_rt_create(data, fib6_event->rt_arr,
0753                       fib6_event->nrt6);
0754     if (IS_ERR(fib6_rt))
0755         return PTR_ERR(fib6_rt);
0756 
0757     fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
0758     if (!fib6_rt_old)
0759         err = nsim_fib6_rt_add(data, fib6_rt);
0760     else
0761         err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old);
0762 
0763     if (err)
0764         nsim_fib6_rt_destroy(fib6_rt);
0765 
0766     return err;
0767 }
0768 
0769 static void nsim_fib6_rt_remove(struct nsim_fib_data *data,
0770                 struct nsim_fib6_event *fib6_event)
0771 {
0772     struct fib6_info *rt = fib6_event->rt_arr[0];
0773     struct nsim_fib6_rt *fib6_rt;
0774     int i;
0775 
0776     /* Multipath routes are first added to the FIB trie and only then
0777      * notified. If we vetoed the addition, we will get a delete
0778      * notification for a route we do not have. Therefore, do not warn if
0779      * route was not found.
0780      */
0781     fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt);
0782     if (!fib6_rt)
0783         return;
0784 
0785     /* If not all the nexthops are deleted, then only reduce the nexthop
0786      * group.
0787      */
0788     if (fib6_event->nrt6 != fib6_rt->nhs) {
0789         for (i = 0; i < fib6_event->nrt6; i++)
0790             nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]);
0791         return;
0792     }
0793 
0794     rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node,
0795                    nsim_fib_rt_ht_params);
0796     nsim_fib6_rt_destroy(fib6_rt);
0797 }
0798 
0799 static int nsim_fib6_event_init(struct nsim_fib6_event *fib6_event,
0800                 struct fib6_entry_notifier_info *fen6_info)
0801 {
0802     struct fib6_info *rt = fen6_info->rt;
0803     struct fib6_info **rt_arr;
0804     struct fib6_info *iter;
0805     unsigned int nrt6;
0806     int i = 0;
0807 
0808     nrt6 = fen6_info->nsiblings + 1;
0809 
0810     rt_arr = kcalloc(nrt6, sizeof(struct fib6_info *), GFP_ATOMIC);
0811     if (!rt_arr)
0812         return -ENOMEM;
0813 
0814     fib6_event->rt_arr = rt_arr;
0815     fib6_event->nrt6 = nrt6;
0816 
0817     rt_arr[0] = rt;
0818     fib6_info_hold(rt);
0819 
0820     if (!fen6_info->nsiblings)
0821         return 0;
0822 
0823     list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) {
0824         if (i == fen6_info->nsiblings)
0825             break;
0826 
0827         rt_arr[i + 1] = iter;
0828         fib6_info_hold(iter);
0829         i++;
0830     }
0831     WARN_ON_ONCE(i != fen6_info->nsiblings);
0832 
0833     return 0;
0834 }
0835 
0836 static void nsim_fib6_event_fini(struct nsim_fib6_event *fib6_event)
0837 {
0838     int i;
0839 
0840     for (i = 0; i < fib6_event->nrt6; i++)
0841         nsim_rt6_release(fib6_event->rt_arr[i]);
0842     kfree(fib6_event->rt_arr);
0843 }
0844 
0845 static int nsim_fib6_event(struct nsim_fib_data *data,
0846                struct nsim_fib6_event *fib6_event,
0847                unsigned long event)
0848 {
0849     int err;
0850 
0851     if (fib6_event->rt_arr[0]->fib6_src.plen)
0852         return 0;
0853 
0854     switch (event) {
0855     case FIB_EVENT_ENTRY_REPLACE:
0856         err = nsim_fib6_rt_insert(data, fib6_event);
0857         if (err)
0858             goto err_rt_offload_failed_flag_set;
0859         break;
0860     case FIB_EVENT_ENTRY_APPEND:
0861         err = nsim_fib6_rt_append(data, fib6_event);
0862         if (err)
0863             goto err_rt_offload_failed_flag_set;
0864         break;
0865     case FIB_EVENT_ENTRY_DEL:
0866         nsim_fib6_rt_remove(data, fib6_event);
0867         break;
0868     default:
0869         break;
0870     }
0871 
0872     return 0;
0873 
0874 err_rt_offload_failed_flag_set:
0875     nsim_fib6_rt_offload_failed_flag_set(data, fib6_event->rt_arr,
0876                          fib6_event->nrt6);
0877     return err;
0878 }
0879 
0880 static void nsim_fib_event(struct nsim_fib_event *fib_event)
0881 {
0882     switch (fib_event->family) {
0883     case AF_INET:
0884         nsim_fib4_event(fib_event->data, &fib_event->fen_info,
0885                 fib_event->event);
0886         fib_info_put(fib_event->fen_info.fi);
0887         break;
0888     case AF_INET6:
0889         nsim_fib6_event(fib_event->data, &fib_event->fib6_event,
0890                 fib_event->event);
0891         nsim_fib6_event_fini(&fib_event->fib6_event);
0892         break;
0893     }
0894 }
0895 
0896 static int nsim_fib4_prepare_event(struct fib_notifier_info *info,
0897                    struct nsim_fib_event *fib_event,
0898                    unsigned long event)
0899 {
0900     struct nsim_fib_data *data = fib_event->data;
0901     struct fib_entry_notifier_info *fen_info;
0902     struct netlink_ext_ack *extack;
0903     int err = 0;
0904 
0905     fen_info = container_of(info, struct fib_entry_notifier_info,
0906                 info);
0907     fib_event->fen_info = *fen_info;
0908     extack = info->extack;
0909 
0910     switch (event) {
0911     case FIB_EVENT_ENTRY_REPLACE:
0912         err = nsim_fib_account(&data->ipv4.fib, true);
0913         if (err) {
0914             NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
0915             return err;
0916         }
0917         break;
0918     case FIB_EVENT_ENTRY_DEL:
0919         if (data->fail_route_delete) {
0920             NL_SET_ERR_MSG_MOD(extack, "Failed to process route deletion");
0921             return -EINVAL;
0922         }
0923         nsim_fib_account(&data->ipv4.fib, false);
0924         break;
0925     }
0926 
0927     /* Take reference on fib_info to prevent it from being
0928      * freed while event is queued. Release it afterwards.
0929      */
0930     fib_info_hold(fib_event->fen_info.fi);
0931 
0932     return 0;
0933 }
0934 
0935 static int nsim_fib6_prepare_event(struct fib_notifier_info *info,
0936                    struct nsim_fib_event *fib_event,
0937                    unsigned long event)
0938 {
0939     struct nsim_fib_data *data = fib_event->data;
0940     struct fib6_entry_notifier_info *fen6_info;
0941     struct netlink_ext_ack *extack;
0942     int err = 0;
0943 
0944     fen6_info = container_of(info, struct fib6_entry_notifier_info,
0945                  info);
0946 
0947     err = nsim_fib6_event_init(&fib_event->fib6_event, fen6_info);
0948     if (err)
0949         return err;
0950 
0951     extack = info->extack;
0952     switch (event) {
0953     case FIB_EVENT_ENTRY_REPLACE:
0954         err = nsim_fib_account(&data->ipv6.fib, true);
0955         if (err) {
0956             NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries");
0957             goto err_fib6_event_fini;
0958         }
0959         break;
0960     case FIB_EVENT_ENTRY_DEL:
0961         if (data->fail_route_delete) {
0962             err = -EINVAL;
0963             NL_SET_ERR_MSG_MOD(extack, "Failed to process route deletion");
0964             goto err_fib6_event_fini;
0965         }
0966         nsim_fib_account(&data->ipv6.fib, false);
0967         break;
0968     }
0969 
0970     return 0;
0971 
0972 err_fib6_event_fini:
0973     nsim_fib6_event_fini(&fib_event->fib6_event);
0974     return err;
0975 }
0976 
0977 static int nsim_fib_event_schedule_work(struct nsim_fib_data *data,
0978                     struct fib_notifier_info *info,
0979                     unsigned long event)
0980 {
0981     struct nsim_fib_event *fib_event;
0982     int err;
0983 
0984     if (info->family != AF_INET && info->family != AF_INET6)
0985         /* netdevsim does not support 'RTNL_FAMILY_IP6MR' and
0986          * 'RTNL_FAMILY_IPMR' and should ignore them.
0987          */
0988         return NOTIFY_DONE;
0989 
0990     fib_event = kzalloc(sizeof(*fib_event), GFP_ATOMIC);
0991     if (!fib_event)
0992         goto err_fib_event_alloc;
0993 
0994     fib_event->data = data;
0995     fib_event->event = event;
0996     fib_event->family = info->family;
0997 
0998     switch (info->family) {
0999     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 
1018 err_fib_prepare_event:
1019     kfree(fib_event);
1020 err_fib_event_alloc:
1021     if (event == FIB_EVENT_ENTRY_DEL)
1022         schedule_work(&data->fib_flush_work);
1023     return NOTIFY_BAD;
1024 }
1025 
1026 static 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 
1049 static 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 
1061 static 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 
1072 static 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 */
1090 static 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 
1112 static 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 
1151 static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop)
1152 {
1153     kfree(nexthop);
1154 }
1155 
1156 static 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 
1177 err_num_decrease:
1178     atomic64_sub(i, &data->nexthops.num);
1179     return err;
1180 
1181 }
1182 
1183 static 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 
1198 static 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 
1220 err_nexthop_dismiss:
1221     nsim_nexthop_account(data, nexthop->occ, false, extack);
1222     return err;
1223 }
1224 
1225 static 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 
1251 err_nexthop_dismiss:
1252     nsim_nexthop_account(data, nexthop->occ, false, extack);
1253     return err;
1254 }
1255 
1256 static 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 
1280 static 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 
1296 static 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 
1307 static 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 
1322 static 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 
1352 static 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 
1364 static 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 
1407 out:
1408     rtnl_unlock();
1409 
1410     *ppos = size;
1411     return err ?: size;
1412 }
1413 
1414 static 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 
1421 static 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 
1428 static 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 
1435 static 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 
1442 static 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 
1449 static 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 
1456 static 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 
1477 static 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 
1500 static 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 
1518 static int
1519 nsim_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 
1546 static void nsim_fib_debugfs_exit(struct nsim_fib_data *data)
1547 {
1548     debugfs_remove_recursive(data->ddir);
1549 }
1550 
1551 struct 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 
1624 err_nexthop_nb_unregister:
1625     unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1626 err_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);
1631 err_rhashtable_nexthop_destroy:
1632     rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1633                     data);
1634     mutex_destroy(&data->fib_lock);
1635 err_debugfs_exit:
1636     mutex_destroy(&data->nh_lock);
1637     nsim_fib_debugfs_exit(data);
1638 err_data_free:
1639     kfree(data);
1640     return ERR_PTR(err);
1641 }
1642 
1643 void 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 }