linux/drivers/net/netdevsim/fib.c
<<
>>
Prefs
   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
  34struct nsim_fib_entry {
  35        u64 max;
  36        atomic64_t num;
  37};
  38
  39struct nsim_per_fib_data {
  40        struct nsim_fib_entry fib;
  41        struct nsim_fib_entry rules;
  42};
  43
  44struct 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
  65struct 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
  72struct 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
  78struct nsim_fib4_rt {
  79        struct nsim_fib_rt common;
  80        struct fib_info *fi;
  81        u8 tos;
  82        u8 type;
  83};
  84
  85struct nsim_fib6_rt {
  86        struct nsim_fib_rt common;
  87        struct list_head nh_list;
  88        unsigned int nhs;
  89};
  90
  91struct nsim_fib6_rt_nh {
  92        struct list_head list;  /* Member of nh_list */
  93        struct fib6_info *rt;
  94};
  95
  96struct nsim_fib6_event {
  97        struct fib6_info **rt_arr;
  98        unsigned int nrt6;
  99};
 100
 101struct 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
 112static 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
 119struct nsim_nexthop {
 120        struct rhash_head ht_node;
 121        u64 occ;
 122        u32 id;
 123        bool is_resilient;
 124};
 125
 126static 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
 133u64 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
 161static 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
 189static 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
 206static 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
 224static 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
 238static 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
 250static void nsim_fib_rt_fini(struct nsim_fib_rt *fib_rt)
 251{
 252        list_del(&fib_rt->list);
 253}
 254
 255static 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
 271static struct nsim_fib4_rt *
 272nsim_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
 292static 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
 299static struct nsim_fib4_rt *
 300nsim_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
 314static void
 315nsim_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
 333static 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
 353static 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
 371err_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
 379static 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
 408static 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
 439static 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
 453static 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
 478static struct nsim_fib6_rt_nh *
 479nsim_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
 492static 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)
 510static void nsim_rt6_release(struct fib6_info *rt)
 511{
 512        fib6_info_release(rt);
 513}
 514#else
 515static void nsim_rt6_release(struct fib6_info *rt)
 516{
 517}
 518#endif
 519
 520static 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
 535static struct nsim_fib6_rt *
 536nsim_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
 566err_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
 575static 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
 586static struct nsim_fib6_rt *
 587nsim_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
 601static 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
 631err_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)
 640static 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
 652static 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)
 660static 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
 671static 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
 678static 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
 695err_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
 703static 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
 733static 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
 766static 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
 796static 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
 833static 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
 842static 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
 871err_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
 877static 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
 893static 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
 928static 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
 960err_fib6_event_fini:
 961        nsim_fib6_event_fini(&fib_event->fib6_event);
 962        return err;
 963}
 964
 965static 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
1006err_fib_prepare_event:
1007        kfree(fib_event);
1008        return NOTIFY_BAD;
1009}
1010
1011static 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
1034static 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
1046static 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
1057static 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 */
1075static 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
1097static 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
1136static void nsim_nexthop_destroy(struct nsim_nexthop *nexthop)
1137{
1138        kfree(nexthop);
1139}
1140
1141static 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
1162err_num_decrease:
1163        atomic64_sub(i, &data->nexthops.num);
1164        return err;
1165
1166}
1167
1168static 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
1183static 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
1205err_nexthop_dismiss:
1206        nsim_nexthop_account(data, nexthop->occ, false, extack);
1207        return err;
1208}
1209
1210static 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
1236err_nexthop_dismiss:
1237        nsim_nexthop_account(data, nexthop->occ, false, extack);
1238        return err;
1239}
1240
1241static 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
1265static 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
1281static 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
1292static 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
1307static 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
1337static 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
1349static 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
1392out:
1393        rtnl_unlock();
1394
1395        *ppos = size;
1396        return err ?: size;
1397}
1398
1399static 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
1406static 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
1413static 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
1420static 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
1427static 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
1434static 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
1441static void nsim_fib_set_max_all(struct nsim_fib_data *data,
1442                                 struct devlink *devlink)
1443{
1444        static const 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
1462static 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
1485static int
1486nsim_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
1509static void nsim_fib_debugfs_exit(struct nsim_fib_data *data)
1510{
1511        debugfs_remove_recursive(data->ddir);
1512}
1513
1514struct 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
1586err_nexthop_nb_unregister:
1587        unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb);
1588err_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);
1592err_rhashtable_nexthop_destroy:
1593        rhashtable_free_and_destroy(&data->nexthop_ht, nsim_nexthop_free,
1594                                    data);
1595        mutex_destroy(&data->fib_lock);
1596err_debugfs_exit:
1597        mutex_destroy(&data->nh_lock);
1598        nsim_fib_debugfs_exit(data);
1599err_data_free:
1600        kfree(data);
1601        return ERR_PTR(err);
1602}
1603
1604void 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