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