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(struct nsim_fib_data *data, 589 const struct nsim_fib6_rt *fib6_rt, 590 bool trap) 591 { 592 struct net *net = devlink_net(data->devlink); 593 struct nsim_fib6_rt_nh *fib6_rt_nh; 594 595 list_for_each_entry(fib6_rt_nh, &fib6_rt->nh_list, list) 596 fib6_info_hw_flags_set(net, fib6_rt_nh->rt, false, trap); 597 } 598 599 static int nsim_fib6_rt_add(struct nsim_fib_data *data, 600 struct nsim_fib6_rt *fib6_rt) 601 { 602 int err; 603 604 err = rhashtable_insert_fast(&data->fib_rt_ht, 605 &fib6_rt->common.ht_node, 606 nsim_fib_rt_ht_params); 607 608 if (err) 609 goto err_fib_dismiss; 610 611 msleep(1); 612 nsim_fib6_rt_hw_flags_set(data, fib6_rt, true); 613 614 return 0; 615 616 err_fib_dismiss: 617 /* Drop the accounting that was increased from the notification 618 * context when FIB_EVENT_ENTRY_REPLACE was triggered. 619 */ 620 nsim_fib_account(&data->ipv6.fib, false); 621 return err; 622 } 623 624 static int nsim_fib6_rt_replace(struct nsim_fib_data *data, 625 struct nsim_fib6_rt *fib6_rt, 626 struct nsim_fib6_rt *fib6_rt_old) 627 { 628 int err; 629 630 /* We are replacing a route, so need to remove the accounting which 631 * was increased when FIB_EVENT_ENTRY_REPLACE was triggered. 632 */ 633 err = nsim_fib_account(&data->ipv6.fib, false); 634 if (err) 635 return err; 636 637 err = rhashtable_replace_fast(&data->fib_rt_ht, 638 &fib6_rt_old->common.ht_node, 639 &fib6_rt->common.ht_node, 640 nsim_fib_rt_ht_params); 641 642 if (err) 643 return err; 644 645 msleep(1); 646 nsim_fib6_rt_hw_flags_set(data, fib6_rt, true); 647 648 nsim_fib6_rt_hw_flags_set(data, fib6_rt_old, false); 649 nsim_fib6_rt_destroy(fib6_rt_old); 650 651 return 0; 652 } 653 654 static int nsim_fib6_rt_insert(struct nsim_fib_data *data, 655 struct nsim_fib6_event *fib6_event) 656 { 657 struct fib6_info *rt = fib6_event->rt_arr[0]; 658 struct nsim_fib6_rt *fib6_rt, *fib6_rt_old; 659 int err; 660 661 fib6_rt = nsim_fib6_rt_create(data, fib6_event->rt_arr, 662 fib6_event->nrt6); 663 if (IS_ERR(fib6_rt)) 664 return PTR_ERR(fib6_rt); 665 666 fib6_rt_old = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt); 667 if (!fib6_rt_old) 668 err = nsim_fib6_rt_add(data, fib6_rt); 669 else 670 err = nsim_fib6_rt_replace(data, fib6_rt, fib6_rt_old); 671 672 if (err) 673 nsim_fib6_rt_destroy(fib6_rt); 674 675 return err; 676 } 677 678 static void nsim_fib6_rt_remove(struct nsim_fib_data *data, 679 struct nsim_fib6_event *fib6_event) 680 { 681 struct fib6_info *rt = fib6_event->rt_arr[0]; 682 struct nsim_fib6_rt *fib6_rt; 683 int i; 684 685 /* Multipath routes are first added to the FIB trie and only then 686 * notified. If we vetoed the addition, we will get a delete 687 * notification for a route we do not have. Therefore, do not warn if 688 * route was not found. 689 */ 690 fib6_rt = nsim_fib6_rt_lookup(&data->fib_rt_ht, rt); 691 if (!fib6_rt) 692 return; 693 694 /* If not all the nexthops are deleted, then only reduce the nexthop 695 * group. 696 */ 697 if (fib6_event->nrt6 != fib6_rt->nhs) { 698 for (i = 0; i < fib6_event->nrt6; i++) 699 nsim_fib6_rt_nh_del(fib6_rt, fib6_event->rt_arr[i]); 700 return; 701 } 702 703 rhashtable_remove_fast(&data->fib_rt_ht, &fib6_rt->common.ht_node, 704 nsim_fib_rt_ht_params); 705 nsim_fib6_rt_destroy(fib6_rt); 706 } 707 708 static int nsim_fib6_event_init(struct nsim_fib6_event *fib6_event, 709 struct fib6_entry_notifier_info *fen6_info) 710 { 711 struct fib6_info *rt = fen6_info->rt; 712 struct fib6_info **rt_arr; 713 struct fib6_info *iter; 714 unsigned int nrt6; 715 int i = 0; 716 717 nrt6 = fen6_info->nsiblings + 1; 718 719 rt_arr = kcalloc(nrt6, sizeof(struct fib6_info *), GFP_ATOMIC); 720 if (!rt_arr) 721 return -ENOMEM; 722 723 fib6_event->rt_arr = rt_arr; 724 fib6_event->nrt6 = nrt6; 725 726 rt_arr[0] = rt; 727 fib6_info_hold(rt); 728 729 if (!fen6_info->nsiblings) 730 return 0; 731 732 list_for_each_entry(iter, &rt->fib6_siblings, fib6_siblings) { 733 if (i == fen6_info->nsiblings) 734 break; 735 736 rt_arr[i + 1] = iter; 737 fib6_info_hold(iter); 738 i++; 739 } 740 WARN_ON_ONCE(i != fen6_info->nsiblings); 741 742 return 0; 743 } 744 745 static void nsim_fib6_event_fini(struct nsim_fib6_event *fib6_event) 746 { 747 int i; 748 749 for (i = 0; i < fib6_event->nrt6; i++) 750 nsim_rt6_release(fib6_event->rt_arr[i]); 751 kfree(fib6_event->rt_arr); 752 } 753 754 static int nsim_fib6_event(struct nsim_fib_data *data, 755 struct nsim_fib6_event *fib6_event, 756 unsigned long event) 757 { 758 int err = 0; 759 760 if (fib6_event->rt_arr[0]->fib6_src.plen) 761 return 0; 762 763 switch (event) { 764 case FIB_EVENT_ENTRY_REPLACE: 765 err = nsim_fib6_rt_insert(data, fib6_event); 766 break; 767 case FIB_EVENT_ENTRY_APPEND: 768 err = nsim_fib6_rt_append(data, fib6_event); 769 break; 770 case FIB_EVENT_ENTRY_DEL: 771 nsim_fib6_rt_remove(data, fib6_event); 772 break; 773 default: 774 break; 775 } 776 777 return err; 778 } 779 780 static int nsim_fib_event(struct nsim_fib_event *fib_event) 781 { 782 int err = 0; 783 784 switch (fib_event->family) { 785 case AF_INET: 786 nsim_fib4_event(fib_event->data, &fib_event->fen_info, 787 fib_event->event); 788 fib_info_put(fib_event->fen_info.fi); 789 break; 790 case AF_INET6: 791 nsim_fib6_event(fib_event->data, &fib_event->fib6_event, 792 fib_event->event); 793 nsim_fib6_event_fini(&fib_event->fib6_event); 794 break; 795 } 796 797 return err; 798 } 799 800 static int nsim_fib4_prepare_event(struct fib_notifier_info *info, 801 struct nsim_fib_event *fib_event, 802 unsigned long event) 803 { 804 struct nsim_fib_data *data = fib_event->data; 805 struct fib_entry_notifier_info *fen_info; 806 struct netlink_ext_ack *extack; 807 int err = 0; 808 809 fen_info = container_of(info, struct fib_entry_notifier_info, 810 info); 811 fib_event->fen_info = *fen_info; 812 extack = info->extack; 813 814 switch (event) { 815 case FIB_EVENT_ENTRY_REPLACE: 816 err = nsim_fib_account(&data->ipv4.fib, true); 817 if (err) { 818 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries"); 819 return err; 820 } 821 break; 822 case FIB_EVENT_ENTRY_DEL: 823 nsim_fib_account(&data->ipv4.fib, false); 824 break; 825 } 826 827 /* Take reference on fib_info to prevent it from being 828 * freed while event is queued. Release it afterwards. 829 */ 830 fib_info_hold(fib_event->fen_info.fi); 831 832 return 0; 833 } 834 835 static int nsim_fib6_prepare_event(struct fib_notifier_info *info, 836 struct nsim_fib_event *fib_event, 837 unsigned long event) 838 { 839 struct nsim_fib_data *data = fib_event->data; 840 struct fib6_entry_notifier_info *fen6_info; 841 struct netlink_ext_ack *extack; 842 int err = 0; 843 844 fen6_info = container_of(info, struct fib6_entry_notifier_info, 845 info); 846 847 err = nsim_fib6_event_init(&fib_event->fib6_event, fen6_info); 848 if (err) 849 return err; 850 851 extack = info->extack; 852 switch (event) { 853 case FIB_EVENT_ENTRY_REPLACE: 854 err = nsim_fib_account(&data->ipv6.fib, true); 855 if (err) { 856 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported fib entries"); 857 goto err_fib6_event_fini; 858 } 859 break; 860 case FIB_EVENT_ENTRY_DEL: 861 nsim_fib_account(&data->ipv6.fib, false); 862 break; 863 } 864 865 return 0; 866 867 err_fib6_event_fini: 868 nsim_fib6_event_fini(&fib_event->fib6_event); 869 return err; 870 } 871 872 static int nsim_fib_event_schedule_work(struct nsim_fib_data *data, 873 struct fib_notifier_info *info, 874 unsigned long event) 875 { 876 struct nsim_fib_event *fib_event; 877 int err; 878 879 if (info->family != AF_INET && info->family != AF_INET6) 880 /* netdevsim does not support 'RTNL_FAMILY_IP6MR' and 881 * 'RTNL_FAMILY_IPMR' and should ignore them. 882 */ 883 return NOTIFY_DONE; 884 885 fib_event = kzalloc(sizeof(*fib_event), GFP_ATOMIC); 886 if (!fib_event) 887 return NOTIFY_BAD; 888 889 fib_event->data = data; 890 fib_event->event = event; 891 fib_event->family = info->family; 892 893 switch (info->family) { 894 case AF_INET: 895 err = nsim_fib4_prepare_event(info, fib_event, event); 896 break; 897 case AF_INET6: 898 err = nsim_fib6_prepare_event(info, fib_event, event); 899 break; 900 } 901 902 if (err) 903 goto err_fib_prepare_event; 904 905 /* Enqueue the event and trigger the work */ 906 spin_lock_bh(&data->fib_event_queue_lock); 907 list_add_tail(&fib_event->list, &data->fib_event_queue); 908 spin_unlock_bh(&data->fib_event_queue_lock); 909 schedule_work(&data->fib_event_work); 910 911 return NOTIFY_DONE; 912 913 err_fib_prepare_event: 914 kfree(fib_event); 915 return NOTIFY_BAD; 916 } 917 918 static int nsim_fib_event_nb(struct notifier_block *nb, unsigned long event, 919 void *ptr) 920 { 921 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, 922 fib_nb); 923 struct fib_notifier_info *info = ptr; 924 int err; 925 926 switch (event) { 927 case FIB_EVENT_RULE_ADD: 928 case FIB_EVENT_RULE_DEL: 929 err = nsim_fib_rule_event(data, info, 930 event == FIB_EVENT_RULE_ADD); 931 return notifier_from_errno(err); 932 case FIB_EVENT_ENTRY_REPLACE: 933 case FIB_EVENT_ENTRY_APPEND: 934 case FIB_EVENT_ENTRY_DEL: 935 return nsim_fib_event_schedule_work(data, info, event); 936 } 937 938 return NOTIFY_DONE; 939 } 940 941 static void nsim_fib4_rt_free(struct nsim_fib_rt *fib_rt, 942 struct nsim_fib_data *data) 943 { 944 struct devlink *devlink = data->devlink; 945 struct nsim_fib4_rt *fib4_rt; 946 947 fib4_rt = container_of(fib_rt, struct nsim_fib4_rt, common); 948 nsim_fib4_rt_hw_flags_set(devlink_net(devlink), fib4_rt, false); 949 nsim_fib_account(&data->ipv4.fib, false); 950 nsim_fib4_rt_destroy(fib4_rt); 951 } 952 953 static void nsim_fib6_rt_free(struct nsim_fib_rt *fib_rt, 954 struct nsim_fib_data *data) 955 { 956 struct nsim_fib6_rt *fib6_rt; 957 958 fib6_rt = container_of(fib_rt, struct nsim_fib6_rt, common); 959 nsim_fib6_rt_hw_flags_set(data, fib6_rt, false); 960 nsim_fib_account(&data->ipv6.fib, false); 961 nsim_fib6_rt_destroy(fib6_rt); 962 } 963 964 static void nsim_fib_rt_free(void *ptr, void *arg) 965 { 966 struct nsim_fib_rt *fib_rt = ptr; 967 struct nsim_fib_data *data = arg; 968 969 switch (fib_rt->key.family) { 970 case AF_INET: 971 nsim_fib4_rt_free(fib_rt, data); 972 break; 973 case AF_INET6: 974 nsim_fib6_rt_free(fib_rt, data); 975 break; 976 default: 977 WARN_ON_ONCE(1); 978 } 979 } 980 981 /* inconsistent dump, trying again */ 982 static void nsim_fib_dump_inconsistent(struct notifier_block *nb) 983 { 984 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, 985 fib_nb); 986 struct nsim_fib_rt *fib_rt, *fib_rt_tmp; 987 988 /* Flush the work to make sure there is no race with notifications. */ 989 flush_work(&data->fib_event_work); 990 991 /* The notifier block is still not registered, so we do not need to 992 * take any locks here. 993 */ 994 list_for_each_entry_safe(fib_rt, fib_rt_tmp, &data->fib_rt_list, list) { 995 rhashtable_remove_fast(&data->fib_rt_ht, &fib_rt->ht_node, 996 nsim_fib_rt_ht_params); 997 nsim_fib_rt_free(fib_rt, data); 998 } 999 1000 atomic64_set(&data->ipv4.rules.num, 0ULL); 1001 atomic64_set(&data->ipv6.rules.num, 0ULL); 1002 } 1003 1004 static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data, 1005 struct nh_notifier_info *info) 1006 { 1007 struct nsim_nexthop *nexthop; 1008 u64 occ = 0; 1009 int i; 1010 1011 nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL); 1012 if (!nexthop) 1013 return ERR_PTR(-ENOMEM); 1014 1015 nexthop->id = info->id; 1016 1017 /* Determine the number of nexthop entries the new nexthop will 1018 * occupy. 1019 */ 1020 1021 switch (info->type) { 1022 case NH_NOTIFIER_INFO_TYPE_SINGLE: 1023 occ = 1; 1024 break; 1025 case NH_NOTIFIER_INFO_TYPE_GRP: 1026 for (i = 0; i < info->nh_grp->num_nh; i++) 1027 occ += info->nh_grp->nh_entries[i].weight; 1028 break; 1029 default: 1030 NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type"); 1031 kfree(nexthop); 1032 return ERR_PTR(-EOPNOTSUPP); 1033 } 1034 1035 nexthop->occ = occ; 1036 return nexthop; 1037 } 1038 1039 static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop) 1040 { 1041 kfree(nexthop); 1042 } 1043 1044 static int nsim_nexthop_account(struct nsim_fib_data *data, u64 occ, 1045 bool add, struct netlink_ext_ack *extack) 1046 { 1047 int i, err = 0; 1048 1049 if (add) { 1050 for (i = 0; i < occ; i++) 1051 if (!atomic64_add_unless(&data->nexthops.num, 1, 1052 data->nexthops.max)) { 1053 err = -ENOSPC; 1054 NL_SET_ERR_MSG_MOD(extack, "Exceeded number of supported nexthops"); 1055 goto err_num_decrease; 1056 } 1057 } else { 1058 if (WARN_ON(occ > atomic64_read(&data->nexthops.num))) 1059 return -EINVAL; 1060 atomic64_sub(occ, &data->nexthops.num); 1061 } 1062 1063 return err; 1064 1065 err_num_decrease: 1066 atomic64_sub(i, &data->nexthops.num); 1067 return err; 1068 1069 } 1070 1071 static int nsim_nexthop_add(struct nsim_fib_data *data, 1072 struct nsim_nexthop *nexthop, 1073 struct netlink_ext_ack *extack) 1074 { 1075 struct net *net = devlink_net(data->devlink); 1076 int err; 1077 1078 err = nsim_nexthop_account(data, nexthop->occ, true, extack); 1079 if (err) 1080 return err; 1081 1082 err = rhashtable_insert_fast(&data->nexthop_ht, &nexthop->ht_node, 1083 nsim_nexthop_ht_params); 1084 if (err) { 1085 NL_SET_ERR_MSG_MOD(extack, "Failed to insert nexthop"); 1086 goto err_nexthop_dismiss; 1087 } 1088 1089 nexthop_set_hw_flags(net, nexthop->id, false, true); 1090 1091 return 0; 1092 1093 err_nexthop_dismiss: 1094 nsim_nexthop_account(data, nexthop->occ, false, extack); 1095 return err; 1096 } 1097 1098 static int nsim_nexthop_replace(struct nsim_fib_data *data, 1099 struct nsim_nexthop *nexthop, 1100 struct nsim_nexthop *nexthop_old, 1101 struct netlink_ext_ack *extack) 1102 { 1103 struct net *net = devlink_net(data->devlink); 1104 int err; 1105 1106 err = nsim_nexthop_account(data, nexthop->occ, true, extack); 1107 if (err) 1108 return err; 1109 1110 err = rhashtable_replace_fast(&data->nexthop_ht, 1111 &nexthop_old->ht_node, &nexthop->ht_node, 1112 nsim_nexthop_ht_params); 1113 if (err) { 1114 NL_SET_ERR_MSG_MOD(extack, "Failed to replace nexthop"); 1115 goto err_nexthop_dismiss; 1116 } 1117 1118 nexthop_set_hw_flags(net, nexthop->id, false, true); 1119 nsim_nexthop_account(data, nexthop_old->occ, false, extack); 1120 nsim_nexthop_destroy(nexthop_old); 1121 1122 return 0; 1123 1124 err_nexthop_dismiss: 1125 nsim_nexthop_account(data, nexthop->occ, false, extack); 1126 return err; 1127 } 1128 1129 static int nsim_nexthop_insert(struct nsim_fib_data *data, 1130 struct nh_notifier_info *info) 1131 { 1132 struct nsim_nexthop *nexthop, *nexthop_old; 1133 int err; 1134 1135 nexthop = nsim_nexthop_create(data, info); 1136 if (IS_ERR(nexthop)) 1137 return PTR_ERR(nexthop); 1138 1139 nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id, 1140 nsim_nexthop_ht_params); 1141 if (!nexthop_old) 1142 err = nsim_nexthop_add(data, nexthop, info->extack); 1143 else 1144 err = nsim_nexthop_replace(data, nexthop, nexthop_old, 1145 info->extack); 1146 1147 if (err) 1148 nsim_nexthop_destroy(nexthop); 1149 1150 return err; 1151 } 1152 1153 static void nsim_nexthop_remove(struct nsim_fib_data *data, 1154 struct nh_notifier_info *info) 1155 { 1156 struct nsim_nexthop *nexthop; 1157 1158 nexthop = rhashtable_lookup_fast(&data->nexthop_ht, &info->id, 1159 nsim_nexthop_ht_params); 1160 if (!nexthop) 1161 return; 1162 1163 rhashtable_remove_fast(&data->nexthop_ht, &nexthop->ht_node, 1164 nsim_nexthop_ht_params); 1165 nsim_nexthop_account(data, nexthop->occ, false, info->extack); 1166 nsim_nexthop_destroy(nexthop); 1167 } 1168 1169 static int nsim_nexthop_event_nb(struct notifier_block *nb, unsigned long event, 1170 void *ptr) 1171 { 1172 struct nsim_fib_data *data = container_of(nb, struct nsim_fib_data, 1173 nexthop_nb); 1174 struct nh_notifier_info *info = ptr; 1175 int err = 0; 1176 1177 ASSERT_RTNL(); 1178 1179 switch (event) { 1180 case NEXTHOP_EVENT_REPLACE: 1181 err = nsim_nexthop_insert(data, info); 1182 break; 1183 case NEXTHOP_EVENT_DEL: 1184 nsim_nexthop_remove(data, info); 1185 break; 1186 default: 1187 break; 1188 } 1189 1190 return notifier_from_errno(err); 1191 } 1192 1193 static void nsim_nexthop_free(void *ptr, void *arg) 1194 { 1195 struct nsim_nexthop *nexthop = ptr; 1196 struct nsim_fib_data *data = arg; 1197 struct net *net; 1198 1199 net = devlink_net(data->devlink); 1200 nexthop_set_hw_flags(net, nexthop->id, false, false); 1201 nsim_nexthop_account(data, nexthop->occ, false, NULL); 1202 nsim_nexthop_destroy(nexthop); 1203 } 1204 1205 static u64 nsim_fib_ipv4_resource_occ_get(void *priv) 1206 { 1207 struct nsim_fib_data *data = priv; 1208 1209 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB, false); 1210 } 1211 1212 static u64 nsim_fib_ipv4_rules_res_occ_get(void *priv) 1213 { 1214 struct nsim_fib_data *data = priv; 1215 1216 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV4_FIB_RULES, false); 1217 } 1218 1219 static u64 nsim_fib_ipv6_resource_occ_get(void *priv) 1220 { 1221 struct nsim_fib_data *data = priv; 1222 1223 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB, false); 1224 } 1225 1226 static u64 nsim_fib_ipv6_rules_res_occ_get(void *priv) 1227 { 1228 struct nsim_fib_data *data = priv; 1229 1230 return nsim_fib_get_val(data, NSIM_RESOURCE_IPV6_FIB_RULES, false); 1231 } 1232 1233 static u64 nsim_fib_nexthops_res_occ_get(void *priv) 1234 { 1235 struct nsim_fib_data *data = priv; 1236 1237 return nsim_fib_get_val(data, NSIM_RESOURCE_NEXTHOPS, false); 1238 } 1239 1240 static void nsim_fib_set_max_all(struct nsim_fib_data *data, 1241 struct devlink *devlink) 1242 { 1243 enum nsim_resource_id res_ids[] = { 1244 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES, 1245 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES, 1246 NSIM_RESOURCE_NEXTHOPS, 1247 }; 1248 int i; 1249 1250 for (i = 0; i < ARRAY_SIZE(res_ids); i++) { 1251 int err; 1252 u64 val; 1253 1254 err = devlink_resource_size_get(devlink, res_ids[i], &val); 1255 if (err) 1256 val = (u64) -1; 1257 nsim_fib_set_max(data, res_ids[i], val); 1258 } 1259 } 1260 1261 static void nsim_fib_event_work(struct work_struct *work) 1262 { 1263 struct nsim_fib_data *data = container_of(work, struct nsim_fib_data, 1264 fib_event_work); 1265 struct nsim_fib_event *fib_event, *next_fib_event; 1266 1267 LIST_HEAD(fib_event_queue); 1268 1269 spin_lock_bh(&data->fib_event_queue_lock); 1270 list_splice_init(&data->fib_event_queue, &fib_event_queue); 1271 spin_unlock_bh(&data->fib_event_queue_lock); 1272 1273 mutex_lock(&data->fib_lock); 1274 list_for_each_entry_safe(fib_event, next_fib_event, &fib_event_queue, 1275 list) { 1276 nsim_fib_event(fib_event); 1277 list_del(&fib_event->list); 1278 kfree(fib_event); 1279 cond_resched(); 1280 } 1281 mutex_unlock(&data->fib_lock); 1282 } 1283 1284 struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, 1285 struct netlink_ext_ack *extack) 1286 { 1287 struct nsim_fib_data *data; 1288 int err; 1289 1290 data = kzalloc(sizeof(*data), GFP_KERNEL); 1291 if (!data) 1292 return ERR_PTR(-ENOMEM); 1293 data->devlink = devlink; 1294 1295 err = rhashtable_init(&data->nexthop_ht, &nsim_nexthop_ht_params); 1296 if (err) 1297 goto err_data_free; 1298 1299 mutex_init(&data->fib_lock); 1300 INIT_LIST_HEAD(&data->fib_rt_list); 1301 err = rhashtable_init(&data->fib_rt_ht, &nsim_fib_rt_ht_params); 1302 if (err) 1303 goto err_rhashtable_nexthop_destroy; 1304 1305 INIT_WORK(&data->fib_event_work, nsim_fib_event_work); 1306 INIT_LIST_HEAD(&data->fib_event_queue); 1307 spin_lock_init(&data->fib_event_queue_lock); 1308 1309 nsim_fib_set_max_all(data, devlink); 1310 1311 data->nexthop_nb.notifier_call = nsim_nexthop_event_nb; 1312 err = register_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb, 1313 extack); 1314 if (err) { 1315 pr_err("Failed to register nexthop notifier\n"); 1316 goto err_rhashtable_fib_destroy; 1317 } 1318 1319 data->fib_nb.notifier_call = nsim_fib_event_nb; 1320 err = register_fib_notifier(devlink_net(devlink), &data->fib_nb, 1321 nsim_fib_dump_inconsistent, extack); 1322 if (err) { 1323 pr_err("Failed to register fib notifier\n"); 1324 goto err_nexthop_nb_unregister; 1325 } 1326 1327 devlink_resource_occ_get_register(devlink, 1328 NSIM_RESOURCE_IPV4_FIB, 1329 nsim_fib_ipv4_resource_occ_get, 1330 data); 1331 devlink_resource_occ_get_register(devlink, 1332 NSIM_RESOURCE_IPV4_FIB_RULES, 1333 nsim_fib_ipv4_rules_res_occ_get, 1334 data); 1335 devlink_resource_occ_get_register(devlink, 1336 NSIM_RESOURCE_IPV6_FIB, 1337 nsim_fib_ipv6_resource_occ_get, 1338 data); 1339 devlink_resource_occ_get_register(devlink, 1340 NSIM_RESOURCE_IPV6_FIB_RULES, 1341 nsim_fib_ipv6_rules_res_occ_get, 1342 data); 1343 devlink_resource_occ_get_register(devlink, 1344 NSIM_RESOURCE_NEXTHOPS, 1345 nsim_fib_nexthops_res_occ_get, 1346 data); 1347 return data; 1348 1349 err_nexthop_nb_unregister: 1350 unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); 1351 err_rhashtable_fib_destroy: 1352 flush_work(&data->fib_event_work); 1353 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free, 1354 data); 1355 err_rhashtable_nexthop_destroy: 1356 rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, 1357 data); 1358 mutex_destroy(&data->fib_lock); 1359 err_data_free: 1360 kfree(data); 1361 return ERR_PTR(err); 1362 } 1363 1364 void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data) 1365 { 1366 devlink_resource_occ_get_unregister(devlink, 1367 NSIM_RESOURCE_NEXTHOPS); 1368 devlink_resource_occ_get_unregister(devlink, 1369 NSIM_RESOURCE_IPV6_FIB_RULES); 1370 devlink_resource_occ_get_unregister(devlink, 1371 NSIM_RESOURCE_IPV6_FIB); 1372 devlink_resource_occ_get_unregister(devlink, 1373 NSIM_RESOURCE_IPV4_FIB_RULES); 1374 devlink_resource_occ_get_unregister(devlink, 1375 NSIM_RESOURCE_IPV4_FIB); 1376 unregister_fib_notifier(devlink_net(devlink), &data->fib_nb); 1377 unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); 1378 flush_work(&data->fib_event_work); 1379 rhashtable_free_and_destroy(&data->fib_rt_ht, nsim_fib_rt_free, 1380 data); 1381 rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free, 1382 data); 1383 WARN_ON_ONCE(!list_empty(&data->fib_event_queue)); 1384 WARN_ON_ONCE(!list_empty(&data->fib_rt_list)); 1385 mutex_destroy(&data->fib_lock); 1386 kfree(data); 1387 } 1388