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