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