Merge branch 'mlxsw-maintain-candidate-rifs'
authorJakub Kicinski <kuba@kernel.org>
Sat, 24 Jun 2023 01:59:16 +0000 (18:59 -0700)
committerJakub Kicinski <kuba@kernel.org>
Sat, 24 Jun 2023 02:01:56 +0000 (19:01 -0700)
Petr Machata says:

====================
mlxsw: Maintain candidate RIFs

The mlxsw driver currently makes the assumption that the user applies
configuration in a bottom-up manner. Thus netdevices need to be added to
the bridge before IP addresses are configured on that bridge or SVI added
on top of it. Enslaving a netdevice to another netdevice that already has
uppers is in fact forbidden by mlxsw for this reason. Despite this safety,
it is rather easy to get into situations where the offloaded configuration
is just plain wrong.

As an example, take a front panel port, configure an IP address: it gets a
RIF. Now enslave the port to the bridge, and the RIF is gone. Remove the
port from the bridge again, but the RIF never comes back. There is a number
of similar situations, where changing the configuration there and back
utterly breaks the offload.

The situation is going to be made better by implementing a range of replays
and post-hoc offloads.

This patch set lays the ground for replay of next hops. The particular
issue that it deals with is that currently, driver-specific bookkeeping for
next hops is hooked off RIF objects, which come and go across the lifetime
of a netdevice. We would rather keep these objects at an entity that
mirrors the lifetime of the netdevice itself. That way they are at hand and
can be offloaded when a RIF is eventually created.

To that end, with this patchset, mlxsw keeps a hash table of CRIFs:
candidate RIFs, persistent handles for netdevices that mlxsw deems
potentially interesting. The lifetime of a CRIF matches that of the
underlying netdevice, and thus a RIF can always assume a CRIF exists. A
CRIF is where next hops are kept, and when RIF is created, these next hops
can be easily offloaded. (Previously only the next hops created after the
RIF was created were offloaded.)

- Patches #1 and #2 are minor adjustments.
- In patches #3 and #4, add CRIF bookkeeping.
- In patch #5, link CRIFs to RIFs such that given a netdevice-backed RIF,
  the corresponding CRIF is easy to look up.
- Patch #6 is a clean-up allowed by the previous patches
- Patches #7 and #8 move next hop tracking to CRIFs

No observable effects are intended as of yet. This will be useful once
there is support for RIF creation for netdevices that become mlxsw uppers,
which will come in following patch sets.
====================

Link: https://lore.kernel.org/r/cover.1687438411.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h

index 43e8f19..445ba7f 100644 (file)
@@ -51,10 +51,27 @@ struct mlxsw_sp_vr;
 struct mlxsw_sp_lpm_tree;
 struct mlxsw_sp_rif_ops;
 
-struct mlxsw_sp_rif {
+struct mlxsw_sp_crif_key {
+       struct net_device *dev;
+};
+
+struct mlxsw_sp_crif {
+       struct mlxsw_sp_crif_key key;
+       struct rhash_head ht_node;
+       bool can_destroy;
        struct list_head nexthop_list;
+       struct mlxsw_sp_rif *rif;
+};
+
+static const struct rhashtable_params mlxsw_sp_crif_ht_params = {
+       .key_offset = offsetof(struct mlxsw_sp_crif, key),
+       .key_len = sizeof_field(struct mlxsw_sp_crif, key),
+       .head_offset = offsetof(struct mlxsw_sp_crif, ht_node),
+};
+
+struct mlxsw_sp_rif {
+       struct mlxsw_sp_crif *crif; /* NULL for underlay RIF */
        struct list_head neigh_list;
-       struct net_device *dev; /* NULL for underlay RIF */
        struct mlxsw_sp_fid *fid;
        unsigned char addr[ETH_ALEN];
        int mtu;
@@ -73,7 +90,9 @@ struct mlxsw_sp_rif {
 
 static struct net_device *mlxsw_sp_rif_dev(const struct mlxsw_sp_rif *rif)
 {
-       return rif->dev;
+       if (!rif->crif)
+               return NULL;
+       return rif->crif->key.dev;
 }
 
 struct mlxsw_sp_rif_params {
@@ -1060,6 +1079,61 @@ u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev)
        return tb_id;
 }
 
+static void
+mlxsw_sp_crif_init(struct mlxsw_sp_crif *crif, struct net_device *dev)
+{
+       crif->key.dev = dev;
+       INIT_LIST_HEAD(&crif->nexthop_list);
+}
+
+static struct mlxsw_sp_crif *
+mlxsw_sp_crif_alloc(struct net_device *dev)
+{
+       struct mlxsw_sp_crif *crif;
+
+       crif = kzalloc(sizeof(*crif), GFP_KERNEL);
+       if (!crif)
+               return NULL;
+
+       mlxsw_sp_crif_init(crif, dev);
+       return crif;
+}
+
+static void mlxsw_sp_crif_free(struct mlxsw_sp_crif *crif)
+{
+       if (WARN_ON(crif->rif))
+               return;
+
+       WARN_ON(!list_empty(&crif->nexthop_list));
+       kfree(crif);
+}
+
+static int mlxsw_sp_crif_insert(struct mlxsw_sp_router *router,
+                               struct mlxsw_sp_crif *crif)
+{
+       return rhashtable_insert_fast(&router->crif_ht, &crif->ht_node,
+                                     mlxsw_sp_crif_ht_params);
+}
+
+static void mlxsw_sp_crif_remove(struct mlxsw_sp_router *router,
+                                struct mlxsw_sp_crif *crif)
+{
+       rhashtable_remove_fast(&router->crif_ht, &crif->ht_node,
+                              mlxsw_sp_crif_ht_params);
+}
+
+static struct mlxsw_sp_crif *
+mlxsw_sp_crif_lookup(struct mlxsw_sp_router *router,
+                    const struct net_device *dev)
+{
+       struct mlxsw_sp_crif_key key = {
+               .dev = (struct net_device *)dev,
+       };
+
+       return rhashtable_lookup_fast(&router->crif_ht, &key,
+                                     mlxsw_sp_crif_ht_params);
+}
+
 static struct mlxsw_sp_rif *
 mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
                    const struct mlxsw_sp_rif_params *params,
@@ -1648,17 +1722,26 @@ static void mlxsw_sp_netdevice_ipip_ol_down_event(struct mlxsw_sp *mlxsw_sp,
                mlxsw_sp_ipip_entry_ol_down_event(mlxsw_sp, ipip_entry);
 }
 
-static void mlxsw_sp_nexthop_rif_migrate(struct mlxsw_sp *mlxsw_sp,
-                                        struct mlxsw_sp_rif *old_rif,
-                                        struct mlxsw_sp_rif *new_rif);
+static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,
+                                       struct mlxsw_sp_rif *rif);
+
 static void mlxsw_sp_rif_migrate_destroy(struct mlxsw_sp *mlxsw_sp,
                                         struct mlxsw_sp_rif *old_rif,
                                         struct mlxsw_sp_rif *new_rif,
                                         bool migrate_nhs)
 {
+       struct mlxsw_sp_crif *crif = old_rif->crif;
+       struct mlxsw_sp_crif mock_crif = {};
+
        if (migrate_nhs)
-               mlxsw_sp_nexthop_rif_migrate(mlxsw_sp, old_rif, new_rif);
+               mlxsw_sp_nexthop_rif_update(mlxsw_sp, new_rif);
 
+       /* Plant a mock CRIF so that destroying the old RIF doesn't unoffload
+        * our nexthops and IPIP tunnels, and doesn't sever the crif->rif link.
+        */
+       mlxsw_sp_crif_init(&mock_crif, crif->key.dev);
+       old_rif->crif = &mock_crif;
+       mock_crif.rif = old_rif;
        mlxsw_sp_rif_destroy(old_rif);
 }
 
@@ -1684,9 +1767,6 @@ mlxsw_sp_ipip_entry_ol_lb_update(struct mlxsw_sp *mlxsw_sp,
        return 0;
 }
 
-static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,
-                                       struct mlxsw_sp_rif *rif);
-
 /**
  * __mlxsw_sp_ipip_entry_update_tunnel - Update offload related to IPIP entry.
  * @mlxsw_sp: mlxsw_sp.
@@ -2915,7 +2995,7 @@ struct mlxsw_sp_nexthop_key {
 
 struct mlxsw_sp_nexthop {
        struct list_head neigh_list_node; /* member of neigh entry list */
-       struct list_head rif_list_node;
+       struct list_head crif_list_node;
        struct list_head router_list_node;
        struct mlxsw_sp_nexthop_group_info *nhgi; /* pointer back to the group
                                                   * this nexthop belongs to
@@ -2928,7 +3008,7 @@ struct mlxsw_sp_nexthop {
        int nh_weight;
        int norm_nh_weight;
        int num_adj_entries;
-       struct mlxsw_sp_rif *rif;
+       struct mlxsw_sp_crif *crif;
        u8 should_offload:1, /* set indicates this nexthop should be written
                              * to the adjacency table.
                              */
@@ -2951,9 +3031,9 @@ struct mlxsw_sp_nexthop {
 static struct net_device *
 mlxsw_sp_nexthop_dev(const struct mlxsw_sp_nexthop *nh)
 {
-       if (nh->rif)
-               return mlxsw_sp_rif_dev(nh->rif);
-       return NULL;
+       if (!nh->crif)
+               return NULL;
+       return nh->crif->key.dev;
 }
 
 enum mlxsw_sp_nexthop_group_type {
@@ -2978,7 +3058,11 @@ struct mlxsw_sp_nexthop_group_info {
 static struct mlxsw_sp_rif *
 mlxsw_sp_nhgi_rif(const struct mlxsw_sp_nexthop_group_info *nhgi)
 {
-       return nhgi->nexthops[0].rif;
+       struct mlxsw_sp_crif *crif = nhgi->nexthops[0].crif;
+
+       if (!crif)
+               return NULL;
+       return crif->rif;
 }
 
 struct mlxsw_sp_nexthop_group_vr_key {
@@ -3102,7 +3186,9 @@ int mlxsw_sp_nexthop_indexes(struct mlxsw_sp_nexthop *nh, u32 *p_adj_index,
 
 struct mlxsw_sp_rif *mlxsw_sp_nexthop_rif(struct mlxsw_sp_nexthop *nh)
 {
-       return nh->rif;
+       if (WARN_ON(!nh->crif))
+               return NULL;
+       return nh->crif->rif;
 }
 
 bool mlxsw_sp_nexthop_group_has_ipip(struct mlxsw_sp_nexthop *nh)
@@ -3487,11 +3573,12 @@ static int __mlxsw_sp_nexthop_eth_update(struct mlxsw_sp *mlxsw_sp,
                                         bool force, char *ratr_pl)
 {
        struct mlxsw_sp_neigh_entry *neigh_entry = nh->neigh_entry;
+       struct mlxsw_sp_rif *rif = mlxsw_sp_nexthop_rif(nh);
        enum mlxsw_reg_ratr_op op;
        u16 rif_index;
 
-       rif_index = nh->rif ? nh->rif->rif_index :
-                             mlxsw_sp->router->lb_rif_index;
+       rif_index = rif ? rif->rif_index :
+                         mlxsw_sp->router->lb_crif->rif->rif_index;
        op = force ? MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY :
                     MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY_ON_ACTIVITY;
        mlxsw_reg_ratr_pack(ratr_pl, op, true, MLXSW_REG_RATR_TYPE_ETHERNET,
@@ -4109,23 +4196,23 @@ mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp,
        }
 }
 
-static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh,
-                                     struct mlxsw_sp_rif *rif)
+static void mlxsw_sp_nexthop_crif_init(struct mlxsw_sp_nexthop *nh,
+                                      struct mlxsw_sp_crif *crif)
 {
-       if (nh->rif)
+       if (nh->crif)
                return;
 
-       nh->rif = rif;
-       list_add(&nh->rif_list_node, &rif->nexthop_list);
+       nh->crif = crif;
+       list_add(&nh->crif_list_node, &crif->nexthop_list);
 }
 
-static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh)
+static void mlxsw_sp_nexthop_crif_fini(struct mlxsw_sp_nexthop *nh)
 {
-       if (!nh->rif)
+       if (!nh->crif)
                return;
 
-       list_del(&nh->rif_list_node);
-       nh->rif = NULL;
+       list_del(&nh->crif_list_node);
+       nh->crif = NULL;
 }
 
 static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
@@ -4137,6 +4224,9 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
        u8 nud_state, dead;
        int err;
 
+       if (WARN_ON(!nh->crif->rif))
+               return 0;
+
        if (!nh->nhgi->gateway || nh->neigh_entry)
                return 0;
        dev = mlxsw_sp_nexthop_dev(nh);
@@ -4227,15 +4317,20 @@ static void mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp,
                                       struct mlxsw_sp_nexthop *nh,
                                       struct mlxsw_sp_ipip_entry *ipip_entry)
 {
+       struct mlxsw_sp_crif *crif;
        bool removing;
 
        if (!nh->nhgi->gateway || nh->ipip_entry)
                return;
 
+       crif = mlxsw_sp_crif_lookup(mlxsw_sp->router, ipip_entry->ol_dev);
+       if (WARN_ON(!crif))
+               return;
+
        nh->ipip_entry = ipip_entry;
        removing = !mlxsw_sp_ipip_netdev_ul_up(ipip_entry->ol_dev);
        __mlxsw_sp_nexthop_neigh_update(nh, removing);
-       mlxsw_sp_nexthop_rif_init(nh, &ipip_entry->ol_lb->common);
+       mlxsw_sp_nexthop_crif_init(nh, crif);
 }
 
 static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp,
@@ -4267,7 +4362,7 @@ static int mlxsw_sp_nexthop_type_init(struct mlxsw_sp *mlxsw_sp,
 {
        const struct mlxsw_sp_ipip_ops *ipip_ops;
        struct mlxsw_sp_ipip_entry *ipip_entry;
-       struct mlxsw_sp_rif *rif;
+       struct mlxsw_sp_crif *crif;
        int err;
 
        ipip_entry = mlxsw_sp_ipip_entry_find_by_ol_dev(mlxsw_sp, dev);
@@ -4281,11 +4376,15 @@ static int mlxsw_sp_nexthop_type_init(struct mlxsw_sp *mlxsw_sp,
        }
 
        nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH;
-       rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
-       if (!rif)
+       crif = mlxsw_sp_crif_lookup(mlxsw_sp->router, dev);
+       if (!crif)
+               return 0;
+
+       mlxsw_sp_nexthop_crif_init(nh, crif);
+
+       if (!crif->rif)
                return 0;
 
-       mlxsw_sp_nexthop_rif_init(nh, rif);
        err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh);
        if (err)
                goto err_neigh_init;
@@ -4293,25 +4392,30 @@ static int mlxsw_sp_nexthop_type_init(struct mlxsw_sp *mlxsw_sp,
        return 0;
 
 err_neigh_init:
-       mlxsw_sp_nexthop_rif_fini(nh);
+       mlxsw_sp_nexthop_crif_fini(nh);
        return err;
 }
 
-static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
-                                      struct mlxsw_sp_nexthop *nh)
+static void mlxsw_sp_nexthop_type_rif_gone(struct mlxsw_sp *mlxsw_sp,
+                                          struct mlxsw_sp_nexthop *nh)
 {
        switch (nh->type) {
        case MLXSW_SP_NEXTHOP_TYPE_ETH:
                mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh);
-               mlxsw_sp_nexthop_rif_fini(nh);
                break;
        case MLXSW_SP_NEXTHOP_TYPE_IPIP:
-               mlxsw_sp_nexthop_rif_fini(nh);
                mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh);
                break;
        }
 }
 
+static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp,
+                                      struct mlxsw_sp_nexthop *nh)
+{
+       mlxsw_sp_nexthop_type_rif_gone(mlxsw_sp, nh);
+       mlxsw_sp_nexthop_crif_fini(nh);
+}
+
 static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_nexthop_group *nh_grp,
                                  struct mlxsw_sp_nexthop *nh,
@@ -4402,7 +4506,7 @@ static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,
        struct mlxsw_sp_nexthop *nh;
        bool removing;
 
-       list_for_each_entry(nh, &rif->nexthop_list, rif_list_node) {
+       list_for_each_entry(nh, &rif->crif->nexthop_list, crif_list_node) {
                switch (nh->type) {
                case MLXSW_SP_NEXTHOP_TYPE_ETH:
                        removing = false;
@@ -4420,25 +4524,14 @@ static void mlxsw_sp_nexthop_rif_update(struct mlxsw_sp *mlxsw_sp,
        }
 }
 
-static void mlxsw_sp_nexthop_rif_migrate(struct mlxsw_sp *mlxsw_sp,
-                                        struct mlxsw_sp_rif *old_rif,
-                                        struct mlxsw_sp_rif *new_rif)
-{
-       struct mlxsw_sp_nexthop *nh;
-
-       list_splice_init(&old_rif->nexthop_list, &new_rif->nexthop_list);
-       list_for_each_entry(nh, &new_rif->nexthop_list, rif_list_node)
-               nh->rif = new_rif;
-       mlxsw_sp_nexthop_rif_update(mlxsw_sp, new_rif);
-}
-
 static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
                                           struct mlxsw_sp_rif *rif)
 {
        struct mlxsw_sp_nexthop *nh, *tmp;
 
-       list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) {
-               mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh);
+       list_for_each_entry_safe(nh, tmp, &rif->crif->nexthop_list,
+                                crif_list_node) {
+               mlxsw_sp_nexthop_type_rif_gone(mlxsw_sp, nh);
                mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nhgi->nh_grp);
        }
 }
@@ -4458,7 +4551,7 @@ static int mlxsw_sp_adj_trap_entry_init(struct mlxsw_sp *mlxsw_sp)
        mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, true,
                            MLXSW_REG_RATR_TYPE_ETHERNET,
                            mlxsw_sp->router->adj_trap_index,
-                           mlxsw_sp->router->lb_rif_index);
+                           mlxsw_sp->router->lb_crif->rif->rif_index);
        mlxsw_reg_ratr_trap_action_set(ratr_pl, trap_action);
        mlxsw_reg_ratr_trap_id_set(ratr_pl, MLXSW_TRAP_ID_RTR_EGRESS0);
        err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
@@ -4774,21 +4867,19 @@ static bool mlxsw_sp_nexthop_obj_is_gateway(struct mlxsw_sp *mlxsw_sp,
 static void mlxsw_sp_nexthop_obj_blackhole_init(struct mlxsw_sp *mlxsw_sp,
                                                struct mlxsw_sp_nexthop *nh)
 {
-       u16 lb_rif_index = mlxsw_sp->router->lb_rif_index;
-
        nh->action = MLXSW_SP_NEXTHOP_ACTION_DISCARD;
        nh->should_offload = 1;
        /* While nexthops that discard packets do not forward packets
         * via an egress RIF, they still need to be programmed using a
         * valid RIF, so use the loopback RIF created during init.
         */
-       nh->rif = mlxsw_sp->router->rifs[lb_rif_index];
+       nh->crif = mlxsw_sp->router->lb_crif;
 }
 
 static void mlxsw_sp_nexthop_obj_blackhole_fini(struct mlxsw_sp *mlxsw_sp,
                                                struct mlxsw_sp_nexthop *nh)
 {
-       nh->rif = NULL;
+       nh->crif = NULL;
        nh->should_offload = 0;
 }
 
@@ -7796,6 +7887,9 @@ static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif)
 static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp,
                                          struct mlxsw_sp_rif *rif)
 {
+       /* Signal to nexthop cleanup that the RIF is going away. */
+       rif->crif->rif = NULL;
+
        mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index);
        mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif);
        mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif);
@@ -7905,23 +7999,26 @@ static void mlxsw_sp_rif_index_free(struct mlxsw_sp *mlxsw_sp, u16 rif_index,
 
 static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
                                               u16 vr_id,
-                                              struct net_device *l3_dev)
+                                              struct mlxsw_sp_crif *crif)
 {
+       struct net_device *l3_dev = crif ? crif->key.dev : NULL;
        struct mlxsw_sp_rif *rif;
 
        rif = kzalloc(rif_size, GFP_KERNEL);
        if (!rif)
                return NULL;
 
-       INIT_LIST_HEAD(&rif->nexthop_list);
        INIT_LIST_HEAD(&rif->neigh_list);
        if (l3_dev) {
                ether_addr_copy(rif->addr, l3_dev->dev_addr);
                rif->mtu = l3_dev->mtu;
-               rif->dev = l3_dev;
        }
        rif->vr_id = vr_id;
        rif->rif_index = rif_index;
+       if (crif) {
+               rif->crif = crif;
+               crif->rif = rif;
+       }
 
        return rif;
 }
@@ -7929,7 +8026,9 @@ static struct mlxsw_sp_rif *mlxsw_sp_rif_alloc(size_t rif_size, u16 rif_index,
 static void mlxsw_sp_rif_free(struct mlxsw_sp_rif *rif)
 {
        WARN_ON(!list_empty(&rif->neigh_list));
-       WARN_ON(!list_empty(&rif->nexthop_list));
+
+       if (rif->crif)
+               rif->crif->rif = NULL;
        kfree(rif);
 }
 
@@ -8163,6 +8262,7 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
        const struct mlxsw_sp_rif_ops *ops;
        struct mlxsw_sp_fid *fid = NULL;
        enum mlxsw_sp_rif_type type;
+       struct mlxsw_sp_crif *crif;
        struct mlxsw_sp_rif *rif;
        struct mlxsw_sp_vr *vr;
        u16 rif_index;
@@ -8182,7 +8282,13 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp,
                goto err_rif_index_alloc;
        }
 
-       rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, params->dev);
+       crif = mlxsw_sp_crif_lookup(mlxsw_sp->router, params->dev);
+       if (WARN_ON(!crif)) {
+               err = -ENOENT;
+               goto err_crif_lookup;
+       }
+
+       rif = mlxsw_sp_rif_alloc(ops->rif_size, rif_index, vr->id, crif);
        if (!rif) {
                err = -ENOMEM;
                goto err_rif_alloc;
@@ -8241,6 +8347,7 @@ err_fid_get:
        dev_put(params->dev);
        mlxsw_sp_rif_free(rif);
 err_rif_alloc:
+err_crif_lookup:
        mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries);
 err_rif_index_alloc:
        vr->rif_count--;
@@ -8253,6 +8360,7 @@ static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
        struct net_device *dev = mlxsw_sp_rif_dev(rif);
        const struct mlxsw_sp_rif_ops *ops = rif->ops;
        struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp;
+       struct mlxsw_sp_crif *crif = rif->crif;
        struct mlxsw_sp_fid *fid = rif->fid;
        u8 rif_entries = rif->rif_entries;
        u16 rif_index = rif->rif_index;
@@ -8283,6 +8391,9 @@ static void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif)
        mlxsw_sp_rif_index_free(mlxsw_sp, rif_index, rif_entries);
        vr->rif_count--;
        mlxsw_sp_vr_put(mlxsw_sp, vr);
+
+       if (crif->can_destroy)
+               mlxsw_sp_crif_free(crif);
 }
 
 void mlxsw_sp_rif_destroy_by_dev(struct mlxsw_sp *mlxsw_sp,
@@ -9148,6 +9259,104 @@ static int mlxsw_sp_router_port_pre_changeaddr_event(struct mlxsw_sp_rif *rif,
        return -ENOBUFS;
 }
 
+static bool mlxsw_sp_router_netdevice_interesting(struct mlxsw_sp *mlxsw_sp,
+                                                 struct net_device *dev)
+{
+       struct vlan_dev_priv *vlan;
+
+       if (netif_is_lag_master(dev) ||
+           netif_is_bridge_master(dev) ||
+           mlxsw_sp_port_dev_check(dev) ||
+           mlxsw_sp_netdev_is_ipip_ol(mlxsw_sp, dev) ||
+           netif_is_l3_master(dev))
+               return true;
+
+       if (!is_vlan_dev(dev))
+               return false;
+
+       vlan = vlan_dev_priv(dev);
+       return netif_is_lag_master(vlan->real_dev) ||
+              netif_is_bridge_master(vlan->real_dev) ||
+              mlxsw_sp_port_dev_check(vlan->real_dev);
+}
+
+static struct mlxsw_sp_crif *
+mlxsw_sp_crif_register(struct mlxsw_sp_router *router, struct net_device *dev)
+{
+       struct mlxsw_sp_crif *crif;
+       int err;
+
+       if (WARN_ON(mlxsw_sp_crif_lookup(router, dev)))
+               return NULL;
+
+       crif = mlxsw_sp_crif_alloc(dev);
+       if (!crif)
+               return ERR_PTR(-ENOMEM);
+
+       err = mlxsw_sp_crif_insert(router, crif);
+       if (err)
+               goto err_netdev_insert;
+
+       return crif;
+
+err_netdev_insert:
+       mlxsw_sp_crif_free(crif);
+       return ERR_PTR(err);
+}
+
+static void mlxsw_sp_crif_unregister(struct mlxsw_sp_router *router,
+                                    struct mlxsw_sp_crif *crif)
+{
+       struct mlxsw_sp_nexthop *nh, *tmp;
+
+       mlxsw_sp_crif_remove(router, crif);
+
+       list_for_each_entry_safe(nh, tmp, &crif->nexthop_list, crif_list_node)
+               mlxsw_sp_nexthop_type_fini(router->mlxsw_sp, nh);
+
+       if (crif->rif)
+               crif->can_destroy = true;
+       else
+               mlxsw_sp_crif_free(crif);
+}
+
+static int mlxsw_sp_netdevice_register(struct mlxsw_sp_router *router,
+                                      struct net_device *dev)
+{
+       struct mlxsw_sp_crif *crif;
+
+       if (!mlxsw_sp_router_netdevice_interesting(router->mlxsw_sp, dev))
+               return 0;
+
+       crif = mlxsw_sp_crif_register(router, dev);
+       return PTR_ERR_OR_ZERO(crif);
+}
+
+static void mlxsw_sp_netdevice_unregister(struct mlxsw_sp_router *router,
+                                         struct net_device *dev)
+{
+       struct mlxsw_sp_crif *crif;
+
+       if (!mlxsw_sp_router_netdevice_interesting(router->mlxsw_sp, dev))
+               return;
+
+       /* netdev_run_todo(), by way of netdev_wait_allrefs_any(), rebroadcasts
+        * the NETDEV_UNREGISTER message, so we can get here twice. If that's
+        * what happened, the netdevice state is NETREG_UNREGISTERED. In that
+        * case, we expect to have collected the CRIF already, and warn if it
+        * still exists. Otherwise we expect the CRIF to exist.
+        */
+       crif = mlxsw_sp_crif_lookup(router, dev);
+       if (dev->reg_state == NETREG_UNREGISTERED) {
+               if (!WARN_ON(crif))
+                       return;
+       }
+       if (WARN_ON(!crif))
+               return;
+
+       mlxsw_sp_crif_unregister(router, crif);
+}
+
 static bool mlxsw_sp_is_offload_xstats_event(unsigned long event)
 {
        switch (event) {
@@ -9367,6 +9576,15 @@ static int mlxsw_sp_router_netdevice_event(struct notifier_block *nb,
 
        mutex_lock(&mlxsw_sp->router->lock);
 
+       if (event == NETDEV_REGISTER) {
+               err = mlxsw_sp_netdevice_register(router, dev);
+               if (err)
+                       /* No need to roll this back, UNREGISTER will collect it
+                        * anyhow.
+                        */
+                       goto out;
+       }
+
        if (mlxsw_sp_is_offload_xstats_event(event))
                err = mlxsw_sp_netdevice_offload_xstats_cmd(mlxsw_sp, dev,
                                                            event, ptr);
@@ -9381,6 +9599,10 @@ static int mlxsw_sp_router_netdevice_event(struct notifier_block *nb,
        else if (mlxsw_sp_is_vrf_event(event, ptr))
                err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr);
 
+       if (event == NETDEV_UNREGISTER)
+               mlxsw_sp_netdevice_unregister(router, dev);
+
+out:
        mutex_unlock(&mlxsw_sp->router->lock);
 
        return notifier_from_errno(err);
@@ -9901,6 +10123,7 @@ mlxsw_sp_rif_ipip_lb_ul_rif_op(struct mlxsw_sp_rif *ul_rif, bool enable)
 
 static struct mlxsw_sp_rif *
 mlxsw_sp_ul_rif_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
+                      struct mlxsw_sp_crif *ul_crif,
                       struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp_rif *ul_rif;
@@ -9914,7 +10137,8 @@ mlxsw_sp_ul_rif_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
                return ERR_PTR(err);
        }
 
-       ul_rif = mlxsw_sp_rif_alloc(sizeof(*ul_rif), rif_index, vr->id, NULL);
+       ul_rif = mlxsw_sp_rif_alloc(sizeof(*ul_rif), rif_index, vr->id,
+                                   ul_crif);
        if (!ul_rif) {
                err = -ENOMEM;
                goto err_rif_alloc;
@@ -9953,6 +10177,7 @@ static void mlxsw_sp_ul_rif_destroy(struct mlxsw_sp_rif *ul_rif)
 
 static struct mlxsw_sp_rif *
 mlxsw_sp_ul_rif_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id,
+                   struct mlxsw_sp_crif *ul_crif,
                    struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp_vr *vr;
@@ -9965,7 +10190,7 @@ mlxsw_sp_ul_rif_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id,
        if (refcount_inc_not_zero(&vr->ul_rif_refcnt))
                return vr->ul_rif;
 
-       vr->ul_rif = mlxsw_sp_ul_rif_create(mlxsw_sp, vr, extack);
+       vr->ul_rif = mlxsw_sp_ul_rif_create(mlxsw_sp, vr, ul_crif, extack);
        if (IS_ERR(vr->ul_rif)) {
                err = PTR_ERR(vr->ul_rif);
                goto err_ul_rif_create;
@@ -10003,7 +10228,7 @@ int mlxsw_sp_router_ul_rif_get(struct mlxsw_sp *mlxsw_sp, u32 ul_tb_id,
        int err = 0;
 
        mutex_lock(&mlxsw_sp->router->lock);
-       ul_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, ul_tb_id, NULL);
+       ul_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, ul_tb_id, NULL, NULL);
        if (IS_ERR(ul_rif)) {
                err = PTR_ERR(ul_rif);
                goto out;
@@ -10039,7 +10264,7 @@ mlxsw_sp2_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif,
        struct mlxsw_sp_rif *ul_rif;
        int err;
 
-       ul_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, ul_tb_id, extack);
+       ul_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, ul_tb_id, NULL, extack);
        if (IS_ERR(ul_rif))
                return PTR_ERR(ul_rif);
 
@@ -10561,28 +10786,41 @@ static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
        mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
 }
 
-static int mlxsw_sp_lb_rif_init(struct mlxsw_sp *mlxsw_sp)
+static int mlxsw_sp_lb_rif_init(struct mlxsw_sp *mlxsw_sp,
+                               struct netlink_ext_ack *extack)
 {
-       u16 lb_rif_index;
+       struct mlxsw_sp_router *router = mlxsw_sp->router;
+       struct mlxsw_sp_rif *lb_rif;
        int err;
 
+       router->lb_crif = mlxsw_sp_crif_alloc(NULL);
+       if (IS_ERR(router->lb_crif))
+               return PTR_ERR(router->lb_crif);
+
        /* Create a generic loopback RIF associated with the main table
         * (default VRF). Any table can be used, but the main table exists
-        * anyway, so we do not waste resources.
+        * anyway, so we do not waste resources. Loopback RIFs are usually
+        * created with a NULL CRIF, but this RIF is used as a fallback RIF
+        * for blackhole nexthops, and nexthops expect to have a valid CRIF.
         */
-       err = mlxsw_sp_router_ul_rif_get(mlxsw_sp, RT_TABLE_MAIN,
-                                        &lb_rif_index);
-       if (err)
-               return err;
-
-       mlxsw_sp->router->lb_rif_index = lb_rif_index;
+       lb_rif = mlxsw_sp_ul_rif_get(mlxsw_sp, RT_TABLE_MAIN, router->lb_crif,
+                                    extack);
+       if (IS_ERR(lb_rif)) {
+               err = PTR_ERR(lb_rif);
+               goto err_ul_rif_get;
+       }
 
        return 0;
+
+err_ul_rif_get:
+       mlxsw_sp_crif_free(router->lb_crif);
+       return err;
 }
 
 static void mlxsw_sp_lb_rif_fini(struct mlxsw_sp *mlxsw_sp)
 {
-       mlxsw_sp_router_ul_rif_put(mlxsw_sp, mlxsw_sp->router->lb_rif_index);
+       mlxsw_sp_ul_rif_put(mlxsw_sp->router->lb_crif->rif);
+       mlxsw_sp_crif_free(mlxsw_sp->router->lb_crif);
 }
 
 static int mlxsw_sp1_router_init(struct mlxsw_sp *mlxsw_sp)
@@ -10647,6 +10885,11 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_ipips_init;
 
+       err = rhashtable_init(&mlxsw_sp->router->crif_ht,
+                             &mlxsw_sp_crif_ht_params);
+       if (err)
+               goto err_crif_ht_init;
+
        err = mlxsw_sp_rifs_init(mlxsw_sp);
        if (err)
                goto err_rifs_init;
@@ -10674,7 +10917,7 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp,
        if (err)
                goto err_vrs_init;
 
-       err = mlxsw_sp_lb_rif_init(mlxsw_sp);
+       err = mlxsw_sp_lb_rif_init(mlxsw_sp, extack);
        if (err)
                goto err_lb_rif_init;
 
@@ -10778,6 +11021,8 @@ err_nexthop_group_ht_init:
 err_nexthop_ht_init:
        mlxsw_sp_rifs_fini(mlxsw_sp);
 err_rifs_init:
+       rhashtable_destroy(&mlxsw_sp->router->crif_ht);
+err_crif_ht_init:
        mlxsw_sp_ipips_fini(mlxsw_sp);
 err_ipips_init:
        __mlxsw_sp_router_fini(mlxsw_sp);
@@ -10813,6 +11058,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
        rhashtable_destroy(&router->nexthop_group_ht);
        rhashtable_destroy(&router->nexthop_ht);
        mlxsw_sp_rifs_fini(mlxsw_sp);
+       rhashtable_destroy(&mlxsw_sp->router->crif_ht);
        mlxsw_sp_ipips_fini(mlxsw_sp);
        __mlxsw_sp_router_fini(mlxsw_sp);
        cancel_delayed_work_sync(&router->nh_grp_activity_dw);
index 5a0babc..9a2669a 100644 (file)
@@ -20,6 +20,7 @@ struct mlxsw_sp_router_nve_decap {
 
 struct mlxsw_sp_router {
        struct mlxsw_sp *mlxsw_sp;
+       struct rhashtable crif_ht;
        struct gen_pool *rifs_table;
        struct mlxsw_sp_rif **rifs;
        struct idr rif_mac_profiles_idr;
@@ -59,7 +60,7 @@ struct mlxsw_sp_router {
        struct mlxsw_sp_router_nve_decap nve_decap_config;
        struct mutex lock; /* Protects shared router resources */
        struct mlxsw_sp_fib_entry_op_ctx *ll_op_ctx;
-       u16 lb_rif_index;
+       struct mlxsw_sp_crif *lb_crif;
        const struct mlxsw_sp_adj_grp_size_range *adj_grp_size_ranges;
        size_t adj_grp_size_ranges_count;
        struct delayed_work nh_grp_activity_dw;