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