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