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