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