From e4f9abbd38e468c73710ad3678005ad05f79c818 Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Fri, 8 Jun 2018 19:26:50 +0300 Subject: [PATCH 01/15] 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: Vlad Buslov Reviewed-by: Jianbo Liu Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 4d97cc47835f..64ce762ec1e6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -119,6 +119,7 @@ struct mlx5e_tc_flow { struct encap_flow_item encaps[MLX5_MAX_FLOW_FWD_VPORTS]; struct mlx5e_tc_flow *peer_flow; 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 peer; /* flows with peer flow */ struct list_head unready; /* flows not ready to be offloaded (e.g due to missing route) */ @@ -167,6 +168,7 @@ struct mlx5e_hairpin_entry { u16 peer_vhca_id; u8 prio; struct mlx5e_hairpin *hp; + refcount_t refcnt; }; struct mod_hdr_key { @@ -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, 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 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 static int mlx5e_hairpin_get_prio(struct mlx5e_priv *priv, @@ -718,6 +738,7 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, INIT_LIST_HEAD(&hpe->flows); hpe->peer_vhca_id = peer_id; hpe->prio = match_prio; + refcount_set(&hpe->refcnt, 1); params.log_data_size = 15; params.log_data_size = min_t(u8, params.log_data_size, @@ -760,6 +781,7 @@ attach_flow: } else { flow->nic_attr->hairpin_tirn = hpe->hp->tirn; } + flow->hpe = hpe; list_add(&flow->hairpin, &hpe->flows); return 0; @@ -772,27 +794,13 @@ create_hairpin_err: static void mlx5e_hairpin_flow_del(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { - struct list_head *next = flow->hairpin.next; - /* flow wasn't fully initialized */ - if (list_empty(&flow->hairpin)) + if (!flow->hpe) return; list_del(&flow->hairpin); - - /* no more hairpin flows for us, release the hairpin pair */ - 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); - } + mlx5e_hairpin_put(priv, flow->hpe); + flow->hpe = NULL; } static int From 73edca736e581a685c98d3928b4cc029932d2d5a Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Thu, 7 Jun 2018 23:01:40 +0300 Subject: [PATCH 02/15] net/mlx5e: Protect hairpin entry flows list with spinlock To remove dependency on rtnl lock, extend hairpin entry with spinlock and use it to protect list of flows attached to hairpin entry from concurrent modifications. Signed-off-by: Vlad Buslov Reviewed-by: Jianbo Liu Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 64ce762ec1e6..0abfa9b3ec54 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -162,6 +162,8 @@ struct mlx5e_hairpin_entry { /* a node of a hash table which keeps all the hairpin entries */ struct hlist_node hairpin_hlist; + /* protects flows list */ + spinlock_t flows_lock; /* flows sharing the same hairpin */ struct list_head flows; @@ -735,6 +737,7 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, if (!hpe) return -ENOMEM; + spin_lock_init(&hpe->flows_lock); INIT_LIST_HEAD(&hpe->flows); hpe->peer_vhca_id = peer_id; hpe->prio = match_prio; @@ -782,7 +785,9 @@ attach_flow: flow->nic_attr->hairpin_tirn = hpe->hp->tirn; } flow->hpe = hpe; + spin_lock(&hpe->flows_lock); list_add(&flow->hairpin, &hpe->flows); + spin_unlock(&hpe->flows_lock); return 0; @@ -798,7 +803,10 @@ static void mlx5e_hairpin_flow_del(struct mlx5e_priv *priv, if (!flow->hpe) return; + spin_lock(&flow->hpe->flows_lock); list_del(&flow->hairpin); + spin_unlock(&flow->hpe->flows_lock); + mlx5e_hairpin_put(priv, flow->hpe); flow->hpe = NULL; } From b32accda8afd36d7de1581375467fd3642d3c73e Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Wed, 31 Jul 2019 18:19:06 +0300 Subject: [PATCH 03/15] net/mlx5e: Protect hairpin hash table with mutex To remove dependency on rtnl lock, protect hairpin hash table from concurrent modifications with new "hairpin_tbl_lock" mutex. Signed-off-by: Vlad Buslov Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en/fs.h | 1 + .../net/ethernet/mellanox/mlx5/core/en_tc.c | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h index 4518ce19112e..100506a3dd58 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h @@ -17,6 +17,7 @@ struct mlx5e_tc_table { struct rhashtable ht; DECLARE_HASHTABLE(mod_hdr_tbl, 8); + struct mutex hairpin_tbl_lock; /* protects hairpin_tbl */ DECLARE_HASHTABLE(hairpin_tbl, 8); struct notifier_block netdevice_nb; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 0abfa9b3ec54..a7acb7fcbf5a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -652,15 +652,16 @@ 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)) + if (!refcount_dec_and_mutex_lock(&hpe->refcnt, &priv->fs.tc.hairpin_tbl_lock)) return; + hash_del(&hpe->hairpin_hlist); + mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); 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); } @@ -729,13 +730,17 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, extack); if (err) return err; + + mutex_lock(&priv->fs.tc.hairpin_tbl_lock); hpe = mlx5e_hairpin_get(priv, peer_id, match_prio); if (hpe) goto attach_flow; hpe = kzalloc(sizeof(*hpe), GFP_KERNEL); - if (!hpe) - return -ENOMEM; + if (!hpe) { + err = -ENOMEM; + goto create_hairpin_err; + } spin_lock_init(&hpe->flows_lock); INIT_LIST_HEAD(&hpe->flows); @@ -784,6 +789,8 @@ attach_flow: } else { flow->nic_attr->hairpin_tirn = hpe->hp->tirn; } + mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); + flow->hpe = hpe; spin_lock(&hpe->flows_lock); list_add(&flow->hairpin, &hpe->flows); @@ -792,6 +799,7 @@ attach_flow: return 0; create_hairpin_err: + mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); kfree(hpe); return err; } @@ -3768,10 +3776,12 @@ static void mlx5e_tc_hairpin_update_dead_peer(struct mlx5e_priv *priv, peer_vhca_id = MLX5_CAP_GEN(peer_mdev, vhca_id); + mutex_lock(&priv->fs.tc.hairpin_tbl_lock); hash_for_each(priv->fs.tc.hairpin_tbl, bkt, hpe, hairpin_hlist) { if (hpe->peer_vhca_id == peer_vhca_id) hpe->hp->pair->peer_gone = true; } + mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); } static int mlx5e_tc_netdev_event(struct notifier_block *this, @@ -3808,6 +3818,7 @@ int mlx5e_tc_nic_init(struct mlx5e_priv *priv) mutex_init(&tc->t_lock); hash_init(tc->mod_hdr_tbl); + mutex_init(&tc->hairpin_tbl_lock); hash_init(tc->hairpin_tbl); err = rhashtable_init(&tc->ht, &tc_ht_params); @@ -3839,6 +3850,8 @@ void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv) if (tc->netdevice_nb.notifier_call) unregister_netdevice_notifier(&tc->netdevice_nb); + mutex_destroy(&tc->hairpin_tbl_lock); + rhashtable_destroy(&tc->ht); if (!IS_ERR_OR_NULL(tc->t)) { From db76ca2424fe28923aaec5e2187e886b025a914c Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Thu, 1 Aug 2019 16:54:54 +0300 Subject: [PATCH 04/15] net/mlx5e: Allow concurrent creation of hairpin entries Hairpin entries creation is fully synchronized by hairpin_tbl_lock. In order to allow concurrent initialization of mlx5e_hairpin structure instances and provisioning of hairpin entries to hardware, extend mlx5e_hairpin_entry with 'res_ready' completion. Move call to mlx5e_hairpin_create() out of hairpin_tbl_lock critical section. Modify code that attaches new flows to existing hpe to wait for 'res_ready' completion before using the hpe. Insert hpe to hairpin table before provisioning it to hardware and modify all users of hairpin table to verify that hpe was fully initialized by checking hpe->hp pointer (and to wait for 'res_ready' completion, if necessary). Modify dead peer update event handling function to save hpe's to temporary list with their reference counter incremented. Wait for completion of hpe's in temporary list and update their 'peer_gone' flag outside of hairpin_tbl_lock critical section. Signed-off-by: Vlad Buslov Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 67 +++++++++++++------ 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index a7acb7fcbf5a..b6a91e3054c0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -166,11 +167,16 @@ struct mlx5e_hairpin_entry { spinlock_t flows_lock; /* flows sharing the same hairpin */ struct list_head flows; + /* hpe's that were not fully initialized when dead peer update event + * function traversed them. + */ + struct list_head dead_peer_wait_list; u16 peer_vhca_id; u8 prio; struct mlx5e_hairpin *hp; refcount_t refcnt; + struct completion res_ready; }; struct mod_hdr_key { @@ -657,11 +663,14 @@ static void mlx5e_hairpin_put(struct mlx5e_priv *priv, hash_del(&hpe->hairpin_hlist); mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); - netdev_dbg(priv->netdev, "del hairpin: peer %s\n", - dev_name(hpe->hp->pair->peer_mdev->device)); + if (!IS_ERR_OR_NULL(hpe->hp)) { + netdev_dbg(priv->netdev, "del hairpin: peer %s\n", + dev_name(hpe->hp->pair->peer_mdev->device)); + + mlx5e_hairpin_destroy(hpe->hp); + } WARN_ON(!list_empty(&hpe->flows)); - mlx5e_hairpin_destroy(hpe->hp); kfree(hpe); } @@ -733,20 +742,34 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, mutex_lock(&priv->fs.tc.hairpin_tbl_lock); hpe = mlx5e_hairpin_get(priv, peer_id, match_prio); - if (hpe) + if (hpe) { + mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); + wait_for_completion(&hpe->res_ready); + + if (IS_ERR(hpe->hp)) { + err = -EREMOTEIO; + goto out_err; + } goto attach_flow; + } hpe = kzalloc(sizeof(*hpe), GFP_KERNEL); if (!hpe) { - err = -ENOMEM; - goto create_hairpin_err; + mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); + return -ENOMEM; } spin_lock_init(&hpe->flows_lock); INIT_LIST_HEAD(&hpe->flows); + INIT_LIST_HEAD(&hpe->dead_peer_wait_list); hpe->peer_vhca_id = peer_id; hpe->prio = match_prio; refcount_set(&hpe->refcnt, 1); + init_completion(&hpe->res_ready); + + hash_add(priv->fs.tc.hairpin_tbl, &hpe->hairpin_hlist, + hash_hairpin_info(peer_id, match_prio)); + mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); params.log_data_size = 15; params.log_data_size = min_t(u8, params.log_data_size, @@ -768,9 +791,11 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, params.num_channels = link_speed64; hp = mlx5e_hairpin_create(priv, ¶ms, peer_ifindex); + hpe->hp = hp; + complete_all(&hpe->res_ready); if (IS_ERR(hp)) { err = PTR_ERR(hp); - goto create_hairpin_err; + goto out_err; } netdev_dbg(priv->netdev, "add hairpin: tirn %x rqn %x peer %s sqn %x prio %d (log) data %d packets %d\n", @@ -778,10 +803,6 @@ static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv, dev_name(hp->pair->peer_mdev->device), hp->pair->sqn[0], match_prio, params.log_data_size, params.log_num_packets); - hpe->hp = hp; - hash_add(priv->fs.tc.hairpin_tbl, &hpe->hairpin_hlist, - hash_hairpin_info(peer_id, match_prio)); - attach_flow: if (hpe->hp->num_channels > 1) { flow_flag_set(flow, HAIRPIN_RSS); @@ -789,7 +810,6 @@ attach_flow: } else { flow->nic_attr->hairpin_tirn = hpe->hp->tirn; } - mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); flow->hpe = hpe; spin_lock(&hpe->flows_lock); @@ -798,9 +818,8 @@ attach_flow: return 0; -create_hairpin_err: - mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); - kfree(hpe); +out_err: + mlx5e_hairpin_put(priv, hpe); return err; } @@ -3767,7 +3786,8 @@ static void mlx5e_tc_hairpin_update_dead_peer(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv) { struct mlx5_core_dev *peer_mdev = peer_priv->mdev; - struct mlx5e_hairpin_entry *hpe; + struct mlx5e_hairpin_entry *hpe, *tmp; + LIST_HEAD(init_wait_list); u16 peer_vhca_id; int bkt; @@ -3777,11 +3797,18 @@ static void mlx5e_tc_hairpin_update_dead_peer(struct mlx5e_priv *priv, peer_vhca_id = MLX5_CAP_GEN(peer_mdev, vhca_id); mutex_lock(&priv->fs.tc.hairpin_tbl_lock); - hash_for_each(priv->fs.tc.hairpin_tbl, bkt, hpe, hairpin_hlist) { - if (hpe->peer_vhca_id == peer_vhca_id) - hpe->hp->pair->peer_gone = true; - } + hash_for_each(priv->fs.tc.hairpin_tbl, bkt, hpe, hairpin_hlist) + if (refcount_inc_not_zero(&hpe->refcnt)) + list_add(&hpe->dead_peer_wait_list, &init_wait_list); mutex_unlock(&priv->fs.tc.hairpin_tbl_lock); + + list_for_each_entry_safe(hpe, tmp, &init_wait_list, dead_peer_wait_list) { + wait_for_completion(&hpe->res_ready); + if (!IS_ERR_OR_NULL(hpe->hp) && hpe->peer_vhca_id == peer_vhca_id) + hpe->hp->pair->peer_gone = true; + + mlx5e_hairpin_put(priv, hpe); + } } static int mlx5e_tc_netdev_event(struct notifier_block *this, From dd58edc328cec1a0d837f3f2f41e9955ec623e3e Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Fri, 1 Jun 2018 21:47:43 +0300 Subject: [PATCH 05/15] net/mlx5e: Extend mod header entry with reference counter List of flows attached to mod header entry is used as implicit reference counter (mod header entry is deallocated when list becomes free) and as a mechanism to obtain mod header entry that flow is attached to (through list head). This is not safe when concurrent modification of list of flows attached to mod header entry is possible. Proper atomic reference counter is required to support concurrent access. As a preparation for extending mod header with reference counting, extract code that lookups and deletes mod header entry into standalone put/get helpers. In order to remove this dependency on external locking, extend mod header entry with reference counter to manage its lifetime and extend flow structure with direct pointer to mod header entry that flow is attached to. To remove code duplication between legacy and switchdev mode implementations that both support mod_hdr functionality, store mod_hdr table in dedicated structure used by both fdb and kernel namespaces. New table structure is extended with table lock by one of the following patches in this series. Implement helper function to get correct mod_hdr table depending on flow namespace. Signed-off-by: Vlad Buslov Reviewed-by: Jianbo Liu Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en/fs.h | 2 +- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 94 +++++++++++-------- .../net/ethernet/mellanox/mlx5/core/eswitch.c | 2 +- .../net/ethernet/mellanox/mlx5/core/eswitch.h | 2 +- include/linux/mlx5/fs.h | 4 + 5 files changed, 61 insertions(+), 43 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h index 100506a3dd58..ca2161b42c7f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h @@ -16,7 +16,7 @@ struct mlx5e_tc_table { struct rhashtable ht; - DECLARE_HASHTABLE(mod_hdr_tbl, 8); + struct mod_hdr_tbl mod_hdr; struct mutex hairpin_tbl_lock; /* protects hairpin_tbl */ DECLARE_HASHTABLE(hairpin_tbl, 8); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index b6a91e3054c0..fe1b04aa910a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -119,6 +119,7 @@ struct mlx5e_tc_flow { */ struct encap_flow_item encaps[MLX5_MAX_FLOW_FWD_VPORTS]; struct mlx5e_tc_flow *peer_flow; + struct mlx5e_mod_hdr_entry *mh; /* attached mod header instance */ 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 */ @@ -194,6 +195,8 @@ struct mlx5e_mod_hdr_entry { struct mod_hdr_key key; u32 mod_hdr_id; + + refcount_t refcnt; }; #define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto) @@ -284,14 +287,51 @@ static inline int cmp_mod_hdr_info(struct mod_hdr_key *a, return memcmp(a->actions, b->actions, a->num_actions * MLX5_MH_ACT_SZ); } +static struct mod_hdr_tbl * +get_mod_hdr_table(struct mlx5e_priv *priv, int namespace) +{ + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + + return namespace == MLX5_FLOW_NAMESPACE_FDB ? &esw->offloads.mod_hdr : + &priv->fs.tc.mod_hdr; +} + +static struct mlx5e_mod_hdr_entry * +mlx5e_mod_hdr_get(struct mod_hdr_tbl *tbl, struct mod_hdr_key *key, u32 hash_key) +{ + struct mlx5e_mod_hdr_entry *mh, *found = NULL; + + hash_for_each_possible(tbl->hlist, mh, mod_hdr_hlist, hash_key) { + if (!cmp_mod_hdr_info(&mh->key, key)) { + refcount_inc(&mh->refcnt); + found = mh; + break; + } + } + + return found; +} + +static void mlx5e_mod_hdr_put(struct mlx5e_priv *priv, + struct mlx5e_mod_hdr_entry *mh) +{ + if (!refcount_dec_and_test(&mh->refcnt)) + return; + + WARN_ON(!list_empty(&mh->flows)); + mlx5_modify_header_dealloc(priv->mdev, mh->mod_hdr_id); + hash_del(&mh->mod_hdr_hlist); + kfree(mh); +} + static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, struct mlx5e_tc_flow_parse_attr *parse_attr) { - struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + bool is_eswitch_flow = mlx5e_is_eswitch_flow(flow); int num_actions, actions_size, namespace, err; - bool found = false, is_eswitch_flow; struct mlx5e_mod_hdr_entry *mh; + struct mod_hdr_tbl *tbl; struct mod_hdr_key key; u32 hash_key; @@ -303,28 +343,12 @@ static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, hash_key = hash_mod_hdr_info(&key); - is_eswitch_flow = mlx5e_is_eswitch_flow(flow); - if (is_eswitch_flow) { - namespace = MLX5_FLOW_NAMESPACE_FDB; - hash_for_each_possible(esw->offloads.mod_hdr_tbl, mh, - mod_hdr_hlist, hash_key) { - if (!cmp_mod_hdr_info(&mh->key, &key)) { - found = true; - break; - } - } - } else { - namespace = MLX5_FLOW_NAMESPACE_KERNEL; - hash_for_each_possible(priv->fs.tc.mod_hdr_tbl, mh, - mod_hdr_hlist, hash_key) { - if (!cmp_mod_hdr_info(&mh->key, &key)) { - found = true; - break; - } - } - } + namespace = is_eswitch_flow ? + MLX5_FLOW_NAMESPACE_FDB : MLX5_FLOW_NAMESPACE_KERNEL; + tbl = get_mod_hdr_table(priv, namespace); - if (found) + mh = mlx5e_mod_hdr_get(tbl, &key, hash_key); + if (mh) goto attach_flow; mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL); @@ -335,6 +359,7 @@ static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, memcpy(mh->key.actions, key.actions, actions_size); mh->key.num_actions = num_actions; INIT_LIST_HEAD(&mh->flows); + refcount_set(&mh->refcnt, 1); err = mlx5_modify_header_alloc(priv->mdev, namespace, mh->key.num_actions, @@ -343,12 +368,10 @@ static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, if (err) goto out_err; - if (is_eswitch_flow) - hash_add(esw->offloads.mod_hdr_tbl, &mh->mod_hdr_hlist, hash_key); - else - hash_add(priv->fs.tc.mod_hdr_tbl, &mh->mod_hdr_hlist, hash_key); + hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key); attach_flow: + flow->mh = mh; list_add(&flow->mod_hdr, &mh->flows); if (is_eswitch_flow) flow->esw_attr->mod_hdr_id = mh->mod_hdr_id; @@ -365,23 +388,14 @@ out_err: static void mlx5e_detach_mod_hdr(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { - struct list_head *next = flow->mod_hdr.next; - /* flow wasn't fully initialized */ - if (list_empty(&flow->mod_hdr)) + if (!flow->mh) return; list_del(&flow->mod_hdr); - if (list_empty(next)) { - struct mlx5e_mod_hdr_entry *mh; - - mh = list_entry(next, struct mlx5e_mod_hdr_entry, flows); - - mlx5_modify_header_dealloc(priv->mdev, mh->mod_hdr_id); - hash_del(&mh->mod_hdr_hlist); - kfree(mh); - } + mlx5e_mod_hdr_put(priv, flow->mh); + flow->mh = NULL; } static @@ -3844,7 +3858,7 @@ int mlx5e_tc_nic_init(struct mlx5e_priv *priv) int err; mutex_init(&tc->t_lock); - hash_init(tc->mod_hdr_tbl); + hash_init(tc->mod_hdr.hlist); mutex_init(&tc->hairpin_tbl_lock); hash_init(tc->hairpin_tbl); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 5fbebee7254d..5ce3c81e3083 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -2000,7 +2000,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) goto abort; hash_init(esw->offloads.encap_tbl); - hash_init(esw->offloads.mod_hdr_tbl); + hash_init(esw->offloads.mod_hdr.hlist); atomic64_set(&esw->offloads.num_flows, 0); mutex_init(&esw->state_lock); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 804912e38dee..fd63ba4ed0da 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -182,7 +182,7 @@ struct mlx5_esw_offload { struct list_head peer_flows; struct mutex peer_mutex; DECLARE_HASHTABLE(encap_tbl, 8); - DECLARE_HASHTABLE(mod_hdr_tbl, 8); + struct mod_hdr_tbl mod_hdr; DECLARE_HASHTABLE(termtbl_tbl, 8); struct mutex termtbl_mutex; /* protects termtbl hash */ const struct mlx5_eswitch_rep_ops *rep_ops[NUM_REP_TYPES]; diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index f049af3f3cd8..96650a33aa91 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -126,6 +126,10 @@ struct mlx5_flow_destination { }; }; +struct mod_hdr_tbl { + DECLARE_HASHTABLE(hlist, 8); +}; + struct mlx5_flow_namespace * mlx5_get_fdb_sub_ns(struct mlx5_core_dev *dev, int n); struct mlx5_flow_namespace * From 83a52f0d525587de65b2bc979a12b4dfad9ea82a Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Fri, 8 Jun 2018 22:10:09 +0300 Subject: [PATCH 06/15] net/mlx5e: Protect mod header entry flows list with spinlock To remove dependency on rtnl lock, extend mod header entry with spinlock and use it to protect list of flows attached to mod header entry from concurrent modifications. Signed-off-by: Vlad Buslov Reviewed-by: Jianbo Liu Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index fe1b04aa910a..09d5cc700297 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -189,6 +189,8 @@ struct mlx5e_mod_hdr_entry { /* a node of a hash table which keeps all the mod_hdr entries */ struct hlist_node mod_hdr_hlist; + /* protects flows list */ + spinlock_t flows_lock; /* flows sharing the same mod_hdr entry */ struct list_head flows; @@ -358,6 +360,7 @@ static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, mh->key.actions = (void *)mh + sizeof(*mh); memcpy(mh->key.actions, key.actions, actions_size); mh->key.num_actions = num_actions; + spin_lock_init(&mh->flows_lock); INIT_LIST_HEAD(&mh->flows); refcount_set(&mh->refcnt, 1); @@ -372,7 +375,9 @@ static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, attach_flow: flow->mh = mh; + spin_lock(&mh->flows_lock); list_add(&flow->mod_hdr, &mh->flows); + spin_unlock(&mh->flows_lock); if (is_eswitch_flow) flow->esw_attr->mod_hdr_id = mh->mod_hdr_id; else @@ -392,7 +397,9 @@ static void mlx5e_detach_mod_hdr(struct mlx5e_priv *priv, if (!flow->mh) return; + spin_lock(&flow->mh->flows_lock); list_del(&flow->mod_hdr); + spin_unlock(&flow->mh->flows_lock); mlx5e_mod_hdr_put(priv, flow->mh); flow->mh = NULL; From d2faae25c3050a87c8ff965a7939e999e3154b62 Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Fri, 9 Aug 2019 13:20:48 +0300 Subject: [PATCH 07/15] net/mlx5e: Protect mod_hdr hash table with mutex To remove dependency on rtnl lock, protect mod_hdr hash table from concurrent modifications with new mutex. Implement helper function to get flow namespace to prevent code duplication. Signed-off-by: Vlad Buslov Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 35 +++++++++++++------ .../net/ethernet/mellanox/mlx5/core/eswitch.c | 2 ++ include/linux/mlx5/fs.h | 1 + 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 09d5cc700297..0600b7878600 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -315,22 +315,31 @@ mlx5e_mod_hdr_get(struct mod_hdr_tbl *tbl, struct mod_hdr_key *key, u32 hash_key } static void mlx5e_mod_hdr_put(struct mlx5e_priv *priv, - struct mlx5e_mod_hdr_entry *mh) + struct mlx5e_mod_hdr_entry *mh, + int namespace) { - if (!refcount_dec_and_test(&mh->refcnt)) + struct mod_hdr_tbl *tbl = get_mod_hdr_table(priv, namespace); + + if (!refcount_dec_and_mutex_lock(&mh->refcnt, &tbl->lock)) return; + hash_del(&mh->mod_hdr_hlist); + mutex_unlock(&tbl->lock); WARN_ON(!list_empty(&mh->flows)); mlx5_modify_header_dealloc(priv->mdev, mh->mod_hdr_id); - hash_del(&mh->mod_hdr_hlist); + kfree(mh); } +static int get_flow_name_space(struct mlx5e_tc_flow *flow) +{ + return mlx5e_is_eswitch_flow(flow) ? + MLX5_FLOW_NAMESPACE_FDB : MLX5_FLOW_NAMESPACE_KERNEL; +} static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, struct mlx5e_tc_flow_parse_attr *parse_attr) { - bool is_eswitch_flow = mlx5e_is_eswitch_flow(flow); int num_actions, actions_size, namespace, err; struct mlx5e_mod_hdr_entry *mh; struct mod_hdr_tbl *tbl; @@ -345,17 +354,19 @@ static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, hash_key = hash_mod_hdr_info(&key); - namespace = is_eswitch_flow ? - MLX5_FLOW_NAMESPACE_FDB : MLX5_FLOW_NAMESPACE_KERNEL; + namespace = get_flow_name_space(flow); tbl = get_mod_hdr_table(priv, namespace); + mutex_lock(&tbl->lock); mh = mlx5e_mod_hdr_get(tbl, &key, hash_key); if (mh) goto attach_flow; mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL); - if (!mh) - return -ENOMEM; + if (!mh) { + err = -ENOMEM; + goto out_err; + } mh->key.actions = (void *)mh + sizeof(*mh); memcpy(mh->key.actions, key.actions, actions_size); @@ -374,11 +385,12 @@ static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key); attach_flow: + mutex_unlock(&tbl->lock); flow->mh = mh; spin_lock(&mh->flows_lock); list_add(&flow->mod_hdr, &mh->flows); spin_unlock(&mh->flows_lock); - if (is_eswitch_flow) + if (mlx5e_is_eswitch_flow(flow)) flow->esw_attr->mod_hdr_id = mh->mod_hdr_id; else flow->nic_attr->mod_hdr_id = mh->mod_hdr_id; @@ -386,6 +398,7 @@ attach_flow: return 0; out_err: + mutex_unlock(&tbl->lock); kfree(mh); return err; } @@ -401,7 +414,7 @@ static void mlx5e_detach_mod_hdr(struct mlx5e_priv *priv, list_del(&flow->mod_hdr); spin_unlock(&flow->mh->flows_lock); - mlx5e_mod_hdr_put(priv, flow->mh); + mlx5e_mod_hdr_put(priv, flow->mh, get_flow_name_space(flow)); flow->mh = NULL; } @@ -3865,6 +3878,7 @@ int mlx5e_tc_nic_init(struct mlx5e_priv *priv) int err; mutex_init(&tc->t_lock); + mutex_init(&tc->mod_hdr.lock); hash_init(tc->mod_hdr.hlist); mutex_init(&tc->hairpin_tbl_lock); hash_init(tc->hairpin_tbl); @@ -3898,6 +3912,7 @@ void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv) if (tc->netdevice_nb.notifier_call) unregister_netdevice_notifier(&tc->netdevice_nb); + mutex_destroy(&tc->mod_hdr.lock); mutex_destroy(&tc->hairpin_tbl_lock); rhashtable_destroy(&tc->ht); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 5ce3c81e3083..2d734ecae719 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -2000,6 +2000,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) goto abort; hash_init(esw->offloads.encap_tbl); + mutex_init(&esw->offloads.mod_hdr.lock); hash_init(esw->offloads.mod_hdr.hlist); atomic64_set(&esw->offloads.num_flows, 0); mutex_init(&esw->state_lock); @@ -2037,6 +2038,7 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) esw->dev->priv.eswitch = NULL; destroy_workqueue(esw->work_queue); esw_offloads_cleanup_reps(esw); + mutex_destroy(&esw->offloads.mod_hdr.lock); kfree(esw->vports); kfree(esw); } diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index 96650a33aa91..1cb1045ce313 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -127,6 +127,7 @@ struct mlx5_flow_destination { }; struct mod_hdr_tbl { + struct mutex lock; /* protects hlist */ DECLARE_HASHTABLE(hlist, 8); }; From a734d007173119fe8e7bde1689ee4123c529e238 Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Thu, 8 Aug 2019 16:53:15 +0300 Subject: [PATCH 08/15] net/mlx5e: Allow concurrent creation of mod_hdr entries Mod_hdr entries creation is fully synchronized by mod_hdr_tbl->lock. In order to allow concurrent allocation of hardware resources used to offload header rewrite, extend mlx5e_mod_hdr_entry with 'res_ready' completion. Move call to mlx5_modify_header_alloc() out of mod_hdr_tbl->lock critical section. Modify code that attaches new flows to existing mh to wait for 'res_ready' completion before using the entry. Insert mh to mod_hdr table before provisioning it to hardware and modify all users of mod_hdr table to verify that mh was fully initialized by checking completion result for negative value (and to wait for 'res_ready' completion, if necessary). Signed-off-by: Vlad Buslov Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 0600b7878600..fcaf9ab9e373 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -199,6 +199,8 @@ struct mlx5e_mod_hdr_entry { u32 mod_hdr_id; refcount_t refcnt; + struct completion res_ready; + int compl_result; }; #define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto) @@ -326,7 +328,8 @@ static void mlx5e_mod_hdr_put(struct mlx5e_priv *priv, mutex_unlock(&tbl->lock); WARN_ON(!list_empty(&mh->flows)); - mlx5_modify_header_dealloc(priv->mdev, mh->mod_hdr_id); + if (mh->compl_result > 0) + mlx5_modify_header_dealloc(priv->mdev, mh->mod_hdr_id); kfree(mh); } @@ -359,13 +362,21 @@ static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, mutex_lock(&tbl->lock); mh = mlx5e_mod_hdr_get(tbl, &key, hash_key); - if (mh) + if (mh) { + mutex_unlock(&tbl->lock); + wait_for_completion(&mh->res_ready); + + if (mh->compl_result < 0) { + err = -EREMOTEIO; + goto attach_header_err; + } goto attach_flow; + } mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL); if (!mh) { - err = -ENOMEM; - goto out_err; + mutex_unlock(&tbl->lock); + return -ENOMEM; } mh->key.actions = (void *)mh + sizeof(*mh); @@ -374,18 +385,23 @@ static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv, spin_lock_init(&mh->flows_lock); INIT_LIST_HEAD(&mh->flows); refcount_set(&mh->refcnt, 1); + init_completion(&mh->res_ready); + + hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key); + mutex_unlock(&tbl->lock); err = mlx5_modify_header_alloc(priv->mdev, namespace, mh->key.num_actions, mh->key.actions, &mh->mod_hdr_id); - if (err) - goto out_err; - - hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key); + if (err) { + mh->compl_result = err; + goto alloc_header_err; + } + mh->compl_result = 1; + complete_all(&mh->res_ready); attach_flow: - mutex_unlock(&tbl->lock); flow->mh = mh; spin_lock(&mh->flows_lock); list_add(&flow->mod_hdr, &mh->flows); @@ -397,9 +413,10 @@ attach_flow: return 0; -out_err: - mutex_unlock(&tbl->lock); - kfree(mh); +alloc_header_err: + complete_all(&mh->res_ready); +attach_header_err: + mlx5e_mod_hdr_put(priv, mh, namespace); return err; } From 948993f2beebbdc1c9d926cfdad9827cf6bb67c0 Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Sun, 3 Jun 2018 20:31:47 +0300 Subject: [PATCH 09/15] net/mlx5e: Extend encap entry with reference counter List of flows attached to encap entry is used as implicit reference counter (encap entry is deallocated when list becomes free) and as a mechanism to obtain encap entry that flow is attached to (through list head). This is not safe when concurrent modification of list of flows attached to encap entry is possible. Proper atomic reference counter is required to support concurrent access. As a preparation for extending encap with reference counting, extract code that lookups and deletes encap entry into standalone put/get helpers. In order to remove this dependency on external locking, extend encap entry with reference counter to manage its lifetime and extend flow structure with direct pointer to encap entry that flow is attached to. Signed-off-by: Vlad Buslov Reviewed-by: Jianbo Liu Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_rep.c | 5 ++ .../net/ethernet/mellanox/mlx5/core/en_rep.h | 1 + .../net/ethernet/mellanox/mlx5/core/en_tc.c | 84 ++++++++++++------- .../net/ethernet/mellanox/mlx5/core/en_tc.h | 2 + 4 files changed, 64 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index b7f113e996e5..cd957ff4e207 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -613,12 +613,17 @@ static void mlx5e_rep_neigh_update(struct work_struct *work) neigh_connected = (nud_state & NUD_VALID) && !dead; list_for_each_entry(e, &nhe->encap_list, encap_list) { + if (!mlx5e_encap_take(e)) + continue; + encap_connected = !!(e->flags & MLX5_ENCAP_ENTRY_VALID); priv = netdev_priv(e->out_dev); if (encap_connected != neigh_connected || !ether_addr_equal(e->h_dest, ha)) mlx5e_rep_update_flows(priv, e, neigh_connected, ha); + + mlx5e_encap_put(priv, e); } mlx5e_rep_neigh_entry_release(nhe); rtnl_unlock(); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index 43eeebe9c8d2..2e970d0729be 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -164,6 +164,7 @@ struct mlx5e_encap_entry { u8 flags; char *encap_header; int encap_size; + refcount_t refcnt; }; struct mlx5e_rep_sq { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index fcaf9ab9e373..4e378200a9d2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -103,6 +103,7 @@ enum { * container_of(helper item, containing struct type, helper field[index]) */ struct encap_flow_item { + struct mlx5e_encap_entry *e; /* attached encap instance */ struct list_head list; int index; }; @@ -1433,8 +1434,11 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe) list_for_each_entry(e, &nhe->encap_list, encap_list) { struct encap_flow_item *efi, *tmp; - if (!(e->flags & MLX5_ENCAP_ENTRY_VALID)) + + if (!(e->flags & MLX5_ENCAP_ENTRY_VALID) || + !mlx5e_encap_take(e)) continue; + list_for_each_entry_safe(efi, tmp, &e->flows, list) { flow = container_of(efi, struct mlx5e_tc_flow, encaps[efi->index]); @@ -1453,6 +1457,8 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe) mlx5e_flow_put(netdev_priv(e->out_dev), flow); } + + mlx5e_encap_put(netdev_priv(e->out_dev), e); if (neigh_used) break; } @@ -1472,29 +1478,33 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe) } } +void mlx5e_encap_put(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); +} + static void mlx5e_detach_encap(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, int out_index) { - struct list_head *next = flow->encaps[out_index].list.next; - /* flow wasn't fully initialized */ - if (list_empty(&flow->encaps[out_index].list)) + if (!flow->encaps[out_index].e) return; list_del(&flow->encaps[out_index].list); - if (list_empty(next)) { - struct mlx5e_encap_entry *e; - e = list_entry(next, struct mlx5e_encap_entry, 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); - } + mlx5e_encap_put(priv, flow->encaps[out_index].e); + flow->encaps[out_index].e = NULL; } static void __mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow) @@ -2817,6 +2827,31 @@ static bool is_merged_eswitch_dev(struct mlx5e_priv *priv, +bool mlx5e_encap_take(struct mlx5e_encap_entry *e) +{ + return refcount_inc_not_zero(&e->refcnt); +} + +static struct mlx5e_encap_entry * +mlx5e_encap_get(struct mlx5e_priv *priv, struct encap_key *key, + uintptr_t hash_key) +{ + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5e_encap_entry *e; + struct encap_key e_key; + + hash_for_each_possible_rcu(esw->offloads.encap_tbl, e, + encap_hlist, hash_key) { + e_key.ip_tun_key = &e->tun_info->key; + e_key.tc_tunnel = e->tunnel; + if (!cmp_encap_info(&e_key, key) && + mlx5e_encap_take(e)) + return e; + } + + return NULL; +} + static int mlx5e_attach_encap(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow, struct net_device *mirred_dev, @@ -2829,11 +2864,10 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, struct mlx5_esw_flow_attr *attr = flow->esw_attr; struct mlx5e_tc_flow_parse_attr *parse_attr; const struct ip_tunnel_info *tun_info; - struct encap_key key, e_key; + struct encap_key key; struct mlx5e_encap_entry *e; unsigned short family; uintptr_t hash_key; - bool found = false; int err = 0; parse_attr = attr->parse_attr; @@ -2848,24 +2882,17 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, hash_key = hash_encap_info(&key); - hash_for_each_possible_rcu(esw->offloads.encap_tbl, e, - encap_hlist, hash_key) { - e_key.ip_tun_key = &e->tun_info->key; - e_key.tc_tunnel = e->tunnel; - if (!cmp_encap_info(&e_key, &key)) { - found = true; - break; - } - } + e = mlx5e_encap_get(priv, &key, hash_key); /* must verify if encap is valid or not */ - if (found) + if (e) goto attach_flow; e = kzalloc(sizeof(*e), GFP_KERNEL); if (!e) return -ENOMEM; + refcount_set(&e->refcnt, 1); e->tun_info = tun_info; err = mlx5e_tc_tun_init_encap_attr(mirred_dev, priv, e, extack); if (err) @@ -2884,6 +2911,7 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key); attach_flow: + flow->encaps[out_index].e = e; list_add(&flow->encaps[out_index].list, &e->flows); flow->encaps[out_index].index = out_index; *encap_dev = e->out_dev; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 20f045e96c92..ea2072e2fe84 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -75,6 +75,8 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e); void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e); +bool mlx5e_encap_take(struct mlx5e_encap_entry *e); +void mlx5e_encap_put(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e); struct mlx5e_neigh_hash_entry; void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe); From 61086f391044fd587af9d70a9b8f6f800dd474ba Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Fri, 2 Aug 2019 22:21:56 +0300 Subject: [PATCH 10/15] net/mlx5e: Protect encap hash table with mutex 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 Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_tc.c | 43 ++++++++++++++----- .../net/ethernet/mellanox/mlx5/core/eswitch.c | 2 + .../net/ethernet/mellanox/mlx5/core/eswitch.h | 1 + 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 4e378200a9d2..c13db9bc1f9b 100644 --- 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(struct mlx5e_neigh_hash_entry *nhe) } } -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) @@ -2882,6 +2900,7 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, 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 */ @@ -2889,8 +2908,10 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, 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; @@ -2922,10 +2943,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; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 2d734ecae719..f0692407f617 100644 --- 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_dev *dev) 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_eswitch *esw) 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); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index fd63ba4ed0da..86db0e9776da 100644 --- 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); From d589e785baf5e48ee80a4fbfed96661a4c7c8c8d Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Thu, 8 Aug 2019 17:01:33 +0300 Subject: [PATCH 11/15] net/mlx5e: Allow concurrent creation of encap entries Encap entries creation is fully synchronized by encap_tbl_lock. In order to allow concurrent allocation of hardware resources used to offload encapsulation, extend mlx5e_encap_entry with 'res_ready' completion. Move call to mlx5e_tc_tun_create_header_ipv{4|6}() out of encap_tbl_lock critical section. Modify code that attaches new flows to existing encap to wait for 'res_ready' completion before using the entry. Insert encap entry to table before provisioning it to hardware and modify all users of the encap table to verify that encap was fully initialized by checking completion result for non-zero value (and to wait for 'res_ready' completion, if necessary). Signed-off-by: Vlad Buslov Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_rep.h | 2 ++ .../net/ethernet/mellanox/mlx5/core/en_tc.c | 33 +++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index 2e970d0729be..8ac96727cad8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -165,6 +165,8 @@ struct mlx5e_encap_entry { char *encap_header; int encap_size; refcount_t refcnt; + struct completion res_ready; + int compl_result; }; struct mlx5e_rep_sq { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index c13db9bc1f9b..5be3da621499 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -2904,8 +2904,18 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, e = mlx5e_encap_get(priv, &key, hash_key); /* must verify if encap is valid or not */ - if (e) + if (e) { + mutex_unlock(&esw->offloads.encap_tbl_lock); + wait_for_completion(&e->res_ready); + + /* Protect against concurrent neigh update. */ + mutex_lock(&esw->offloads.encap_tbl_lock); + if (e->compl_result) { + err = -EREMOTEIO; + goto out_err; + } goto attach_flow; + } e = kzalloc(sizeof(*e), GFP_KERNEL); if (!e) { @@ -2914,22 +2924,32 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, } refcount_set(&e->refcnt, 1); + init_completion(&e->res_ready); + e->tun_info = tun_info; err = mlx5e_tc_tun_init_encap_attr(mirred_dev, priv, e, extack); - if (err) + if (err) { + kfree(e); + e = NULL; goto out_err; + } INIT_LIST_HEAD(&e->flows); + hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key); + mutex_unlock(&esw->offloads.encap_tbl_lock); if (family == AF_INET) err = mlx5e_tc_tun_create_header_ipv4(priv, mirred_dev, e); else if (family == AF_INET6) err = mlx5e_tc_tun_create_header_ipv6(priv, mirred_dev, e); - if (err) + /* Protect against concurrent neigh update. */ + mutex_lock(&esw->offloads.encap_tbl_lock); + complete_all(&e->res_ready); + if (err) { + e->compl_result = err; goto out_err; - - hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key); + } attach_flow: flow->encaps[out_index].e = e; @@ -2949,7 +2969,8 @@ attach_flow: out_err: mutex_unlock(&esw->offloads.encap_tbl_lock); - kfree(e); + if (e) + mlx5e_encap_put(priv, e); return err; } From ef2e4094e076858343ea1202046443f642a245cd Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Fri, 26 Jul 2019 08:26:52 -0500 Subject: [PATCH 12/15] net/mlx5: E-switch, Removed unused hwid Currently mlx5_eswitch_rep stores same hw ID for all representors. However it is never used from this structure. It is always used from mlx5_vport. Hence, remove unused field. Signed-off-by: Parav Pandit Reviewed-by: Vu Pham Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c | 6 +----- include/linux/mlx5/eswitch.h | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 8fe5dddf18d0..42cc5001255b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -1393,10 +1393,9 @@ void esw_offloads_cleanup_reps(struct mlx5_eswitch *esw) int esw_offloads_init_reps(struct mlx5_eswitch *esw) { int total_vports = esw->total_vports; - struct mlx5_core_dev *dev = esw->dev; struct mlx5_eswitch_rep *rep; - u8 hw_id[ETH_ALEN], rep_type; int vport_index; + u8 rep_type; esw->offloads.vport_reps = kcalloc(total_vports, sizeof(struct mlx5_eswitch_rep), @@ -1404,12 +1403,9 @@ int esw_offloads_init_reps(struct mlx5_eswitch *esw) if (!esw->offloads.vport_reps) return -ENOMEM; - mlx5_query_mac_address(dev, hw_id); - mlx5_esw_for_all_reps(esw, vport_index, rep) { rep->vport = mlx5_eswitch_index_to_vport_num(esw, vport_index); rep->vport_index = vport_index; - ether_addr_copy(rep->hw_id, hw_id); for (rep_type = 0; rep_type < NUM_REP_TYPES; rep_type++) atomic_set(&rep->rep_data[rep_type].state, diff --git a/include/linux/mlx5/eswitch.h b/include/linux/mlx5/eswitch.h index 46b5ba029802..38a70d16d8d5 100644 --- a/include/linux/mlx5/eswitch.h +++ b/include/linux/mlx5/eswitch.h @@ -44,7 +44,6 @@ struct mlx5_eswitch_rep_data { struct mlx5_eswitch_rep { struct mlx5_eswitch_rep_data rep_data[NUM_REP_TYPES]; u16 vport; - u8 hw_id[ETH_ALEN]; u16 vlan; /* Only IB rep is using vport_index */ u16 vport_index; From 724ee17912c9a8e5362751a22fd955166ed0550a Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Fri, 26 Jul 2019 13:42:04 -0500 Subject: [PATCH 13/15] net/mlx5e: Simplify querying port representor parent id System image GUID doesn't depend on eswitch switchdev mode. Hence, remove the check which simplifies the code. Signed-off-by: Parav Pandit Reviewed-by: Vu Pham Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index cd957ff4e207..33ae66dc72e2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -389,24 +389,17 @@ static const struct ethtool_ops mlx5e_uplink_rep_ethtool_ops = { .set_pauseparam = mlx5e_uplink_rep_set_pauseparam, }; -static int mlx5e_rep_get_port_parent_id(struct net_device *dev, - struct netdev_phys_item_id *ppid) +static void mlx5e_rep_get_port_parent_id(struct net_device *dev, + struct netdev_phys_item_id *ppid) { - struct mlx5_eswitch *esw; struct mlx5e_priv *priv; u64 parent_id; priv = netdev_priv(dev); - esw = priv->mdev->priv.eswitch; - - if (esw->mode == MLX5_ESWITCH_NONE) - return -EOPNOTSUPP; parent_id = mlx5_query_nic_system_image_guid(priv->mdev); ppid->id_len = sizeof(parent_id); memcpy(ppid->id, &parent_id, sizeof(parent_id)); - - return 0; } static void mlx5e_sqs2vport_stop(struct mlx5_eswitch *esw, @@ -1759,14 +1752,11 @@ static int register_devlink_port(struct mlx5_core_dev *dev, struct devlink *devlink = priv_to_devlink(dev); struct mlx5_eswitch_rep *rep = rpriv->rep; struct netdev_phys_item_id ppid = {}; - int ret; if (!is_devlink_port_supported(dev, rpriv)) return 0; - ret = mlx5e_rep_get_port_parent_id(rpriv->netdev, &ppid); - if (ret) - return ret; + mlx5e_rep_get_port_parent_id(rpriv->netdev, &ppid); if (rep->vport == MLX5_VPORT_UPLINK) devlink_port_attrs_set(&rpriv->dl_port, From c938451f6b9ccbf25eceb27fe1d1c24fd98af923 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Sat, 20 Jul 2019 22:20:58 -0500 Subject: [PATCH 14/15] net/mlx5e: Use vhca_id in generating representor port_index It is desired to use unique port indices when multiple pci devices' devlink instance have the same switch-id. Make use of vhca-id to generate such unique devlink port indices. Signed-off-by: Parav Pandit Signed-off-by: Saeed Mahameed --- .../net/ethernet/mellanox/mlx5/core/en_rep.c | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 33ae66dc72e2..7ce5cb6e527e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -1746,34 +1746,46 @@ is_devlink_port_supported(const struct mlx5_core_dev *dev, mlx5_eswitch_is_vf_vport(dev->priv.eswitch, rpriv->rep->vport); } +static unsigned int +vport_to_devlink_port_index(const struct mlx5_core_dev *dev, u16 vport_num) +{ + return (MLX5_CAP_GEN(dev, vhca_id) << 16) | vport_num; +} + static int register_devlink_port(struct mlx5_core_dev *dev, struct mlx5e_rep_priv *rpriv) { struct devlink *devlink = priv_to_devlink(dev); struct mlx5_eswitch_rep *rep = rpriv->rep; struct netdev_phys_item_id ppid = {}; + unsigned int dl_port_index = 0; if (!is_devlink_port_supported(dev, rpriv)) return 0; mlx5e_rep_get_port_parent_id(rpriv->netdev, &ppid); - if (rep->vport == MLX5_VPORT_UPLINK) + if (rep->vport == MLX5_VPORT_UPLINK) { devlink_port_attrs_set(&rpriv->dl_port, DEVLINK_PORT_FLAVOUR_PHYSICAL, PCI_FUNC(dev->pdev->devfn), false, 0, &ppid.id[0], ppid.id_len); - else if (rep->vport == MLX5_VPORT_PF) + dl_port_index = vport_to_devlink_port_index(dev, rep->vport); + } else if (rep->vport == MLX5_VPORT_PF) { devlink_port_attrs_pci_pf_set(&rpriv->dl_port, &ppid.id[0], ppid.id_len, dev->pdev->devfn); - else if (mlx5_eswitch_is_vf_vport(dev->priv.eswitch, rpriv->rep->vport)) + dl_port_index = rep->vport; + } else if (mlx5_eswitch_is_vf_vport(dev->priv.eswitch, + rpriv->rep->vport)) { devlink_port_attrs_pci_vf_set(&rpriv->dl_port, &ppid.id[0], ppid.id_len, dev->pdev->devfn, rep->vport - 1); + dl_port_index = vport_to_devlink_port_index(dev, rep->vport); + } - return devlink_port_register(devlink, &rpriv->dl_port, rep->vport); + return devlink_port_register(devlink, &rpriv->dl_port, dl_port_index); } static void unregister_devlink_port(struct mlx5_core_dev *dev, From b51c225e6c4e987e131b8b1332f66969382bf328 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Sat, 3 Aug 2019 00:48:28 +0800 Subject: [PATCH 15/15] net/mlx5e: Use refcount_t for refcount refcount_t is better for reference counters since its implementation can prevent overflows. So convert atomic_t ref counters to refcount_t. Signed-off-by: Chuhong Yuan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c index b9d4f4e19ff9..148b55c3db7a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include "mlx5_core.h" @@ -48,7 +49,7 @@ struct mlx5_vxlan { struct mlx5_vxlan_port { struct hlist_node hlist; - atomic_t refcount; + refcount_t refcount; u16 udp_port; }; @@ -113,7 +114,7 @@ int mlx5_vxlan_add_port(struct mlx5_vxlan *vxlan, u16 port) vxlanp = mlx5_vxlan_lookup_port(vxlan, port); if (vxlanp) { - atomic_inc(&vxlanp->refcount); + refcount_inc(&vxlanp->refcount); return 0; } @@ -137,7 +138,7 @@ int mlx5_vxlan_add_port(struct mlx5_vxlan *vxlan, u16 port) } vxlanp->udp_port = port; - atomic_set(&vxlanp->refcount, 1); + refcount_set(&vxlanp->refcount, 1); spin_lock_bh(&vxlan->lock); hash_add(vxlan->htable, &vxlanp->hlist, port); @@ -170,7 +171,7 @@ int mlx5_vxlan_del_port(struct mlx5_vxlan *vxlan, u16 port) goto out_unlock; } - if (atomic_dec_and_test(&vxlanp->refcount)) { + if (refcount_dec_and_test(&vxlanp->refcount)) { hash_del(&vxlanp->hlist); remove = true; }