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