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