Blob Blame History Raw
From: Vlad Buslov <vladbu@mellanox.com>
Date: Fri, 2 Aug 2019 22:21:56 +0300
Subject: net/mlx5e: Protect encap hash table with mutex
Patch-mainline: v5.4-rc1
Git-commit: 61086f391044fd587af9d70a9b8f6f800dd474ba
References: jsc#SLE-8464

To remove dependency on rtnl lock, protect encap hash table from concurrent
modifications with new "encap_tbl_lock" mutex. Use the mutex to protect
internal encap entry state from concurrent modification. This is necessary
because a flow can be attached to multiple encap entries simultaneously,
which significantly complicates using finer grained per-entry lock.

Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Reviewed-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
Acked-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
---
 drivers/net/ethernet/mellanox/mlx5/core/en_tc.c   |   43 ++++++++++++++++------
 drivers/net/ethernet/mellanox/mlx5/core/eswitch.c |    2 +
 drivers/net/ethernet/mellanox/mlx5/core/eswitch.h |    1 
 3 files changed, 36 insertions(+), 10 deletions(-)

--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -1478,33 +1478,51 @@ void mlx5e_tc_update_neigh_used_value(st
 	}
 }
 
-void mlx5e_encap_put(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e)
+static void mlx5e_encap_dealloc(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e)
 {
-	if (!refcount_dec_and_test(&e->refcnt))
-		return;
-
 	WARN_ON(!list_empty(&e->flows));
 	mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
 
 	if (e->flags & MLX5_ENCAP_ENTRY_VALID)
 		mlx5_packet_reformat_dealloc(priv->mdev, e->encap_id);
 
-	hash_del_rcu(&e->encap_hlist);
 	kfree(e->encap_header);
 	kfree(e);
 }
 
+void mlx5e_encap_put(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e)
+{
+	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+
+	if (!refcount_dec_and_mutex_lock(&e->refcnt, &esw->offloads.encap_tbl_lock))
+		return;
+	hash_del_rcu(&e->encap_hlist);
+	mutex_unlock(&esw->offloads.encap_tbl_lock);
+
+	mlx5e_encap_dealloc(priv, e);
+}
+
 static void mlx5e_detach_encap(struct mlx5e_priv *priv,
 			       struct mlx5e_tc_flow *flow, int out_index)
 {
+	struct mlx5e_encap_entry *e = flow->encaps[out_index].e;
+	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+
 	/* flow wasn't fully initialized */
-	if (!flow->encaps[out_index].e)
+	if (!e)
 		return;
 
+	mutex_lock(&esw->offloads.encap_tbl_lock);
 	list_del(&flow->encaps[out_index].list);
-
-	mlx5e_encap_put(priv, flow->encaps[out_index].e);
 	flow->encaps[out_index].e = NULL;
+	if (!refcount_dec_and_test(&e->refcnt)) {
+		mutex_unlock(&esw->offloads.encap_tbl_lock);
+		return;
+	}
+	hash_del_rcu(&e->encap_hlist);
+	mutex_unlock(&esw->offloads.encap_tbl_lock);
+
+	mlx5e_encap_dealloc(priv, e);
 }
 
 static void __mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow)
@@ -2891,6 +2909,7 @@ static int mlx5e_attach_encap(struct mlx
 
 	hash_key = hash_encap_info(&key);
 
+	mutex_lock(&esw->offloads.encap_tbl_lock);
 	e = mlx5e_encap_get(priv, &key, hash_key);
 
 	/* must verify if encap is valid or not */
@@ -2898,8 +2917,10 @@ static int mlx5e_attach_encap(struct mlx
 		goto attach_flow;
 
 	e = kzalloc(sizeof(*e), GFP_KERNEL);
-	if (!e)
-		return -ENOMEM;
+	if (!e) {
+		err = -ENOMEM;
+		goto out_err;
+	}
 
 	refcount_set(&e->refcnt, 1);
 	e->tun_info = tun_info;
@@ -2931,10 +2952,12 @@ attach_flow:
 	} else {
 		*encap_valid = false;
 	}
+	mutex_unlock(&esw->offloads.encap_tbl_lock);
 
 	return err;
 
 out_err:
+	mutex_unlock(&esw->offloads.encap_tbl_lock);
 	kfree(e);
 	return err;
 }
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -1999,6 +1999,7 @@ int mlx5_eswitch_init(struct mlx5_core_d
 	if (err)
 		goto abort;
 
+	mutex_init(&esw->offloads.encap_tbl_lock);
 	hash_init(esw->offloads.encap_tbl);
 	mutex_init(&esw->offloads.mod_hdr.lock);
 	hash_init(esw->offloads.mod_hdr.hlist);
@@ -2039,6 +2040,7 @@ void mlx5_eswitch_cleanup(struct mlx5_es
 	destroy_workqueue(esw->work_queue);
 	esw_offloads_cleanup_reps(esw);
 	mutex_destroy(&esw->offloads.mod_hdr.lock);
+	mutex_destroy(&esw->offloads.encap_tbl_lock);
 	kfree(esw->vports);
 	kfree(esw);
 }
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -181,6 +181,7 @@ struct mlx5_esw_offload {
 	struct mlx5_eswitch_rep *vport_reps;
 	struct list_head peer_flows;
 	struct mutex peer_mutex;
+	struct mutex encap_tbl_lock; /* protects encap_tbl */
 	DECLARE_HASHTABLE(encap_tbl, 8);
 	struct mod_hdr_tbl mod_hdr;
 	DECLARE_HASHTABLE(termtbl_tbl, 8);