summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
diff options
context:
space:
mode:
authorPetr Machata <petrm@nvidia.com>2023-06-22 15:33:04 +0200
committerJakub Kicinski <kuba@kernel.org>2023-06-23 19:01:56 -0700
commit4796c287b70a0f60fbc6a5df2ab33d92e8971732 (patch)
treed7b0f7edc384b902ee4465b68efde17bd3f46e20 /drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
parentf3c85eed1ac364ae2cb2729959c6150813cc9c20 (diff)
mlxsw: spectrum_router: Maintain a hash table of CRIFs
CRIFs are objects that mlxsw maintains for netdevices that may not have an associated RIF (i.e. they may not have been instantiated in the ASIC), but if indeed they do not, it is quite possible they will in the future. These netdevices are candidate RIFs, hence CRIFs. Netdevices for which CRIFs are created include e.g. bridges, LAGs, or front panel ports. The idea is that next hops would be kept at CRIFs, not RIFs, and thus it would be easier to offload and unoffload the entities that have been added before the RIF was created. In this patch, add the code for low-level CRIF maintenance: create and destroy, and keep in a table keyed by the netdevice pointer for easy recall. Signed-off-by: Petr Machata <petrm@nvidia.com> Reviewed-by: Danielle Ratson <danieller@nvidia.com> Link: https://lore.kernel.org/r/186d44e399c475159da20689f2c540719f2d1ed0.1687438411.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c')
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 15ce0d557f39..d251a926d140 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -51,6 +51,21 @@ struct mlxsw_sp_vr;
struct mlxsw_sp_lpm_tree;
struct mlxsw_sp_rif_ops;
+struct mlxsw_sp_crif_key {
+ struct net_device *dev;
+};
+
+struct mlxsw_sp_crif {
+ struct mlxsw_sp_crif_key key;
+ struct rhash_head ht_node;
+};
+
+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 list_head nexthop_list;
struct list_head neigh_list;
@@ -1060,6 +1075,56 @@ 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;
+}
+
+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)
+{
+ 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,
@@ -9148,6 +9213,95 @@ 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)
+{
+ mlxsw_sp_crif_remove(router, crif);
+ 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 +9521,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 +9544,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);
@@ -10649,6 +10816,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;
@@ -10780,6 +10952,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);
@@ -10815,6 +10989,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);