Commit e4f9abbd authored by Vlad Buslov's avatar Vlad Buslov Committed by Saeed Mahameed

net/mlx5e: Extend hairpin entry with reference counter

List of flows attached to hairpin entry is used as implicit reference
counter (hairpin entry is deallocated when list becomes free) and as a
mechanism to obtain hairpin entry that flow is attached to (through list
head). This is not safe when concurrent modification of list of flows
attached to hairpin entry is possible. Proper atomic reference counter is
required to support concurrent access.

As a preparation for extending hairpin with reference counting, extract
code that deletes hairpin entry into standalone function. In order to
remove this dependency on external locking, extend hairpin entry with
reference counter to manage its lifetime and extend flow structure with
direct pointer to hairpin entry that flow is attached to.
Signed-off-by: default avatarVlad Buslov <vladbu@mellanox.com>
Reviewed-by: default avatarJianbo Liu <jianbol@mellanox.com>
Reviewed-by: default avatarRoi Dayan <roid@mellanox.com>
Signed-off-by: default avatarSaeed Mahameed <saeedm@mellanox.com>
parent ca497fb6
...@@ -119,6 +119,7 @@ struct mlx5e_tc_flow { ...@@ -119,6 +119,7 @@ struct mlx5e_tc_flow {
struct encap_flow_item encaps[MLX5_MAX_FLOW_FWD_VPORTS]; struct encap_flow_item encaps[MLX5_MAX_FLOW_FWD_VPORTS];
struct mlx5e_tc_flow *peer_flow; struct mlx5e_tc_flow *peer_flow;
struct list_head mod_hdr; /* flows sharing the same mod hdr ID */ struct list_head mod_hdr; /* flows sharing the same mod hdr ID */
struct mlx5e_hairpin_entry *hpe; /* attached hairpin instance */
struct list_head hairpin; /* flows sharing the same hairpin */ struct list_head hairpin; /* flows sharing the same hairpin */
struct list_head peer; /* flows with peer flow */ struct list_head peer; /* flows with peer flow */
struct list_head unready; /* flows not ready to be offloaded (e.g due to missing route) */ struct list_head unready; /* flows not ready to be offloaded (e.g due to missing route) */
...@@ -167,6 +168,7 @@ struct mlx5e_hairpin_entry { ...@@ -167,6 +168,7 @@ struct mlx5e_hairpin_entry {
u16 peer_vhca_id; u16 peer_vhca_id;
u8 prio; u8 prio;
struct mlx5e_hairpin *hp; struct mlx5e_hairpin *hp;
refcount_t refcnt;
}; };
struct mod_hdr_key { struct mod_hdr_key {
...@@ -635,13 +637,31 @@ static struct mlx5e_hairpin_entry *mlx5e_hairpin_get(struct mlx5e_priv *priv, ...@@ -635,13 +637,31 @@ static struct mlx5e_hairpin_entry *mlx5e_hairpin_get(struct mlx5e_priv *priv,
hash_for_each_possible(priv->fs.tc.hairpin_tbl, hpe, hash_for_each_possible(priv->fs.tc.hairpin_tbl, hpe,
hairpin_hlist, hash_key) { hairpin_hlist, hash_key) {
if (hpe->peer_vhca_id == peer_vhca_id && hpe->prio == prio) if (hpe->peer_vhca_id == peer_vhca_id && hpe->prio == prio) {
refcount_inc(&hpe->refcnt);
return hpe; return hpe;
}
} }
return NULL; return NULL;
} }
static void mlx5e_hairpin_put(struct mlx5e_priv *priv,
struct mlx5e_hairpin_entry *hpe)
{
/* no more hairpin flows for us, release the hairpin pair */
if (!refcount_dec_and_test(&hpe->refcnt))
return;
netdev_dbg(priv->netdev, "del hairpin: peer %s\n",
dev_name(hpe->hp->pair->peer_mdev->device));
WARN_ON(!list_empty(&hpe->flows));
mlx5e_hairpin_destroy(hpe->hp);
hash_del(&hpe->hairpin_hlist);
kfree(hpe);
}
#define UNKNOWN_MATCH_PRIO 8 #define UNKNOWN_MATCH_PRIO 8
static int mlx5e_hairpin_get_prio(struct mlx5e_priv *priv, static int mlx5e_hairpin_get_prio(struct mlx5e_priv *priv,
...@@ -718,6 +738,7 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, ...@@ -718,6 +738,7 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv,
INIT_LIST_HEAD(&hpe->flows); INIT_LIST_HEAD(&hpe->flows);
hpe->peer_vhca_id = peer_id; hpe->peer_vhca_id = peer_id;
hpe->prio = match_prio; hpe->prio = match_prio;
refcount_set(&hpe->refcnt, 1);
params.log_data_size = 15; params.log_data_size = 15;
params.log_data_size = min_t(u8, params.log_data_size, params.log_data_size = min_t(u8, params.log_data_size,
...@@ -760,6 +781,7 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, ...@@ -760,6 +781,7 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv,
} else { } else {
flow->nic_attr->hairpin_tirn = hpe->hp->tirn; flow->nic_attr->hairpin_tirn = hpe->hp->tirn;
} }
flow->hpe = hpe;
list_add(&flow->hairpin, &hpe->flows); list_add(&flow->hairpin, &hpe->flows);
return 0; return 0;
...@@ -772,27 +794,13 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, ...@@ -772,27 +794,13 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv,
static void mlx5e_hairpin_flow_del(struct mlx5e_priv *priv, static void mlx5e_hairpin_flow_del(struct mlx5e_priv *priv,
struct mlx5e_tc_flow *flow) struct mlx5e_tc_flow *flow)
{ {
struct list_head *next = flow->hairpin.next;
/* flow wasn't fully initialized */ /* flow wasn't fully initialized */
if (list_empty(&flow->hairpin)) if (!flow->hpe)
return; return;
list_del(&flow->hairpin); list_del(&flow->hairpin);
mlx5e_hairpin_put(priv, flow->hpe);
/* no more hairpin flows for us, release the hairpin pair */ flow->hpe = NULL;
if (list_empty(next)) {
struct mlx5e_hairpin_entry *hpe;
hpe = list_entry(next, struct mlx5e_hairpin_entry, flows);
netdev_dbg(priv->netdev, "del hairpin: peer %s\n",
dev_name(hpe->hp->pair->peer_mdev->device));
mlx5e_hairpin_destroy(hpe->hp);
hash_del(&hpe->hairpin_hlist);
kfree(hpe);
}
} }
static int static int
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment