diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_smfs.c | 182 |
1 files changed, 94 insertions, 88 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_smfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_smfs.c index 6da8b87c0475..59988e24b704 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_smfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/ct_fs_smfs.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. */ +#include <linux/refcount.h> + #include "en_tc.h" #include "en/tc_priv.h" #include "en/tc_ct.h" @@ -13,24 +15,32 @@ netdev_dbg(fs->netdev, "ct_fs_smfs debug: " fmt "\n", ##args) #define MLX5_CT_TCP_FLAGS_MASK cpu_to_be16(be32_to_cpu(TCP_FLAG_RST | TCP_FLAG_FIN) >> 16) +struct mlx5_ct_fs_smfs_matcher { + struct mlx5dr_matcher *dr_matcher; + struct list_head list; + int prio; + refcount_t ref; +}; + struct mlx5_ct_fs_smfs_matchers { - struct mlx5dr_matcher *ipv4_tcp; - struct mlx5dr_matcher *ipv4_udp; - struct mlx5dr_matcher *ipv6_tcp; - struct mlx5dr_matcher *ipv6_udp; + struct mlx5_ct_fs_smfs_matcher smfs_matchers[4]; + struct list_head used; }; struct mlx5_ct_fs_smfs { - struct mlx5_ct_fs_smfs_matchers ct_matchers; - struct mlx5_ct_fs_smfs_matchers ct_matchers_nat; + struct mlx5dr_table *ct_tbl, *ct_nat_tbl; + struct mlx5_ct_fs_smfs_matchers matchers; + struct mlx5_ct_fs_smfs_matchers matchers_nat; struct mlx5dr_action *fwd_action; struct mlx5_flow_table *ct_nat; + struct mutex lock; /* Guards matchers */ }; struct mlx5_ct_fs_smfs_rule { struct mlx5_ct_fs_rule fs_rule; struct mlx5dr_rule *rule; struct mlx5dr_action *count_action; + struct mlx5_ct_fs_smfs_matcher *smfs_matcher; }; static inline void @@ -97,71 +107,75 @@ mlx5_ct_fs_smfs_matcher_create(struct mlx5_ct_fs *fs, struct mlx5dr_table *tbl, return dr_matcher; } -static int -mlx5_ct_fs_smfs_matchers_create(struct mlx5_ct_fs *fs, struct mlx5dr_table *tbl, - struct mlx5_ct_fs_smfs_matchers *ct_matchers) +static struct mlx5_ct_fs_smfs_matcher * +mlx5_ct_fs_smfs_matcher_get(struct mlx5_ct_fs *fs, bool nat, bool ipv4, bool tcp) { - const struct net_device *netdev = fs->netdev; - u32 prio = 0; - int err; - - ct_matchers->ipv4_tcp = mlx5_ct_fs_smfs_matcher_create(fs, tbl, true, true, prio); - if (IS_ERR(ct_matchers->ipv4_tcp)) { - err = PTR_ERR(ct_matchers->ipv4_tcp); - netdev_warn(netdev, - "%s, failed to create ipv4 tcp matcher, err: %d\n", - INIT_ERR_PREFIX, err); - return err; - } - - ++prio; - ct_matchers->ipv4_udp = mlx5_ct_fs_smfs_matcher_create(fs, tbl, true, false, prio); - if (IS_ERR(ct_matchers->ipv4_udp)) { - err = PTR_ERR(ct_matchers->ipv4_udp); - netdev_warn(netdev, - "%s, failed to create ipv4 udp matcher, err: %d\n", - INIT_ERR_PREFIX, err); - goto err_matcher_ipv4_udp; + struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); + struct mlx5_ct_fs_smfs_matcher *m, *smfs_matcher; + struct mlx5_ct_fs_smfs_matchers *matchers; + struct mlx5dr_matcher *dr_matcher; + struct mlx5dr_table *tbl; + struct list_head *prev; + int prio; + + matchers = nat ? &fs_smfs->matchers_nat : &fs_smfs->matchers; + smfs_matcher = &matchers->smfs_matchers[ipv4 * 2 + tcp]; + + if (refcount_inc_not_zero(&smfs_matcher->ref)) + return smfs_matcher; + + mutex_lock(&fs_smfs->lock); + + /* Retry with lock, as another thread might have already created the relevant matcher + * till we acquired the lock + */ + if (refcount_inc_not_zero(&smfs_matcher->ref)) + goto out_unlock; + + // Find next available priority in sorted used list + prio = 0; + prev = &matchers->used; + list_for_each_entry(m, &matchers->used, list) { + prev = &m->list; + + if (m->prio == prio) + prio = m->prio + 1; + else + break; } - ++prio; - ct_matchers->ipv6_tcp = mlx5_ct_fs_smfs_matcher_create(fs, tbl, false, true, prio); - if (IS_ERR(ct_matchers->ipv6_tcp)) { - err = PTR_ERR(ct_matchers->ipv6_tcp); - netdev_warn(netdev, - "%s, failed to create ipv6 tcp matcher, err: %d\n", - INIT_ERR_PREFIX, err); - goto err_matcher_ipv6_tcp; - } + tbl = nat ? fs_smfs->ct_nat_tbl : fs_smfs->ct_tbl; + dr_matcher = mlx5_ct_fs_smfs_matcher_create(fs, tbl, ipv4, tcp, prio); + if (IS_ERR(dr_matcher)) { + netdev_warn(fs->netdev, + "ct_fs_smfs: failed to create matcher (nat %d, ipv4 %d, tcp %d), err: %ld\n", + nat, ipv4, tcp, PTR_ERR(dr_matcher)); - ++prio; - ct_matchers->ipv6_udp = mlx5_ct_fs_smfs_matcher_create(fs, tbl, false, false, prio); - if (IS_ERR(ct_matchers->ipv6_udp)) { - err = PTR_ERR(ct_matchers->ipv6_udp); - netdev_warn(netdev, - "%s, failed to create ipv6 tcp matcher, err: %d\n", - INIT_ERR_PREFIX, err); - goto err_matcher_ipv6_udp; + smfs_matcher = ERR_CAST(dr_matcher); + goto out_unlock; } - return 0; + smfs_matcher->dr_matcher = dr_matcher; + smfs_matcher->prio = prio; + list_add(&smfs_matcher->list, prev); + refcount_set(&smfs_matcher->ref, 1); -err_matcher_ipv6_udp: - mlx5_smfs_matcher_destroy(ct_matchers->ipv6_tcp); -err_matcher_ipv6_tcp: - mlx5_smfs_matcher_destroy(ct_matchers->ipv4_udp); -err_matcher_ipv4_udp: - mlx5_smfs_matcher_destroy(ct_matchers->ipv4_tcp); - return 0; +out_unlock: + mutex_unlock(&fs_smfs->lock); + return smfs_matcher; } static void -mlx5_ct_fs_smfs_matchers_destroy(struct mlx5_ct_fs_smfs_matchers *ct_matchers) +mlx5_ct_fs_smfs_matcher_put(struct mlx5_ct_fs *fs, struct mlx5_ct_fs_smfs_matcher *smfs_matcher) { - mlx5_smfs_matcher_destroy(ct_matchers->ipv6_udp); - mlx5_smfs_matcher_destroy(ct_matchers->ipv6_tcp); - mlx5_smfs_matcher_destroy(ct_matchers->ipv4_udp); - mlx5_smfs_matcher_destroy(ct_matchers->ipv4_tcp); + struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); + + if (!refcount_dec_and_mutex_lock(&smfs_matcher->ref, &fs_smfs->lock)) + return; + + mlx5_smfs_matcher_destroy(smfs_matcher->dr_matcher); + list_del(&smfs_matcher->list); + mutex_unlock(&fs_smfs->lock); } static int @@ -170,7 +184,6 @@ mlx5_ct_fs_smfs_init(struct mlx5_ct_fs *fs, struct mlx5_flow_table *ct, { struct mlx5dr_table *ct_tbl, *ct_nat_tbl, *post_ct_tbl; struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); - int err; post_ct_tbl = mlx5_smfs_table_get_from_fs_ft(post_ct); ct_nat_tbl = mlx5_smfs_table_get_from_fs_ft(ct_nat); @@ -184,28 +197,18 @@ mlx5_ct_fs_smfs_init(struct mlx5_ct_fs *fs, struct mlx5_flow_table *ct, ct_dbg("using smfs steering"); - err = mlx5_ct_fs_smfs_matchers_create(fs, ct_tbl, &fs_smfs->ct_matchers); - if (err) - goto err_init; - - err = mlx5_ct_fs_smfs_matchers_create(fs, ct_nat_tbl, &fs_smfs->ct_matchers_nat); - if (err) - goto err_matchers_nat; - fs_smfs->fwd_action = mlx5_smfs_action_create_dest_table(post_ct_tbl); if (!fs_smfs->fwd_action) { - err = -EINVAL; - goto err_action_create; + return -EINVAL; } - return 0; + fs_smfs->ct_tbl = ct_tbl; + fs_smfs->ct_nat_tbl = ct_nat_tbl; + mutex_init(&fs_smfs->lock); + INIT_LIST_HEAD(&fs_smfs->matchers.used); + INIT_LIST_HEAD(&fs_smfs->matchers_nat.used); -err_action_create: - mlx5_ct_fs_smfs_matchers_destroy(&fs_smfs->ct_matchers_nat); -err_matchers_nat: - mlx5_ct_fs_smfs_matchers_destroy(&fs_smfs->ct_matchers); -err_init: - return err; + return 0; } static void @@ -214,8 +217,6 @@ mlx5_ct_fs_smfs_destroy(struct mlx5_ct_fs *fs) struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); mlx5_smfs_action_destroy(fs_smfs->fwd_action); - mlx5_ct_fs_smfs_matchers_destroy(&fs_smfs->ct_matchers_nat); - mlx5_ct_fs_smfs_matchers_destroy(&fs_smfs->ct_matchers); } static inline bool @@ -285,10 +286,9 @@ mlx5_ct_fs_smfs_ct_rule_add(struct mlx5_ct_fs *fs, struct mlx5_flow_spec *spec, struct mlx5_flow_attr *attr, struct flow_rule *flow_rule) { struct mlx5_ct_fs_smfs *fs_smfs = mlx5_ct_fs_priv(fs); - struct mlx5_ct_fs_smfs_matchers *matchers; + struct mlx5_ct_fs_smfs_matcher *smfs_matcher; struct mlx5_ct_fs_smfs_rule *smfs_rule; struct mlx5dr_action *actions[5]; - struct mlx5dr_matcher *matcher; struct mlx5dr_rule *rule; int num_actions = 0, err; bool nat, tcp, ipv4; @@ -315,22 +315,27 @@ mlx5_ct_fs_smfs_ct_rule_add(struct mlx5_ct_fs *fs, struct mlx5_flow_spec *spec, tcp = MLX5_GET(fte_match_param, spec->match_value, outer_headers.ip_protocol) == IPPROTO_TCP; - matchers = nat ? &fs_smfs->ct_matchers_nat : &fs_smfs->ct_matchers; - matcher = ipv4 ? (tcp ? matchers->ipv4_tcp : matchers->ipv4_udp) : - (tcp ? matchers->ipv6_tcp : matchers->ipv6_udp); + smfs_matcher = mlx5_ct_fs_smfs_matcher_get(fs, nat, ipv4, tcp); + if (IS_ERR(smfs_matcher)) { + err = PTR_ERR(smfs_matcher); + goto err_matcher; + } - rule = mlx5_smfs_rule_create(matcher, spec, num_actions, actions, + rule = mlx5_smfs_rule_create(smfs_matcher->dr_matcher, spec, num_actions, actions, MLX5_FLOW_CONTEXT_FLOW_SOURCE_ANY_VPORT); if (!rule) { err = -EINVAL; - goto err_rule; + goto err_create; } smfs_rule->rule = rule; + smfs_rule->smfs_matcher = smfs_matcher; return &smfs_rule->fs_rule; -err_rule: +err_create: + mlx5_ct_fs_smfs_matcher_put(fs, smfs_matcher); +err_matcher: mlx5_smfs_action_destroy(smfs_rule->count_action); err_count: kfree(smfs_rule); @@ -345,6 +350,7 @@ mlx5_ct_fs_smfs_ct_rule_del(struct mlx5_ct_fs *fs, struct mlx5_ct_fs_rule *fs_ru fs_rule); mlx5_smfs_rule_destroy(smfs_rule->rule); + mlx5_ct_fs_smfs_matcher_put(fs, smfs_rule->smfs_matcher); mlx5_smfs_action_destroy(smfs_rule->count_action); kfree(smfs_rule); } |