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