1e8f887acSAmir Vadai /*
2e8f887acSAmir Vadai  * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
3e8f887acSAmir Vadai  *
4e8f887acSAmir Vadai  * This software is available to you under a choice of one of two
5e8f887acSAmir Vadai  * licenses.  You may choose to be licensed under the terms of the GNU
6e8f887acSAmir Vadai  * General Public License (GPL) Version 2, available from the file
7e8f887acSAmir Vadai  * COPYING in the main directory of this source tree, or the
8e8f887acSAmir Vadai  * OpenIB.org BSD license below:
9e8f887acSAmir Vadai  *
10e8f887acSAmir Vadai  *     Redistribution and use in source and binary forms, with or
11e8f887acSAmir Vadai  *     without modification, are permitted provided that the following
12e8f887acSAmir Vadai  *     conditions are met:
13e8f887acSAmir Vadai  *
14e8f887acSAmir Vadai  *      - Redistributions of source code must retain the above
15e8f887acSAmir Vadai  *        copyright notice, this list of conditions and the following
16e8f887acSAmir Vadai  *        disclaimer.
17e8f887acSAmir Vadai  *
18e8f887acSAmir Vadai  *      - Redistributions in binary form must reproduce the above
19e8f887acSAmir Vadai  *        copyright notice, this list of conditions and the following
20e8f887acSAmir Vadai  *        disclaimer in the documentation and/or other materials
21e8f887acSAmir Vadai  *        provided with the distribution.
22e8f887acSAmir Vadai  *
23e8f887acSAmir Vadai  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24e8f887acSAmir Vadai  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25e8f887acSAmir Vadai  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26e8f887acSAmir Vadai  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27e8f887acSAmir Vadai  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28e8f887acSAmir Vadai  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29e8f887acSAmir Vadai  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30e8f887acSAmir Vadai  * SOFTWARE.
31e8f887acSAmir Vadai  */
32e8f887acSAmir Vadai 
33e3a2b7edSAmir Vadai #include <net/flow_dissector.h>
343f7d0eb4SOr Gerlitz #include <net/sch_generic.h>
35e3a2b7edSAmir Vadai #include <net/pkt_cls.h>
36e3a2b7edSAmir Vadai #include <net/tc_act/tc_gact.h>
3712185a9fSAmir Vadai #include <net/tc_act/tc_skbedit.h>
38e8f887acSAmir Vadai #include <linux/mlx5/fs.h>
39e8f887acSAmir Vadai #include <linux/mlx5/device.h>
40e8f887acSAmir Vadai #include <linux/rhashtable.h>
4103a9d11eSOr Gerlitz #include <net/switchdev.h>
4203a9d11eSOr Gerlitz #include <net/tc_act/tc_mirred.h>
43776b12b6SOr Gerlitz #include <net/tc_act/tc_vlan.h>
44bbd00f7eSHadar Hen Zion #include <net/tc_act/tc_tunnel_key.h>
45d79b6df6SOr Gerlitz #include <net/tc_act/tc_pedit.h>
4626c02749SOr Gerlitz #include <net/tc_act/tc_csum.h>
47a54e20b4SHadar Hen Zion #include <net/vxlan.h>
48f6dfb4c3SHadar Hen Zion #include <net/arp.h>
49e8f887acSAmir Vadai #include "en.h"
501d447a39SSaeed Mahameed #include "en_rep.h"
51232c0013SHadar Hen Zion #include "en_tc.h"
5203a9d11eSOr Gerlitz #include "eswitch.h"
53358aa5ceSSaeed Mahameed #include "lib/vxlan.h"
543f6d08d1SOr Gerlitz #include "fs_core.h"
552c81bfd5SHuy Nguyen #include "en/port.h"
56e8f887acSAmir Vadai 
573bc4b7bfSOr Gerlitz struct mlx5_nic_flow_attr {
583bc4b7bfSOr Gerlitz 	u32 action;
593bc4b7bfSOr Gerlitz 	u32 flow_tag;
602f4fe4caSOr Gerlitz 	u32 mod_hdr_id;
615c65c564SOr Gerlitz 	u32 hairpin_tirn;
6238aa51c1SOr Gerlitz 	u8 match_level;
633f6d08d1SOr Gerlitz 	struct mlx5_flow_table	*hairpin_ft;
64b8aee822SMark Bloch 	struct mlx5_fc		*counter;
653bc4b7bfSOr Gerlitz };
663bc4b7bfSOr Gerlitz 
6760bd4af8SOr Gerlitz #define MLX5E_TC_FLOW_BASE (MLX5E_TC_LAST_EXPORTED_BIT + 1)
6860bd4af8SOr Gerlitz 
6965ba8fb7SOr Gerlitz enum {
7060bd4af8SOr Gerlitz 	MLX5E_TC_FLOW_INGRESS	= MLX5E_TC_INGRESS,
7160bd4af8SOr Gerlitz 	MLX5E_TC_FLOW_EGRESS	= MLX5E_TC_EGRESS,
7260bd4af8SOr Gerlitz 	MLX5E_TC_FLOW_ESWITCH	= BIT(MLX5E_TC_FLOW_BASE),
7360bd4af8SOr Gerlitz 	MLX5E_TC_FLOW_NIC	= BIT(MLX5E_TC_FLOW_BASE + 1),
7460bd4af8SOr Gerlitz 	MLX5E_TC_FLOW_OFFLOADED	= BIT(MLX5E_TC_FLOW_BASE + 2),
7560bd4af8SOr Gerlitz 	MLX5E_TC_FLOW_HAIRPIN	= BIT(MLX5E_TC_FLOW_BASE + 3),
7660bd4af8SOr Gerlitz 	MLX5E_TC_FLOW_HAIRPIN_RSS = BIT(MLX5E_TC_FLOW_BASE + 4),
775dbe906fSPaul Blakey 	MLX5E_TC_FLOW_SLOW	  = BIT(MLX5E_TC_FLOW_BASE + 5),
7865ba8fb7SOr Gerlitz };
7965ba8fb7SOr Gerlitz 
80e4ad91f2SChris Mi #define MLX5E_TC_MAX_SPLITS 1
81e4ad91f2SChris Mi 
82e8f887acSAmir Vadai struct mlx5e_tc_flow {
83e8f887acSAmir Vadai 	struct rhash_head	node;
84655dc3d2SOr Gerlitz 	struct mlx5e_priv	*priv;
85e8f887acSAmir Vadai 	u64			cookie;
865dbe906fSPaul Blakey 	u16			flags;
87e4ad91f2SChris Mi 	struct mlx5_flow_handle *rule[MLX5E_TC_MAX_SPLITS + 1];
8811c9c548SOr Gerlitz 	struct list_head	encap;   /* flows sharing the same encap ID */
8911c9c548SOr Gerlitz 	struct list_head	mod_hdr; /* flows sharing the same mod hdr ID */
905c65c564SOr Gerlitz 	struct list_head	hairpin; /* flows sharing the same hairpin */
913bc4b7bfSOr Gerlitz 	union {
92ecf5bb79SOr Gerlitz 		struct mlx5_esw_flow_attr esw_attr[0];
933bc4b7bfSOr Gerlitz 		struct mlx5_nic_flow_attr nic_attr[0];
943bc4b7bfSOr Gerlitz 	};
95e8f887acSAmir Vadai };
96e8f887acSAmir Vadai 
9717091853SOr Gerlitz struct mlx5e_tc_flow_parse_attr {
983c37745eSOr Gerlitz 	struct ip_tunnel_info tun_info;
99d11afc26SOz Shlomo 	struct net_device *filter_dev;
10017091853SOr Gerlitz 	struct mlx5_flow_spec spec;
101d79b6df6SOr Gerlitz 	int num_mod_hdr_actions;
102d79b6df6SOr Gerlitz 	void *mod_hdr_actions;
1033c37745eSOr Gerlitz 	int mirred_ifindex;
10417091853SOr Gerlitz };
10517091853SOr Gerlitz 
106acff797cSMaor Gottlieb #define MLX5E_TC_TABLE_NUM_GROUPS 4
107b3a433deSOr Gerlitz #define MLX5E_TC_TABLE_MAX_GROUP_SIZE BIT(16)
108e8f887acSAmir Vadai 
10977ab67b7SOr Gerlitz struct mlx5e_hairpin {
11077ab67b7SOr Gerlitz 	struct mlx5_hairpin *pair;
11177ab67b7SOr Gerlitz 
11277ab67b7SOr Gerlitz 	struct mlx5_core_dev *func_mdev;
1133f6d08d1SOr Gerlitz 	struct mlx5e_priv *func_priv;
11477ab67b7SOr Gerlitz 	u32 tdn;
11577ab67b7SOr Gerlitz 	u32 tirn;
1163f6d08d1SOr Gerlitz 
1173f6d08d1SOr Gerlitz 	int num_channels;
1183f6d08d1SOr Gerlitz 	struct mlx5e_rqt indir_rqt;
1193f6d08d1SOr Gerlitz 	u32 indir_tirn[MLX5E_NUM_INDIR_TIRS];
1203f6d08d1SOr Gerlitz 	struct mlx5e_ttc_table ttc;
12177ab67b7SOr Gerlitz };
12277ab67b7SOr Gerlitz 
1235c65c564SOr Gerlitz struct mlx5e_hairpin_entry {
1245c65c564SOr Gerlitz 	/* a node of a hash table which keeps all the  hairpin entries */
1255c65c564SOr Gerlitz 	struct hlist_node hairpin_hlist;
1265c65c564SOr Gerlitz 
1275c65c564SOr Gerlitz 	/* flows sharing the same hairpin */
1285c65c564SOr Gerlitz 	struct list_head flows;
1295c65c564SOr Gerlitz 
130d8822868SOr Gerlitz 	u16 peer_vhca_id;
131106be53bSOr Gerlitz 	u8 prio;
1325c65c564SOr Gerlitz 	struct mlx5e_hairpin *hp;
1335c65c564SOr Gerlitz };
1345c65c564SOr Gerlitz 
13511c9c548SOr Gerlitz struct mod_hdr_key {
13611c9c548SOr Gerlitz 	int num_actions;
13711c9c548SOr Gerlitz 	void *actions;
13811c9c548SOr Gerlitz };
13911c9c548SOr Gerlitz 
14011c9c548SOr Gerlitz struct mlx5e_mod_hdr_entry {
14111c9c548SOr Gerlitz 	/* a node of a hash table which keeps all the mod_hdr entries */
14211c9c548SOr Gerlitz 	struct hlist_node mod_hdr_hlist;
14311c9c548SOr Gerlitz 
14411c9c548SOr Gerlitz 	/* flows sharing the same mod_hdr entry */
14511c9c548SOr Gerlitz 	struct list_head flows;
14611c9c548SOr Gerlitz 
14711c9c548SOr Gerlitz 	struct mod_hdr_key key;
14811c9c548SOr Gerlitz 
14911c9c548SOr Gerlitz 	u32 mod_hdr_id;
15011c9c548SOr Gerlitz };
15111c9c548SOr Gerlitz 
15211c9c548SOr Gerlitz #define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto)
15311c9c548SOr Gerlitz 
15411c9c548SOr Gerlitz static inline u32 hash_mod_hdr_info(struct mod_hdr_key *key)
15511c9c548SOr Gerlitz {
15611c9c548SOr Gerlitz 	return jhash(key->actions,
15711c9c548SOr Gerlitz 		     key->num_actions * MLX5_MH_ACT_SZ, 0);
15811c9c548SOr Gerlitz }
15911c9c548SOr Gerlitz 
16011c9c548SOr Gerlitz static inline int cmp_mod_hdr_info(struct mod_hdr_key *a,
16111c9c548SOr Gerlitz 				   struct mod_hdr_key *b)
16211c9c548SOr Gerlitz {
16311c9c548SOr Gerlitz 	if (a->num_actions != b->num_actions)
16411c9c548SOr Gerlitz 		return 1;
16511c9c548SOr Gerlitz 
16611c9c548SOr Gerlitz 	return memcmp(a->actions, b->actions, a->num_actions * MLX5_MH_ACT_SZ);
16711c9c548SOr Gerlitz }
16811c9c548SOr Gerlitz 
16911c9c548SOr Gerlitz static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv,
17011c9c548SOr Gerlitz 				struct mlx5e_tc_flow *flow,
17111c9c548SOr Gerlitz 				struct mlx5e_tc_flow_parse_attr *parse_attr)
17211c9c548SOr Gerlitz {
17311c9c548SOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
17411c9c548SOr Gerlitz 	int num_actions, actions_size, namespace, err;
17511c9c548SOr Gerlitz 	struct mlx5e_mod_hdr_entry *mh;
17611c9c548SOr Gerlitz 	struct mod_hdr_key key;
17711c9c548SOr Gerlitz 	bool found = false;
17811c9c548SOr Gerlitz 	u32 hash_key;
17911c9c548SOr Gerlitz 
18011c9c548SOr Gerlitz 	num_actions  = parse_attr->num_mod_hdr_actions;
18111c9c548SOr Gerlitz 	actions_size = MLX5_MH_ACT_SZ * num_actions;
18211c9c548SOr Gerlitz 
18311c9c548SOr Gerlitz 	key.actions = parse_attr->mod_hdr_actions;
18411c9c548SOr Gerlitz 	key.num_actions = num_actions;
18511c9c548SOr Gerlitz 
18611c9c548SOr Gerlitz 	hash_key = hash_mod_hdr_info(&key);
18711c9c548SOr Gerlitz 
18811c9c548SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH) {
18911c9c548SOr Gerlitz 		namespace = MLX5_FLOW_NAMESPACE_FDB;
19011c9c548SOr Gerlitz 		hash_for_each_possible(esw->offloads.mod_hdr_tbl, mh,
19111c9c548SOr Gerlitz 				       mod_hdr_hlist, hash_key) {
19211c9c548SOr Gerlitz 			if (!cmp_mod_hdr_info(&mh->key, &key)) {
19311c9c548SOr Gerlitz 				found = true;
19411c9c548SOr Gerlitz 				break;
19511c9c548SOr Gerlitz 			}
19611c9c548SOr Gerlitz 		}
19711c9c548SOr Gerlitz 	} else {
19811c9c548SOr Gerlitz 		namespace = MLX5_FLOW_NAMESPACE_KERNEL;
19911c9c548SOr Gerlitz 		hash_for_each_possible(priv->fs.tc.mod_hdr_tbl, mh,
20011c9c548SOr Gerlitz 				       mod_hdr_hlist, hash_key) {
20111c9c548SOr Gerlitz 			if (!cmp_mod_hdr_info(&mh->key, &key)) {
20211c9c548SOr Gerlitz 				found = true;
20311c9c548SOr Gerlitz 				break;
20411c9c548SOr Gerlitz 			}
20511c9c548SOr Gerlitz 		}
20611c9c548SOr Gerlitz 	}
20711c9c548SOr Gerlitz 
20811c9c548SOr Gerlitz 	if (found)
20911c9c548SOr Gerlitz 		goto attach_flow;
21011c9c548SOr Gerlitz 
21111c9c548SOr Gerlitz 	mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL);
21211c9c548SOr Gerlitz 	if (!mh)
21311c9c548SOr Gerlitz 		return -ENOMEM;
21411c9c548SOr Gerlitz 
21511c9c548SOr Gerlitz 	mh->key.actions = (void *)mh + sizeof(*mh);
21611c9c548SOr Gerlitz 	memcpy(mh->key.actions, key.actions, actions_size);
21711c9c548SOr Gerlitz 	mh->key.num_actions = num_actions;
21811c9c548SOr Gerlitz 	INIT_LIST_HEAD(&mh->flows);
21911c9c548SOr Gerlitz 
22011c9c548SOr Gerlitz 	err = mlx5_modify_header_alloc(priv->mdev, namespace,
22111c9c548SOr Gerlitz 				       mh->key.num_actions,
22211c9c548SOr Gerlitz 				       mh->key.actions,
22311c9c548SOr Gerlitz 				       &mh->mod_hdr_id);
22411c9c548SOr Gerlitz 	if (err)
22511c9c548SOr Gerlitz 		goto out_err;
22611c9c548SOr Gerlitz 
22711c9c548SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
22811c9c548SOr Gerlitz 		hash_add(esw->offloads.mod_hdr_tbl, &mh->mod_hdr_hlist, hash_key);
22911c9c548SOr Gerlitz 	else
23011c9c548SOr Gerlitz 		hash_add(priv->fs.tc.mod_hdr_tbl, &mh->mod_hdr_hlist, hash_key);
23111c9c548SOr Gerlitz 
23211c9c548SOr Gerlitz attach_flow:
23311c9c548SOr Gerlitz 	list_add(&flow->mod_hdr, &mh->flows);
23411c9c548SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
23511c9c548SOr Gerlitz 		flow->esw_attr->mod_hdr_id = mh->mod_hdr_id;
23611c9c548SOr Gerlitz 	else
23711c9c548SOr Gerlitz 		flow->nic_attr->mod_hdr_id = mh->mod_hdr_id;
23811c9c548SOr Gerlitz 
23911c9c548SOr Gerlitz 	return 0;
24011c9c548SOr Gerlitz 
24111c9c548SOr Gerlitz out_err:
24211c9c548SOr Gerlitz 	kfree(mh);
24311c9c548SOr Gerlitz 	return err;
24411c9c548SOr Gerlitz }
24511c9c548SOr Gerlitz 
24611c9c548SOr Gerlitz static void mlx5e_detach_mod_hdr(struct mlx5e_priv *priv,
24711c9c548SOr Gerlitz 				 struct mlx5e_tc_flow *flow)
24811c9c548SOr Gerlitz {
24911c9c548SOr Gerlitz 	struct list_head *next = flow->mod_hdr.next;
25011c9c548SOr Gerlitz 
25111c9c548SOr Gerlitz 	list_del(&flow->mod_hdr);
25211c9c548SOr Gerlitz 
25311c9c548SOr Gerlitz 	if (list_empty(next)) {
25411c9c548SOr Gerlitz 		struct mlx5e_mod_hdr_entry *mh;
25511c9c548SOr Gerlitz 
25611c9c548SOr Gerlitz 		mh = list_entry(next, struct mlx5e_mod_hdr_entry, flows);
25711c9c548SOr Gerlitz 
25811c9c548SOr Gerlitz 		mlx5_modify_header_dealloc(priv->mdev, mh->mod_hdr_id);
25911c9c548SOr Gerlitz 		hash_del(&mh->mod_hdr_hlist);
26011c9c548SOr Gerlitz 		kfree(mh);
26111c9c548SOr Gerlitz 	}
26211c9c548SOr Gerlitz }
26311c9c548SOr Gerlitz 
26477ab67b7SOr Gerlitz static
26577ab67b7SOr Gerlitz struct mlx5_core_dev *mlx5e_hairpin_get_mdev(struct net *net, int ifindex)
26677ab67b7SOr Gerlitz {
26777ab67b7SOr Gerlitz 	struct net_device *netdev;
26877ab67b7SOr Gerlitz 	struct mlx5e_priv *priv;
26977ab67b7SOr Gerlitz 
27077ab67b7SOr Gerlitz 	netdev = __dev_get_by_index(net, ifindex);
27177ab67b7SOr Gerlitz 	priv = netdev_priv(netdev);
27277ab67b7SOr Gerlitz 	return priv->mdev;
27377ab67b7SOr Gerlitz }
27477ab67b7SOr Gerlitz 
27577ab67b7SOr Gerlitz static int mlx5e_hairpin_create_transport(struct mlx5e_hairpin *hp)
27677ab67b7SOr Gerlitz {
27777ab67b7SOr Gerlitz 	u32 in[MLX5_ST_SZ_DW(create_tir_in)] = {0};
27877ab67b7SOr Gerlitz 	void *tirc;
27977ab67b7SOr Gerlitz 	int err;
28077ab67b7SOr Gerlitz 
28177ab67b7SOr Gerlitz 	err = mlx5_core_alloc_transport_domain(hp->func_mdev, &hp->tdn);
28277ab67b7SOr Gerlitz 	if (err)
28377ab67b7SOr Gerlitz 		goto alloc_tdn_err;
28477ab67b7SOr Gerlitz 
28577ab67b7SOr Gerlitz 	tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
28677ab67b7SOr Gerlitz 
28777ab67b7SOr Gerlitz 	MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_DIRECT);
288ddae74acSOr Gerlitz 	MLX5_SET(tirc, tirc, inline_rqn, hp->pair->rqn[0]);
28977ab67b7SOr Gerlitz 	MLX5_SET(tirc, tirc, transport_domain, hp->tdn);
29077ab67b7SOr Gerlitz 
29177ab67b7SOr Gerlitz 	err = mlx5_core_create_tir(hp->func_mdev, in, MLX5_ST_SZ_BYTES(create_tir_in), &hp->tirn);
29277ab67b7SOr Gerlitz 	if (err)
29377ab67b7SOr Gerlitz 		goto create_tir_err;
29477ab67b7SOr Gerlitz 
29577ab67b7SOr Gerlitz 	return 0;
29677ab67b7SOr Gerlitz 
29777ab67b7SOr Gerlitz create_tir_err:
29877ab67b7SOr Gerlitz 	mlx5_core_dealloc_transport_domain(hp->func_mdev, hp->tdn);
29977ab67b7SOr Gerlitz alloc_tdn_err:
30077ab67b7SOr Gerlitz 	return err;
30177ab67b7SOr Gerlitz }
30277ab67b7SOr Gerlitz 
30377ab67b7SOr Gerlitz static void mlx5e_hairpin_destroy_transport(struct mlx5e_hairpin *hp)
30477ab67b7SOr Gerlitz {
30577ab67b7SOr Gerlitz 	mlx5_core_destroy_tir(hp->func_mdev, hp->tirn);
30677ab67b7SOr Gerlitz 	mlx5_core_dealloc_transport_domain(hp->func_mdev, hp->tdn);
30777ab67b7SOr Gerlitz }
30877ab67b7SOr Gerlitz 
3093f6d08d1SOr Gerlitz static void mlx5e_hairpin_fill_rqt_rqns(struct mlx5e_hairpin *hp, void *rqtc)
3103f6d08d1SOr Gerlitz {
3113f6d08d1SOr Gerlitz 	u32 indirection_rqt[MLX5E_INDIR_RQT_SIZE], rqn;
3123f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
3133f6d08d1SOr Gerlitz 	int i, ix, sz = MLX5E_INDIR_RQT_SIZE;
3143f6d08d1SOr Gerlitz 
3153f6d08d1SOr Gerlitz 	mlx5e_build_default_indir_rqt(indirection_rqt, sz,
3163f6d08d1SOr Gerlitz 				      hp->num_channels);
3173f6d08d1SOr Gerlitz 
3183f6d08d1SOr Gerlitz 	for (i = 0; i < sz; i++) {
3193f6d08d1SOr Gerlitz 		ix = i;
320bbeb53b8SAya Levin 		if (priv->rss_params.hfunc == ETH_RSS_HASH_XOR)
3213f6d08d1SOr Gerlitz 			ix = mlx5e_bits_invert(i, ilog2(sz));
3223f6d08d1SOr Gerlitz 		ix = indirection_rqt[ix];
3233f6d08d1SOr Gerlitz 		rqn = hp->pair->rqn[ix];
3243f6d08d1SOr Gerlitz 		MLX5_SET(rqtc, rqtc, rq_num[i], rqn);
3253f6d08d1SOr Gerlitz 	}
3263f6d08d1SOr Gerlitz }
3273f6d08d1SOr Gerlitz 
3283f6d08d1SOr Gerlitz static int mlx5e_hairpin_create_indirect_rqt(struct mlx5e_hairpin *hp)
3293f6d08d1SOr Gerlitz {
3303f6d08d1SOr Gerlitz 	int inlen, err, sz = MLX5E_INDIR_RQT_SIZE;
3313f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
3323f6d08d1SOr Gerlitz 	struct mlx5_core_dev *mdev = priv->mdev;
3333f6d08d1SOr Gerlitz 	void *rqtc;
3343f6d08d1SOr Gerlitz 	u32 *in;
3353f6d08d1SOr Gerlitz 
3363f6d08d1SOr Gerlitz 	inlen = MLX5_ST_SZ_BYTES(create_rqt_in) + sizeof(u32) * sz;
3373f6d08d1SOr Gerlitz 	in = kvzalloc(inlen, GFP_KERNEL);
3383f6d08d1SOr Gerlitz 	if (!in)
3393f6d08d1SOr Gerlitz 		return -ENOMEM;
3403f6d08d1SOr Gerlitz 
3413f6d08d1SOr Gerlitz 	rqtc = MLX5_ADDR_OF(create_rqt_in, in, rqt_context);
3423f6d08d1SOr Gerlitz 
3433f6d08d1SOr Gerlitz 	MLX5_SET(rqtc, rqtc, rqt_actual_size, sz);
3443f6d08d1SOr Gerlitz 	MLX5_SET(rqtc, rqtc, rqt_max_size, sz);
3453f6d08d1SOr Gerlitz 
3463f6d08d1SOr Gerlitz 	mlx5e_hairpin_fill_rqt_rqns(hp, rqtc);
3473f6d08d1SOr Gerlitz 
3483f6d08d1SOr Gerlitz 	err = mlx5_core_create_rqt(mdev, in, inlen, &hp->indir_rqt.rqtn);
3493f6d08d1SOr Gerlitz 	if (!err)
3503f6d08d1SOr Gerlitz 		hp->indir_rqt.enabled = true;
3513f6d08d1SOr Gerlitz 
3523f6d08d1SOr Gerlitz 	kvfree(in);
3533f6d08d1SOr Gerlitz 	return err;
3543f6d08d1SOr Gerlitz }
3553f6d08d1SOr Gerlitz 
3563f6d08d1SOr Gerlitz static int mlx5e_hairpin_create_indirect_tirs(struct mlx5e_hairpin *hp)
3573f6d08d1SOr Gerlitz {
3583f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
3593f6d08d1SOr Gerlitz 	u32 in[MLX5_ST_SZ_DW(create_tir_in)];
3603f6d08d1SOr Gerlitz 	int tt, i, err;
3613f6d08d1SOr Gerlitz 	void *tirc;
3623f6d08d1SOr Gerlitz 
3633f6d08d1SOr Gerlitz 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
364d930ac79SAya Levin 		struct mlx5e_tirc_config ttconfig = mlx5e_tirc_get_default_config(tt);
365d930ac79SAya Levin 
3663f6d08d1SOr Gerlitz 		memset(in, 0, MLX5_ST_SZ_BYTES(create_tir_in));
3673f6d08d1SOr Gerlitz 		tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
3683f6d08d1SOr Gerlitz 
3693f6d08d1SOr Gerlitz 		MLX5_SET(tirc, tirc, transport_domain, hp->tdn);
3703f6d08d1SOr Gerlitz 		MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT);
3713f6d08d1SOr Gerlitz 		MLX5_SET(tirc, tirc, indirect_table, hp->indir_rqt.rqtn);
372bbeb53b8SAya Levin 		mlx5e_build_indir_tir_ctx_hash(&priv->rss_params, &ttconfig, tirc, false);
373bbeb53b8SAya Levin 
3743f6d08d1SOr Gerlitz 		err = mlx5_core_create_tir(hp->func_mdev, in,
3753f6d08d1SOr Gerlitz 					   MLX5_ST_SZ_BYTES(create_tir_in), &hp->indir_tirn[tt]);
3763f6d08d1SOr Gerlitz 		if (err) {
3773f6d08d1SOr Gerlitz 			mlx5_core_warn(hp->func_mdev, "create indirect tirs failed, %d\n", err);
3783f6d08d1SOr Gerlitz 			goto err_destroy_tirs;
3793f6d08d1SOr Gerlitz 		}
3803f6d08d1SOr Gerlitz 	}
3813f6d08d1SOr Gerlitz 	return 0;
3823f6d08d1SOr Gerlitz 
3833f6d08d1SOr Gerlitz err_destroy_tirs:
3843f6d08d1SOr Gerlitz 	for (i = 0; i < tt; i++)
3853f6d08d1SOr Gerlitz 		mlx5_core_destroy_tir(hp->func_mdev, hp->indir_tirn[i]);
3863f6d08d1SOr Gerlitz 	return err;
3873f6d08d1SOr Gerlitz }
3883f6d08d1SOr Gerlitz 
3893f6d08d1SOr Gerlitz static void mlx5e_hairpin_destroy_indirect_tirs(struct mlx5e_hairpin *hp)
3903f6d08d1SOr Gerlitz {
3913f6d08d1SOr Gerlitz 	int tt;
3923f6d08d1SOr Gerlitz 
3933f6d08d1SOr Gerlitz 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
3943f6d08d1SOr Gerlitz 		mlx5_core_destroy_tir(hp->func_mdev, hp->indir_tirn[tt]);
3953f6d08d1SOr Gerlitz }
3963f6d08d1SOr Gerlitz 
3973f6d08d1SOr Gerlitz static void mlx5e_hairpin_set_ttc_params(struct mlx5e_hairpin *hp,
3983f6d08d1SOr Gerlitz 					 struct ttc_params *ttc_params)
3993f6d08d1SOr Gerlitz {
4003f6d08d1SOr Gerlitz 	struct mlx5_flow_table_attr *ft_attr = &ttc_params->ft_attr;
4013f6d08d1SOr Gerlitz 	int tt;
4023f6d08d1SOr Gerlitz 
4033f6d08d1SOr Gerlitz 	memset(ttc_params, 0, sizeof(*ttc_params));
4043f6d08d1SOr Gerlitz 
4053f6d08d1SOr Gerlitz 	ttc_params->any_tt_tirn = hp->tirn;
4063f6d08d1SOr Gerlitz 
4073f6d08d1SOr Gerlitz 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
4083f6d08d1SOr Gerlitz 		ttc_params->indir_tirn[tt] = hp->indir_tirn[tt];
4093f6d08d1SOr Gerlitz 
4103f6d08d1SOr Gerlitz 	ft_attr->max_fte = MLX5E_NUM_TT;
4113f6d08d1SOr Gerlitz 	ft_attr->level = MLX5E_TC_TTC_FT_LEVEL;
4123f6d08d1SOr Gerlitz 	ft_attr->prio = MLX5E_TC_PRIO;
4133f6d08d1SOr Gerlitz }
4143f6d08d1SOr Gerlitz 
4153f6d08d1SOr Gerlitz static int mlx5e_hairpin_rss_init(struct mlx5e_hairpin *hp)
4163f6d08d1SOr Gerlitz {
4173f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
4183f6d08d1SOr Gerlitz 	struct ttc_params ttc_params;
4193f6d08d1SOr Gerlitz 	int err;
4203f6d08d1SOr Gerlitz 
4213f6d08d1SOr Gerlitz 	err = mlx5e_hairpin_create_indirect_rqt(hp);
4223f6d08d1SOr Gerlitz 	if (err)
4233f6d08d1SOr Gerlitz 		return err;
4243f6d08d1SOr Gerlitz 
4253f6d08d1SOr Gerlitz 	err = mlx5e_hairpin_create_indirect_tirs(hp);
4263f6d08d1SOr Gerlitz 	if (err)
4273f6d08d1SOr Gerlitz 		goto err_create_indirect_tirs;
4283f6d08d1SOr Gerlitz 
4293f6d08d1SOr Gerlitz 	mlx5e_hairpin_set_ttc_params(hp, &ttc_params);
4303f6d08d1SOr Gerlitz 	err = mlx5e_create_ttc_table(priv, &ttc_params, &hp->ttc);
4313f6d08d1SOr Gerlitz 	if (err)
4323f6d08d1SOr Gerlitz 		goto err_create_ttc_table;
4333f6d08d1SOr Gerlitz 
4343f6d08d1SOr Gerlitz 	netdev_dbg(priv->netdev, "add hairpin: using %d channels rss ttc table id %x\n",
4353f6d08d1SOr Gerlitz 		   hp->num_channels, hp->ttc.ft.t->id);
4363f6d08d1SOr Gerlitz 
4373f6d08d1SOr Gerlitz 	return 0;
4383f6d08d1SOr Gerlitz 
4393f6d08d1SOr Gerlitz err_create_ttc_table:
4403f6d08d1SOr Gerlitz 	mlx5e_hairpin_destroy_indirect_tirs(hp);
4413f6d08d1SOr Gerlitz err_create_indirect_tirs:
4423f6d08d1SOr Gerlitz 	mlx5e_destroy_rqt(priv, &hp->indir_rqt);
4433f6d08d1SOr Gerlitz 
4443f6d08d1SOr Gerlitz 	return err;
4453f6d08d1SOr Gerlitz }
4463f6d08d1SOr Gerlitz 
4473f6d08d1SOr Gerlitz static void mlx5e_hairpin_rss_cleanup(struct mlx5e_hairpin *hp)
4483f6d08d1SOr Gerlitz {
4493f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
4503f6d08d1SOr Gerlitz 
4513f6d08d1SOr Gerlitz 	mlx5e_destroy_ttc_table(priv, &hp->ttc);
4523f6d08d1SOr Gerlitz 	mlx5e_hairpin_destroy_indirect_tirs(hp);
4533f6d08d1SOr Gerlitz 	mlx5e_destroy_rqt(priv, &hp->indir_rqt);
4543f6d08d1SOr Gerlitz }
4553f6d08d1SOr Gerlitz 
45677ab67b7SOr Gerlitz static struct mlx5e_hairpin *
45777ab67b7SOr Gerlitz mlx5e_hairpin_create(struct mlx5e_priv *priv, struct mlx5_hairpin_params *params,
45877ab67b7SOr Gerlitz 		     int peer_ifindex)
45977ab67b7SOr Gerlitz {
46077ab67b7SOr Gerlitz 	struct mlx5_core_dev *func_mdev, *peer_mdev;
46177ab67b7SOr Gerlitz 	struct mlx5e_hairpin *hp;
46277ab67b7SOr Gerlitz 	struct mlx5_hairpin *pair;
46377ab67b7SOr Gerlitz 	int err;
46477ab67b7SOr Gerlitz 
46577ab67b7SOr Gerlitz 	hp = kzalloc(sizeof(*hp), GFP_KERNEL);
46677ab67b7SOr Gerlitz 	if (!hp)
46777ab67b7SOr Gerlitz 		return ERR_PTR(-ENOMEM);
46877ab67b7SOr Gerlitz 
46977ab67b7SOr Gerlitz 	func_mdev = priv->mdev;
47077ab67b7SOr Gerlitz 	peer_mdev = mlx5e_hairpin_get_mdev(dev_net(priv->netdev), peer_ifindex);
47177ab67b7SOr Gerlitz 
47277ab67b7SOr Gerlitz 	pair = mlx5_core_hairpin_create(func_mdev, peer_mdev, params);
47377ab67b7SOr Gerlitz 	if (IS_ERR(pair)) {
47477ab67b7SOr Gerlitz 		err = PTR_ERR(pair);
47577ab67b7SOr Gerlitz 		goto create_pair_err;
47677ab67b7SOr Gerlitz 	}
47777ab67b7SOr Gerlitz 	hp->pair = pair;
47877ab67b7SOr Gerlitz 	hp->func_mdev = func_mdev;
4793f6d08d1SOr Gerlitz 	hp->func_priv = priv;
4803f6d08d1SOr Gerlitz 	hp->num_channels = params->num_channels;
48177ab67b7SOr Gerlitz 
48277ab67b7SOr Gerlitz 	err = mlx5e_hairpin_create_transport(hp);
48377ab67b7SOr Gerlitz 	if (err)
48477ab67b7SOr Gerlitz 		goto create_transport_err;
48577ab67b7SOr Gerlitz 
4863f6d08d1SOr Gerlitz 	if (hp->num_channels > 1) {
4873f6d08d1SOr Gerlitz 		err = mlx5e_hairpin_rss_init(hp);
4883f6d08d1SOr Gerlitz 		if (err)
4893f6d08d1SOr Gerlitz 			goto rss_init_err;
4903f6d08d1SOr Gerlitz 	}
4913f6d08d1SOr Gerlitz 
49277ab67b7SOr Gerlitz 	return hp;
49377ab67b7SOr Gerlitz 
4943f6d08d1SOr Gerlitz rss_init_err:
4953f6d08d1SOr Gerlitz 	mlx5e_hairpin_destroy_transport(hp);
49677ab67b7SOr Gerlitz create_transport_err:
49777ab67b7SOr Gerlitz 	mlx5_core_hairpin_destroy(hp->pair);
49877ab67b7SOr Gerlitz create_pair_err:
49977ab67b7SOr Gerlitz 	kfree(hp);
50077ab67b7SOr Gerlitz 	return ERR_PTR(err);
50177ab67b7SOr Gerlitz }
50277ab67b7SOr Gerlitz 
50377ab67b7SOr Gerlitz static void mlx5e_hairpin_destroy(struct mlx5e_hairpin *hp)
50477ab67b7SOr Gerlitz {
5053f6d08d1SOr Gerlitz 	if (hp->num_channels > 1)
5063f6d08d1SOr Gerlitz 		mlx5e_hairpin_rss_cleanup(hp);
50777ab67b7SOr Gerlitz 	mlx5e_hairpin_destroy_transport(hp);
50877ab67b7SOr Gerlitz 	mlx5_core_hairpin_destroy(hp->pair);
50977ab67b7SOr Gerlitz 	kvfree(hp);
51077ab67b7SOr Gerlitz }
51177ab67b7SOr Gerlitz 
512106be53bSOr Gerlitz static inline u32 hash_hairpin_info(u16 peer_vhca_id, u8 prio)
513106be53bSOr Gerlitz {
514106be53bSOr Gerlitz 	return (peer_vhca_id << 16 | prio);
515106be53bSOr Gerlitz }
516106be53bSOr Gerlitz 
5175c65c564SOr Gerlitz static struct mlx5e_hairpin_entry *mlx5e_hairpin_get(struct mlx5e_priv *priv,
518106be53bSOr Gerlitz 						     u16 peer_vhca_id, u8 prio)
5195c65c564SOr Gerlitz {
5205c65c564SOr Gerlitz 	struct mlx5e_hairpin_entry *hpe;
521106be53bSOr Gerlitz 	u32 hash_key = hash_hairpin_info(peer_vhca_id, prio);
5225c65c564SOr Gerlitz 
5235c65c564SOr Gerlitz 	hash_for_each_possible(priv->fs.tc.hairpin_tbl, hpe,
524106be53bSOr Gerlitz 			       hairpin_hlist, hash_key) {
525106be53bSOr Gerlitz 		if (hpe->peer_vhca_id == peer_vhca_id && hpe->prio == prio)
5265c65c564SOr Gerlitz 			return hpe;
5275c65c564SOr Gerlitz 	}
5285c65c564SOr Gerlitz 
5295c65c564SOr Gerlitz 	return NULL;
5305c65c564SOr Gerlitz }
5315c65c564SOr Gerlitz 
532106be53bSOr Gerlitz #define UNKNOWN_MATCH_PRIO 8
533106be53bSOr Gerlitz 
534106be53bSOr Gerlitz static int mlx5e_hairpin_get_prio(struct mlx5e_priv *priv,
535e98bedf5SEli Britstein 				  struct mlx5_flow_spec *spec, u8 *match_prio,
536e98bedf5SEli Britstein 				  struct netlink_ext_ack *extack)
537106be53bSOr Gerlitz {
538106be53bSOr Gerlitz 	void *headers_c, *headers_v;
539106be53bSOr Gerlitz 	u8 prio_val, prio_mask = 0;
540106be53bSOr Gerlitz 	bool vlan_present;
541106be53bSOr Gerlitz 
542106be53bSOr Gerlitz #ifdef CONFIG_MLX5_CORE_EN_DCB
543106be53bSOr Gerlitz 	if (priv->dcbx_dp.trust_state != MLX5_QPTS_TRUST_PCP) {
544e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
545e98bedf5SEli Britstein 				   "only PCP trust state supported for hairpin");
546106be53bSOr Gerlitz 		return -EOPNOTSUPP;
547106be53bSOr Gerlitz 	}
548106be53bSOr Gerlitz #endif
549106be53bSOr Gerlitz 	headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, outer_headers);
550106be53bSOr Gerlitz 	headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers);
551106be53bSOr Gerlitz 
552106be53bSOr Gerlitz 	vlan_present = MLX5_GET(fte_match_set_lyr_2_4, headers_v, cvlan_tag);
553106be53bSOr Gerlitz 	if (vlan_present) {
554106be53bSOr Gerlitz 		prio_mask = MLX5_GET(fte_match_set_lyr_2_4, headers_c, first_prio);
555106be53bSOr Gerlitz 		prio_val = MLX5_GET(fte_match_set_lyr_2_4, headers_v, first_prio);
556106be53bSOr Gerlitz 	}
557106be53bSOr Gerlitz 
558106be53bSOr Gerlitz 	if (!vlan_present || !prio_mask) {
559106be53bSOr Gerlitz 		prio_val = UNKNOWN_MATCH_PRIO;
560106be53bSOr Gerlitz 	} else if (prio_mask != 0x7) {
561e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
562e98bedf5SEli Britstein 				   "masked priority match not supported for hairpin");
563106be53bSOr Gerlitz 		return -EOPNOTSUPP;
564106be53bSOr Gerlitz 	}
565106be53bSOr Gerlitz 
566106be53bSOr Gerlitz 	*match_prio = prio_val;
567106be53bSOr Gerlitz 	return 0;
568106be53bSOr Gerlitz }
569106be53bSOr Gerlitz 
5705c65c564SOr Gerlitz static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv,
5715c65c564SOr Gerlitz 				  struct mlx5e_tc_flow *flow,
572e98bedf5SEli Britstein 				  struct mlx5e_tc_flow_parse_attr *parse_attr,
573e98bedf5SEli Britstein 				  struct netlink_ext_ack *extack)
5745c65c564SOr Gerlitz {
5755c65c564SOr Gerlitz 	int peer_ifindex = parse_attr->mirred_ifindex;
5765c65c564SOr Gerlitz 	struct mlx5_hairpin_params params;
577d8822868SOr Gerlitz 	struct mlx5_core_dev *peer_mdev;
5785c65c564SOr Gerlitz 	struct mlx5e_hairpin_entry *hpe;
5795c65c564SOr Gerlitz 	struct mlx5e_hairpin *hp;
5803f6d08d1SOr Gerlitz 	u64 link_speed64;
5813f6d08d1SOr Gerlitz 	u32 link_speed;
582106be53bSOr Gerlitz 	u8 match_prio;
583d8822868SOr Gerlitz 	u16 peer_id;
5845c65c564SOr Gerlitz 	int err;
5855c65c564SOr Gerlitz 
586d8822868SOr Gerlitz 	peer_mdev = mlx5e_hairpin_get_mdev(dev_net(priv->netdev), peer_ifindex);
587d8822868SOr Gerlitz 	if (!MLX5_CAP_GEN(priv->mdev, hairpin) || !MLX5_CAP_GEN(peer_mdev, hairpin)) {
588e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack, "hairpin is not supported");
5895c65c564SOr Gerlitz 		return -EOPNOTSUPP;
5905c65c564SOr Gerlitz 	}
5915c65c564SOr Gerlitz 
592d8822868SOr Gerlitz 	peer_id = MLX5_CAP_GEN(peer_mdev, vhca_id);
593e98bedf5SEli Britstein 	err = mlx5e_hairpin_get_prio(priv, &parse_attr->spec, &match_prio,
594e98bedf5SEli Britstein 				     extack);
595106be53bSOr Gerlitz 	if (err)
596106be53bSOr Gerlitz 		return err;
597106be53bSOr Gerlitz 	hpe = mlx5e_hairpin_get(priv, peer_id, match_prio);
5985c65c564SOr Gerlitz 	if (hpe)
5995c65c564SOr Gerlitz 		goto attach_flow;
6005c65c564SOr Gerlitz 
6015c65c564SOr Gerlitz 	hpe = kzalloc(sizeof(*hpe), GFP_KERNEL);
6025c65c564SOr Gerlitz 	if (!hpe)
6035c65c564SOr Gerlitz 		return -ENOMEM;
6045c65c564SOr Gerlitz 
6055c65c564SOr Gerlitz 	INIT_LIST_HEAD(&hpe->flows);
606d8822868SOr Gerlitz 	hpe->peer_vhca_id = peer_id;
607106be53bSOr Gerlitz 	hpe->prio = match_prio;
6085c65c564SOr Gerlitz 
6095c65c564SOr Gerlitz 	params.log_data_size = 15;
6105c65c564SOr Gerlitz 	params.log_data_size = min_t(u8, params.log_data_size,
6115c65c564SOr Gerlitz 				     MLX5_CAP_GEN(priv->mdev, log_max_hairpin_wq_data_sz));
6125c65c564SOr Gerlitz 	params.log_data_size = max_t(u8, params.log_data_size,
6135c65c564SOr Gerlitz 				     MLX5_CAP_GEN(priv->mdev, log_min_hairpin_wq_data_sz));
6145c65c564SOr Gerlitz 
615eb9180f7SOr Gerlitz 	params.log_num_packets = params.log_data_size -
616eb9180f7SOr Gerlitz 				 MLX5_MPWRQ_MIN_LOG_STRIDE_SZ(priv->mdev);
617eb9180f7SOr Gerlitz 	params.log_num_packets = min_t(u8, params.log_num_packets,
618eb9180f7SOr Gerlitz 				       MLX5_CAP_GEN(priv->mdev, log_max_hairpin_num_packets));
619eb9180f7SOr Gerlitz 
620eb9180f7SOr Gerlitz 	params.q_counter = priv->q_counter;
6213f6d08d1SOr Gerlitz 	/* set hairpin pair per each 50Gbs share of the link */
6222c81bfd5SHuy Nguyen 	mlx5e_port_max_linkspeed(priv->mdev, &link_speed);
6233f6d08d1SOr Gerlitz 	link_speed = max_t(u32, link_speed, 50000);
6243f6d08d1SOr Gerlitz 	link_speed64 = link_speed;
6253f6d08d1SOr Gerlitz 	do_div(link_speed64, 50000);
6263f6d08d1SOr Gerlitz 	params.num_channels = link_speed64;
6273f6d08d1SOr Gerlitz 
6285c65c564SOr Gerlitz 	hp = mlx5e_hairpin_create(priv, &params, peer_ifindex);
6295c65c564SOr Gerlitz 	if (IS_ERR(hp)) {
6305c65c564SOr Gerlitz 		err = PTR_ERR(hp);
6315c65c564SOr Gerlitz 		goto create_hairpin_err;
6325c65c564SOr Gerlitz 	}
6335c65c564SOr Gerlitz 
634eb9180f7SOr Gerlitz 	netdev_dbg(priv->netdev, "add hairpin: tirn %x rqn %x peer %s sqn %x prio %d (log) data %d packets %d\n",
635ddae74acSOr Gerlitz 		   hp->tirn, hp->pair->rqn[0], hp->pair->peer_mdev->priv.name,
636eb9180f7SOr Gerlitz 		   hp->pair->sqn[0], match_prio, params.log_data_size, params.log_num_packets);
6375c65c564SOr Gerlitz 
6385c65c564SOr Gerlitz 	hpe->hp = hp;
639106be53bSOr Gerlitz 	hash_add(priv->fs.tc.hairpin_tbl, &hpe->hairpin_hlist,
640106be53bSOr Gerlitz 		 hash_hairpin_info(peer_id, match_prio));
6415c65c564SOr Gerlitz 
6425c65c564SOr Gerlitz attach_flow:
6433f6d08d1SOr Gerlitz 	if (hpe->hp->num_channels > 1) {
6443f6d08d1SOr Gerlitz 		flow->flags |= MLX5E_TC_FLOW_HAIRPIN_RSS;
6453f6d08d1SOr Gerlitz 		flow->nic_attr->hairpin_ft = hpe->hp->ttc.ft.t;
6463f6d08d1SOr Gerlitz 	} else {
6475c65c564SOr Gerlitz 		flow->nic_attr->hairpin_tirn = hpe->hp->tirn;
6483f6d08d1SOr Gerlitz 	}
6495c65c564SOr Gerlitz 	list_add(&flow->hairpin, &hpe->flows);
6503f6d08d1SOr Gerlitz 
6515c65c564SOr Gerlitz 	return 0;
6525c65c564SOr Gerlitz 
6535c65c564SOr Gerlitz create_hairpin_err:
6545c65c564SOr Gerlitz 	kfree(hpe);
6555c65c564SOr Gerlitz 	return err;
6565c65c564SOr Gerlitz }
6575c65c564SOr Gerlitz 
6585c65c564SOr Gerlitz static void mlx5e_hairpin_flow_del(struct mlx5e_priv *priv,
6595c65c564SOr Gerlitz 				   struct mlx5e_tc_flow *flow)
6605c65c564SOr Gerlitz {
6615c65c564SOr Gerlitz 	struct list_head *next = flow->hairpin.next;
6625c65c564SOr Gerlitz 
6635c65c564SOr Gerlitz 	list_del(&flow->hairpin);
6645c65c564SOr Gerlitz 
6655c65c564SOr Gerlitz 	/* no more hairpin flows for us, release the hairpin pair */
6665c65c564SOr Gerlitz 	if (list_empty(next)) {
6675c65c564SOr Gerlitz 		struct mlx5e_hairpin_entry *hpe;
6685c65c564SOr Gerlitz 
6695c65c564SOr Gerlitz 		hpe = list_entry(next, struct mlx5e_hairpin_entry, flows);
6705c65c564SOr Gerlitz 
6715c65c564SOr Gerlitz 		netdev_dbg(priv->netdev, "del hairpin: peer %s\n",
6725c65c564SOr Gerlitz 			   hpe->hp->pair->peer_mdev->priv.name);
6735c65c564SOr Gerlitz 
6745c65c564SOr Gerlitz 		mlx5e_hairpin_destroy(hpe->hp);
6755c65c564SOr Gerlitz 		hash_del(&hpe->hairpin_hlist);
6765c65c564SOr Gerlitz 		kfree(hpe);
6775c65c564SOr Gerlitz 	}
6785c65c564SOr Gerlitz }
6795c65c564SOr Gerlitz 
680c83954abSRabie Loulou static int
68174491de9SMark Bloch mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
68217091853SOr Gerlitz 		      struct mlx5e_tc_flow_parse_attr *parse_attr,
683e98bedf5SEli Britstein 		      struct mlx5e_tc_flow *flow,
684e98bedf5SEli Britstein 		      struct netlink_ext_ack *extack)
685e8f887acSAmir Vadai {
686aa0cbbaeSOr Gerlitz 	struct mlx5_nic_flow_attr *attr = flow->nic_attr;
687aad7e08dSAmir Vadai 	struct mlx5_core_dev *dev = priv->mdev;
6885c65c564SOr Gerlitz 	struct mlx5_flow_destination dest[2] = {};
68966958ed9SHadar Hen Zion 	struct mlx5_flow_act flow_act = {
6903bc4b7bfSOr Gerlitz 		.action = attr->action,
6913bc4b7bfSOr Gerlitz 		.flow_tag = attr->flow_tag,
69260786f09SMark Bloch 		.reformat_id = 0,
69342f7ad67SPaul Blakey 		.flags    = FLOW_ACT_HAS_TAG | FLOW_ACT_NO_APPEND,
69466958ed9SHadar Hen Zion 	};
695aad7e08dSAmir Vadai 	struct mlx5_fc *counter = NULL;
696e8f887acSAmir Vadai 	bool table_created = false;
6975c65c564SOr Gerlitz 	int err, dest_ix = 0;
698e8f887acSAmir Vadai 
6995c65c564SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_HAIRPIN) {
700e98bedf5SEli Britstein 		err = mlx5e_hairpin_flow_add(priv, flow, parse_attr, extack);
7015c65c564SOr Gerlitz 		if (err) {
7025c65c564SOr Gerlitz 			goto err_add_hairpin_flow;
7035c65c564SOr Gerlitz 		}
7043f6d08d1SOr Gerlitz 		if (flow->flags & MLX5E_TC_FLOW_HAIRPIN_RSS) {
7053f6d08d1SOr Gerlitz 			dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
7063f6d08d1SOr Gerlitz 			dest[dest_ix].ft = attr->hairpin_ft;
7073f6d08d1SOr Gerlitz 		} else {
7085c65c564SOr Gerlitz 			dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_TIR;
7095c65c564SOr Gerlitz 			dest[dest_ix].tir_num = attr->hairpin_tirn;
7103f6d08d1SOr Gerlitz 		}
7113f6d08d1SOr Gerlitz 		dest_ix++;
7123f6d08d1SOr Gerlitz 	} else if (attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
7135c65c564SOr Gerlitz 		dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
7145c65c564SOr Gerlitz 		dest[dest_ix].ft = priv->fs.vlan.ft.t;
7155c65c564SOr Gerlitz 		dest_ix++;
7165c65c564SOr Gerlitz 	}
717aad7e08dSAmir Vadai 
7185c65c564SOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
7195c65c564SOr Gerlitz 		counter = mlx5_fc_create(dev, true);
7205c65c564SOr Gerlitz 		if (IS_ERR(counter)) {
721c83954abSRabie Loulou 			err = PTR_ERR(counter);
7225c65c564SOr Gerlitz 			goto err_fc_create;
7235c65c564SOr Gerlitz 		}
7245c65c564SOr Gerlitz 		dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
725171c7625SMark Bloch 		dest[dest_ix].counter_id = mlx5_fc_id(counter);
7265c65c564SOr Gerlitz 		dest_ix++;
727b8aee822SMark Bloch 		attr->counter = counter;
728aad7e08dSAmir Vadai 	}
729aad7e08dSAmir Vadai 
7302f4fe4caSOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
7313099eb5aSOr Gerlitz 		err = mlx5e_attach_mod_hdr(priv, flow, parse_attr);
732d7e75a32SOr Gerlitz 		flow_act.modify_id = attr->mod_hdr_id;
7332f4fe4caSOr Gerlitz 		kfree(parse_attr->mod_hdr_actions);
734c83954abSRabie Loulou 		if (err)
7352f4fe4caSOr Gerlitz 			goto err_create_mod_hdr_id;
7362f4fe4caSOr Gerlitz 	}
7372f4fe4caSOr Gerlitz 
738acff797cSMaor Gottlieb 	if (IS_ERR_OR_NULL(priv->fs.tc.t)) {
73921b9c144SOr Gerlitz 		int tc_grp_size, tc_tbl_size;
74021b9c144SOr Gerlitz 		u32 max_flow_counter;
74121b9c144SOr Gerlitz 
74221b9c144SOr Gerlitz 		max_flow_counter = (MLX5_CAP_GEN(dev, max_flow_counter_31_16) << 16) |
74321b9c144SOr Gerlitz 				    MLX5_CAP_GEN(dev, max_flow_counter_15_0);
74421b9c144SOr Gerlitz 
74521b9c144SOr Gerlitz 		tc_grp_size = min_t(int, max_flow_counter, MLX5E_TC_TABLE_MAX_GROUP_SIZE);
74621b9c144SOr Gerlitz 
74721b9c144SOr Gerlitz 		tc_tbl_size = min_t(int, tc_grp_size * MLX5E_TC_TABLE_NUM_GROUPS,
74821b9c144SOr Gerlitz 				    BIT(MLX5_CAP_FLOWTABLE_NIC_RX(dev, log_max_ft_size)));
74921b9c144SOr Gerlitz 
750acff797cSMaor Gottlieb 		priv->fs.tc.t =
751acff797cSMaor Gottlieb 			mlx5_create_auto_grouped_flow_table(priv->fs.ns,
752acff797cSMaor Gottlieb 							    MLX5E_TC_PRIO,
75321b9c144SOr Gerlitz 							    tc_tbl_size,
754acff797cSMaor Gottlieb 							    MLX5E_TC_TABLE_NUM_GROUPS,
7553f6d08d1SOr Gerlitz 							    MLX5E_TC_FT_LEVEL, 0);
756acff797cSMaor Gottlieb 		if (IS_ERR(priv->fs.tc.t)) {
757e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
758e98bedf5SEli Britstein 					   "Failed to create tc offload table\n");
759e8f887acSAmir Vadai 			netdev_err(priv->netdev,
760e8f887acSAmir Vadai 				   "Failed to create tc offload table\n");
761c83954abSRabie Loulou 			err = PTR_ERR(priv->fs.tc.t);
762aad7e08dSAmir Vadai 			goto err_create_ft;
763e8f887acSAmir Vadai 		}
764e8f887acSAmir Vadai 
765e8f887acSAmir Vadai 		table_created = true;
766e8f887acSAmir Vadai 	}
767e8f887acSAmir Vadai 
76838aa51c1SOr Gerlitz 	if (attr->match_level != MLX5_MATCH_NONE)
76917091853SOr Gerlitz 		parse_attr->spec.match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
77038aa51c1SOr Gerlitz 
771c83954abSRabie Loulou 	flow->rule[0] = mlx5_add_flow_rules(priv->fs.tc.t, &parse_attr->spec,
7725c65c564SOr Gerlitz 					    &flow_act, dest, dest_ix);
773e8f887acSAmir Vadai 
774c83954abSRabie Loulou 	if (IS_ERR(flow->rule[0])) {
775c83954abSRabie Loulou 		err = PTR_ERR(flow->rule[0]);
776aad7e08dSAmir Vadai 		goto err_add_rule;
777c83954abSRabie Loulou 	}
778aad7e08dSAmir Vadai 
779c83954abSRabie Loulou 	return 0;
780aad7e08dSAmir Vadai 
781aad7e08dSAmir Vadai err_add_rule:
782aad7e08dSAmir Vadai 	if (table_created) {
783acff797cSMaor Gottlieb 		mlx5_destroy_flow_table(priv->fs.tc.t);
784acff797cSMaor Gottlieb 		priv->fs.tc.t = NULL;
785e8f887acSAmir Vadai 	}
786aad7e08dSAmir Vadai err_create_ft:
7872f4fe4caSOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
7883099eb5aSOr Gerlitz 		mlx5e_detach_mod_hdr(priv, flow);
7892f4fe4caSOr Gerlitz err_create_mod_hdr_id:
790aad7e08dSAmir Vadai 	mlx5_fc_destroy(dev, counter);
7915c65c564SOr Gerlitz err_fc_create:
7925c65c564SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_HAIRPIN)
7935c65c564SOr Gerlitz 		mlx5e_hairpin_flow_del(priv, flow);
7945c65c564SOr Gerlitz err_add_hairpin_flow:
795c83954abSRabie Loulou 	return err;
796e8f887acSAmir Vadai }
797e8f887acSAmir Vadai 
798d85cdccbSOr Gerlitz static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
799d85cdccbSOr Gerlitz 				  struct mlx5e_tc_flow *flow)
800d85cdccbSOr Gerlitz {
801513f8f7fSOr Gerlitz 	struct mlx5_nic_flow_attr *attr = flow->nic_attr;
802d85cdccbSOr Gerlitz 	struct mlx5_fc *counter = NULL;
803d85cdccbSOr Gerlitz 
804b8aee822SMark Bloch 	counter = attr->counter;
805e4ad91f2SChris Mi 	mlx5_del_flow_rules(flow->rule[0]);
806d85cdccbSOr Gerlitz 	mlx5_fc_destroy(priv->mdev, counter);
807d85cdccbSOr Gerlitz 
808b3a433deSOr Gerlitz 	if (!mlx5e_tc_num_filters(priv) && priv->fs.tc.t) {
809d85cdccbSOr Gerlitz 		mlx5_destroy_flow_table(priv->fs.tc.t);
810d85cdccbSOr Gerlitz 		priv->fs.tc.t = NULL;
811d85cdccbSOr Gerlitz 	}
8122f4fe4caSOr Gerlitz 
813513f8f7fSOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
8143099eb5aSOr Gerlitz 		mlx5e_detach_mod_hdr(priv, flow);
8155c65c564SOr Gerlitz 
8165c65c564SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_HAIRPIN)
8175c65c564SOr Gerlitz 		mlx5e_hairpin_flow_del(priv, flow);
818d85cdccbSOr Gerlitz }
819d85cdccbSOr Gerlitz 
820aa0cbbaeSOr Gerlitz static void mlx5e_detach_encap(struct mlx5e_priv *priv,
821aa0cbbaeSOr Gerlitz 			       struct mlx5e_tc_flow *flow);
822aa0cbbaeSOr Gerlitz 
8233c37745eSOr Gerlitz static int mlx5e_attach_encap(struct mlx5e_priv *priv,
8243c37745eSOr Gerlitz 			      struct ip_tunnel_info *tun_info,
8253c37745eSOr Gerlitz 			      struct net_device *mirred_dev,
8263c37745eSOr Gerlitz 			      struct net_device **encap_dev,
827e98bedf5SEli Britstein 			      struct mlx5e_tc_flow *flow,
828e98bedf5SEli Britstein 			      struct netlink_ext_ack *extack);
8293c37745eSOr Gerlitz 
8306d2a3ed0SOr Gerlitz static struct mlx5_flow_handle *
8316d2a3ed0SOr Gerlitz mlx5e_tc_offload_fdb_rules(struct mlx5_eswitch *esw,
8326d2a3ed0SOr Gerlitz 			   struct mlx5e_tc_flow *flow,
8336d2a3ed0SOr Gerlitz 			   struct mlx5_flow_spec *spec,
8346d2a3ed0SOr Gerlitz 			   struct mlx5_esw_flow_attr *attr)
8356d2a3ed0SOr Gerlitz {
8366d2a3ed0SOr Gerlitz 	struct mlx5_flow_handle *rule;
8376d2a3ed0SOr Gerlitz 
8386d2a3ed0SOr Gerlitz 	rule = mlx5_eswitch_add_offloaded_rule(esw, spec, attr);
8396d2a3ed0SOr Gerlitz 	if (IS_ERR(rule))
8406d2a3ed0SOr Gerlitz 		return rule;
8416d2a3ed0SOr Gerlitz 
8426d2a3ed0SOr Gerlitz 	if (attr->mirror_count) {
8436d2a3ed0SOr Gerlitz 		flow->rule[1] = mlx5_eswitch_add_fwd_rule(esw, spec, attr);
8446d2a3ed0SOr Gerlitz 		if (IS_ERR(flow->rule[1])) {
8456d2a3ed0SOr Gerlitz 			mlx5_eswitch_del_offloaded_rule(esw, rule, attr);
8466d2a3ed0SOr Gerlitz 			return flow->rule[1];
8476d2a3ed0SOr Gerlitz 		}
8486d2a3ed0SOr Gerlitz 	}
8496d2a3ed0SOr Gerlitz 
8506d2a3ed0SOr Gerlitz 	flow->flags |= MLX5E_TC_FLOW_OFFLOADED;
8516d2a3ed0SOr Gerlitz 	return rule;
8526d2a3ed0SOr Gerlitz }
8536d2a3ed0SOr Gerlitz 
8546d2a3ed0SOr Gerlitz static void
8556d2a3ed0SOr Gerlitz mlx5e_tc_unoffload_fdb_rules(struct mlx5_eswitch *esw,
8566d2a3ed0SOr Gerlitz 			     struct mlx5e_tc_flow *flow,
8576d2a3ed0SOr Gerlitz 			   struct mlx5_esw_flow_attr *attr)
8586d2a3ed0SOr Gerlitz {
8596d2a3ed0SOr Gerlitz 	flow->flags &= ~MLX5E_TC_FLOW_OFFLOADED;
8606d2a3ed0SOr Gerlitz 
8616d2a3ed0SOr Gerlitz 	if (attr->mirror_count)
8626d2a3ed0SOr Gerlitz 		mlx5_eswitch_del_fwd_rule(esw, flow->rule[1], attr);
8636d2a3ed0SOr Gerlitz 
8646d2a3ed0SOr Gerlitz 	mlx5_eswitch_del_offloaded_rule(esw, flow->rule[0], attr);
8656d2a3ed0SOr Gerlitz }
8666d2a3ed0SOr Gerlitz 
8675dbe906fSPaul Blakey static struct mlx5_flow_handle *
8685dbe906fSPaul Blakey mlx5e_tc_offload_to_slow_path(struct mlx5_eswitch *esw,
8695dbe906fSPaul Blakey 			      struct mlx5e_tc_flow *flow,
8705dbe906fSPaul Blakey 			      struct mlx5_flow_spec *spec,
8715dbe906fSPaul Blakey 			      struct mlx5_esw_flow_attr *slow_attr)
8725dbe906fSPaul Blakey {
8735dbe906fSPaul Blakey 	struct mlx5_flow_handle *rule;
8745dbe906fSPaul Blakey 
8755dbe906fSPaul Blakey 	memcpy(slow_attr, flow->esw_attr, sizeof(*slow_attr));
8765dbe906fSPaul Blakey 	slow_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
8775dbe906fSPaul Blakey 	slow_attr->mirror_count = 0,
8785dbe906fSPaul Blakey 	slow_attr->dest_chain = FDB_SLOW_PATH_CHAIN,
8795dbe906fSPaul Blakey 
8805dbe906fSPaul Blakey 	rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, slow_attr);
8815dbe906fSPaul Blakey 	if (!IS_ERR(rule))
8825dbe906fSPaul Blakey 		flow->flags |= MLX5E_TC_FLOW_SLOW;
8835dbe906fSPaul Blakey 
8845dbe906fSPaul Blakey 	return rule;
8855dbe906fSPaul Blakey }
8865dbe906fSPaul Blakey 
8875dbe906fSPaul Blakey static void
8885dbe906fSPaul Blakey mlx5e_tc_unoffload_from_slow_path(struct mlx5_eswitch *esw,
8895dbe906fSPaul Blakey 				  struct mlx5e_tc_flow *flow,
8905dbe906fSPaul Blakey 				  struct mlx5_esw_flow_attr *slow_attr)
8915dbe906fSPaul Blakey {
8925dbe906fSPaul Blakey 	memcpy(slow_attr, flow->esw_attr, sizeof(*slow_attr));
8935dbe906fSPaul Blakey 	mlx5e_tc_unoffload_fdb_rules(esw, flow, slow_attr);
8945dbe906fSPaul Blakey 	flow->flags &= ~MLX5E_TC_FLOW_SLOW;
8955dbe906fSPaul Blakey }
8965dbe906fSPaul Blakey 
897c83954abSRabie Loulou static int
89874491de9SMark Bloch mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
89917091853SOr Gerlitz 		      struct mlx5e_tc_flow_parse_attr *parse_attr,
900e98bedf5SEli Britstein 		      struct mlx5e_tc_flow *flow,
901e98bedf5SEli Britstein 		      struct netlink_ext_ack *extack)
902adb4c123SOr Gerlitz {
903adb4c123SOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
904bf07aa73SPaul Blakey 	u32 max_chain = mlx5_eswitch_get_chain_range(esw);
905aa0cbbaeSOr Gerlitz 	struct mlx5_esw_flow_attr *attr = flow->esw_attr;
906bf07aa73SPaul Blakey 	u16 max_prio = mlx5_eswitch_get_prio_range(esw);
9073c37745eSOr Gerlitz 	struct net_device *out_dev, *encap_dev = NULL;
908b8aee822SMark Bloch 	struct mlx5_fc *counter = NULL;
9093c37745eSOr Gerlitz 	struct mlx5e_rep_priv *rpriv;
9103c37745eSOr Gerlitz 	struct mlx5e_priv *out_priv;
911c83954abSRabie Loulou 	int err = 0, encap_err = 0;
9128b32580dSOr Gerlitz 
913bf07aa73SPaul Blakey 	/* if prios are not supported, keep the old behaviour of using same prio
914bf07aa73SPaul Blakey 	 * for all offloaded rules.
915bf07aa73SPaul Blakey 	 */
916bf07aa73SPaul Blakey 	if (!mlx5_eswitch_prios_supported(esw))
917e52c2802SPaul Blakey 		attr->prio = 1;
918e52c2802SPaul Blakey 
919bf07aa73SPaul Blakey 	if (attr->chain > max_chain) {
920bf07aa73SPaul Blakey 		NL_SET_ERR_MSG(extack, "Requested chain is out of supported range");
921bf07aa73SPaul Blakey 		err = -EOPNOTSUPP;
922bf07aa73SPaul Blakey 		goto err_max_prio_chain;
923bf07aa73SPaul Blakey 	}
924bf07aa73SPaul Blakey 
925bf07aa73SPaul Blakey 	if (attr->prio > max_prio) {
926bf07aa73SPaul Blakey 		NL_SET_ERR_MSG(extack, "Requested priority is out of supported range");
927bf07aa73SPaul Blakey 		err = -EOPNOTSUPP;
928bf07aa73SPaul Blakey 		goto err_max_prio_chain;
929bf07aa73SPaul Blakey 	}
930bf07aa73SPaul Blakey 
93160786f09SMark Bloch 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT) {
9323c37745eSOr Gerlitz 		out_dev = __dev_get_by_index(dev_net(priv->netdev),
9333c37745eSOr Gerlitz 					     attr->parse_attr->mirred_ifindex);
934c83954abSRabie Loulou 		encap_err = mlx5e_attach_encap(priv, &parse_attr->tun_info,
935c83954abSRabie Loulou 					       out_dev, &encap_dev, flow,
936c83954abSRabie Loulou 					       extack);
937c83954abSRabie Loulou 		if (encap_err && encap_err != -EAGAIN) {
938c83954abSRabie Loulou 			err = encap_err;
9393c37745eSOr Gerlitz 			goto err_attach_encap;
9403c37745eSOr Gerlitz 		}
9413c37745eSOr Gerlitz 		out_priv = netdev_priv(encap_dev);
9423c37745eSOr Gerlitz 		rpriv = out_priv->ppriv;
943592d3651SChris Mi 		attr->out_rep[attr->out_count] = rpriv->rep;
944592d3651SChris Mi 		attr->out_mdev[attr->out_count++] = out_priv->mdev;
9453c37745eSOr Gerlitz 	}
9463c37745eSOr Gerlitz 
9478b32580dSOr Gerlitz 	err = mlx5_eswitch_add_vlan_action(esw, attr);
948c83954abSRabie Loulou 	if (err)
949aa0cbbaeSOr Gerlitz 		goto err_add_vlan;
950adb4c123SOr Gerlitz 
951d7e75a32SOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
9521a9527bbSOr Gerlitz 		err = mlx5e_attach_mod_hdr(priv, flow, parse_attr);
953d7e75a32SOr Gerlitz 		kfree(parse_attr->mod_hdr_actions);
954c83954abSRabie Loulou 		if (err)
955d7e75a32SOr Gerlitz 			goto err_mod_hdr;
956d7e75a32SOr Gerlitz 	}
957d7e75a32SOr Gerlitz 
958b8aee822SMark Bloch 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
959b8aee822SMark Bloch 		counter = mlx5_fc_create(esw->dev, true);
960b8aee822SMark Bloch 		if (IS_ERR(counter)) {
961c83954abSRabie Loulou 			err = PTR_ERR(counter);
962b8aee822SMark Bloch 			goto err_create_counter;
963b8aee822SMark Bloch 		}
964b8aee822SMark Bloch 
965b8aee822SMark Bloch 		attr->counter = counter;
966b8aee822SMark Bloch 	}
967b8aee822SMark Bloch 
968c83954abSRabie Loulou 	/* we get here if (1) there's no error or when
9693c37745eSOr Gerlitz 	 * (2) there's an encap action and we're on -EAGAIN (no valid neigh)
9703c37745eSOr Gerlitz 	 */
9715dbe906fSPaul Blakey 	if (encap_err == -EAGAIN) {
9725dbe906fSPaul Blakey 		/* continue with goto slow path rule instead */
9735dbe906fSPaul Blakey 		struct mlx5_esw_flow_attr slow_attr;
9745dbe906fSPaul Blakey 
9755dbe906fSPaul Blakey 		flow->rule[0] = mlx5e_tc_offload_to_slow_path(esw, flow, &parse_attr->spec, &slow_attr);
9765dbe906fSPaul Blakey 	} else {
9776d2a3ed0SOr Gerlitz 		flow->rule[0] = mlx5e_tc_offload_fdb_rules(esw, flow, &parse_attr->spec, attr);
9785dbe906fSPaul Blakey 	}
9795dbe906fSPaul Blakey 
980c83954abSRabie Loulou 	if (IS_ERR(flow->rule[0])) {
981c83954abSRabie Loulou 		err = PTR_ERR(flow->rule[0]);
982aa0cbbaeSOr Gerlitz 		goto err_add_rule;
983c83954abSRabie Loulou 	}
984c83954abSRabie Loulou 
9855dbe906fSPaul Blakey 	return 0;
986aa0cbbaeSOr Gerlitz 
987aa0cbbaeSOr Gerlitz err_add_rule:
988b8aee822SMark Bloch 	mlx5_fc_destroy(esw->dev, counter);
989b8aee822SMark Bloch err_create_counter:
990513f8f7fSOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
9911a9527bbSOr Gerlitz 		mlx5e_detach_mod_hdr(priv, flow);
992d7e75a32SOr Gerlitz err_mod_hdr:
993aa0cbbaeSOr Gerlitz 	mlx5_eswitch_del_vlan_action(esw, attr);
994aa0cbbaeSOr Gerlitz err_add_vlan:
99560786f09SMark Bloch 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT)
996aa0cbbaeSOr Gerlitz 		mlx5e_detach_encap(priv, flow);
9973c37745eSOr Gerlitz err_attach_encap:
998bf07aa73SPaul Blakey err_max_prio_chain:
999c83954abSRabie Loulou 	return err;
1000aa0cbbaeSOr Gerlitz }
1001d85cdccbSOr Gerlitz 
1002d85cdccbSOr Gerlitz static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
1003d85cdccbSOr Gerlitz 				  struct mlx5e_tc_flow *flow)
1004d85cdccbSOr Gerlitz {
1005d85cdccbSOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
1006d7e75a32SOr Gerlitz 	struct mlx5_esw_flow_attr *attr = flow->esw_attr;
10075dbe906fSPaul Blakey 	struct mlx5_esw_flow_attr slow_attr;
1008d85cdccbSOr Gerlitz 
10095dbe906fSPaul Blakey 	if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
10105dbe906fSPaul Blakey 		if (flow->flags & MLX5E_TC_FLOW_SLOW)
10115dbe906fSPaul Blakey 			mlx5e_tc_unoffload_from_slow_path(esw, flow, &slow_attr);
10125dbe906fSPaul Blakey 		else
10135dbe906fSPaul Blakey 			mlx5e_tc_unoffload_fdb_rules(esw, flow, attr);
10145dbe906fSPaul Blakey 	}
1015d85cdccbSOr Gerlitz 
1016513f8f7fSOr Gerlitz 	mlx5_eswitch_del_vlan_action(esw, attr);
1017d85cdccbSOr Gerlitz 
101860786f09SMark Bloch 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT) {
1019d85cdccbSOr Gerlitz 		mlx5e_detach_encap(priv, flow);
1020513f8f7fSOr Gerlitz 		kvfree(attr->parse_attr);
1021232c0013SHadar Hen Zion 	}
1022d7e75a32SOr Gerlitz 
1023513f8f7fSOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
10241a9527bbSOr Gerlitz 		mlx5e_detach_mod_hdr(priv, flow);
1025b8aee822SMark Bloch 
1026b8aee822SMark Bloch 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT)
1027b8aee822SMark Bloch 		mlx5_fc_destroy(esw->dev, attr->counter);
1028d85cdccbSOr Gerlitz }
1029d85cdccbSOr Gerlitz 
1030232c0013SHadar Hen Zion void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv,
1031232c0013SHadar Hen Zion 			      struct mlx5e_encap_entry *e)
1032232c0013SHadar Hen Zion {
10333c37745eSOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
10345dbe906fSPaul Blakey 	struct mlx5_esw_flow_attr slow_attr, *esw_attr;
10356d2a3ed0SOr Gerlitz 	struct mlx5_flow_handle *rule;
10366d2a3ed0SOr Gerlitz 	struct mlx5_flow_spec *spec;
1037232c0013SHadar Hen Zion 	struct mlx5e_tc_flow *flow;
1038232c0013SHadar Hen Zion 	int err;
1039232c0013SHadar Hen Zion 
104060786f09SMark Bloch 	err = mlx5_packet_reformat_alloc(priv->mdev, e->tunnel_type,
1041232c0013SHadar Hen Zion 					 e->encap_size, e->encap_header,
104231ca3648SMark Bloch 					 MLX5_FLOW_NAMESPACE_FDB,
1043232c0013SHadar Hen Zion 					 &e->encap_id);
1044232c0013SHadar Hen Zion 	if (err) {
1045232c0013SHadar Hen Zion 		mlx5_core_warn(priv->mdev, "Failed to offload cached encapsulation header, %d\n",
1046232c0013SHadar Hen Zion 			       err);
1047232c0013SHadar Hen Zion 		return;
1048232c0013SHadar Hen Zion 	}
1049232c0013SHadar Hen Zion 	e->flags |= MLX5_ENCAP_ENTRY_VALID;
1050f6dfb4c3SHadar Hen Zion 	mlx5e_rep_queue_neigh_stats_work(priv);
1051232c0013SHadar Hen Zion 
1052232c0013SHadar Hen Zion 	list_for_each_entry(flow, &e->flows, encap) {
10533c37745eSOr Gerlitz 		esw_attr = flow->esw_attr;
10543c37745eSOr Gerlitz 		esw_attr->encap_id = e->encap_id;
10556d2a3ed0SOr Gerlitz 		spec = &esw_attr->parse_attr->spec;
10566d2a3ed0SOr Gerlitz 
10575dbe906fSPaul Blakey 		/* update from slow path rule to encap rule */
10586d2a3ed0SOr Gerlitz 		rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, esw_attr);
10596d2a3ed0SOr Gerlitz 		if (IS_ERR(rule)) {
10606d2a3ed0SOr Gerlitz 			err = PTR_ERR(rule);
1061232c0013SHadar Hen Zion 			mlx5_core_warn(priv->mdev, "Failed to update cached encapsulation flow, %d\n",
1062232c0013SHadar Hen Zion 				       err);
1063232c0013SHadar Hen Zion 			continue;
1064232c0013SHadar Hen Zion 		}
10655dbe906fSPaul Blakey 
10665dbe906fSPaul Blakey 		mlx5e_tc_unoffload_from_slow_path(esw, flow, &slow_attr);
10675dbe906fSPaul Blakey 		flow->flags |= MLX5E_TC_FLOW_OFFLOADED; /* was unset when slow path rule removed */
10686d2a3ed0SOr Gerlitz 		flow->rule[0] = rule;
1069232c0013SHadar Hen Zion 	}
1070232c0013SHadar Hen Zion }
1071232c0013SHadar Hen Zion 
1072232c0013SHadar Hen Zion void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv,
1073232c0013SHadar Hen Zion 			      struct mlx5e_encap_entry *e)
1074232c0013SHadar Hen Zion {
10753c37745eSOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
10765dbe906fSPaul Blakey 	struct mlx5_esw_flow_attr slow_attr;
10775dbe906fSPaul Blakey 	struct mlx5_flow_handle *rule;
10785dbe906fSPaul Blakey 	struct mlx5_flow_spec *spec;
1079232c0013SHadar Hen Zion 	struct mlx5e_tc_flow *flow;
10805dbe906fSPaul Blakey 	int err;
1081232c0013SHadar Hen Zion 
1082232c0013SHadar Hen Zion 	list_for_each_entry(flow, &e->flows, encap) {
10835dbe906fSPaul Blakey 		spec = &flow->esw_attr->parse_attr->spec;
10845dbe906fSPaul Blakey 
10855dbe906fSPaul Blakey 		/* update from encap rule to slow path rule */
10865dbe906fSPaul Blakey 		rule = mlx5e_tc_offload_to_slow_path(esw, flow, spec, &slow_attr);
10875dbe906fSPaul Blakey 
10885dbe906fSPaul Blakey 		if (IS_ERR(rule)) {
10895dbe906fSPaul Blakey 			err = PTR_ERR(rule);
10905dbe906fSPaul Blakey 			mlx5_core_warn(priv->mdev, "Failed to update slow path (encap) flow, %d\n",
10915dbe906fSPaul Blakey 				       err);
10925dbe906fSPaul Blakey 			continue;
10935dbe906fSPaul Blakey 		}
10945dbe906fSPaul Blakey 
10956d2a3ed0SOr Gerlitz 		mlx5e_tc_unoffload_fdb_rules(esw, flow, flow->esw_attr);
10965dbe906fSPaul Blakey 		flow->flags |= MLX5E_TC_FLOW_OFFLOADED; /* was unset when fast path rule removed */
10975dbe906fSPaul Blakey 		flow->rule[0] = rule;
1098232c0013SHadar Hen Zion 	}
1099232c0013SHadar Hen Zion 
1100232c0013SHadar Hen Zion 	if (e->flags & MLX5_ENCAP_ENTRY_VALID) {
1101232c0013SHadar Hen Zion 		e->flags &= ~MLX5_ENCAP_ENTRY_VALID;
110260786f09SMark Bloch 		mlx5_packet_reformat_dealloc(priv->mdev, e->encap_id);
1103232c0013SHadar Hen Zion 	}
1104232c0013SHadar Hen Zion }
1105232c0013SHadar Hen Zion 
1106b8aee822SMark Bloch static struct mlx5_fc *mlx5e_tc_get_counter(struct mlx5e_tc_flow *flow)
1107b8aee822SMark Bloch {
1108b8aee822SMark Bloch 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
1109b8aee822SMark Bloch 		return flow->esw_attr->counter;
1110b8aee822SMark Bloch 	else
1111b8aee822SMark Bloch 		return flow->nic_attr->counter;
1112b8aee822SMark Bloch }
1113b8aee822SMark Bloch 
1114f6dfb4c3SHadar Hen Zion void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
1115f6dfb4c3SHadar Hen Zion {
1116f6dfb4c3SHadar Hen Zion 	struct mlx5e_neigh *m_neigh = &nhe->m_neigh;
1117f6dfb4c3SHadar Hen Zion 	u64 bytes, packets, lastuse = 0;
1118f6dfb4c3SHadar Hen Zion 	struct mlx5e_tc_flow *flow;
1119f6dfb4c3SHadar Hen Zion 	struct mlx5e_encap_entry *e;
1120f6dfb4c3SHadar Hen Zion 	struct mlx5_fc *counter;
1121f6dfb4c3SHadar Hen Zion 	struct neigh_table *tbl;
1122f6dfb4c3SHadar Hen Zion 	bool neigh_used = false;
1123f6dfb4c3SHadar Hen Zion 	struct neighbour *n;
1124f6dfb4c3SHadar Hen Zion 
1125f6dfb4c3SHadar Hen Zion 	if (m_neigh->family == AF_INET)
1126f6dfb4c3SHadar Hen Zion 		tbl = &arp_tbl;
1127f6dfb4c3SHadar Hen Zion #if IS_ENABLED(CONFIG_IPV6)
1128f6dfb4c3SHadar Hen Zion 	else if (m_neigh->family == AF_INET6)
1129423c9db2SOr Gerlitz 		tbl = &nd_tbl;
1130f6dfb4c3SHadar Hen Zion #endif
1131f6dfb4c3SHadar Hen Zion 	else
1132f6dfb4c3SHadar Hen Zion 		return;
1133f6dfb4c3SHadar Hen Zion 
1134f6dfb4c3SHadar Hen Zion 	list_for_each_entry(e, &nhe->encap_list, encap_list) {
1135f6dfb4c3SHadar Hen Zion 		if (!(e->flags & MLX5_ENCAP_ENTRY_VALID))
1136f6dfb4c3SHadar Hen Zion 			continue;
1137f6dfb4c3SHadar Hen Zion 		list_for_each_entry(flow, &e->flows, encap) {
1138f6dfb4c3SHadar Hen Zion 			if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
1139b8aee822SMark Bloch 				counter = mlx5e_tc_get_counter(flow);
1140f6dfb4c3SHadar Hen Zion 				mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
1141f6dfb4c3SHadar Hen Zion 				if (time_after((unsigned long)lastuse, nhe->reported_lastuse)) {
1142f6dfb4c3SHadar Hen Zion 					neigh_used = true;
1143f6dfb4c3SHadar Hen Zion 					break;
1144f6dfb4c3SHadar Hen Zion 				}
1145f6dfb4c3SHadar Hen Zion 			}
1146f6dfb4c3SHadar Hen Zion 		}
1147e36d4810SRoi Dayan 		if (neigh_used)
1148e36d4810SRoi Dayan 			break;
1149f6dfb4c3SHadar Hen Zion 	}
1150f6dfb4c3SHadar Hen Zion 
1151f6dfb4c3SHadar Hen Zion 	if (neigh_used) {
1152f6dfb4c3SHadar Hen Zion 		nhe->reported_lastuse = jiffies;
1153f6dfb4c3SHadar Hen Zion 
1154f6dfb4c3SHadar Hen Zion 		/* find the relevant neigh according to the cached device and
1155f6dfb4c3SHadar Hen Zion 		 * dst ip pair
1156f6dfb4c3SHadar Hen Zion 		 */
1157f6dfb4c3SHadar Hen Zion 		n = neigh_lookup(tbl, &m_neigh->dst_ip, m_neigh->dev);
1158c7f7ba8dSRoi Dayan 		if (!n)
1159f6dfb4c3SHadar Hen Zion 			return;
1160f6dfb4c3SHadar Hen Zion 
1161f6dfb4c3SHadar Hen Zion 		neigh_event_send(n, NULL);
1162f6dfb4c3SHadar Hen Zion 		neigh_release(n);
1163f6dfb4c3SHadar Hen Zion 	}
1164f6dfb4c3SHadar Hen Zion }
1165f6dfb4c3SHadar Hen Zion 
1166d85cdccbSOr Gerlitz static void mlx5e_detach_encap(struct mlx5e_priv *priv,
1167d85cdccbSOr Gerlitz 			       struct mlx5e_tc_flow *flow)
1168d85cdccbSOr Gerlitz {
11695067b602SRoi Dayan 	struct list_head *next = flow->encap.next;
11705067b602SRoi Dayan 
11715067b602SRoi Dayan 	list_del(&flow->encap);
11725067b602SRoi Dayan 	if (list_empty(next)) {
1173c1ae1152SOr Gerlitz 		struct mlx5e_encap_entry *e;
11745067b602SRoi Dayan 
1175c1ae1152SOr Gerlitz 		e = list_entry(next, struct mlx5e_encap_entry, flows);
1176232c0013SHadar Hen Zion 		mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
1177232c0013SHadar Hen Zion 
1178232c0013SHadar Hen Zion 		if (e->flags & MLX5_ENCAP_ENTRY_VALID)
117960786f09SMark Bloch 			mlx5_packet_reformat_dealloc(priv->mdev, e->encap_id);
1180232c0013SHadar Hen Zion 
1181cdc5a7f3SOr Gerlitz 		hash_del_rcu(&e->encap_hlist);
1182232c0013SHadar Hen Zion 		kfree(e->encap_header);
11835067b602SRoi Dayan 		kfree(e);
11845067b602SRoi Dayan 	}
11855067b602SRoi Dayan }
11865067b602SRoi Dayan 
1187e8f887acSAmir Vadai static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
1188961e8979SRoi Dayan 			      struct mlx5e_tc_flow *flow)
1189e8f887acSAmir Vadai {
1190d85cdccbSOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
1191d85cdccbSOr Gerlitz 		mlx5e_tc_del_fdb_flow(priv, flow);
1192d85cdccbSOr Gerlitz 	else
1193d85cdccbSOr Gerlitz 		mlx5e_tc_del_nic_flow(priv, flow);
1194e8f887acSAmir Vadai }
1195e8f887acSAmir Vadai 
1196bbd00f7eSHadar Hen Zion static void parse_vxlan_attr(struct mlx5_flow_spec *spec,
1197bbd00f7eSHadar Hen Zion 			     struct tc_cls_flower_offload *f)
1198bbd00f7eSHadar Hen Zion {
1199bbd00f7eSHadar Hen Zion 	void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
1200bbd00f7eSHadar Hen Zion 				       outer_headers);
1201bbd00f7eSHadar Hen Zion 	void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
1202bbd00f7eSHadar Hen Zion 				       outer_headers);
1203bbd00f7eSHadar Hen Zion 	void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
1204bbd00f7eSHadar Hen Zion 				    misc_parameters);
1205bbd00f7eSHadar Hen Zion 	void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
1206bbd00f7eSHadar Hen Zion 				    misc_parameters);
1207bbd00f7eSHadar Hen Zion 
1208bbd00f7eSHadar Hen Zion 	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ip_protocol);
1209bbd00f7eSHadar Hen Zion 	MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol, IPPROTO_UDP);
1210bbd00f7eSHadar Hen Zion 
1211bbd00f7eSHadar Hen Zion 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
1212bbd00f7eSHadar Hen Zion 		struct flow_dissector_key_keyid *key =
1213bbd00f7eSHadar Hen Zion 			skb_flow_dissector_target(f->dissector,
1214bbd00f7eSHadar Hen Zion 						  FLOW_DISSECTOR_KEY_ENC_KEYID,
1215bbd00f7eSHadar Hen Zion 						  f->key);
1216bbd00f7eSHadar Hen Zion 		struct flow_dissector_key_keyid *mask =
1217bbd00f7eSHadar Hen Zion 			skb_flow_dissector_target(f->dissector,
1218bbd00f7eSHadar Hen Zion 						  FLOW_DISSECTOR_KEY_ENC_KEYID,
1219bbd00f7eSHadar Hen Zion 						  f->mask);
1220bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
1221bbd00f7eSHadar Hen Zion 			 be32_to_cpu(mask->keyid));
1222bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
1223bbd00f7eSHadar Hen Zion 			 be32_to_cpu(key->keyid));
1224bbd00f7eSHadar Hen Zion 	}
1225bbd00f7eSHadar Hen Zion }
1226bbd00f7eSHadar Hen Zion 
1227bbd00f7eSHadar Hen Zion static int parse_tunnel_attr(struct mlx5e_priv *priv,
1228bbd00f7eSHadar Hen Zion 			     struct mlx5_flow_spec *spec,
1229bbd00f7eSHadar Hen Zion 			     struct tc_cls_flower_offload *f)
1230bbd00f7eSHadar Hen Zion {
1231e98bedf5SEli Britstein 	struct netlink_ext_ack *extack = f->common.extack;
1232bbd00f7eSHadar Hen Zion 	void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
1233bbd00f7eSHadar Hen Zion 				       outer_headers);
1234bbd00f7eSHadar Hen Zion 	void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
1235bbd00f7eSHadar Hen Zion 				       outer_headers);
1236bbd00f7eSHadar Hen Zion 
12372e72eb43SOr Gerlitz 	struct flow_dissector_key_control *enc_control =
12382e72eb43SOr Gerlitz 		skb_flow_dissector_target(f->dissector,
12392e72eb43SOr Gerlitz 					  FLOW_DISSECTOR_KEY_ENC_CONTROL,
12402e72eb43SOr Gerlitz 					  f->key);
12412e72eb43SOr Gerlitz 
1242bbd00f7eSHadar Hen Zion 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
1243bbd00f7eSHadar Hen Zion 		struct flow_dissector_key_ports *key =
1244bbd00f7eSHadar Hen Zion 			skb_flow_dissector_target(f->dissector,
1245bbd00f7eSHadar Hen Zion 						  FLOW_DISSECTOR_KEY_ENC_PORTS,
1246bbd00f7eSHadar Hen Zion 						  f->key);
1247bbd00f7eSHadar Hen Zion 		struct flow_dissector_key_ports *mask =
1248bbd00f7eSHadar Hen Zion 			skb_flow_dissector_target(f->dissector,
1249bbd00f7eSHadar Hen Zion 						  FLOW_DISSECTOR_KEY_ENC_PORTS,
1250bbd00f7eSHadar Hen Zion 						  f->mask);
1251bbd00f7eSHadar Hen Zion 
1252bbd00f7eSHadar Hen Zion 		/* Full udp dst port must be given */
1253bbd00f7eSHadar Hen Zion 		if (memchr_inv(&mask->dst, 0xff, sizeof(mask->dst)))
12542fcd82e9SOr Gerlitz 			goto vxlan_match_offload_err;
1255bbd00f7eSHadar Hen Zion 
1256a3e67366SSaeed Mahameed 		if (mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(key->dst)) &&
1257bbd00f7eSHadar Hen Zion 		    MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap))
1258bbd00f7eSHadar Hen Zion 			parse_vxlan_attr(spec, f);
12592fcd82e9SOr Gerlitz 		else {
1260e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1261e98bedf5SEli Britstein 					   "port isn't an offloaded vxlan udp dport");
12622fcd82e9SOr Gerlitz 			netdev_warn(priv->netdev,
12632fcd82e9SOr Gerlitz 				    "%d isn't an offloaded vxlan udp dport\n", be16_to_cpu(key->dst));
1264bbd00f7eSHadar Hen Zion 			return -EOPNOTSUPP;
12652fcd82e9SOr Gerlitz 		}
1266bbd00f7eSHadar Hen Zion 
1267bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1268bbd00f7eSHadar Hen Zion 			 udp_dport, ntohs(mask->dst));
1269bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1270bbd00f7eSHadar Hen Zion 			 udp_dport, ntohs(key->dst));
1271bbd00f7eSHadar Hen Zion 
1272cd377663SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1273cd377663SOr Gerlitz 			 udp_sport, ntohs(mask->src));
1274cd377663SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1275cd377663SOr Gerlitz 			 udp_sport, ntohs(key->src));
1276bbd00f7eSHadar Hen Zion 	} else { /* udp dst port must be given */
12772fcd82e9SOr Gerlitz vxlan_match_offload_err:
1278e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
1279e98bedf5SEli Britstein 				   "IP tunnel decap offload supported only for vxlan, must set UDP dport");
12802fcd82e9SOr Gerlitz 		netdev_warn(priv->netdev,
12812fcd82e9SOr Gerlitz 			    "IP tunnel decap offload supported only for vxlan, must set UDP dport\n");
1282bbd00f7eSHadar Hen Zion 		return -EOPNOTSUPP;
1283bbd00f7eSHadar Hen Zion 	}
1284bbd00f7eSHadar Hen Zion 
12852e72eb43SOr Gerlitz 	if (enc_control->addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
1286bbd00f7eSHadar Hen Zion 		struct flow_dissector_key_ipv4_addrs *key =
1287bbd00f7eSHadar Hen Zion 			skb_flow_dissector_target(f->dissector,
1288bbd00f7eSHadar Hen Zion 						  FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
1289bbd00f7eSHadar Hen Zion 						  f->key);
1290bbd00f7eSHadar Hen Zion 		struct flow_dissector_key_ipv4_addrs *mask =
1291bbd00f7eSHadar Hen Zion 			skb_flow_dissector_target(f->dissector,
1292bbd00f7eSHadar Hen Zion 						  FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
1293bbd00f7eSHadar Hen Zion 						  f->mask);
1294bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1295bbd00f7eSHadar Hen Zion 			 src_ipv4_src_ipv6.ipv4_layout.ipv4,
1296bbd00f7eSHadar Hen Zion 			 ntohl(mask->src));
1297bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1298bbd00f7eSHadar Hen Zion 			 src_ipv4_src_ipv6.ipv4_layout.ipv4,
1299bbd00f7eSHadar Hen Zion 			 ntohl(key->src));
1300bbd00f7eSHadar Hen Zion 
1301bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1302bbd00f7eSHadar Hen Zion 			 dst_ipv4_dst_ipv6.ipv4_layout.ipv4,
1303bbd00f7eSHadar Hen Zion 			 ntohl(mask->dst));
1304bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1305bbd00f7eSHadar Hen Zion 			 dst_ipv4_dst_ipv6.ipv4_layout.ipv4,
1306bbd00f7eSHadar Hen Zion 			 ntohl(key->dst));
1307bbd00f7eSHadar Hen Zion 
1308bbd00f7eSHadar Hen Zion 		MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ethertype);
1309bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, ETH_P_IP);
131019f44401SOr Gerlitz 	} else if (enc_control->addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
131119f44401SOr Gerlitz 		struct flow_dissector_key_ipv6_addrs *key =
131219f44401SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
131319f44401SOr Gerlitz 						  FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS,
131419f44401SOr Gerlitz 						  f->key);
131519f44401SOr Gerlitz 		struct flow_dissector_key_ipv6_addrs *mask =
131619f44401SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
131719f44401SOr Gerlitz 						  FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS,
131819f44401SOr Gerlitz 						  f->mask);
131919f44401SOr Gerlitz 
132019f44401SOr Gerlitz 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
132119f44401SOr Gerlitz 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
132219f44401SOr Gerlitz 		       &mask->src, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
132319f44401SOr Gerlitz 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
132419f44401SOr Gerlitz 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
132519f44401SOr Gerlitz 		       &key->src, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
132619f44401SOr Gerlitz 
132719f44401SOr Gerlitz 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
132819f44401SOr Gerlitz 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
132919f44401SOr Gerlitz 		       &mask->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
133019f44401SOr Gerlitz 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
133119f44401SOr Gerlitz 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
133219f44401SOr Gerlitz 		       &key->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
133319f44401SOr Gerlitz 
133419f44401SOr Gerlitz 		MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ethertype);
133519f44401SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, ETH_P_IPV6);
13362e72eb43SOr Gerlitz 	}
1337bbd00f7eSHadar Hen Zion 
1338bcef735cSOr Gerlitz 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_IP)) {
1339bcef735cSOr Gerlitz 		struct flow_dissector_key_ip *key =
1340bcef735cSOr Gerlitz 			skb_flow_dissector_target(f->dissector,
1341bcef735cSOr Gerlitz 						  FLOW_DISSECTOR_KEY_ENC_IP,
1342bcef735cSOr Gerlitz 						  f->key);
1343bcef735cSOr Gerlitz 		struct flow_dissector_key_ip *mask =
1344bcef735cSOr Gerlitz 			skb_flow_dissector_target(f->dissector,
1345bcef735cSOr Gerlitz 						  FLOW_DISSECTOR_KEY_ENC_IP,
1346bcef735cSOr Gerlitz 						  f->mask);
1347bcef735cSOr Gerlitz 
1348bcef735cSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn, mask->tos & 0x3);
1349bcef735cSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn, key->tos & 0x3);
1350bcef735cSOr Gerlitz 
1351bcef735cSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp, mask->tos >> 2);
1352bcef735cSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp, key->tos  >> 2);
1353bcef735cSOr Gerlitz 
1354bcef735cSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit, mask->ttl);
1355bcef735cSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit, key->ttl);
1356e98bedf5SEli Britstein 
1357e98bedf5SEli Britstein 		if (mask->ttl &&
1358e98bedf5SEli Britstein 		    !MLX5_CAP_ESW_FLOWTABLE_FDB
1359e98bedf5SEli Britstein 			(priv->mdev,
1360e98bedf5SEli Britstein 			 ft_field_support.outer_ipv4_ttl)) {
1361e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1362e98bedf5SEli Britstein 					   "Matching on TTL is not supported");
1363e98bedf5SEli Britstein 			return -EOPNOTSUPP;
1364e98bedf5SEli Britstein 		}
1365e98bedf5SEli Britstein 
1366bcef735cSOr Gerlitz 	}
1367bcef735cSOr Gerlitz 
1368bbd00f7eSHadar Hen Zion 	/* Enforce DMAC when offloading incoming tunneled flows.
1369bbd00f7eSHadar Hen Zion 	 * Flow counters require a match on the DMAC.
1370bbd00f7eSHadar Hen Zion 	 */
1371bbd00f7eSHadar Hen Zion 	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, dmac_47_16);
1372bbd00f7eSHadar Hen Zion 	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, dmac_15_0);
1373bbd00f7eSHadar Hen Zion 	ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1374bbd00f7eSHadar Hen Zion 				     dmac_47_16), priv->netdev->dev_addr);
1375bbd00f7eSHadar Hen Zion 
1376bbd00f7eSHadar Hen Zion 	/* let software handle IP fragments */
1377bbd00f7eSHadar Hen Zion 	MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1);
1378bbd00f7eSHadar Hen Zion 	MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag, 0);
1379bbd00f7eSHadar Hen Zion 
1380bbd00f7eSHadar Hen Zion 	return 0;
1381bbd00f7eSHadar Hen Zion }
1382bbd00f7eSHadar Hen Zion 
1383de0af0bfSRoi Dayan static int __parse_cls_flower(struct mlx5e_priv *priv,
1384de0af0bfSRoi Dayan 			      struct mlx5_flow_spec *spec,
1385de0af0bfSRoi Dayan 			      struct tc_cls_flower_offload *f,
1386d708f902SOr Gerlitz 			      u8 *match_level)
1387e3a2b7edSAmir Vadai {
1388e98bedf5SEli Britstein 	struct netlink_ext_ack *extack = f->common.extack;
1389c5bb1730SMaor Gottlieb 	void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
1390c5bb1730SMaor Gottlieb 				       outer_headers);
1391c5bb1730SMaor Gottlieb 	void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
1392c5bb1730SMaor Gottlieb 				       outer_headers);
1393699e96ddSJianbo Liu 	void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
1394699e96ddSJianbo Liu 				    misc_parameters);
1395699e96ddSJianbo Liu 	void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
1396699e96ddSJianbo Liu 				    misc_parameters);
1397e3a2b7edSAmir Vadai 	u16 addr_type = 0;
1398e3a2b7edSAmir Vadai 	u8 ip_proto = 0;
1399e3a2b7edSAmir Vadai 
1400d708f902SOr Gerlitz 	*match_level = MLX5_MATCH_NONE;
1401de0af0bfSRoi Dayan 
1402e3a2b7edSAmir Vadai 	if (f->dissector->used_keys &
1403e3a2b7edSAmir Vadai 	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
1404e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
1405e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
1406095b6cfdSOr Gerlitz 	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
1407699e96ddSJianbo Liu 	      BIT(FLOW_DISSECTOR_KEY_CVLAN) |
1408e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
1409e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
1410bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_PORTS) |
1411bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
1412bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
1413bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
1414bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_ENC_PORTS)	|
1415e77834ecSOr Gerlitz 	      BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL) |
1416fd7da28bSOr Gerlitz 	      BIT(FLOW_DISSECTOR_KEY_TCP) |
1417bcef735cSOr Gerlitz 	      BIT(FLOW_DISSECTOR_KEY_IP)  |
1418bcef735cSOr Gerlitz 	      BIT(FLOW_DISSECTOR_KEY_ENC_IP))) {
1419e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack, "Unsupported key");
1420e3a2b7edSAmir Vadai 		netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
1421e3a2b7edSAmir Vadai 			    f->dissector->used_keys);
1422e3a2b7edSAmir Vadai 		return -EOPNOTSUPP;
1423e3a2b7edSAmir Vadai 	}
1424e3a2b7edSAmir Vadai 
1425bbd00f7eSHadar Hen Zion 	if ((dissector_uses_key(f->dissector,
1426bbd00f7eSHadar Hen Zion 				FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) ||
1427bbd00f7eSHadar Hen Zion 	     dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID) ||
1428bbd00f7eSHadar Hen Zion 	     dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) &&
1429bbd00f7eSHadar Hen Zion 	    dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_CONTROL)) {
1430bbd00f7eSHadar Hen Zion 		struct flow_dissector_key_control *key =
1431bbd00f7eSHadar Hen Zion 			skb_flow_dissector_target(f->dissector,
1432bbd00f7eSHadar Hen Zion 						  FLOW_DISSECTOR_KEY_ENC_CONTROL,
1433bbd00f7eSHadar Hen Zion 						  f->key);
1434bbd00f7eSHadar Hen Zion 		switch (key->addr_type) {
1435bbd00f7eSHadar Hen Zion 		case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
143619f44401SOr Gerlitz 		case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
1437bbd00f7eSHadar Hen Zion 			if (parse_tunnel_attr(priv, spec, f))
1438bbd00f7eSHadar Hen Zion 				return -EOPNOTSUPP;
1439bbd00f7eSHadar Hen Zion 			break;
1440bbd00f7eSHadar Hen Zion 		default:
1441bbd00f7eSHadar Hen Zion 			return -EOPNOTSUPP;
1442bbd00f7eSHadar Hen Zion 		}
1443bbd00f7eSHadar Hen Zion 
1444bbd00f7eSHadar Hen Zion 		/* In decap flow, header pointers should point to the inner
1445bbd00f7eSHadar Hen Zion 		 * headers, outer header were already set by parse_tunnel_attr
1446bbd00f7eSHadar Hen Zion 		 */
1447bbd00f7eSHadar Hen Zion 		headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
1448bbd00f7eSHadar Hen Zion 					 inner_headers);
1449bbd00f7eSHadar Hen Zion 		headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
1450bbd00f7eSHadar Hen Zion 					 inner_headers);
1451bbd00f7eSHadar Hen Zion 	}
1452bbd00f7eSHadar Hen Zion 
1453d3a80bb5SOr Gerlitz 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
1454d3a80bb5SOr Gerlitz 		struct flow_dissector_key_basic *key =
1455e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
1456d3a80bb5SOr Gerlitz 						  FLOW_DISSECTOR_KEY_BASIC,
1457e3a2b7edSAmir Vadai 						  f->key);
1458d3a80bb5SOr Gerlitz 		struct flow_dissector_key_basic *mask =
1459e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
1460d3a80bb5SOr Gerlitz 						  FLOW_DISSECTOR_KEY_BASIC,
1461e3a2b7edSAmir Vadai 						  f->mask);
1462d3a80bb5SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype,
1463d3a80bb5SOr Gerlitz 			 ntohs(mask->n_proto));
1464d3a80bb5SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype,
1465d3a80bb5SOr Gerlitz 			 ntohs(key->n_proto));
1466e3a2b7edSAmir Vadai 
1467d3a80bb5SOr Gerlitz 		if (mask->n_proto)
1468d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L2;
1469e3a2b7edSAmir Vadai 	}
1470e3a2b7edSAmir Vadai 
1471095b6cfdSOr Gerlitz 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
1472095b6cfdSOr Gerlitz 		struct flow_dissector_key_vlan *key =
1473095b6cfdSOr Gerlitz 			skb_flow_dissector_target(f->dissector,
1474095b6cfdSOr Gerlitz 						  FLOW_DISSECTOR_KEY_VLAN,
1475095b6cfdSOr Gerlitz 						  f->key);
1476095b6cfdSOr Gerlitz 		struct flow_dissector_key_vlan *mask =
1477095b6cfdSOr Gerlitz 			skb_flow_dissector_target(f->dissector,
1478095b6cfdSOr Gerlitz 						  FLOW_DISSECTOR_KEY_VLAN,
1479095b6cfdSOr Gerlitz 						  f->mask);
1480699e96ddSJianbo Liu 		if (mask->vlan_id || mask->vlan_priority || mask->vlan_tpid) {
1481699e96ddSJianbo Liu 			if (key->vlan_tpid == htons(ETH_P_8021AD)) {
1482699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1483699e96ddSJianbo Liu 					 svlan_tag, 1);
1484699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1485699e96ddSJianbo Liu 					 svlan_tag, 1);
1486699e96ddSJianbo Liu 			} else {
1487699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1488699e96ddSJianbo Liu 					 cvlan_tag, 1);
1489699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1490699e96ddSJianbo Liu 					 cvlan_tag, 1);
1491699e96ddSJianbo Liu 			}
1492095b6cfdSOr Gerlitz 
1493095b6cfdSOr Gerlitz 			MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_vid, mask->vlan_id);
1494095b6cfdSOr Gerlitz 			MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_vid, key->vlan_id);
1495358d79a4SOr Gerlitz 
1496358d79a4SOr Gerlitz 			MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_prio, mask->vlan_priority);
1497358d79a4SOr Gerlitz 			MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_prio, key->vlan_priority);
149854782900SOr Gerlitz 
1499d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L2;
1500095b6cfdSOr Gerlitz 		}
1501d3a80bb5SOr Gerlitz 	} else if (*match_level != MLX5_MATCH_NONE) {
1502cee26487SJianbo Liu 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, svlan_tag, 1);
1503cee26487SJianbo Liu 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1);
1504d3a80bb5SOr Gerlitz 		*match_level = MLX5_MATCH_L2;
1505095b6cfdSOr Gerlitz 	}
1506095b6cfdSOr Gerlitz 
1507699e96ddSJianbo Liu 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CVLAN)) {
1508699e96ddSJianbo Liu 		struct flow_dissector_key_vlan *key =
1509699e96ddSJianbo Liu 			skb_flow_dissector_target(f->dissector,
1510699e96ddSJianbo Liu 						  FLOW_DISSECTOR_KEY_CVLAN,
1511699e96ddSJianbo Liu 						  f->key);
1512699e96ddSJianbo Liu 		struct flow_dissector_key_vlan *mask =
1513699e96ddSJianbo Liu 			skb_flow_dissector_target(f->dissector,
1514699e96ddSJianbo Liu 						  FLOW_DISSECTOR_KEY_CVLAN,
1515699e96ddSJianbo Liu 						  f->mask);
1516699e96ddSJianbo Liu 		if (mask->vlan_id || mask->vlan_priority || mask->vlan_tpid) {
1517699e96ddSJianbo Liu 			if (key->vlan_tpid == htons(ETH_P_8021AD)) {
1518699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_misc, misc_c,
1519699e96ddSJianbo Liu 					 outer_second_svlan_tag, 1);
1520699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_misc, misc_v,
1521699e96ddSJianbo Liu 					 outer_second_svlan_tag, 1);
1522699e96ddSJianbo Liu 			} else {
1523699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_misc, misc_c,
1524699e96ddSJianbo Liu 					 outer_second_cvlan_tag, 1);
1525699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_misc, misc_v,
1526699e96ddSJianbo Liu 					 outer_second_cvlan_tag, 1);
1527699e96ddSJianbo Liu 			}
1528699e96ddSJianbo Liu 
1529699e96ddSJianbo Liu 			MLX5_SET(fte_match_set_misc, misc_c, outer_second_vid,
1530699e96ddSJianbo Liu 				 mask->vlan_id);
1531699e96ddSJianbo Liu 			MLX5_SET(fte_match_set_misc, misc_v, outer_second_vid,
1532699e96ddSJianbo Liu 				 key->vlan_id);
1533699e96ddSJianbo Liu 			MLX5_SET(fte_match_set_misc, misc_c, outer_second_prio,
1534699e96ddSJianbo Liu 				 mask->vlan_priority);
1535699e96ddSJianbo Liu 			MLX5_SET(fte_match_set_misc, misc_v, outer_second_prio,
1536699e96ddSJianbo Liu 				 key->vlan_priority);
1537699e96ddSJianbo Liu 
1538699e96ddSJianbo Liu 			*match_level = MLX5_MATCH_L2;
1539699e96ddSJianbo Liu 		}
1540699e96ddSJianbo Liu 	}
1541699e96ddSJianbo Liu 
1542d3a80bb5SOr Gerlitz 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
1543d3a80bb5SOr Gerlitz 		struct flow_dissector_key_eth_addrs *key =
154454782900SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
1545d3a80bb5SOr Gerlitz 						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
154654782900SOr Gerlitz 						  f->key);
1547d3a80bb5SOr Gerlitz 		struct flow_dissector_key_eth_addrs *mask =
154854782900SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
1549d3a80bb5SOr Gerlitz 						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
155054782900SOr Gerlitz 						  f->mask);
155154782900SOr Gerlitz 
1552d3a80bb5SOr Gerlitz 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1553d3a80bb5SOr Gerlitz 					     dmac_47_16),
1554d3a80bb5SOr Gerlitz 				mask->dst);
1555d3a80bb5SOr Gerlitz 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1556d3a80bb5SOr Gerlitz 					     dmac_47_16),
1557d3a80bb5SOr Gerlitz 				key->dst);
1558d3a80bb5SOr Gerlitz 
1559d3a80bb5SOr Gerlitz 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1560d3a80bb5SOr Gerlitz 					     smac_47_16),
1561d3a80bb5SOr Gerlitz 				mask->src);
1562d3a80bb5SOr Gerlitz 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1563d3a80bb5SOr Gerlitz 					     smac_47_16),
1564d3a80bb5SOr Gerlitz 				key->src);
1565d3a80bb5SOr Gerlitz 
1566d3a80bb5SOr Gerlitz 		if (!is_zero_ether_addr(mask->src) || !is_zero_ether_addr(mask->dst))
1567d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L2;
156854782900SOr Gerlitz 	}
156954782900SOr Gerlitz 
157054782900SOr Gerlitz 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
157154782900SOr Gerlitz 		struct flow_dissector_key_control *key =
157254782900SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
157354782900SOr Gerlitz 						  FLOW_DISSECTOR_KEY_CONTROL,
157454782900SOr Gerlitz 						  f->key);
157554782900SOr Gerlitz 
157654782900SOr Gerlitz 		struct flow_dissector_key_control *mask =
157754782900SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
157854782900SOr Gerlitz 						  FLOW_DISSECTOR_KEY_CONTROL,
157954782900SOr Gerlitz 						  f->mask);
158054782900SOr Gerlitz 		addr_type = key->addr_type;
158154782900SOr Gerlitz 
158254782900SOr Gerlitz 		/* the HW doesn't support frag first/later */
158354782900SOr Gerlitz 		if (mask->flags & FLOW_DIS_FIRST_FRAG)
158454782900SOr Gerlitz 			return -EOPNOTSUPP;
158554782900SOr Gerlitz 
158654782900SOr Gerlitz 		if (mask->flags & FLOW_DIS_IS_FRAGMENT) {
158754782900SOr Gerlitz 			MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1);
158854782900SOr Gerlitz 			MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag,
158954782900SOr Gerlitz 				 key->flags & FLOW_DIS_IS_FRAGMENT);
159054782900SOr Gerlitz 
159154782900SOr Gerlitz 			/* the HW doesn't need L3 inline to match on frag=no */
159254782900SOr Gerlitz 			if (!(key->flags & FLOW_DIS_IS_FRAGMENT))
159383621b7dSOr Gerlitz 				*match_level = MLX5_MATCH_L2;
159454782900SOr Gerlitz 	/* ***  L2 attributes parsing up to here *** */
159554782900SOr Gerlitz 			else
159683621b7dSOr Gerlitz 				*match_level = MLX5_MATCH_L3;
159754782900SOr Gerlitz 		}
159854782900SOr Gerlitz 	}
159954782900SOr Gerlitz 
160054782900SOr Gerlitz 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
160154782900SOr Gerlitz 		struct flow_dissector_key_basic *key =
160254782900SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
160354782900SOr Gerlitz 						  FLOW_DISSECTOR_KEY_BASIC,
160454782900SOr Gerlitz 						  f->key);
160554782900SOr Gerlitz 		struct flow_dissector_key_basic *mask =
160654782900SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
160754782900SOr Gerlitz 						  FLOW_DISSECTOR_KEY_BASIC,
160854782900SOr Gerlitz 						  f->mask);
160954782900SOr Gerlitz 		ip_proto = key->ip_proto;
161054782900SOr Gerlitz 
161154782900SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
161254782900SOr Gerlitz 			 mask->ip_proto);
161354782900SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
161454782900SOr Gerlitz 			 key->ip_proto);
161554782900SOr Gerlitz 
161654782900SOr Gerlitz 		if (mask->ip_proto)
1617d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L3;
161854782900SOr Gerlitz 	}
161954782900SOr Gerlitz 
1620e3a2b7edSAmir Vadai 	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
1621e3a2b7edSAmir Vadai 		struct flow_dissector_key_ipv4_addrs *key =
1622e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
1623e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
1624e3a2b7edSAmir Vadai 						  f->key);
1625e3a2b7edSAmir Vadai 		struct flow_dissector_key_ipv4_addrs *mask =
1626e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
1627e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
1628e3a2b7edSAmir Vadai 						  f->mask);
1629e3a2b7edSAmir Vadai 
1630e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1631e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv4_layout.ipv4),
1632e3a2b7edSAmir Vadai 		       &mask->src, sizeof(mask->src));
1633e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1634e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv4_layout.ipv4),
1635e3a2b7edSAmir Vadai 		       &key->src, sizeof(key->src));
1636e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1637e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
1638e3a2b7edSAmir Vadai 		       &mask->dst, sizeof(mask->dst));
1639e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1640e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
1641e3a2b7edSAmir Vadai 		       &key->dst, sizeof(key->dst));
1642de0af0bfSRoi Dayan 
1643de0af0bfSRoi Dayan 		if (mask->src || mask->dst)
1644d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L3;
1645e3a2b7edSAmir Vadai 	}
1646e3a2b7edSAmir Vadai 
1647e3a2b7edSAmir Vadai 	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
1648e3a2b7edSAmir Vadai 		struct flow_dissector_key_ipv6_addrs *key =
1649e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
1650e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
1651e3a2b7edSAmir Vadai 						  f->key);
1652e3a2b7edSAmir Vadai 		struct flow_dissector_key_ipv6_addrs *mask =
1653e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
1654e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
1655e3a2b7edSAmir Vadai 						  f->mask);
1656e3a2b7edSAmir Vadai 
1657e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1658e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
1659e3a2b7edSAmir Vadai 		       &mask->src, sizeof(mask->src));
1660e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1661e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
1662e3a2b7edSAmir Vadai 		       &key->src, sizeof(key->src));
1663e3a2b7edSAmir Vadai 
1664e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1665e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
1666e3a2b7edSAmir Vadai 		       &mask->dst, sizeof(mask->dst));
1667e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1668e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
1669e3a2b7edSAmir Vadai 		       &key->dst, sizeof(key->dst));
1670de0af0bfSRoi Dayan 
1671de0af0bfSRoi Dayan 		if (ipv6_addr_type(&mask->src) != IPV6_ADDR_ANY ||
1672de0af0bfSRoi Dayan 		    ipv6_addr_type(&mask->dst) != IPV6_ADDR_ANY)
1673d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L3;
1674e3a2b7edSAmir Vadai 	}
1675e3a2b7edSAmir Vadai 
16761f97a526SOr Gerlitz 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IP)) {
16771f97a526SOr Gerlitz 		struct flow_dissector_key_ip *key =
16781f97a526SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
16791f97a526SOr Gerlitz 						  FLOW_DISSECTOR_KEY_IP,
16801f97a526SOr Gerlitz 						  f->key);
16811f97a526SOr Gerlitz 		struct flow_dissector_key_ip *mask =
16821f97a526SOr Gerlitz 			skb_flow_dissector_target(f->dissector,
16831f97a526SOr Gerlitz 						  FLOW_DISSECTOR_KEY_IP,
16841f97a526SOr Gerlitz 						  f->mask);
16851f97a526SOr Gerlitz 
16861f97a526SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn, mask->tos & 0x3);
16871f97a526SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn, key->tos & 0x3);
16881f97a526SOr Gerlitz 
16891f97a526SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp, mask->tos >> 2);
16901f97a526SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp, key->tos  >> 2);
16911f97a526SOr Gerlitz 
1692a8ade55fSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit, mask->ttl);
1693a8ade55fSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit, key->ttl);
16941f97a526SOr Gerlitz 
1695a8ade55fSOr Gerlitz 		if (mask->ttl &&
1696a8ade55fSOr Gerlitz 		    !MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev,
1697e98bedf5SEli Britstein 						ft_field_support.outer_ipv4_ttl)) {
1698e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1699e98bedf5SEli Britstein 					   "Matching on TTL is not supported");
17001f97a526SOr Gerlitz 			return -EOPNOTSUPP;
1701e98bedf5SEli Britstein 		}
1702a8ade55fSOr Gerlitz 
1703a8ade55fSOr Gerlitz 		if (mask->tos || mask->ttl)
1704d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L3;
17051f97a526SOr Gerlitz 	}
17061f97a526SOr Gerlitz 
170754782900SOr Gerlitz 	/* ***  L3 attributes parsing up to here *** */
170854782900SOr Gerlitz 
1709e3a2b7edSAmir Vadai 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
1710e3a2b7edSAmir Vadai 		struct flow_dissector_key_ports *key =
1711e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
1712e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_PORTS,
1713e3a2b7edSAmir Vadai 						  f->key);
1714e3a2b7edSAmir Vadai 		struct flow_dissector_key_ports *mask =
1715e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
1716e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_PORTS,
1717e3a2b7edSAmir Vadai 						  f->mask);
1718e3a2b7edSAmir Vadai 		switch (ip_proto) {
1719e3a2b7edSAmir Vadai 		case IPPROTO_TCP:
1720e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1721e3a2b7edSAmir Vadai 				 tcp_sport, ntohs(mask->src));
1722e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1723e3a2b7edSAmir Vadai 				 tcp_sport, ntohs(key->src));
1724e3a2b7edSAmir Vadai 
1725e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1726e3a2b7edSAmir Vadai 				 tcp_dport, ntohs(mask->dst));
1727e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1728e3a2b7edSAmir Vadai 				 tcp_dport, ntohs(key->dst));
1729e3a2b7edSAmir Vadai 			break;
1730e3a2b7edSAmir Vadai 
1731e3a2b7edSAmir Vadai 		case IPPROTO_UDP:
1732e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1733e3a2b7edSAmir Vadai 				 udp_sport, ntohs(mask->src));
1734e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1735e3a2b7edSAmir Vadai 				 udp_sport, ntohs(key->src));
1736e3a2b7edSAmir Vadai 
1737e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1738e3a2b7edSAmir Vadai 				 udp_dport, ntohs(mask->dst));
1739e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1740e3a2b7edSAmir Vadai 				 udp_dport, ntohs(key->dst));
1741e3a2b7edSAmir Vadai 			break;
1742e3a2b7edSAmir Vadai 		default:
1743e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1744e98bedf5SEli Britstein 					   "Only UDP and TCP transports are supported for L4 matching");
1745e3a2b7edSAmir Vadai 			netdev_err(priv->netdev,
1746e3a2b7edSAmir Vadai 				   "Only UDP and TCP transport are supported\n");
1747e3a2b7edSAmir Vadai 			return -EINVAL;
1748e3a2b7edSAmir Vadai 		}
1749de0af0bfSRoi Dayan 
1750de0af0bfSRoi Dayan 		if (mask->src || mask->dst)
1751d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L4;
1752e3a2b7edSAmir Vadai 	}
1753e3a2b7edSAmir Vadai 
1754e77834ecSOr Gerlitz 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_TCP)) {
1755e77834ecSOr Gerlitz 		struct flow_dissector_key_tcp *key =
1756e77834ecSOr Gerlitz 			skb_flow_dissector_target(f->dissector,
1757e77834ecSOr Gerlitz 						  FLOW_DISSECTOR_KEY_TCP,
1758e77834ecSOr Gerlitz 						  f->key);
1759e77834ecSOr Gerlitz 		struct flow_dissector_key_tcp *mask =
1760e77834ecSOr Gerlitz 			skb_flow_dissector_target(f->dissector,
1761e77834ecSOr Gerlitz 						  FLOW_DISSECTOR_KEY_TCP,
1762e77834ecSOr Gerlitz 						  f->mask);
1763e77834ecSOr Gerlitz 
1764e77834ecSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, tcp_flags,
1765e77834ecSOr Gerlitz 			 ntohs(mask->flags));
1766e77834ecSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, tcp_flags,
1767e77834ecSOr Gerlitz 			 ntohs(key->flags));
1768e77834ecSOr Gerlitz 
1769e77834ecSOr Gerlitz 		if (mask->flags)
1770d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L4;
1771e77834ecSOr Gerlitz 	}
1772e77834ecSOr Gerlitz 
1773e3a2b7edSAmir Vadai 	return 0;
1774e3a2b7edSAmir Vadai }
1775e3a2b7edSAmir Vadai 
1776de0af0bfSRoi Dayan static int parse_cls_flower(struct mlx5e_priv *priv,
177765ba8fb7SOr Gerlitz 			    struct mlx5e_tc_flow *flow,
1778de0af0bfSRoi Dayan 			    struct mlx5_flow_spec *spec,
1779de0af0bfSRoi Dayan 			    struct tc_cls_flower_offload *f)
1780de0af0bfSRoi Dayan {
1781e98bedf5SEli Britstein 	struct netlink_ext_ack *extack = f->common.extack;
1782de0af0bfSRoi Dayan 	struct mlx5_core_dev *dev = priv->mdev;
1783de0af0bfSRoi Dayan 	struct mlx5_eswitch *esw = dev->priv.eswitch;
17841d447a39SSaeed Mahameed 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
17851d447a39SSaeed Mahameed 	struct mlx5_eswitch_rep *rep;
1786d708f902SOr Gerlitz 	u8 match_level;
1787de0af0bfSRoi Dayan 	int err;
1788de0af0bfSRoi Dayan 
1789d708f902SOr Gerlitz 	err = __parse_cls_flower(priv, spec, f, &match_level);
1790de0af0bfSRoi Dayan 
17911d447a39SSaeed Mahameed 	if (!err && (flow->flags & MLX5E_TC_FLOW_ESWITCH)) {
17921d447a39SSaeed Mahameed 		rep = rpriv->rep;
17931d447a39SSaeed Mahameed 		if (rep->vport != FDB_UPLINK_VPORT &&
17941d447a39SSaeed Mahameed 		    (esw->offloads.inline_mode != MLX5_INLINE_MODE_NONE &&
1795d708f902SOr Gerlitz 		    esw->offloads.inline_mode < match_level)) {
1796e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1797e98bedf5SEli Britstein 					   "Flow is not offloaded due to min inline setting");
1798de0af0bfSRoi Dayan 			netdev_warn(priv->netdev,
1799de0af0bfSRoi Dayan 				    "Flow is not offloaded due to min inline setting, required %d actual %d\n",
1800d708f902SOr Gerlitz 				    match_level, esw->offloads.inline_mode);
1801de0af0bfSRoi Dayan 			return -EOPNOTSUPP;
1802de0af0bfSRoi Dayan 		}
1803de0af0bfSRoi Dayan 	}
1804de0af0bfSRoi Dayan 
180538aa51c1SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
180638aa51c1SOr Gerlitz 		flow->esw_attr->match_level = match_level;
180738aa51c1SOr Gerlitz 	else
180838aa51c1SOr Gerlitz 		flow->nic_attr->match_level = match_level;
180938aa51c1SOr Gerlitz 
1810de0af0bfSRoi Dayan 	return err;
1811de0af0bfSRoi Dayan }
1812de0af0bfSRoi Dayan 
1813d79b6df6SOr Gerlitz struct pedit_headers {
1814d79b6df6SOr Gerlitz 	struct ethhdr  eth;
1815d79b6df6SOr Gerlitz 	struct iphdr   ip4;
1816d79b6df6SOr Gerlitz 	struct ipv6hdr ip6;
1817d79b6df6SOr Gerlitz 	struct tcphdr  tcp;
1818d79b6df6SOr Gerlitz 	struct udphdr  udp;
1819d79b6df6SOr Gerlitz };
1820d79b6df6SOr Gerlitz 
1821d79b6df6SOr Gerlitz static int pedit_header_offsets[] = {
1822d79b6df6SOr Gerlitz 	[TCA_PEDIT_KEY_EX_HDR_TYPE_ETH] = offsetof(struct pedit_headers, eth),
1823d79b6df6SOr Gerlitz 	[TCA_PEDIT_KEY_EX_HDR_TYPE_IP4] = offsetof(struct pedit_headers, ip4),
1824d79b6df6SOr Gerlitz 	[TCA_PEDIT_KEY_EX_HDR_TYPE_IP6] = offsetof(struct pedit_headers, ip6),
1825d79b6df6SOr Gerlitz 	[TCA_PEDIT_KEY_EX_HDR_TYPE_TCP] = offsetof(struct pedit_headers, tcp),
1826d79b6df6SOr Gerlitz 	[TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = offsetof(struct pedit_headers, udp),
1827d79b6df6SOr Gerlitz };
1828d79b6df6SOr Gerlitz 
1829d79b6df6SOr Gerlitz #define pedit_header(_ph, _htype) ((void *)(_ph) + pedit_header_offsets[_htype])
1830d79b6df6SOr Gerlitz 
1831d79b6df6SOr Gerlitz static int set_pedit_val(u8 hdr_type, u32 mask, u32 val, u32 offset,
1832d79b6df6SOr Gerlitz 			 struct pedit_headers *masks,
1833d79b6df6SOr Gerlitz 			 struct pedit_headers *vals)
1834d79b6df6SOr Gerlitz {
1835d79b6df6SOr Gerlitz 	u32 *curr_pmask, *curr_pval;
1836d79b6df6SOr Gerlitz 
1837d79b6df6SOr Gerlitz 	if (hdr_type >= __PEDIT_HDR_TYPE_MAX)
1838d79b6df6SOr Gerlitz 		goto out_err;
1839d79b6df6SOr Gerlitz 
1840d79b6df6SOr Gerlitz 	curr_pmask = (u32 *)(pedit_header(masks, hdr_type) + offset);
1841d79b6df6SOr Gerlitz 	curr_pval  = (u32 *)(pedit_header(vals, hdr_type) + offset);
1842d79b6df6SOr Gerlitz 
1843d79b6df6SOr Gerlitz 	if (*curr_pmask & mask)  /* disallow acting twice on the same location */
1844d79b6df6SOr Gerlitz 		goto out_err;
1845d79b6df6SOr Gerlitz 
1846d79b6df6SOr Gerlitz 	*curr_pmask |= mask;
1847d79b6df6SOr Gerlitz 	*curr_pval  |= (val & mask);
1848d79b6df6SOr Gerlitz 
1849d79b6df6SOr Gerlitz 	return 0;
1850d79b6df6SOr Gerlitz 
1851d79b6df6SOr Gerlitz out_err:
1852d79b6df6SOr Gerlitz 	return -EOPNOTSUPP;
1853d79b6df6SOr Gerlitz }
1854d79b6df6SOr Gerlitz 
1855d79b6df6SOr Gerlitz struct mlx5_fields {
1856d79b6df6SOr Gerlitz 	u8  field;
1857d79b6df6SOr Gerlitz 	u8  size;
1858d79b6df6SOr Gerlitz 	u32 offset;
1859d79b6df6SOr Gerlitz };
1860d79b6df6SOr Gerlitz 
1861a8e4f0c4SOr Gerlitz #define OFFLOAD(fw_field, size, field, off) \
1862a8e4f0c4SOr Gerlitz 		{MLX5_ACTION_IN_FIELD_OUT_ ## fw_field, size, offsetof(struct pedit_headers, field) + (off)}
1863a8e4f0c4SOr Gerlitz 
1864d79b6df6SOr Gerlitz static struct mlx5_fields fields[] = {
1865a8e4f0c4SOr Gerlitz 	OFFLOAD(DMAC_47_16, 4, eth.h_dest[0], 0),
1866a8e4f0c4SOr Gerlitz 	OFFLOAD(DMAC_15_0,  2, eth.h_dest[4], 0),
1867a8e4f0c4SOr Gerlitz 	OFFLOAD(SMAC_47_16, 4, eth.h_source[0], 0),
1868a8e4f0c4SOr Gerlitz 	OFFLOAD(SMAC_15_0,  2, eth.h_source[4], 0),
1869a8e4f0c4SOr Gerlitz 	OFFLOAD(ETHERTYPE,  2, eth.h_proto, 0),
1870d79b6df6SOr Gerlitz 
1871a8e4f0c4SOr Gerlitz 	OFFLOAD(IP_TTL, 1, ip4.ttl,   0),
1872a8e4f0c4SOr Gerlitz 	OFFLOAD(SIPV4,  4, ip4.saddr, 0),
1873a8e4f0c4SOr Gerlitz 	OFFLOAD(DIPV4,  4, ip4.daddr, 0),
1874d79b6df6SOr Gerlitz 
1875a8e4f0c4SOr Gerlitz 	OFFLOAD(SIPV6_127_96, 4, ip6.saddr.s6_addr32[0], 0),
1876a8e4f0c4SOr Gerlitz 	OFFLOAD(SIPV6_95_64,  4, ip6.saddr.s6_addr32[1], 0),
1877a8e4f0c4SOr Gerlitz 	OFFLOAD(SIPV6_63_32,  4, ip6.saddr.s6_addr32[2], 0),
1878a8e4f0c4SOr Gerlitz 	OFFLOAD(SIPV6_31_0,   4, ip6.saddr.s6_addr32[3], 0),
1879a8e4f0c4SOr Gerlitz 	OFFLOAD(DIPV6_127_96, 4, ip6.daddr.s6_addr32[0], 0),
1880a8e4f0c4SOr Gerlitz 	OFFLOAD(DIPV6_95_64,  4, ip6.daddr.s6_addr32[1], 0),
1881a8e4f0c4SOr Gerlitz 	OFFLOAD(DIPV6_63_32,  4, ip6.daddr.s6_addr32[2], 0),
1882a8e4f0c4SOr Gerlitz 	OFFLOAD(DIPV6_31_0,   4, ip6.daddr.s6_addr32[3], 0),
18830c0316f5SOr Gerlitz 	OFFLOAD(IPV6_HOPLIMIT, 1, ip6.hop_limit, 0),
1884d79b6df6SOr Gerlitz 
1885a8e4f0c4SOr Gerlitz 	OFFLOAD(TCP_SPORT, 2, tcp.source,  0),
1886a8e4f0c4SOr Gerlitz 	OFFLOAD(TCP_DPORT, 2, tcp.dest,    0),
1887a8e4f0c4SOr Gerlitz 	OFFLOAD(TCP_FLAGS, 1, tcp.ack_seq, 5),
1888d79b6df6SOr Gerlitz 
1889a8e4f0c4SOr Gerlitz 	OFFLOAD(UDP_SPORT, 2, udp.source, 0),
1890a8e4f0c4SOr Gerlitz 	OFFLOAD(UDP_DPORT, 2, udp.dest,   0),
1891d79b6df6SOr Gerlitz };
1892d79b6df6SOr Gerlitz 
1893d79b6df6SOr Gerlitz /* On input attr->num_mod_hdr_actions tells how many HW actions can be parsed at
1894d79b6df6SOr Gerlitz  * max from the SW pedit action. On success, it says how many HW actions were
1895d79b6df6SOr Gerlitz  * actually parsed.
1896d79b6df6SOr Gerlitz  */
1897d79b6df6SOr Gerlitz static int offload_pedit_fields(struct pedit_headers *masks,
1898d79b6df6SOr Gerlitz 				struct pedit_headers *vals,
1899e98bedf5SEli Britstein 				struct mlx5e_tc_flow_parse_attr *parse_attr,
1900e98bedf5SEli Britstein 				struct netlink_ext_ack *extack)
1901d79b6df6SOr Gerlitz {
1902d79b6df6SOr Gerlitz 	struct pedit_headers *set_masks, *add_masks, *set_vals, *add_vals;
19032b64bebaSOr Gerlitz 	int i, action_size, nactions, max_actions, first, last, next_z;
1904d79b6df6SOr Gerlitz 	void *s_masks_p, *a_masks_p, *vals_p;
1905d79b6df6SOr Gerlitz 	struct mlx5_fields *f;
1906d79b6df6SOr Gerlitz 	u8 cmd, field_bsize;
1907e3ca4e05SOr Gerlitz 	u32 s_mask, a_mask;
1908d79b6df6SOr Gerlitz 	unsigned long mask;
19092b64bebaSOr Gerlitz 	__be32 mask_be32;
19102b64bebaSOr Gerlitz 	__be16 mask_be16;
1911d79b6df6SOr Gerlitz 	void *action;
1912d79b6df6SOr Gerlitz 
1913d79b6df6SOr Gerlitz 	set_masks = &masks[TCA_PEDIT_KEY_EX_CMD_SET];
1914d79b6df6SOr Gerlitz 	add_masks = &masks[TCA_PEDIT_KEY_EX_CMD_ADD];
1915d79b6df6SOr Gerlitz 	set_vals = &vals[TCA_PEDIT_KEY_EX_CMD_SET];
1916d79b6df6SOr Gerlitz 	add_vals = &vals[TCA_PEDIT_KEY_EX_CMD_ADD];
1917d79b6df6SOr Gerlitz 
1918d79b6df6SOr Gerlitz 	action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto);
1919d79b6df6SOr Gerlitz 	action = parse_attr->mod_hdr_actions;
1920d79b6df6SOr Gerlitz 	max_actions = parse_attr->num_mod_hdr_actions;
1921d79b6df6SOr Gerlitz 	nactions = 0;
1922d79b6df6SOr Gerlitz 
1923d79b6df6SOr Gerlitz 	for (i = 0; i < ARRAY_SIZE(fields); i++) {
1924d79b6df6SOr Gerlitz 		f = &fields[i];
1925d79b6df6SOr Gerlitz 		/* avoid seeing bits set from previous iterations */
1926e3ca4e05SOr Gerlitz 		s_mask = 0;
1927e3ca4e05SOr Gerlitz 		a_mask = 0;
1928d79b6df6SOr Gerlitz 
1929d79b6df6SOr Gerlitz 		s_masks_p = (void *)set_masks + f->offset;
1930d79b6df6SOr Gerlitz 		a_masks_p = (void *)add_masks + f->offset;
1931d79b6df6SOr Gerlitz 
1932d79b6df6SOr Gerlitz 		memcpy(&s_mask, s_masks_p, f->size);
1933d79b6df6SOr Gerlitz 		memcpy(&a_mask, a_masks_p, f->size);
1934d79b6df6SOr Gerlitz 
1935d79b6df6SOr Gerlitz 		if (!s_mask && !a_mask) /* nothing to offload here */
1936d79b6df6SOr Gerlitz 			continue;
1937d79b6df6SOr Gerlitz 
1938d79b6df6SOr Gerlitz 		if (s_mask && a_mask) {
1939e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1940e98bedf5SEli Britstein 					   "can't set and add to the same HW field");
1941d79b6df6SOr Gerlitz 			printk(KERN_WARNING "mlx5: can't set and add to the same HW field (%x)\n", f->field);
1942d79b6df6SOr Gerlitz 			return -EOPNOTSUPP;
1943d79b6df6SOr Gerlitz 		}
1944d79b6df6SOr Gerlitz 
1945d79b6df6SOr Gerlitz 		if (nactions == max_actions) {
1946e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1947e98bedf5SEli Britstein 					   "too many pedit actions, can't offload");
1948d79b6df6SOr Gerlitz 			printk(KERN_WARNING "mlx5: parsed %d pedit actions, can't do more\n", nactions);
1949d79b6df6SOr Gerlitz 			return -EOPNOTSUPP;
1950d79b6df6SOr Gerlitz 		}
1951d79b6df6SOr Gerlitz 
1952d79b6df6SOr Gerlitz 		if (s_mask) {
1953d79b6df6SOr Gerlitz 			cmd  = MLX5_ACTION_TYPE_SET;
1954d79b6df6SOr Gerlitz 			mask = s_mask;
1955d79b6df6SOr Gerlitz 			vals_p = (void *)set_vals + f->offset;
1956d79b6df6SOr Gerlitz 			/* clear to denote we consumed this field */
1957d79b6df6SOr Gerlitz 			memset(s_masks_p, 0, f->size);
1958d79b6df6SOr Gerlitz 		} else {
1959d79b6df6SOr Gerlitz 			cmd  = MLX5_ACTION_TYPE_ADD;
1960d79b6df6SOr Gerlitz 			mask = a_mask;
1961d79b6df6SOr Gerlitz 			vals_p = (void *)add_vals + f->offset;
1962d79b6df6SOr Gerlitz 			/* clear to denote we consumed this field */
1963d79b6df6SOr Gerlitz 			memset(a_masks_p, 0, f->size);
1964d79b6df6SOr Gerlitz 		}
1965d79b6df6SOr Gerlitz 
1966d79b6df6SOr Gerlitz 		field_bsize = f->size * BITS_PER_BYTE;
1967e3ca4e05SOr Gerlitz 
19682b64bebaSOr Gerlitz 		if (field_bsize == 32) {
19692b64bebaSOr Gerlitz 			mask_be32 = *(__be32 *)&mask;
19702b64bebaSOr Gerlitz 			mask = (__force unsigned long)cpu_to_le32(be32_to_cpu(mask_be32));
19712b64bebaSOr Gerlitz 		} else if (field_bsize == 16) {
19722b64bebaSOr Gerlitz 			mask_be16 = *(__be16 *)&mask;
19732b64bebaSOr Gerlitz 			mask = (__force unsigned long)cpu_to_le16(be16_to_cpu(mask_be16));
19742b64bebaSOr Gerlitz 		}
19752b64bebaSOr Gerlitz 
1976d79b6df6SOr Gerlitz 		first = find_first_bit(&mask, field_bsize);
19772b64bebaSOr Gerlitz 		next_z = find_next_zero_bit(&mask, field_bsize, first);
1978d79b6df6SOr Gerlitz 		last  = find_last_bit(&mask, field_bsize);
19792b64bebaSOr Gerlitz 		if (first < next_z && next_z < last) {
1980e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1981e98bedf5SEli Britstein 					   "rewrite of few sub-fields isn't supported");
19822b64bebaSOr Gerlitz 			printk(KERN_WARNING "mlx5: rewrite of few sub-fields (mask %lx) isn't offloaded\n",
1983d79b6df6SOr Gerlitz 			       mask);
1984d79b6df6SOr Gerlitz 			return -EOPNOTSUPP;
1985d79b6df6SOr Gerlitz 		}
1986d79b6df6SOr Gerlitz 
1987d79b6df6SOr Gerlitz 		MLX5_SET(set_action_in, action, action_type, cmd);
1988d79b6df6SOr Gerlitz 		MLX5_SET(set_action_in, action, field, f->field);
1989d79b6df6SOr Gerlitz 
1990d79b6df6SOr Gerlitz 		if (cmd == MLX5_ACTION_TYPE_SET) {
19912b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, offset, first);
1992d79b6df6SOr Gerlitz 			/* length is num of bits to be written, zero means length of 32 */
19932b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, length, (last - first + 1));
1994d79b6df6SOr Gerlitz 		}
1995d79b6df6SOr Gerlitz 
1996d79b6df6SOr Gerlitz 		if (field_bsize == 32)
19972b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, data, ntohl(*(__be32 *)vals_p) >> first);
1998d79b6df6SOr Gerlitz 		else if (field_bsize == 16)
19992b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, data, ntohs(*(__be16 *)vals_p) >> first);
2000d79b6df6SOr Gerlitz 		else if (field_bsize == 8)
20012b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, data, *(u8 *)vals_p >> first);
2002d79b6df6SOr Gerlitz 
2003d79b6df6SOr Gerlitz 		action += action_size;
2004d79b6df6SOr Gerlitz 		nactions++;
2005d79b6df6SOr Gerlitz 	}
2006d79b6df6SOr Gerlitz 
2007d79b6df6SOr Gerlitz 	parse_attr->num_mod_hdr_actions = nactions;
2008d79b6df6SOr Gerlitz 	return 0;
2009d79b6df6SOr Gerlitz }
2010d79b6df6SOr Gerlitz 
2011d79b6df6SOr Gerlitz static int alloc_mod_hdr_actions(struct mlx5e_priv *priv,
2012d79b6df6SOr Gerlitz 				 const struct tc_action *a, int namespace,
2013d79b6df6SOr Gerlitz 				 struct mlx5e_tc_flow_parse_attr *parse_attr)
2014d79b6df6SOr Gerlitz {
2015d79b6df6SOr Gerlitz 	int nkeys, action_size, max_actions;
2016d79b6df6SOr Gerlitz 
2017d79b6df6SOr Gerlitz 	nkeys = tcf_pedit_nkeys(a);
2018d79b6df6SOr Gerlitz 	action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto);
2019d79b6df6SOr Gerlitz 
2020d79b6df6SOr Gerlitz 	if (namespace == MLX5_FLOW_NAMESPACE_FDB) /* FDB offloading */
2021d79b6df6SOr Gerlitz 		max_actions = MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, max_modify_header_actions);
2022d79b6df6SOr Gerlitz 	else /* namespace is MLX5_FLOW_NAMESPACE_KERNEL - NIC offloading */
2023d79b6df6SOr Gerlitz 		max_actions = MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, max_modify_header_actions);
2024d79b6df6SOr Gerlitz 
2025d79b6df6SOr Gerlitz 	/* can get up to crazingly 16 HW actions in 32 bits pedit SW key */
2026d79b6df6SOr Gerlitz 	max_actions = min(max_actions, nkeys * 16);
2027d79b6df6SOr Gerlitz 
2028d79b6df6SOr Gerlitz 	parse_attr->mod_hdr_actions = kcalloc(max_actions, action_size, GFP_KERNEL);
2029d79b6df6SOr Gerlitz 	if (!parse_attr->mod_hdr_actions)
2030d79b6df6SOr Gerlitz 		return -ENOMEM;
2031d79b6df6SOr Gerlitz 
2032d79b6df6SOr Gerlitz 	parse_attr->num_mod_hdr_actions = max_actions;
2033d79b6df6SOr Gerlitz 	return 0;
2034d79b6df6SOr Gerlitz }
2035d79b6df6SOr Gerlitz 
2036d79b6df6SOr Gerlitz static const struct pedit_headers zero_masks = {};
2037d79b6df6SOr Gerlitz 
2038d79b6df6SOr Gerlitz static int parse_tc_pedit_action(struct mlx5e_priv *priv,
2039d79b6df6SOr Gerlitz 				 const struct tc_action *a, int namespace,
2040e98bedf5SEli Britstein 				 struct mlx5e_tc_flow_parse_attr *parse_attr,
2041e98bedf5SEli Britstein 				 struct netlink_ext_ack *extack)
2042d79b6df6SOr Gerlitz {
2043d79b6df6SOr Gerlitz 	struct pedit_headers masks[__PEDIT_CMD_MAX], vals[__PEDIT_CMD_MAX], *cmd_masks;
2044d79b6df6SOr Gerlitz 	int nkeys, i, err = -EOPNOTSUPP;
2045d79b6df6SOr Gerlitz 	u32 mask, val, offset;
2046d79b6df6SOr Gerlitz 	u8 cmd, htype;
2047d79b6df6SOr Gerlitz 
2048d79b6df6SOr Gerlitz 	nkeys = tcf_pedit_nkeys(a);
2049d79b6df6SOr Gerlitz 
2050d79b6df6SOr Gerlitz 	memset(masks, 0, sizeof(struct pedit_headers) * __PEDIT_CMD_MAX);
2051d79b6df6SOr Gerlitz 	memset(vals,  0, sizeof(struct pedit_headers) * __PEDIT_CMD_MAX);
2052d79b6df6SOr Gerlitz 
2053d79b6df6SOr Gerlitz 	for (i = 0; i < nkeys; i++) {
2054d79b6df6SOr Gerlitz 		htype = tcf_pedit_htype(a, i);
2055d79b6df6SOr Gerlitz 		cmd = tcf_pedit_cmd(a, i);
2056d79b6df6SOr Gerlitz 		err = -EOPNOTSUPP; /* can't be all optimistic */
2057d79b6df6SOr Gerlitz 
2058d79b6df6SOr Gerlitz 		if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) {
2059e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
2060e98bedf5SEli Britstein 					   "legacy pedit isn't offloaded");
2061d79b6df6SOr Gerlitz 			goto out_err;
2062d79b6df6SOr Gerlitz 		}
2063d79b6df6SOr Gerlitz 
2064d79b6df6SOr Gerlitz 		if (cmd != TCA_PEDIT_KEY_EX_CMD_SET && cmd != TCA_PEDIT_KEY_EX_CMD_ADD) {
2065e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack, "pedit cmd isn't offloaded");
2066d79b6df6SOr Gerlitz 			goto out_err;
2067d79b6df6SOr Gerlitz 		}
2068d79b6df6SOr Gerlitz 
2069d79b6df6SOr Gerlitz 		mask = tcf_pedit_mask(a, i);
2070d79b6df6SOr Gerlitz 		val = tcf_pedit_val(a, i);
2071d79b6df6SOr Gerlitz 		offset = tcf_pedit_offset(a, i);
2072d79b6df6SOr Gerlitz 
2073d79b6df6SOr Gerlitz 		err = set_pedit_val(htype, ~mask, val, offset, &masks[cmd], &vals[cmd]);
2074d79b6df6SOr Gerlitz 		if (err)
2075d79b6df6SOr Gerlitz 			goto out_err;
2076d79b6df6SOr Gerlitz 	}
2077d79b6df6SOr Gerlitz 
2078d79b6df6SOr Gerlitz 	err = alloc_mod_hdr_actions(priv, a, namespace, parse_attr);
2079d79b6df6SOr Gerlitz 	if (err)
2080d79b6df6SOr Gerlitz 		goto out_err;
2081d79b6df6SOr Gerlitz 
2082e98bedf5SEli Britstein 	err = offload_pedit_fields(masks, vals, parse_attr, extack);
2083d79b6df6SOr Gerlitz 	if (err < 0)
2084d79b6df6SOr Gerlitz 		goto out_dealloc_parsed_actions;
2085d79b6df6SOr Gerlitz 
2086d79b6df6SOr Gerlitz 	for (cmd = 0; cmd < __PEDIT_CMD_MAX; cmd++) {
2087d79b6df6SOr Gerlitz 		cmd_masks = &masks[cmd];
2088d79b6df6SOr Gerlitz 		if (memcmp(cmd_masks, &zero_masks, sizeof(zero_masks))) {
2089e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
2090e98bedf5SEli Britstein 					   "attempt to offload an unsupported field");
2091b3a433deSOr Gerlitz 			netdev_warn(priv->netdev, "attempt to offload an unsupported field (cmd %d)\n", cmd);
2092d79b6df6SOr Gerlitz 			print_hex_dump(KERN_WARNING, "mask: ", DUMP_PREFIX_ADDRESS,
2093d79b6df6SOr Gerlitz 				       16, 1, cmd_masks, sizeof(zero_masks), true);
2094d79b6df6SOr Gerlitz 			err = -EOPNOTSUPP;
2095d79b6df6SOr Gerlitz 			goto out_dealloc_parsed_actions;
2096d79b6df6SOr Gerlitz 		}
2097d79b6df6SOr Gerlitz 	}
2098d79b6df6SOr Gerlitz 
2099d79b6df6SOr Gerlitz 	return 0;
2100d79b6df6SOr Gerlitz 
2101d79b6df6SOr Gerlitz out_dealloc_parsed_actions:
2102d79b6df6SOr Gerlitz 	kfree(parse_attr->mod_hdr_actions);
2103d79b6df6SOr Gerlitz out_err:
2104d79b6df6SOr Gerlitz 	return err;
2105d79b6df6SOr Gerlitz }
2106d79b6df6SOr Gerlitz 
2107e98bedf5SEli Britstein static bool csum_offload_supported(struct mlx5e_priv *priv,
2108e98bedf5SEli Britstein 				   u32 action,
2109e98bedf5SEli Britstein 				   u32 update_flags,
2110e98bedf5SEli Britstein 				   struct netlink_ext_ack *extack)
211126c02749SOr Gerlitz {
211226c02749SOr Gerlitz 	u32 prot_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR | TCA_CSUM_UPDATE_FLAG_TCP |
211326c02749SOr Gerlitz 			 TCA_CSUM_UPDATE_FLAG_UDP;
211426c02749SOr Gerlitz 
211526c02749SOr Gerlitz 	/*  The HW recalcs checksums only if re-writing headers */
211626c02749SOr Gerlitz 	if (!(action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)) {
2117e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
2118e98bedf5SEli Britstein 				   "TC csum action is only offloaded with pedit");
211926c02749SOr Gerlitz 		netdev_warn(priv->netdev,
212026c02749SOr Gerlitz 			    "TC csum action is only offloaded with pedit\n");
212126c02749SOr Gerlitz 		return false;
212226c02749SOr Gerlitz 	}
212326c02749SOr Gerlitz 
212426c02749SOr Gerlitz 	if (update_flags & ~prot_flags) {
2125e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
2126e98bedf5SEli Britstein 				   "can't offload TC csum action for some header/s");
212726c02749SOr Gerlitz 		netdev_warn(priv->netdev,
212826c02749SOr Gerlitz 			    "can't offload TC csum action for some header/s - flags %#x\n",
212926c02749SOr Gerlitz 			    update_flags);
213026c02749SOr Gerlitz 		return false;
213126c02749SOr Gerlitz 	}
213226c02749SOr Gerlitz 
213326c02749SOr Gerlitz 	return true;
213426c02749SOr Gerlitz }
213526c02749SOr Gerlitz 
2136bdd66ac0SOr Gerlitz static bool modify_header_match_supported(struct mlx5_flow_spec *spec,
2137e98bedf5SEli Britstein 					  struct tcf_exts *exts,
2138e98bedf5SEli Britstein 					  struct netlink_ext_ack *extack)
2139bdd66ac0SOr Gerlitz {
2140bdd66ac0SOr Gerlitz 	const struct tc_action *a;
2141bdd66ac0SOr Gerlitz 	bool modify_ip_header;
2142bdd66ac0SOr Gerlitz 	LIST_HEAD(actions);
2143bdd66ac0SOr Gerlitz 	u8 htype, ip_proto;
2144bdd66ac0SOr Gerlitz 	void *headers_v;
2145bdd66ac0SOr Gerlitz 	u16 ethertype;
2146bdd66ac0SOr Gerlitz 	int nkeys, i;
2147bdd66ac0SOr Gerlitz 
2148bdd66ac0SOr Gerlitz 	headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers);
2149bdd66ac0SOr Gerlitz 	ethertype = MLX5_GET(fte_match_set_lyr_2_4, headers_v, ethertype);
2150bdd66ac0SOr Gerlitz 
2151bdd66ac0SOr Gerlitz 	/* for non-IP we only re-write MACs, so we're okay */
2152bdd66ac0SOr Gerlitz 	if (ethertype != ETH_P_IP && ethertype != ETH_P_IPV6)
2153bdd66ac0SOr Gerlitz 		goto out_ok;
2154bdd66ac0SOr Gerlitz 
2155bdd66ac0SOr Gerlitz 	modify_ip_header = false;
2156244cd96aSCong Wang 	tcf_exts_for_each_action(i, a, exts) {
2157244cd96aSCong Wang 		int k;
2158244cd96aSCong Wang 
2159bdd66ac0SOr Gerlitz 		if (!is_tcf_pedit(a))
2160bdd66ac0SOr Gerlitz 			continue;
2161bdd66ac0SOr Gerlitz 
2162bdd66ac0SOr Gerlitz 		nkeys = tcf_pedit_nkeys(a);
2163244cd96aSCong Wang 		for (k = 0; k < nkeys; k++) {
2164244cd96aSCong Wang 			htype = tcf_pedit_htype(a, k);
2165bdd66ac0SOr Gerlitz 			if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_IP4 ||
2166bdd66ac0SOr Gerlitz 			    htype == TCA_PEDIT_KEY_EX_HDR_TYPE_IP6) {
2167bdd66ac0SOr Gerlitz 				modify_ip_header = true;
2168bdd66ac0SOr Gerlitz 				break;
2169bdd66ac0SOr Gerlitz 			}
2170bdd66ac0SOr Gerlitz 		}
2171bdd66ac0SOr Gerlitz 	}
2172bdd66ac0SOr Gerlitz 
2173bdd66ac0SOr Gerlitz 	ip_proto = MLX5_GET(fte_match_set_lyr_2_4, headers_v, ip_protocol);
21741ccef350SJianbo Liu 	if (modify_ip_header && ip_proto != IPPROTO_TCP &&
21751ccef350SJianbo Liu 	    ip_proto != IPPROTO_UDP && ip_proto != IPPROTO_ICMP) {
2176e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
2177e98bedf5SEli Britstein 				   "can't offload re-write of non TCP/UDP");
2178bdd66ac0SOr Gerlitz 		pr_info("can't offload re-write of ip proto %d\n", ip_proto);
2179bdd66ac0SOr Gerlitz 		return false;
2180bdd66ac0SOr Gerlitz 	}
2181bdd66ac0SOr Gerlitz 
2182bdd66ac0SOr Gerlitz out_ok:
2183bdd66ac0SOr Gerlitz 	return true;
2184bdd66ac0SOr Gerlitz }
2185bdd66ac0SOr Gerlitz 
2186bdd66ac0SOr Gerlitz static bool actions_match_supported(struct mlx5e_priv *priv,
2187bdd66ac0SOr Gerlitz 				    struct tcf_exts *exts,
2188bdd66ac0SOr Gerlitz 				    struct mlx5e_tc_flow_parse_attr *parse_attr,
2189e98bedf5SEli Britstein 				    struct mlx5e_tc_flow *flow,
2190e98bedf5SEli Britstein 				    struct netlink_ext_ack *extack)
2191bdd66ac0SOr Gerlitz {
2192bdd66ac0SOr Gerlitz 	u32 actions;
2193bdd66ac0SOr Gerlitz 
2194bdd66ac0SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
2195bdd66ac0SOr Gerlitz 		actions = flow->esw_attr->action;
2196bdd66ac0SOr Gerlitz 	else
2197bdd66ac0SOr Gerlitz 		actions = flow->nic_attr->action;
2198bdd66ac0SOr Gerlitz 
21997e29392eSRoi Dayan 	if (flow->flags & MLX5E_TC_FLOW_EGRESS &&
22007e29392eSRoi Dayan 	    !(actions & MLX5_FLOW_CONTEXT_ACTION_DECAP))
22017e29392eSRoi Dayan 		return false;
22027e29392eSRoi Dayan 
2203bdd66ac0SOr Gerlitz 	if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
2204e98bedf5SEli Britstein 		return modify_header_match_supported(&parse_attr->spec, exts,
2205e98bedf5SEli Britstein 						     extack);
2206bdd66ac0SOr Gerlitz 
2207bdd66ac0SOr Gerlitz 	return true;
2208bdd66ac0SOr Gerlitz }
2209bdd66ac0SOr Gerlitz 
22105c65c564SOr Gerlitz static bool same_hw_devs(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv)
22115c65c564SOr Gerlitz {
22125c65c564SOr Gerlitz 	struct mlx5_core_dev *fmdev, *pmdev;
2213816f6706SOr Gerlitz 	u64 fsystem_guid, psystem_guid;
22145c65c564SOr Gerlitz 
22155c65c564SOr Gerlitz 	fmdev = priv->mdev;
22165c65c564SOr Gerlitz 	pmdev = peer_priv->mdev;
22175c65c564SOr Gerlitz 
221859c9d35eSAlaa Hleihel 	fsystem_guid = mlx5_query_nic_system_image_guid(fmdev);
221959c9d35eSAlaa Hleihel 	psystem_guid = mlx5_query_nic_system_image_guid(pmdev);
22205c65c564SOr Gerlitz 
2221816f6706SOr Gerlitz 	return (fsystem_guid == psystem_guid);
22225c65c564SOr Gerlitz }
22235c65c564SOr Gerlitz 
22245c40348cSOr Gerlitz static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
2225aa0cbbaeSOr Gerlitz 				struct mlx5e_tc_flow_parse_attr *parse_attr,
2226e98bedf5SEli Britstein 				struct mlx5e_tc_flow *flow,
2227e98bedf5SEli Britstein 				struct netlink_ext_ack *extack)
2228e3a2b7edSAmir Vadai {
2229aa0cbbaeSOr Gerlitz 	struct mlx5_nic_flow_attr *attr = flow->nic_attr;
2230e3a2b7edSAmir Vadai 	const struct tc_action *a;
223122dc13c8SWANG Cong 	LIST_HEAD(actions);
22321cab1cd7SOr Gerlitz 	u32 action = 0;
2233244cd96aSCong Wang 	int err, i;
2234e3a2b7edSAmir Vadai 
22353bcc0cecSJiri Pirko 	if (!tcf_exts_has_actions(exts))
2236e3a2b7edSAmir Vadai 		return -EINVAL;
2237e3a2b7edSAmir Vadai 
22383bc4b7bfSOr Gerlitz 	attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
2239e3a2b7edSAmir Vadai 
2240244cd96aSCong Wang 	tcf_exts_for_each_action(i, a, exts) {
2241e3a2b7edSAmir Vadai 		if (is_tcf_gact_shot(a)) {
22421cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
2243aad7e08dSAmir Vadai 			if (MLX5_CAP_FLOWTABLE(priv->mdev,
2244aad7e08dSAmir Vadai 					       flow_table_properties_nic_receive.flow_counter))
22451cab1cd7SOr Gerlitz 				action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
2246e3a2b7edSAmir Vadai 			continue;
2247e3a2b7edSAmir Vadai 		}
2248e3a2b7edSAmir Vadai 
22492f4fe4caSOr Gerlitz 		if (is_tcf_pedit(a)) {
22502f4fe4caSOr Gerlitz 			err = parse_tc_pedit_action(priv, a, MLX5_FLOW_NAMESPACE_KERNEL,
2251e98bedf5SEli Britstein 						    parse_attr, extack);
22522f4fe4caSOr Gerlitz 			if (err)
22532f4fe4caSOr Gerlitz 				return err;
22542f4fe4caSOr Gerlitz 
22551cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
22562f4fe4caSOr Gerlitz 				  MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
22572f4fe4caSOr Gerlitz 			continue;
22582f4fe4caSOr Gerlitz 		}
22592f4fe4caSOr Gerlitz 
226026c02749SOr Gerlitz 		if (is_tcf_csum(a)) {
22611cab1cd7SOr Gerlitz 			if (csum_offload_supported(priv, action,
2262e98bedf5SEli Britstein 						   tcf_csum_update_flags(a),
2263e98bedf5SEli Britstein 						   extack))
226426c02749SOr Gerlitz 				continue;
226526c02749SOr Gerlitz 
226626c02749SOr Gerlitz 			return -EOPNOTSUPP;
226726c02749SOr Gerlitz 		}
226826c02749SOr Gerlitz 
22695c65c564SOr Gerlitz 		if (is_tcf_mirred_egress_redirect(a)) {
22705c65c564SOr Gerlitz 			struct net_device *peer_dev = tcf_mirred_dev(a);
22715c65c564SOr Gerlitz 
22725c65c564SOr Gerlitz 			if (priv->netdev->netdev_ops == peer_dev->netdev_ops &&
22735c65c564SOr Gerlitz 			    same_hw_devs(priv, netdev_priv(peer_dev))) {
22745c65c564SOr Gerlitz 				parse_attr->mirred_ifindex = peer_dev->ifindex;
22755c65c564SOr Gerlitz 				flow->flags |= MLX5E_TC_FLOW_HAIRPIN;
22761cab1cd7SOr Gerlitz 				action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
22775c65c564SOr Gerlitz 					  MLX5_FLOW_CONTEXT_ACTION_COUNT;
22785c65c564SOr Gerlitz 			} else {
2279e98bedf5SEli Britstein 				NL_SET_ERR_MSG_MOD(extack,
2280e98bedf5SEli Britstein 						   "device is not on same HW, can't offload");
22815c65c564SOr Gerlitz 				netdev_warn(priv->netdev, "device %s not on same HW, can't offload\n",
22825c65c564SOr Gerlitz 					    peer_dev->name);
22835c65c564SOr Gerlitz 				return -EINVAL;
22845c65c564SOr Gerlitz 			}
22855c65c564SOr Gerlitz 			continue;
22865c65c564SOr Gerlitz 		}
22875c65c564SOr Gerlitz 
2288e3a2b7edSAmir Vadai 		if (is_tcf_skbedit_mark(a)) {
2289e3a2b7edSAmir Vadai 			u32 mark = tcf_skbedit_mark(a);
2290e3a2b7edSAmir Vadai 
2291e3a2b7edSAmir Vadai 			if (mark & ~MLX5E_TC_FLOW_ID_MASK) {
2292e98bedf5SEli Britstein 				NL_SET_ERR_MSG_MOD(extack,
2293e98bedf5SEli Britstein 						   "Bad flow mark - only 16 bit is supported");
2294e3a2b7edSAmir Vadai 				return -EINVAL;
2295e3a2b7edSAmir Vadai 			}
2296e3a2b7edSAmir Vadai 
22973bc4b7bfSOr Gerlitz 			attr->flow_tag = mark;
22981cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
2299e3a2b7edSAmir Vadai 			continue;
2300e3a2b7edSAmir Vadai 		}
2301e3a2b7edSAmir Vadai 
2302e3a2b7edSAmir Vadai 		return -EINVAL;
2303e3a2b7edSAmir Vadai 	}
2304e3a2b7edSAmir Vadai 
23051cab1cd7SOr Gerlitz 	attr->action = action;
2306e98bedf5SEli Britstein 	if (!actions_match_supported(priv, exts, parse_attr, flow, extack))
2307bdd66ac0SOr Gerlitz 		return -EOPNOTSUPP;
2308bdd66ac0SOr Gerlitz 
2309e3a2b7edSAmir Vadai 	return 0;
2310e3a2b7edSAmir Vadai }
2311e3a2b7edSAmir Vadai 
231276f7444dSOr Gerlitz static inline int cmp_encap_info(struct ip_tunnel_key *a,
231376f7444dSOr Gerlitz 				 struct ip_tunnel_key *b)
2314a54e20b4SHadar Hen Zion {
2315a54e20b4SHadar Hen Zion 	return memcmp(a, b, sizeof(*a));
2316a54e20b4SHadar Hen Zion }
2317a54e20b4SHadar Hen Zion 
231876f7444dSOr Gerlitz static inline int hash_encap_info(struct ip_tunnel_key *key)
2319a54e20b4SHadar Hen Zion {
232076f7444dSOr Gerlitz 	return jhash(key, sizeof(*key), 0);
2321a54e20b4SHadar Hen Zion }
2322a54e20b4SHadar Hen Zion 
2323a54e20b4SHadar Hen Zion static int mlx5e_route_lookup_ipv4(struct mlx5e_priv *priv,
2324a54e20b4SHadar Hen Zion 				   struct net_device *mirred_dev,
2325a54e20b4SHadar Hen Zion 				   struct net_device **out_dev,
2326a54e20b4SHadar Hen Zion 				   struct flowi4 *fl4,
2327a54e20b4SHadar Hen Zion 				   struct neighbour **out_n,
23286360cd62SOr Gerlitz 				   u8 *out_ttl)
2329a54e20b4SHadar Hen Zion {
23303e621b19SHadar Hen Zion 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
23315ed99fb4SMark Bloch 	struct mlx5e_rep_priv *uplink_rpriv;
2332a54e20b4SHadar Hen Zion 	struct rtable *rt;
2333a54e20b4SHadar Hen Zion 	struct neighbour *n = NULL;
2334a54e20b4SHadar Hen Zion 
2335a54e20b4SHadar Hen Zion #if IS_ENABLED(CONFIG_INET)
2336abeffce9SArnd Bergmann 	int ret;
2337abeffce9SArnd Bergmann 
2338a54e20b4SHadar Hen Zion 	rt = ip_route_output_key(dev_net(mirred_dev), fl4);
2339abeffce9SArnd Bergmann 	ret = PTR_ERR_OR_ZERO(rt);
2340abeffce9SArnd Bergmann 	if (ret)
2341abeffce9SArnd Bergmann 		return ret;
2342a54e20b4SHadar Hen Zion #else
2343a54e20b4SHadar Hen Zion 	return -EOPNOTSUPP;
2344a54e20b4SHadar Hen Zion #endif
2345a4b97ab4SMark Bloch 	uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
23463e621b19SHadar Hen Zion 	/* if the egress device isn't on the same HW e-switch, we use the uplink */
23473e621b19SHadar Hen Zion 	if (!switchdev_port_same_parent_id(priv->netdev, rt->dst.dev))
23485ed99fb4SMark Bloch 		*out_dev = uplink_rpriv->netdev;
23493e621b19SHadar Hen Zion 	else
23503e621b19SHadar Hen Zion 		*out_dev = rt->dst.dev;
2351a54e20b4SHadar Hen Zion 
23526360cd62SOr Gerlitz 	if (!(*out_ttl))
235375c33da8SOr Gerlitz 		*out_ttl = ip4_dst_hoplimit(&rt->dst);
2354a54e20b4SHadar Hen Zion 	n = dst_neigh_lookup(&rt->dst, &fl4->daddr);
2355a54e20b4SHadar Hen Zion 	ip_rt_put(rt);
2356a54e20b4SHadar Hen Zion 	if (!n)
2357a54e20b4SHadar Hen Zion 		return -ENOMEM;
2358a54e20b4SHadar Hen Zion 
2359a54e20b4SHadar Hen Zion 	*out_n = n;
2360a54e20b4SHadar Hen Zion 	return 0;
2361a54e20b4SHadar Hen Zion }
2362a54e20b4SHadar Hen Zion 
2363b1d90e6bSRabie Loulou static bool is_merged_eswitch_dev(struct mlx5e_priv *priv,
2364b1d90e6bSRabie Loulou 				  struct net_device *peer_netdev)
2365b1d90e6bSRabie Loulou {
2366b1d90e6bSRabie Loulou 	struct mlx5e_priv *peer_priv;
2367b1d90e6bSRabie Loulou 
2368b1d90e6bSRabie Loulou 	peer_priv = netdev_priv(peer_netdev);
2369b1d90e6bSRabie Loulou 
2370b1d90e6bSRabie Loulou 	return (MLX5_CAP_ESW(priv->mdev, merged_eswitch) &&
2371b1d90e6bSRabie Loulou 		(priv->netdev->netdev_ops == peer_netdev->netdev_ops) &&
2372b1d90e6bSRabie Loulou 		same_hw_devs(priv, peer_priv) &&
2373b1d90e6bSRabie Loulou 		MLX5_VPORT_MANAGER(peer_priv->mdev) &&
2374b1d90e6bSRabie Loulou 		(peer_priv->mdev->priv.eswitch->mode == SRIOV_OFFLOADS));
2375b1d90e6bSRabie Loulou }
2376b1d90e6bSRabie Loulou 
2377ce99f6b9SOr Gerlitz static int mlx5e_route_lookup_ipv6(struct mlx5e_priv *priv,
2378ce99f6b9SOr Gerlitz 				   struct net_device *mirred_dev,
2379ce99f6b9SOr Gerlitz 				   struct net_device **out_dev,
2380ce99f6b9SOr Gerlitz 				   struct flowi6 *fl6,
2381ce99f6b9SOr Gerlitz 				   struct neighbour **out_n,
23826360cd62SOr Gerlitz 				   u8 *out_ttl)
2383ce99f6b9SOr Gerlitz {
2384ce99f6b9SOr Gerlitz 	struct neighbour *n = NULL;
2385ce99f6b9SOr Gerlitz 	struct dst_entry *dst;
2386ce99f6b9SOr Gerlitz 
2387ce99f6b9SOr Gerlitz #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
238874bd5d56SArnd Bergmann 	struct mlx5e_rep_priv *uplink_rpriv;
2389ce99f6b9SOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
2390ce99f6b9SOr Gerlitz 	int ret;
2391ce99f6b9SOr Gerlitz 
239208820528SPaul Blakey 	ret = ipv6_stub->ipv6_dst_lookup(dev_net(mirred_dev), NULL, &dst,
239308820528SPaul Blakey 					 fl6);
239408820528SPaul Blakey 	if (ret < 0)
2395ce99f6b9SOr Gerlitz 		return ret;
2396ce99f6b9SOr Gerlitz 
23976360cd62SOr Gerlitz 	if (!(*out_ttl))
2398ce99f6b9SOr Gerlitz 		*out_ttl = ip6_dst_hoplimit(dst);
2399ce99f6b9SOr Gerlitz 
2400a4b97ab4SMark Bloch 	uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
2401ce99f6b9SOr Gerlitz 	/* if the egress device isn't on the same HW e-switch, we use the uplink */
2402ce99f6b9SOr Gerlitz 	if (!switchdev_port_same_parent_id(priv->netdev, dst->dev))
24035ed99fb4SMark Bloch 		*out_dev = uplink_rpriv->netdev;
2404ce99f6b9SOr Gerlitz 	else
2405ce99f6b9SOr Gerlitz 		*out_dev = dst->dev;
2406ce99f6b9SOr Gerlitz #else
2407ce99f6b9SOr Gerlitz 	return -EOPNOTSUPP;
2408ce99f6b9SOr Gerlitz #endif
2409ce99f6b9SOr Gerlitz 
2410ce99f6b9SOr Gerlitz 	n = dst_neigh_lookup(dst, &fl6->daddr);
2411ce99f6b9SOr Gerlitz 	dst_release(dst);
2412ce99f6b9SOr Gerlitz 	if (!n)
2413ce99f6b9SOr Gerlitz 		return -ENOMEM;
2414ce99f6b9SOr Gerlitz 
2415ce99f6b9SOr Gerlitz 	*out_n = n;
2416ce99f6b9SOr Gerlitz 	return 0;
2417ce99f6b9SOr Gerlitz }
2418ce99f6b9SOr Gerlitz 
241932f3671fSOr Gerlitz static void gen_vxlan_header_ipv4(struct net_device *out_dev,
242032f3671fSOr Gerlitz 				  char buf[], int encap_size,
2421a54e20b4SHadar Hen Zion 				  unsigned char h_dest[ETH_ALEN],
2422f35f800dSOr Gerlitz 				  u8 tos, u8 ttl,
2423a54e20b4SHadar Hen Zion 				  __be32 daddr,
2424a54e20b4SHadar Hen Zion 				  __be32 saddr,
2425a54e20b4SHadar Hen Zion 				  __be16 udp_dst_port,
2426a54e20b4SHadar Hen Zion 				  __be32 vx_vni)
2427a54e20b4SHadar Hen Zion {
2428a54e20b4SHadar Hen Zion 	struct ethhdr *eth = (struct ethhdr *)buf;
2429a54e20b4SHadar Hen Zion 	struct iphdr  *ip = (struct iphdr *)((char *)eth + sizeof(struct ethhdr));
2430a54e20b4SHadar Hen Zion 	struct udphdr *udp = (struct udphdr *)((char *)ip + sizeof(struct iphdr));
2431a54e20b4SHadar Hen Zion 	struct vxlanhdr *vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr));
2432a54e20b4SHadar Hen Zion 
2433a54e20b4SHadar Hen Zion 	memset(buf, 0, encap_size);
2434a54e20b4SHadar Hen Zion 
2435a54e20b4SHadar Hen Zion 	ether_addr_copy(eth->h_dest, h_dest);
2436a54e20b4SHadar Hen Zion 	ether_addr_copy(eth->h_source, out_dev->dev_addr);
2437a54e20b4SHadar Hen Zion 	eth->h_proto = htons(ETH_P_IP);
2438a54e20b4SHadar Hen Zion 
2439a54e20b4SHadar Hen Zion 	ip->daddr = daddr;
2440a54e20b4SHadar Hen Zion 	ip->saddr = saddr;
2441a54e20b4SHadar Hen Zion 
2442f35f800dSOr Gerlitz 	ip->tos = tos;
2443a54e20b4SHadar Hen Zion 	ip->ttl = ttl;
2444a54e20b4SHadar Hen Zion 	ip->protocol = IPPROTO_UDP;
2445a54e20b4SHadar Hen Zion 	ip->version = 0x4;
2446a54e20b4SHadar Hen Zion 	ip->ihl = 0x5;
2447a54e20b4SHadar Hen Zion 
2448a54e20b4SHadar Hen Zion 	udp->dest = udp_dst_port;
2449a54e20b4SHadar Hen Zion 	vxh->vx_flags = VXLAN_HF_VNI;
2450a54e20b4SHadar Hen Zion 	vxh->vx_vni = vxlan_vni_field(vx_vni);
2451a54e20b4SHadar Hen Zion }
2452a54e20b4SHadar Hen Zion 
2453225aabafSOr Gerlitz static void gen_vxlan_header_ipv6(struct net_device *out_dev,
2454225aabafSOr Gerlitz 				  char buf[], int encap_size,
2455ce99f6b9SOr Gerlitz 				  unsigned char h_dest[ETH_ALEN],
2456f35f800dSOr Gerlitz 				  u8 tos, u8 ttl,
2457ce99f6b9SOr Gerlitz 				  struct in6_addr *daddr,
2458ce99f6b9SOr Gerlitz 				  struct in6_addr *saddr,
2459ce99f6b9SOr Gerlitz 				  __be16 udp_dst_port,
2460ce99f6b9SOr Gerlitz 				  __be32 vx_vni)
2461ce99f6b9SOr Gerlitz {
2462ce99f6b9SOr Gerlitz 	struct ethhdr *eth = (struct ethhdr *)buf;
2463ce99f6b9SOr Gerlitz 	struct ipv6hdr *ip6h = (struct ipv6hdr *)((char *)eth + sizeof(struct ethhdr));
2464ce99f6b9SOr Gerlitz 	struct udphdr *udp = (struct udphdr *)((char *)ip6h + sizeof(struct ipv6hdr));
2465ce99f6b9SOr Gerlitz 	struct vxlanhdr *vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr));
2466ce99f6b9SOr Gerlitz 
2467ce99f6b9SOr Gerlitz 	memset(buf, 0, encap_size);
2468ce99f6b9SOr Gerlitz 
2469ce99f6b9SOr Gerlitz 	ether_addr_copy(eth->h_dest, h_dest);
2470ce99f6b9SOr Gerlitz 	ether_addr_copy(eth->h_source, out_dev->dev_addr);
2471ce99f6b9SOr Gerlitz 	eth->h_proto = htons(ETH_P_IPV6);
2472ce99f6b9SOr Gerlitz 
2473f35f800dSOr Gerlitz 	ip6_flow_hdr(ip6h, tos, 0);
2474ce99f6b9SOr Gerlitz 	/* the HW fills up ipv6 payload len */
2475ce99f6b9SOr Gerlitz 	ip6h->nexthdr     = IPPROTO_UDP;
2476ce99f6b9SOr Gerlitz 	ip6h->hop_limit   = ttl;
2477ce99f6b9SOr Gerlitz 	ip6h->daddr	  = *daddr;
2478ce99f6b9SOr Gerlitz 	ip6h->saddr	  = *saddr;
2479ce99f6b9SOr Gerlitz 
2480ce99f6b9SOr Gerlitz 	udp->dest = udp_dst_port;
2481ce99f6b9SOr Gerlitz 	vxh->vx_flags = VXLAN_HF_VNI;
2482ce99f6b9SOr Gerlitz 	vxh->vx_vni = vxlan_vni_field(vx_vni);
2483ce99f6b9SOr Gerlitz }
2484ce99f6b9SOr Gerlitz 
2485a54e20b4SHadar Hen Zion static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv,
2486a54e20b4SHadar Hen Zion 					  struct net_device *mirred_dev,
24871a8552bdSHadar Hen Zion 					  struct mlx5e_encap_entry *e)
2488a54e20b4SHadar Hen Zion {
2489a54e20b4SHadar Hen Zion 	int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
249032f3671fSOr Gerlitz 	int ipv4_encap_size = ETH_HLEN + sizeof(struct iphdr) + VXLAN_HLEN;
249176f7444dSOr Gerlitz 	struct ip_tunnel_key *tun_key = &e->tun_info.key;
24921a8552bdSHadar Hen Zion 	struct net_device *out_dev;
2493a42485ebSOr Gerlitz 	struct neighbour *n = NULL;
2494a54e20b4SHadar Hen Zion 	struct flowi4 fl4 = {};
2495f35f800dSOr Gerlitz 	u8 nud_state, tos, ttl;
2496a54e20b4SHadar Hen Zion 	char *encap_header;
24976360cd62SOr Gerlitz 	int err;
2498a54e20b4SHadar Hen Zion 
249932f3671fSOr Gerlitz 	if (max_encap_size < ipv4_encap_size) {
250032f3671fSOr Gerlitz 		mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
250132f3671fSOr Gerlitz 			       ipv4_encap_size, max_encap_size);
250232f3671fSOr Gerlitz 		return -EOPNOTSUPP;
250332f3671fSOr Gerlitz 	}
250432f3671fSOr Gerlitz 
250532f3671fSOr Gerlitz 	encap_header = kzalloc(ipv4_encap_size, GFP_KERNEL);
2506a54e20b4SHadar Hen Zion 	if (!encap_header)
2507a54e20b4SHadar Hen Zion 		return -ENOMEM;
2508a54e20b4SHadar Hen Zion 
2509a54e20b4SHadar Hen Zion 	switch (e->tunnel_type) {
251060786f09SMark Bloch 	case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
2511a54e20b4SHadar Hen Zion 		fl4.flowi4_proto = IPPROTO_UDP;
251276f7444dSOr Gerlitz 		fl4.fl4_dport = tun_key->tp_dst;
2513a54e20b4SHadar Hen Zion 		break;
2514a54e20b4SHadar Hen Zion 	default:
2515a54e20b4SHadar Hen Zion 		err = -EOPNOTSUPP;
2516ace74321SPaul Blakey 		goto free_encap;
2517a54e20b4SHadar Hen Zion 	}
25186360cd62SOr Gerlitz 
2519f35f800dSOr Gerlitz 	tos = tun_key->tos;
2520f35f800dSOr Gerlitz 	ttl = tun_key->ttl;
25216360cd62SOr Gerlitz 
25229a941117SOr Gerlitz 	fl4.flowi4_tos = tun_key->tos;
252376f7444dSOr Gerlitz 	fl4.daddr = tun_key->u.ipv4.dst;
25249a941117SOr Gerlitz 	fl4.saddr = tun_key->u.ipv4.src;
2525a54e20b4SHadar Hen Zion 
25261a8552bdSHadar Hen Zion 	err = mlx5e_route_lookup_ipv4(priv, mirred_dev, &out_dev,
25279a941117SOr Gerlitz 				      &fl4, &n, &ttl);
2528a54e20b4SHadar Hen Zion 	if (err)
2529ace74321SPaul Blakey 		goto free_encap;
2530a54e20b4SHadar Hen Zion 
2531232c0013SHadar Hen Zion 	/* used by mlx5e_detach_encap to lookup a neigh hash table
2532232c0013SHadar Hen Zion 	 * entry in the neigh hash table when a user deletes a rule
2533232c0013SHadar Hen Zion 	 */
2534232c0013SHadar Hen Zion 	e->m_neigh.dev = n->dev;
2535f6dfb4c3SHadar Hen Zion 	e->m_neigh.family = n->ops->family;
2536232c0013SHadar Hen Zion 	memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len);
2537232c0013SHadar Hen Zion 	e->out_dev = out_dev;
2538232c0013SHadar Hen Zion 
2539232c0013SHadar Hen Zion 	/* It's importent to add the neigh to the hash table before checking
2540232c0013SHadar Hen Zion 	 * the neigh validity state. So if we'll get a notification, in case the
2541232c0013SHadar Hen Zion 	 * neigh changes it's validity state, we would find the relevant neigh
2542232c0013SHadar Hen Zion 	 * in the hash.
2543232c0013SHadar Hen Zion 	 */
2544232c0013SHadar Hen Zion 	err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e);
2545232c0013SHadar Hen Zion 	if (err)
2546ace74321SPaul Blakey 		goto free_encap;
2547232c0013SHadar Hen Zion 
2548033354d5SHadar Hen Zion 	read_lock_bh(&n->lock);
2549033354d5SHadar Hen Zion 	nud_state = n->nud_state;
2550033354d5SHadar Hen Zion 	ether_addr_copy(e->h_dest, n->ha);
2551033354d5SHadar Hen Zion 	read_unlock_bh(&n->lock);
2552033354d5SHadar Hen Zion 
2553a54e20b4SHadar Hen Zion 	switch (e->tunnel_type) {
255460786f09SMark Bloch 	case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
25551a8552bdSHadar Hen Zion 		gen_vxlan_header_ipv4(out_dev, encap_header,
2556f35f800dSOr Gerlitz 				      ipv4_encap_size, e->h_dest, tos, ttl,
25579a941117SOr Gerlitz 				      fl4.daddr,
25589a941117SOr Gerlitz 				      fl4.saddr, tun_key->tp_dst,
255976f7444dSOr Gerlitz 				      tunnel_id_to_key32(tun_key->tun_id));
2560a54e20b4SHadar Hen Zion 		break;
2561a54e20b4SHadar Hen Zion 	default:
2562a54e20b4SHadar Hen Zion 		err = -EOPNOTSUPP;
2563232c0013SHadar Hen Zion 		goto destroy_neigh_entry;
2564232c0013SHadar Hen Zion 	}
2565232c0013SHadar Hen Zion 	e->encap_size = ipv4_encap_size;
2566232c0013SHadar Hen Zion 	e->encap_header = encap_header;
2567232c0013SHadar Hen Zion 
2568232c0013SHadar Hen Zion 	if (!(nud_state & NUD_VALID)) {
2569232c0013SHadar Hen Zion 		neigh_event_send(n, NULL);
257027902f08SWei Yongjun 		err = -EAGAIN;
257127902f08SWei Yongjun 		goto out;
2572a54e20b4SHadar Hen Zion 	}
2573a54e20b4SHadar Hen Zion 
257460786f09SMark Bloch 	err = mlx5_packet_reformat_alloc(priv->mdev, e->tunnel_type,
257560786f09SMark Bloch 					 ipv4_encap_size, encap_header,
257631ca3648SMark Bloch 					 MLX5_FLOW_NAMESPACE_FDB,
257760786f09SMark Bloch 					 &e->encap_id);
2578232c0013SHadar Hen Zion 	if (err)
2579232c0013SHadar Hen Zion 		goto destroy_neigh_entry;
2580232c0013SHadar Hen Zion 
2581232c0013SHadar Hen Zion 	e->flags |= MLX5_ENCAP_ENTRY_VALID;
2582f6dfb4c3SHadar Hen Zion 	mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev));
2583a42485ebSOr Gerlitz 	neigh_release(n);
2584232c0013SHadar Hen Zion 	return err;
2585232c0013SHadar Hen Zion 
2586232c0013SHadar Hen Zion destroy_neigh_entry:
2587232c0013SHadar Hen Zion 	mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
2588ace74321SPaul Blakey free_encap:
2589a54e20b4SHadar Hen Zion 	kfree(encap_header);
2590ace74321SPaul Blakey out:
2591232c0013SHadar Hen Zion 	if (n)
2592232c0013SHadar Hen Zion 		neigh_release(n);
2593a54e20b4SHadar Hen Zion 	return err;
2594a54e20b4SHadar Hen Zion }
2595a54e20b4SHadar Hen Zion 
2596ce99f6b9SOr Gerlitz static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv,
2597ce99f6b9SOr Gerlitz 					  struct net_device *mirred_dev,
25981a8552bdSHadar Hen Zion 					  struct mlx5e_encap_entry *e)
2599ce99f6b9SOr Gerlitz {
2600ce99f6b9SOr Gerlitz 	int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size);
2601225aabafSOr Gerlitz 	int ipv6_encap_size = ETH_HLEN + sizeof(struct ipv6hdr) + VXLAN_HLEN;
2602ce99f6b9SOr Gerlitz 	struct ip_tunnel_key *tun_key = &e->tun_info.key;
26031a8552bdSHadar Hen Zion 	struct net_device *out_dev;
2604ce99f6b9SOr Gerlitz 	struct neighbour *n = NULL;
2605ce99f6b9SOr Gerlitz 	struct flowi6 fl6 = {};
2606f35f800dSOr Gerlitz 	u8 nud_state, tos, ttl;
2607ce99f6b9SOr Gerlitz 	char *encap_header;
26086360cd62SOr Gerlitz 	int err;
2609ce99f6b9SOr Gerlitz 
2610225aabafSOr Gerlitz 	if (max_encap_size < ipv6_encap_size) {
2611225aabafSOr Gerlitz 		mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n",
2612225aabafSOr Gerlitz 			       ipv6_encap_size, max_encap_size);
2613225aabafSOr Gerlitz 		return -EOPNOTSUPP;
2614225aabafSOr Gerlitz 	}
2615225aabafSOr Gerlitz 
2616225aabafSOr Gerlitz 	encap_header = kzalloc(ipv6_encap_size, GFP_KERNEL);
2617ce99f6b9SOr Gerlitz 	if (!encap_header)
2618ce99f6b9SOr Gerlitz 		return -ENOMEM;
2619ce99f6b9SOr Gerlitz 
2620ce99f6b9SOr Gerlitz 	switch (e->tunnel_type) {
262160786f09SMark Bloch 	case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
2622ce99f6b9SOr Gerlitz 		fl6.flowi6_proto = IPPROTO_UDP;
2623ce99f6b9SOr Gerlitz 		fl6.fl6_dport = tun_key->tp_dst;
2624ce99f6b9SOr Gerlitz 		break;
2625ce99f6b9SOr Gerlitz 	default:
2626ce99f6b9SOr Gerlitz 		err = -EOPNOTSUPP;
2627ace74321SPaul Blakey 		goto free_encap;
2628ce99f6b9SOr Gerlitz 	}
2629ce99f6b9SOr Gerlitz 
2630f35f800dSOr Gerlitz 	tos = tun_key->tos;
2631f35f800dSOr Gerlitz 	ttl = tun_key->ttl;
26326360cd62SOr Gerlitz 
2633ce99f6b9SOr Gerlitz 	fl6.flowlabel = ip6_make_flowinfo(RT_TOS(tun_key->tos), tun_key->label);
2634ce99f6b9SOr Gerlitz 	fl6.daddr = tun_key->u.ipv6.dst;
2635ce99f6b9SOr Gerlitz 	fl6.saddr = tun_key->u.ipv6.src;
2636ce99f6b9SOr Gerlitz 
26371a8552bdSHadar Hen Zion 	err = mlx5e_route_lookup_ipv6(priv, mirred_dev, &out_dev,
2638ce99f6b9SOr Gerlitz 				      &fl6, &n, &ttl);
2639ce99f6b9SOr Gerlitz 	if (err)
2640ace74321SPaul Blakey 		goto free_encap;
2641ce99f6b9SOr Gerlitz 
2642232c0013SHadar Hen Zion 	/* used by mlx5e_detach_encap to lookup a neigh hash table
2643232c0013SHadar Hen Zion 	 * entry in the neigh hash table when a user deletes a rule
2644232c0013SHadar Hen Zion 	 */
2645232c0013SHadar Hen Zion 	e->m_neigh.dev = n->dev;
2646f6dfb4c3SHadar Hen Zion 	e->m_neigh.family = n->ops->family;
2647232c0013SHadar Hen Zion 	memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len);
2648232c0013SHadar Hen Zion 	e->out_dev = out_dev;
2649232c0013SHadar Hen Zion 
2650232c0013SHadar Hen Zion 	/* It's importent to add the neigh to the hash table before checking
2651232c0013SHadar Hen Zion 	 * the neigh validity state. So if we'll get a notification, in case the
2652232c0013SHadar Hen Zion 	 * neigh changes it's validity state, we would find the relevant neigh
2653232c0013SHadar Hen Zion 	 * in the hash.
2654232c0013SHadar Hen Zion 	 */
2655232c0013SHadar Hen Zion 	err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e);
2656232c0013SHadar Hen Zion 	if (err)
2657ace74321SPaul Blakey 		goto free_encap;
2658232c0013SHadar Hen Zion 
2659033354d5SHadar Hen Zion 	read_lock_bh(&n->lock);
2660033354d5SHadar Hen Zion 	nud_state = n->nud_state;
2661033354d5SHadar Hen Zion 	ether_addr_copy(e->h_dest, n->ha);
2662033354d5SHadar Hen Zion 	read_unlock_bh(&n->lock);
2663033354d5SHadar Hen Zion 
2664ce99f6b9SOr Gerlitz 	switch (e->tunnel_type) {
266560786f09SMark Bloch 	case MLX5_REFORMAT_TYPE_L2_TO_VXLAN:
26661a8552bdSHadar Hen Zion 		gen_vxlan_header_ipv6(out_dev, encap_header,
2667f35f800dSOr Gerlitz 				      ipv6_encap_size, e->h_dest, tos, ttl,
2668ce99f6b9SOr Gerlitz 				      &fl6.daddr,
2669ce99f6b9SOr Gerlitz 				      &fl6.saddr, tun_key->tp_dst,
2670ce99f6b9SOr Gerlitz 				      tunnel_id_to_key32(tun_key->tun_id));
2671ce99f6b9SOr Gerlitz 		break;
2672ce99f6b9SOr Gerlitz 	default:
2673ce99f6b9SOr Gerlitz 		err = -EOPNOTSUPP;
2674232c0013SHadar Hen Zion 		goto destroy_neigh_entry;
2675232c0013SHadar Hen Zion 	}
2676232c0013SHadar Hen Zion 
2677232c0013SHadar Hen Zion 	e->encap_size = ipv6_encap_size;
2678232c0013SHadar Hen Zion 	e->encap_header = encap_header;
2679232c0013SHadar Hen Zion 
2680232c0013SHadar Hen Zion 	if (!(nud_state & NUD_VALID)) {
2681232c0013SHadar Hen Zion 		neigh_event_send(n, NULL);
268227902f08SWei Yongjun 		err = -EAGAIN;
268327902f08SWei Yongjun 		goto out;
2684ce99f6b9SOr Gerlitz 	}
2685ce99f6b9SOr Gerlitz 
268660786f09SMark Bloch 	err = mlx5_packet_reformat_alloc(priv->mdev, e->tunnel_type,
268760786f09SMark Bloch 					 ipv6_encap_size, encap_header,
268831ca3648SMark Bloch 					 MLX5_FLOW_NAMESPACE_FDB,
268960786f09SMark Bloch 					 &e->encap_id);
2690232c0013SHadar Hen Zion 	if (err)
2691232c0013SHadar Hen Zion 		goto destroy_neigh_entry;
2692232c0013SHadar Hen Zion 
2693232c0013SHadar Hen Zion 	e->flags |= MLX5_ENCAP_ENTRY_VALID;
2694f6dfb4c3SHadar Hen Zion 	mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev));
2695ce99f6b9SOr Gerlitz 	neigh_release(n);
2696232c0013SHadar Hen Zion 	return err;
2697232c0013SHadar Hen Zion 
2698232c0013SHadar Hen Zion destroy_neigh_entry:
2699232c0013SHadar Hen Zion 	mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
2700ace74321SPaul Blakey free_encap:
2701ce99f6b9SOr Gerlitz 	kfree(encap_header);
2702ace74321SPaul Blakey out:
2703232c0013SHadar Hen Zion 	if (n)
2704232c0013SHadar Hen Zion 		neigh_release(n);
2705ce99f6b9SOr Gerlitz 	return err;
2706ce99f6b9SOr Gerlitz }
2707ce99f6b9SOr Gerlitz 
2708f5bc2c5dSOz Shlomo bool mlx5e_tc_tun_device_to_offload(struct mlx5e_priv *priv,
2709f5bc2c5dSOz Shlomo 				    struct net_device *netdev)
2710f5bc2c5dSOz Shlomo {
2711f5bc2c5dSOz Shlomo 	if (netif_is_vxlan(netdev) &&
2712f5bc2c5dSOz Shlomo 	    MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap))
2713f5bc2c5dSOz Shlomo 		return true;
2714f5bc2c5dSOz Shlomo 
2715f5bc2c5dSOz Shlomo 	return false;
2716f5bc2c5dSOz Shlomo }
2717f5bc2c5dSOz Shlomo 
2718a54e20b4SHadar Hen Zion static int mlx5e_attach_encap(struct mlx5e_priv *priv,
2719a54e20b4SHadar Hen Zion 			      struct ip_tunnel_info *tun_info,
2720a54e20b4SHadar Hen Zion 			      struct net_device *mirred_dev,
272145247bf2SOr Gerlitz 			      struct net_device **encap_dev,
2722e98bedf5SEli Britstein 			      struct mlx5e_tc_flow *flow,
2723e98bedf5SEli Britstein 			      struct netlink_ext_ack *extack)
272403a9d11eSOr Gerlitz {
2725a54e20b4SHadar Hen Zion 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
2726a54e20b4SHadar Hen Zion 	unsigned short family = ip_tunnel_info_af(tun_info);
272745247bf2SOr Gerlitz 	struct mlx5_esw_flow_attr *attr = flow->esw_attr;
2728a54e20b4SHadar Hen Zion 	struct ip_tunnel_key *key = &tun_info->key;
2729c1ae1152SOr Gerlitz 	struct mlx5e_encap_entry *e;
273045247bf2SOr Gerlitz 	int tunnel_type, err = 0;
2731a54e20b4SHadar Hen Zion 	uintptr_t hash_key;
2732a54e20b4SHadar Hen Zion 	bool found = false;
2733a54e20b4SHadar Hen Zion 
27342fcd82e9SOr Gerlitz 	/* udp dst port must be set */
2735a54e20b4SHadar Hen Zion 	if (!memchr_inv(&key->tp_dst, 0, sizeof(key->tp_dst)))
27362fcd82e9SOr Gerlitz 		goto vxlan_encap_offload_err;
2737a54e20b4SHadar Hen Zion 
2738cd377663SOr Gerlitz 	/* setting udp src port isn't supported */
27392fcd82e9SOr Gerlitz 	if (memchr_inv(&key->tp_src, 0, sizeof(key->tp_src))) {
27402fcd82e9SOr Gerlitz vxlan_encap_offload_err:
2741e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
2742e98bedf5SEli Britstein 				   "must set udp dst port and not set udp src port");
27432fcd82e9SOr Gerlitz 		netdev_warn(priv->netdev,
27442fcd82e9SOr Gerlitz 			    "must set udp dst port and not set udp src port\n");
2745cd377663SOr Gerlitz 		return -EOPNOTSUPP;
27462fcd82e9SOr Gerlitz 	}
2747cd377663SOr Gerlitz 
2748a3e67366SSaeed Mahameed 	if (mlx5_vxlan_lookup_port(priv->mdev->vxlan, be16_to_cpu(key->tp_dst)) &&
2749a54e20b4SHadar Hen Zion 	    MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap)) {
275060786f09SMark Bloch 		tunnel_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
2751a54e20b4SHadar Hen Zion 	} else {
2752e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
2753e98bedf5SEli Britstein 				   "port isn't an offloaded vxlan udp dport");
27542fcd82e9SOr Gerlitz 		netdev_warn(priv->netdev,
27552fcd82e9SOr Gerlitz 			    "%d isn't an offloaded vxlan udp dport\n", be16_to_cpu(key->tp_dst));
2756a54e20b4SHadar Hen Zion 		return -EOPNOTSUPP;
2757a54e20b4SHadar Hen Zion 	}
2758a54e20b4SHadar Hen Zion 
275976f7444dSOr Gerlitz 	hash_key = hash_encap_info(key);
2760a54e20b4SHadar Hen Zion 
2761a54e20b4SHadar Hen Zion 	hash_for_each_possible_rcu(esw->offloads.encap_tbl, e,
2762a54e20b4SHadar Hen Zion 				   encap_hlist, hash_key) {
276376f7444dSOr Gerlitz 		if (!cmp_encap_info(&e->tun_info.key, key)) {
2764a54e20b4SHadar Hen Zion 			found = true;
2765a54e20b4SHadar Hen Zion 			break;
2766a54e20b4SHadar Hen Zion 		}
2767a54e20b4SHadar Hen Zion 	}
2768a54e20b4SHadar Hen Zion 
2769b2812089SVlad Buslov 	/* must verify if encap is valid or not */
277045247bf2SOr Gerlitz 	if (found)
277145247bf2SOr Gerlitz 		goto attach_flow;
2772a54e20b4SHadar Hen Zion 
2773a54e20b4SHadar Hen Zion 	e = kzalloc(sizeof(*e), GFP_KERNEL);
2774a54e20b4SHadar Hen Zion 	if (!e)
2775a54e20b4SHadar Hen Zion 		return -ENOMEM;
2776a54e20b4SHadar Hen Zion 
277776f7444dSOr Gerlitz 	e->tun_info = *tun_info;
2778a54e20b4SHadar Hen Zion 	e->tunnel_type = tunnel_type;
2779a54e20b4SHadar Hen Zion 	INIT_LIST_HEAD(&e->flows);
2780a54e20b4SHadar Hen Zion 
2781ce99f6b9SOr Gerlitz 	if (family == AF_INET)
27821a8552bdSHadar Hen Zion 		err = mlx5e_create_encap_header_ipv4(priv, mirred_dev, e);
2783ce99f6b9SOr Gerlitz 	else if (family == AF_INET6)
27841a8552bdSHadar Hen Zion 		err = mlx5e_create_encap_header_ipv6(priv, mirred_dev, e);
2785ce99f6b9SOr Gerlitz 
2786232c0013SHadar Hen Zion 	if (err && err != -EAGAIN)
2787a54e20b4SHadar Hen Zion 		goto out_err;
2788a54e20b4SHadar Hen Zion 
2789a54e20b4SHadar Hen Zion 	hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key);
2790a54e20b4SHadar Hen Zion 
279145247bf2SOr Gerlitz attach_flow:
279245247bf2SOr Gerlitz 	list_add(&flow->encap, &e->flows);
279345247bf2SOr Gerlitz 	*encap_dev = e->out_dev;
2794232c0013SHadar Hen Zion 	if (e->flags & MLX5_ENCAP_ENTRY_VALID)
279545247bf2SOr Gerlitz 		attr->encap_id = e->encap_id;
2796b2812089SVlad Buslov 	else
2797b2812089SVlad Buslov 		err = -EAGAIN;
279845247bf2SOr Gerlitz 
2799232c0013SHadar Hen Zion 	return err;
2800a54e20b4SHadar Hen Zion 
2801a54e20b4SHadar Hen Zion out_err:
2802a54e20b4SHadar Hen Zion 	kfree(e);
2803a54e20b4SHadar Hen Zion 	return err;
2804a54e20b4SHadar Hen Zion }
2805a54e20b4SHadar Hen Zion 
28061482bd3dSJianbo Liu static int parse_tc_vlan_action(struct mlx5e_priv *priv,
28071482bd3dSJianbo Liu 				const struct tc_action *a,
28081482bd3dSJianbo Liu 				struct mlx5_esw_flow_attr *attr,
28091482bd3dSJianbo Liu 				u32 *action)
28101482bd3dSJianbo Liu {
2811cc495188SJianbo Liu 	u8 vlan_idx = attr->total_vlan;
2812cc495188SJianbo Liu 
2813cc495188SJianbo Liu 	if (vlan_idx >= MLX5_FS_VLAN_DEPTH)
28141482bd3dSJianbo Liu 		return -EOPNOTSUPP;
2815cc495188SJianbo Liu 
2816cc495188SJianbo Liu 	if (tcf_vlan_action(a) == TCA_VLAN_ACT_POP) {
2817cc495188SJianbo Liu 		if (vlan_idx) {
2818cc495188SJianbo Liu 			if (!mlx5_eswitch_vlan_actions_supported(priv->mdev,
2819cc495188SJianbo Liu 								 MLX5_FS_VLAN_DEPTH))
2820cc495188SJianbo Liu 				return -EOPNOTSUPP;
2821cc495188SJianbo Liu 
2822cc495188SJianbo Liu 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2;
2823cc495188SJianbo Liu 		} else {
2824cc495188SJianbo Liu 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
2825cc495188SJianbo Liu 		}
2826cc495188SJianbo Liu 	} else if (tcf_vlan_action(a) == TCA_VLAN_ACT_PUSH) {
2827cc495188SJianbo Liu 		attr->vlan_vid[vlan_idx] = tcf_vlan_push_vid(a);
2828cc495188SJianbo Liu 		attr->vlan_prio[vlan_idx] = tcf_vlan_push_prio(a);
2829cc495188SJianbo Liu 		attr->vlan_proto[vlan_idx] = tcf_vlan_push_proto(a);
2830cc495188SJianbo Liu 		if (!attr->vlan_proto[vlan_idx])
2831cc495188SJianbo Liu 			attr->vlan_proto[vlan_idx] = htons(ETH_P_8021Q);
2832cc495188SJianbo Liu 
2833cc495188SJianbo Liu 		if (vlan_idx) {
2834cc495188SJianbo Liu 			if (!mlx5_eswitch_vlan_actions_supported(priv->mdev,
2835cc495188SJianbo Liu 								 MLX5_FS_VLAN_DEPTH))
2836cc495188SJianbo Liu 				return -EOPNOTSUPP;
2837cc495188SJianbo Liu 
2838cc495188SJianbo Liu 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2;
2839cc495188SJianbo Liu 		} else {
2840cc495188SJianbo Liu 			if (!mlx5_eswitch_vlan_actions_supported(priv->mdev, 1) &&
2841cc495188SJianbo Liu 			    (tcf_vlan_push_proto(a) != htons(ETH_P_8021Q) ||
2842cc495188SJianbo Liu 			     tcf_vlan_push_prio(a)))
2843cc495188SJianbo Liu 				return -EOPNOTSUPP;
2844cc495188SJianbo Liu 
2845cc495188SJianbo Liu 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
28461482bd3dSJianbo Liu 		}
28471482bd3dSJianbo Liu 	} else { /* action is TCA_VLAN_ACT_MODIFY */
28481482bd3dSJianbo Liu 		return -EOPNOTSUPP;
28491482bd3dSJianbo Liu 	}
28501482bd3dSJianbo Liu 
2851cc495188SJianbo Liu 	attr->total_vlan = vlan_idx + 1;
2852cc495188SJianbo Liu 
28531482bd3dSJianbo Liu 	return 0;
28541482bd3dSJianbo Liu }
28551482bd3dSJianbo Liu 
2856a54e20b4SHadar Hen Zion static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
2857d7e75a32SOr Gerlitz 				struct mlx5e_tc_flow_parse_attr *parse_attr,
2858e98bedf5SEli Britstein 				struct mlx5e_tc_flow *flow,
2859e98bedf5SEli Britstein 				struct netlink_ext_ack *extack)
2860a54e20b4SHadar Hen Zion {
2861bf07aa73SPaul Blakey 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
2862ecf5bb79SOr Gerlitz 	struct mlx5_esw_flow_attr *attr = flow->esw_attr;
28631d447a39SSaeed Mahameed 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
2864a54e20b4SHadar Hen Zion 	struct ip_tunnel_info *info = NULL;
286503a9d11eSOr Gerlitz 	const struct tc_action *a;
286622dc13c8SWANG Cong 	LIST_HEAD(actions);
2867a54e20b4SHadar Hen Zion 	bool encap = false;
28681cab1cd7SOr Gerlitz 	u32 action = 0;
2869244cd96aSCong Wang 	int err, i;
287003a9d11eSOr Gerlitz 
28713bcc0cecSJiri Pirko 	if (!tcf_exts_has_actions(exts))
287203a9d11eSOr Gerlitz 		return -EINVAL;
287303a9d11eSOr Gerlitz 
28741d447a39SSaeed Mahameed 	attr->in_rep = rpriv->rep;
287510ff5359SShahar Klein 	attr->in_mdev = priv->mdev;
287603a9d11eSOr Gerlitz 
2877244cd96aSCong Wang 	tcf_exts_for_each_action(i, a, exts) {
287803a9d11eSOr Gerlitz 		if (is_tcf_gact_shot(a)) {
28791cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_DROP |
288003a9d11eSOr Gerlitz 				  MLX5_FLOW_CONTEXT_ACTION_COUNT;
288103a9d11eSOr Gerlitz 			continue;
288203a9d11eSOr Gerlitz 		}
288303a9d11eSOr Gerlitz 
2884d7e75a32SOr Gerlitz 		if (is_tcf_pedit(a)) {
2885d7e75a32SOr Gerlitz 			err = parse_tc_pedit_action(priv, a, MLX5_FLOW_NAMESPACE_FDB,
2886e98bedf5SEli Britstein 						    parse_attr, extack);
2887d7e75a32SOr Gerlitz 			if (err)
2888d7e75a32SOr Gerlitz 				return err;
2889d7e75a32SOr Gerlitz 
28901cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
2891592d3651SChris Mi 			attr->mirror_count = attr->out_count;
2892d7e75a32SOr Gerlitz 			continue;
2893d7e75a32SOr Gerlitz 		}
2894d7e75a32SOr Gerlitz 
289526c02749SOr Gerlitz 		if (is_tcf_csum(a)) {
28961cab1cd7SOr Gerlitz 			if (csum_offload_supported(priv, action,
2897e98bedf5SEli Britstein 						   tcf_csum_update_flags(a),
2898e98bedf5SEli Britstein 						   extack))
289926c02749SOr Gerlitz 				continue;
290026c02749SOr Gerlitz 
290126c02749SOr Gerlitz 			return -EOPNOTSUPP;
290226c02749SOr Gerlitz 		}
290326c02749SOr Gerlitz 
2904592d3651SChris Mi 		if (is_tcf_mirred_egress_redirect(a) || is_tcf_mirred_egress_mirror(a)) {
290503a9d11eSOr Gerlitz 			struct mlx5e_priv *out_priv;
2906592d3651SChris Mi 			struct net_device *out_dev;
290703a9d11eSOr Gerlitz 
29089f8a739eSCong Wang 			out_dev = tcf_mirred_dev(a);
2909ef381359SOz Shlomo 			if (!out_dev) {
2910ef381359SOz Shlomo 				/* out_dev is NULL when filters with
2911ef381359SOz Shlomo 				 * non-existing mirred device are replayed to
2912ef381359SOz Shlomo 				 * the driver.
2913ef381359SOz Shlomo 				 */
2914ef381359SOz Shlomo 				return -EINVAL;
2915ef381359SOz Shlomo 			}
291603a9d11eSOr Gerlitz 
2917592d3651SChris Mi 			if (attr->out_count >= MLX5_MAX_FLOW_FWD_VPORTS) {
2918e98bedf5SEli Britstein 				NL_SET_ERR_MSG_MOD(extack,
2919e98bedf5SEli Britstein 						   "can't support more output ports, can't offload forwarding");
2920592d3651SChris Mi 				pr_err("can't support more than %d output ports, can't offload forwarding\n",
2921592d3651SChris Mi 				       attr->out_count);
2922592d3651SChris Mi 				return -EOPNOTSUPP;
2923592d3651SChris Mi 			}
2924592d3651SChris Mi 
2925a54e20b4SHadar Hen Zion 			if (switchdev_port_same_parent_id(priv->netdev,
2926b1d90e6bSRabie Loulou 							  out_dev) ||
2927b1d90e6bSRabie Loulou 			    is_merged_eswitch_dev(priv, out_dev)) {
29281cab1cd7SOr Gerlitz 				action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
2929e37a79e5SMark Bloch 					  MLX5_FLOW_CONTEXT_ACTION_COUNT;
293003a9d11eSOr Gerlitz 				out_priv = netdev_priv(out_dev);
29311d447a39SSaeed Mahameed 				rpriv = out_priv->ppriv;
2932592d3651SChris Mi 				attr->out_rep[attr->out_count] = rpriv->rep;
2933592d3651SChris Mi 				attr->out_mdev[attr->out_count++] = out_priv->mdev;
2934a54e20b4SHadar Hen Zion 			} else if (encap) {
29359f8a739eSCong Wang 				parse_attr->mirred_ifindex = out_dev->ifindex;
29363c37745eSOr Gerlitz 				parse_attr->tun_info = *info;
29373c37745eSOr Gerlitz 				attr->parse_attr = parse_attr;
293860786f09SMark Bloch 				action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT |
2939a54e20b4SHadar Hen Zion 					  MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
2940a54e20b4SHadar Hen Zion 					  MLX5_FLOW_CONTEXT_ACTION_COUNT;
29413c37745eSOr Gerlitz 				/* attr->out_rep is resolved when we handle encap */
2942ef381359SOz Shlomo 			} else if (parse_attr->filter_dev != priv->netdev) {
2943ef381359SOz Shlomo 				/* All mlx5 devices are called to configure
2944ef381359SOz Shlomo 				 * high level device filters. Therefore, the
2945ef381359SOz Shlomo 				 * *attempt* to  install a filter on invalid
2946ef381359SOz Shlomo 				 * eswitch should not trigger an explicit error
2947ef381359SOz Shlomo 				 */
2948ef381359SOz Shlomo 				return -EINVAL;
2949a54e20b4SHadar Hen Zion 			} else {
2950e98bedf5SEli Britstein 				NL_SET_ERR_MSG_MOD(extack,
2951e98bedf5SEli Britstein 						   "devices are not on same switch HW, can't offload forwarding");
2952a54e20b4SHadar Hen Zion 				pr_err("devices %s %s not on same switch HW, can't offload forwarding\n",
2953a54e20b4SHadar Hen Zion 				       priv->netdev->name, out_dev->name);
2954a54e20b4SHadar Hen Zion 				return -EINVAL;
2955a54e20b4SHadar Hen Zion 			}
2956a54e20b4SHadar Hen Zion 			continue;
2957a54e20b4SHadar Hen Zion 		}
2958a54e20b4SHadar Hen Zion 
2959a54e20b4SHadar Hen Zion 		if (is_tcf_tunnel_set(a)) {
2960a54e20b4SHadar Hen Zion 			info = tcf_tunnel_info(a);
2961a54e20b4SHadar Hen Zion 			if (info)
2962a54e20b4SHadar Hen Zion 				encap = true;
2963a54e20b4SHadar Hen Zion 			else
2964a54e20b4SHadar Hen Zion 				return -EOPNOTSUPP;
2965592d3651SChris Mi 			attr->mirror_count = attr->out_count;
296603a9d11eSOr Gerlitz 			continue;
296703a9d11eSOr Gerlitz 		}
296803a9d11eSOr Gerlitz 
29698b32580dSOr Gerlitz 		if (is_tcf_vlan(a)) {
29701482bd3dSJianbo Liu 			err = parse_tc_vlan_action(priv, a, attr, &action);
29711482bd3dSJianbo Liu 
29721482bd3dSJianbo Liu 			if (err)
29731482bd3dSJianbo Liu 				return err;
29741482bd3dSJianbo Liu 
2975592d3651SChris Mi 			attr->mirror_count = attr->out_count;
29768b32580dSOr Gerlitz 			continue;
29778b32580dSOr Gerlitz 		}
29788b32580dSOr Gerlitz 
2979bbd00f7eSHadar Hen Zion 		if (is_tcf_tunnel_release(a)) {
29801cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_DECAP;
2981bbd00f7eSHadar Hen Zion 			continue;
2982bbd00f7eSHadar Hen Zion 		}
2983bbd00f7eSHadar Hen Zion 
2984bf07aa73SPaul Blakey 		if (is_tcf_gact_goto_chain(a)) {
2985bf07aa73SPaul Blakey 			u32 dest_chain = tcf_gact_goto_chain_index(a);
2986bf07aa73SPaul Blakey 			u32 max_chain = mlx5_eswitch_get_chain_range(esw);
2987bf07aa73SPaul Blakey 
2988bf07aa73SPaul Blakey 			if (dest_chain <= attr->chain) {
2989bf07aa73SPaul Blakey 				NL_SET_ERR_MSG(extack, "Goto earlier chain isn't supported");
2990bf07aa73SPaul Blakey 				return -EOPNOTSUPP;
2991bf07aa73SPaul Blakey 			}
2992bf07aa73SPaul Blakey 			if (dest_chain > max_chain) {
2993bf07aa73SPaul Blakey 				NL_SET_ERR_MSG(extack, "Requested destination chain is out of supported range");
2994bf07aa73SPaul Blakey 				return -EOPNOTSUPP;
2995bf07aa73SPaul Blakey 			}
2996bf07aa73SPaul Blakey 			action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
2997bf07aa73SPaul Blakey 				  MLX5_FLOW_CONTEXT_ACTION_COUNT;
2998bf07aa73SPaul Blakey 			attr->dest_chain = dest_chain;
2999bf07aa73SPaul Blakey 
3000bf07aa73SPaul Blakey 			continue;
3001bf07aa73SPaul Blakey 		}
3002bf07aa73SPaul Blakey 
300303a9d11eSOr Gerlitz 		return -EINVAL;
300403a9d11eSOr Gerlitz 	}
3005bdd66ac0SOr Gerlitz 
30061cab1cd7SOr Gerlitz 	attr->action = action;
3007e98bedf5SEli Britstein 	if (!actions_match_supported(priv, exts, parse_attr, flow, extack))
3008bdd66ac0SOr Gerlitz 		return -EOPNOTSUPP;
3009bdd66ac0SOr Gerlitz 
30101392f44bSRoi Dayan 	if (attr->mirror_count > 0 && !mlx5_esw_has_fwd_fdb(priv->mdev)) {
3011e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
3012e98bedf5SEli Britstein 				   "current firmware doesn't support split rule for port mirroring");
3013592d3651SChris Mi 		netdev_warn_once(priv->netdev, "current firmware doesn't support split rule for port mirroring\n");
3014592d3651SChris Mi 		return -EOPNOTSUPP;
3015592d3651SChris Mi 	}
3016592d3651SChris Mi 
301731c8eba5SOr Gerlitz 	return 0;
301803a9d11eSOr Gerlitz }
301903a9d11eSOr Gerlitz 
30205dbe906fSPaul Blakey static void get_flags(int flags, u16 *flow_flags)
302160bd4af8SOr Gerlitz {
30225dbe906fSPaul Blakey 	u16 __flow_flags = 0;
302360bd4af8SOr Gerlitz 
302460bd4af8SOr Gerlitz 	if (flags & MLX5E_TC_INGRESS)
302560bd4af8SOr Gerlitz 		__flow_flags |= MLX5E_TC_FLOW_INGRESS;
302660bd4af8SOr Gerlitz 	if (flags & MLX5E_TC_EGRESS)
302760bd4af8SOr Gerlitz 		__flow_flags |= MLX5E_TC_FLOW_EGRESS;
302860bd4af8SOr Gerlitz 
302960bd4af8SOr Gerlitz 	*flow_flags = __flow_flags;
303060bd4af8SOr Gerlitz }
303160bd4af8SOr Gerlitz 
303205866c82SOr Gerlitz static const struct rhashtable_params tc_ht_params = {
303305866c82SOr Gerlitz 	.head_offset = offsetof(struct mlx5e_tc_flow, node),
303405866c82SOr Gerlitz 	.key_offset = offsetof(struct mlx5e_tc_flow, cookie),
303505866c82SOr Gerlitz 	.key_len = sizeof(((struct mlx5e_tc_flow *)0)->cookie),
303605866c82SOr Gerlitz 	.automatic_shrinking = true,
303705866c82SOr Gerlitz };
303805866c82SOr Gerlitz 
303905866c82SOr Gerlitz static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv)
304005866c82SOr Gerlitz {
3041655dc3d2SOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
3042655dc3d2SOr Gerlitz 	struct mlx5e_rep_priv *uplink_rpriv;
3043655dc3d2SOr Gerlitz 
3044655dc3d2SOr Gerlitz 	if (MLX5_VPORT_MANAGER(priv->mdev) && esw->mode == SRIOV_OFFLOADS) {
3045655dc3d2SOr Gerlitz 		uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
3046ec1366c2SOz Shlomo 		return &uplink_rpriv->uplink_priv.tc_ht;
3047655dc3d2SOr Gerlitz 	} else
304805866c82SOr Gerlitz 		return &priv->fs.tc.ht;
304905866c82SOr Gerlitz }
305005866c82SOr Gerlitz 
3051a88780a9SRoi Dayan static int
3052a88780a9SRoi Dayan mlx5e_alloc_flow(struct mlx5e_priv *priv, int attr_size,
30535dbe906fSPaul Blakey 		 struct tc_cls_flower_offload *f, u16 flow_flags,
3054a88780a9SRoi Dayan 		 struct mlx5e_tc_flow_parse_attr **__parse_attr,
3055a88780a9SRoi Dayan 		 struct mlx5e_tc_flow **__flow)
3056e3a2b7edSAmir Vadai {
305717091853SOr Gerlitz 	struct mlx5e_tc_flow_parse_attr *parse_attr;
30583bc4b7bfSOr Gerlitz 	struct mlx5e_tc_flow *flow;
3059a88780a9SRoi Dayan 	int err;
3060776b12b6SOr Gerlitz 
306165ba8fb7SOr Gerlitz 	flow = kzalloc(sizeof(*flow) + attr_size, GFP_KERNEL);
30621b9a07eeSLeon Romanovsky 	parse_attr = kvzalloc(sizeof(*parse_attr), GFP_KERNEL);
306317091853SOr Gerlitz 	if (!parse_attr || !flow) {
3064e3a2b7edSAmir Vadai 		err = -ENOMEM;
3065e3a2b7edSAmir Vadai 		goto err_free;
3066e3a2b7edSAmir Vadai 	}
3067e3a2b7edSAmir Vadai 
3068e3a2b7edSAmir Vadai 	flow->cookie = f->cookie;
306965ba8fb7SOr Gerlitz 	flow->flags = flow_flags;
3070655dc3d2SOr Gerlitz 	flow->priv = priv;
3071e3a2b7edSAmir Vadai 
3072a88780a9SRoi Dayan 	*__flow = flow;
3073a88780a9SRoi Dayan 	*__parse_attr = parse_attr;
3074a88780a9SRoi Dayan 
3075a88780a9SRoi Dayan 	return 0;
3076a88780a9SRoi Dayan 
3077a88780a9SRoi Dayan err_free:
3078a88780a9SRoi Dayan 	kfree(flow);
3079a88780a9SRoi Dayan 	kvfree(parse_attr);
3080a88780a9SRoi Dayan 	return err;
3081adb4c123SOr Gerlitz }
3082adb4c123SOr Gerlitz 
3083a88780a9SRoi Dayan static int
3084a88780a9SRoi Dayan mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
3085a88780a9SRoi Dayan 		   struct tc_cls_flower_offload *f,
30865dbe906fSPaul Blakey 		   u16 flow_flags,
3087d11afc26SOz Shlomo 		   struct net_device *filter_dev,
3088a88780a9SRoi Dayan 		   struct mlx5e_tc_flow **__flow)
3089a88780a9SRoi Dayan {
3090a88780a9SRoi Dayan 	struct netlink_ext_ack *extack = f->common.extack;
3091a88780a9SRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr;
3092a88780a9SRoi Dayan 	struct mlx5e_tc_flow *flow;
3093a88780a9SRoi Dayan 	int attr_size, err;
3094a88780a9SRoi Dayan 
3095a88780a9SRoi Dayan 	flow_flags |= MLX5E_TC_FLOW_ESWITCH;
3096a88780a9SRoi Dayan 	attr_size  = sizeof(struct mlx5_esw_flow_attr);
3097a88780a9SRoi Dayan 	err = mlx5e_alloc_flow(priv, attr_size, f, flow_flags,
3098a88780a9SRoi Dayan 			       &parse_attr, &flow);
3099a88780a9SRoi Dayan 	if (err)
3100a88780a9SRoi Dayan 		goto out;
3101d11afc26SOz Shlomo 	parse_attr->filter_dev = filter_dev;
3102d11afc26SOz Shlomo 	flow->esw_attr->parse_attr = parse_attr;
3103d11afc26SOz Shlomo 	err = parse_cls_flower(flow->priv, flow, &parse_attr->spec, f);
3104d11afc26SOz Shlomo 	if (err)
3105d11afc26SOz Shlomo 		goto err_free;
3106a88780a9SRoi Dayan 
3107bf07aa73SPaul Blakey 	flow->esw_attr->chain = f->common.chain_index;
3108bf07aa73SPaul Blakey 	flow->esw_attr->prio = TC_H_MAJ(f->common.prio) >> 16;
3109a88780a9SRoi Dayan 	err = parse_tc_fdb_actions(priv, f->exts, parse_attr, flow, extack);
3110a88780a9SRoi Dayan 	if (err)
3111a88780a9SRoi Dayan 		goto err_free;
3112a88780a9SRoi Dayan 
3113a88780a9SRoi Dayan 	err = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow, extack);
31145dbe906fSPaul Blakey 	if (err)
3115aa0cbbaeSOr Gerlitz 		goto err_free;
31165c40348cSOr Gerlitz 
3117a88780a9SRoi Dayan 	if (!(flow->esw_attr->action &
311860786f09SMark Bloch 	      MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT))
3119232c0013SHadar Hen Zion 		kvfree(parse_attr);
3120e3a2b7edSAmir Vadai 
3121a88780a9SRoi Dayan 	*__flow = flow;
3122af1607c3SJianbo Liu 
3123a88780a9SRoi Dayan 	return 0;
3124e3a2b7edSAmir Vadai 
3125e3a2b7edSAmir Vadai err_free:
3126232c0013SHadar Hen Zion 	kfree(flow);
3127a88780a9SRoi Dayan 	kvfree(parse_attr);
3128a88780a9SRoi Dayan out:
3129a88780a9SRoi Dayan 	return err;
3130a88780a9SRoi Dayan }
3131a88780a9SRoi Dayan 
3132a88780a9SRoi Dayan static int
3133a88780a9SRoi Dayan mlx5e_add_nic_flow(struct mlx5e_priv *priv,
3134a88780a9SRoi Dayan 		   struct tc_cls_flower_offload *f,
31355dbe906fSPaul Blakey 		   u16 flow_flags,
3136d11afc26SOz Shlomo 		   struct net_device *filter_dev,
3137a88780a9SRoi Dayan 		   struct mlx5e_tc_flow **__flow)
3138a88780a9SRoi Dayan {
3139a88780a9SRoi Dayan 	struct netlink_ext_ack *extack = f->common.extack;
3140a88780a9SRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr;
3141a88780a9SRoi Dayan 	struct mlx5e_tc_flow *flow;
3142a88780a9SRoi Dayan 	int attr_size, err;
3143a88780a9SRoi Dayan 
3144bf07aa73SPaul Blakey 	/* multi-chain not supported for NIC rules */
3145bf07aa73SPaul Blakey 	if (!tc_cls_can_offload_and_chain0(priv->netdev, &f->common))
3146bf07aa73SPaul Blakey 		return -EOPNOTSUPP;
3147bf07aa73SPaul Blakey 
3148a88780a9SRoi Dayan 	flow_flags |= MLX5E_TC_FLOW_NIC;
3149a88780a9SRoi Dayan 	attr_size  = sizeof(struct mlx5_nic_flow_attr);
3150a88780a9SRoi Dayan 	err = mlx5e_alloc_flow(priv, attr_size, f, flow_flags,
3151a88780a9SRoi Dayan 			       &parse_attr, &flow);
3152a88780a9SRoi Dayan 	if (err)
3153a88780a9SRoi Dayan 		goto out;
3154a88780a9SRoi Dayan 
3155d11afc26SOz Shlomo 	parse_attr->filter_dev = filter_dev;
3156d11afc26SOz Shlomo 	err = parse_cls_flower(flow->priv, flow, &parse_attr->spec, f);
3157d11afc26SOz Shlomo 	if (err)
3158d11afc26SOz Shlomo 		goto err_free;
3159d11afc26SOz Shlomo 
3160a88780a9SRoi Dayan 	err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow, extack);
3161a88780a9SRoi Dayan 	if (err)
3162a88780a9SRoi Dayan 		goto err_free;
3163a88780a9SRoi Dayan 
3164a88780a9SRoi Dayan 	err = mlx5e_tc_add_nic_flow(priv, parse_attr, flow, extack);
3165a88780a9SRoi Dayan 	if (err)
3166a88780a9SRoi Dayan 		goto err_free;
3167a88780a9SRoi Dayan 
3168a88780a9SRoi Dayan 	flow->flags |= MLX5E_TC_FLOW_OFFLOADED;
3169a88780a9SRoi Dayan 	kvfree(parse_attr);
3170a88780a9SRoi Dayan 	*__flow = flow;
3171a88780a9SRoi Dayan 
3172a88780a9SRoi Dayan 	return 0;
3173a88780a9SRoi Dayan 
3174a88780a9SRoi Dayan err_free:
3175a88780a9SRoi Dayan 	kfree(flow);
3176a88780a9SRoi Dayan 	kvfree(parse_attr);
3177a88780a9SRoi Dayan out:
3178a88780a9SRoi Dayan 	return err;
3179a88780a9SRoi Dayan }
3180a88780a9SRoi Dayan 
3181a88780a9SRoi Dayan static int
3182a88780a9SRoi Dayan mlx5e_tc_add_flow(struct mlx5e_priv *priv,
3183a88780a9SRoi Dayan 		  struct tc_cls_flower_offload *f,
3184a88780a9SRoi Dayan 		  int flags,
3185d11afc26SOz Shlomo 		  struct net_device *filter_dev,
3186a88780a9SRoi Dayan 		  struct mlx5e_tc_flow **flow)
3187a88780a9SRoi Dayan {
3188a88780a9SRoi Dayan 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
31895dbe906fSPaul Blakey 	u16 flow_flags;
3190a88780a9SRoi Dayan 	int err;
3191a88780a9SRoi Dayan 
3192a88780a9SRoi Dayan 	get_flags(flags, &flow_flags);
3193a88780a9SRoi Dayan 
3194bf07aa73SPaul Blakey 	if (!tc_can_offload_extack(priv->netdev, f->common.extack))
3195bf07aa73SPaul Blakey 		return -EOPNOTSUPP;
3196bf07aa73SPaul Blakey 
3197a88780a9SRoi Dayan 	if (esw && esw->mode == SRIOV_OFFLOADS)
3198d11afc26SOz Shlomo 		err = mlx5e_add_fdb_flow(priv, f, flow_flags,
3199d11afc26SOz Shlomo 					 filter_dev, flow);
3200a88780a9SRoi Dayan 	else
3201d11afc26SOz Shlomo 		err = mlx5e_add_nic_flow(priv, f, flow_flags,
3202d11afc26SOz Shlomo 					 filter_dev, flow);
3203a88780a9SRoi Dayan 
3204a88780a9SRoi Dayan 	return err;
3205a88780a9SRoi Dayan }
3206a88780a9SRoi Dayan 
320771d82d2aSOz Shlomo int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
3208a88780a9SRoi Dayan 			   struct tc_cls_flower_offload *f, int flags)
3209a88780a9SRoi Dayan {
3210a88780a9SRoi Dayan 	struct netlink_ext_ack *extack = f->common.extack;
3211a88780a9SRoi Dayan 	struct rhashtable *tc_ht = get_tc_ht(priv);
3212a88780a9SRoi Dayan 	struct mlx5e_tc_flow *flow;
3213a88780a9SRoi Dayan 	int err = 0;
3214a88780a9SRoi Dayan 
3215a88780a9SRoi Dayan 	flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
3216a88780a9SRoi Dayan 	if (flow) {
3217a88780a9SRoi Dayan 		NL_SET_ERR_MSG_MOD(extack,
3218a88780a9SRoi Dayan 				   "flow cookie already exists, ignoring");
3219a88780a9SRoi Dayan 		netdev_warn_once(priv->netdev,
3220a88780a9SRoi Dayan 				 "flow cookie %lx already exists, ignoring\n",
3221a88780a9SRoi Dayan 				 f->cookie);
3222a88780a9SRoi Dayan 		goto out;
3223a88780a9SRoi Dayan 	}
3224a88780a9SRoi Dayan 
3225d11afc26SOz Shlomo 	err = mlx5e_tc_add_flow(priv, f, flags, dev, &flow);
3226a88780a9SRoi Dayan 	if (err)
3227a88780a9SRoi Dayan 		goto out;
3228a88780a9SRoi Dayan 
3229a88780a9SRoi Dayan 	err = rhashtable_insert_fast(tc_ht, &flow->node, tc_ht_params);
3230a88780a9SRoi Dayan 	if (err)
3231a88780a9SRoi Dayan 		goto err_free;
3232a88780a9SRoi Dayan 
3233a88780a9SRoi Dayan 	return 0;
3234a88780a9SRoi Dayan 
3235a88780a9SRoi Dayan err_free:
3236a88780a9SRoi Dayan 	mlx5e_tc_del_flow(priv, flow);
3237a88780a9SRoi Dayan 	kfree(flow);
3238a88780a9SRoi Dayan out:
3239e3a2b7edSAmir Vadai 	return err;
3240e3a2b7edSAmir Vadai }
3241e3a2b7edSAmir Vadai 
32428f8ae895SOr Gerlitz #define DIRECTION_MASK (MLX5E_TC_INGRESS | MLX5E_TC_EGRESS)
32438f8ae895SOr Gerlitz #define FLOW_DIRECTION_MASK (MLX5E_TC_FLOW_INGRESS | MLX5E_TC_FLOW_EGRESS)
32448f8ae895SOr Gerlitz 
32458f8ae895SOr Gerlitz static bool same_flow_direction(struct mlx5e_tc_flow *flow, int flags)
32468f8ae895SOr Gerlitz {
32478f8ae895SOr Gerlitz 	if ((flow->flags & FLOW_DIRECTION_MASK) == (flags & DIRECTION_MASK))
32488f8ae895SOr Gerlitz 		return true;
32498f8ae895SOr Gerlitz 
32508f8ae895SOr Gerlitz 	return false;
32518f8ae895SOr Gerlitz }
32528f8ae895SOr Gerlitz 
325371d82d2aSOz Shlomo int mlx5e_delete_flower(struct net_device *dev, struct mlx5e_priv *priv,
325460bd4af8SOr Gerlitz 			struct tc_cls_flower_offload *f, int flags)
3255e3a2b7edSAmir Vadai {
325605866c82SOr Gerlitz 	struct rhashtable *tc_ht = get_tc_ht(priv);
3257e3a2b7edSAmir Vadai 	struct mlx5e_tc_flow *flow;
3258e3a2b7edSAmir Vadai 
325905866c82SOr Gerlitz 	flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
32608f8ae895SOr Gerlitz 	if (!flow || !same_flow_direction(flow, flags))
3261e3a2b7edSAmir Vadai 		return -EINVAL;
3262e3a2b7edSAmir Vadai 
326305866c82SOr Gerlitz 	rhashtable_remove_fast(tc_ht, &flow->node, tc_ht_params);
3264e3a2b7edSAmir Vadai 
3265961e8979SRoi Dayan 	mlx5e_tc_del_flow(priv, flow);
3266e3a2b7edSAmir Vadai 
3267e3a2b7edSAmir Vadai 	kfree(flow);
3268e3a2b7edSAmir Vadai 
3269e3a2b7edSAmir Vadai 	return 0;
3270e3a2b7edSAmir Vadai }
3271e3a2b7edSAmir Vadai 
327271d82d2aSOz Shlomo int mlx5e_stats_flower(struct net_device *dev, struct mlx5e_priv *priv,
327360bd4af8SOr Gerlitz 		       struct tc_cls_flower_offload *f, int flags)
3274aad7e08dSAmir Vadai {
327505866c82SOr Gerlitz 	struct rhashtable *tc_ht = get_tc_ht(priv);
3276aad7e08dSAmir Vadai 	struct mlx5e_tc_flow *flow;
3277aad7e08dSAmir Vadai 	struct mlx5_fc *counter;
3278aad7e08dSAmir Vadai 	u64 bytes;
3279aad7e08dSAmir Vadai 	u64 packets;
3280aad7e08dSAmir Vadai 	u64 lastuse;
3281aad7e08dSAmir Vadai 
328205866c82SOr Gerlitz 	flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
32838f8ae895SOr Gerlitz 	if (!flow || !same_flow_direction(flow, flags))
3284aad7e08dSAmir Vadai 		return -EINVAL;
3285aad7e08dSAmir Vadai 
32860b67a38fSHadar Hen Zion 	if (!(flow->flags & MLX5E_TC_FLOW_OFFLOADED))
32870b67a38fSHadar Hen Zion 		return 0;
32880b67a38fSHadar Hen Zion 
3289b8aee822SMark Bloch 	counter = mlx5e_tc_get_counter(flow);
3290aad7e08dSAmir Vadai 	if (!counter)
3291aad7e08dSAmir Vadai 		return 0;
3292aad7e08dSAmir Vadai 
3293aad7e08dSAmir Vadai 	mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
3294aad7e08dSAmir Vadai 
3295d897a638SJakub Kicinski 	tcf_exts_stats_update(f->exts, bytes, packets, lastuse);
3296fed06ee8SOr Gerlitz 
3297aad7e08dSAmir Vadai 	return 0;
3298aad7e08dSAmir Vadai }
3299aad7e08dSAmir Vadai 
33004d8fcf21SAlaa Hleihel static void mlx5e_tc_hairpin_update_dead_peer(struct mlx5e_priv *priv,
33014d8fcf21SAlaa Hleihel 					      struct mlx5e_priv *peer_priv)
33024d8fcf21SAlaa Hleihel {
33034d8fcf21SAlaa Hleihel 	struct mlx5_core_dev *peer_mdev = peer_priv->mdev;
33044d8fcf21SAlaa Hleihel 	struct mlx5e_hairpin_entry *hpe;
33054d8fcf21SAlaa Hleihel 	u16 peer_vhca_id;
33064d8fcf21SAlaa Hleihel 	int bkt;
33074d8fcf21SAlaa Hleihel 
33084d8fcf21SAlaa Hleihel 	if (!same_hw_devs(priv, peer_priv))
33094d8fcf21SAlaa Hleihel 		return;
33104d8fcf21SAlaa Hleihel 
33114d8fcf21SAlaa Hleihel 	peer_vhca_id = MLX5_CAP_GEN(peer_mdev, vhca_id);
33124d8fcf21SAlaa Hleihel 
33134d8fcf21SAlaa Hleihel 	hash_for_each(priv->fs.tc.hairpin_tbl, bkt, hpe, hairpin_hlist) {
33144d8fcf21SAlaa Hleihel 		if (hpe->peer_vhca_id == peer_vhca_id)
33154d8fcf21SAlaa Hleihel 			hpe->hp->pair->peer_gone = true;
33164d8fcf21SAlaa Hleihel 	}
33174d8fcf21SAlaa Hleihel }
33184d8fcf21SAlaa Hleihel 
33194d8fcf21SAlaa Hleihel static int mlx5e_tc_netdev_event(struct notifier_block *this,
33204d8fcf21SAlaa Hleihel 				 unsigned long event, void *ptr)
33214d8fcf21SAlaa Hleihel {
33224d8fcf21SAlaa Hleihel 	struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
33234d8fcf21SAlaa Hleihel 	struct mlx5e_flow_steering *fs;
33244d8fcf21SAlaa Hleihel 	struct mlx5e_priv *peer_priv;
33254d8fcf21SAlaa Hleihel 	struct mlx5e_tc_table *tc;
33264d8fcf21SAlaa Hleihel 	struct mlx5e_priv *priv;
33274d8fcf21SAlaa Hleihel 
33284d8fcf21SAlaa Hleihel 	if (ndev->netdev_ops != &mlx5e_netdev_ops ||
33294d8fcf21SAlaa Hleihel 	    event != NETDEV_UNREGISTER ||
33304d8fcf21SAlaa Hleihel 	    ndev->reg_state == NETREG_REGISTERED)
33314d8fcf21SAlaa Hleihel 		return NOTIFY_DONE;
33324d8fcf21SAlaa Hleihel 
33334d8fcf21SAlaa Hleihel 	tc = container_of(this, struct mlx5e_tc_table, netdevice_nb);
33344d8fcf21SAlaa Hleihel 	fs = container_of(tc, struct mlx5e_flow_steering, tc);
33354d8fcf21SAlaa Hleihel 	priv = container_of(fs, struct mlx5e_priv, fs);
33364d8fcf21SAlaa Hleihel 	peer_priv = netdev_priv(ndev);
33374d8fcf21SAlaa Hleihel 	if (priv == peer_priv ||
33384d8fcf21SAlaa Hleihel 	    !(priv->netdev->features & NETIF_F_HW_TC))
33394d8fcf21SAlaa Hleihel 		return NOTIFY_DONE;
33404d8fcf21SAlaa Hleihel 
33414d8fcf21SAlaa Hleihel 	mlx5e_tc_hairpin_update_dead_peer(priv, peer_priv);
33424d8fcf21SAlaa Hleihel 
33434d8fcf21SAlaa Hleihel 	return NOTIFY_DONE;
33444d8fcf21SAlaa Hleihel }
33454d8fcf21SAlaa Hleihel 
3346655dc3d2SOr Gerlitz int mlx5e_tc_nic_init(struct mlx5e_priv *priv)
3347e8f887acSAmir Vadai {
3348acff797cSMaor Gottlieb 	struct mlx5e_tc_table *tc = &priv->fs.tc;
33494d8fcf21SAlaa Hleihel 	int err;
3350e8f887acSAmir Vadai 
335111c9c548SOr Gerlitz 	hash_init(tc->mod_hdr_tbl);
33525c65c564SOr Gerlitz 	hash_init(tc->hairpin_tbl);
335311c9c548SOr Gerlitz 
33544d8fcf21SAlaa Hleihel 	err = rhashtable_init(&tc->ht, &tc_ht_params);
33554d8fcf21SAlaa Hleihel 	if (err)
33564d8fcf21SAlaa Hleihel 		return err;
33574d8fcf21SAlaa Hleihel 
33584d8fcf21SAlaa Hleihel 	tc->netdevice_nb.notifier_call = mlx5e_tc_netdev_event;
33594d8fcf21SAlaa Hleihel 	if (register_netdevice_notifier(&tc->netdevice_nb)) {
33604d8fcf21SAlaa Hleihel 		tc->netdevice_nb.notifier_call = NULL;
33614d8fcf21SAlaa Hleihel 		mlx5_core_warn(priv->mdev, "Failed to register netdev notifier\n");
33624d8fcf21SAlaa Hleihel 	}
33634d8fcf21SAlaa Hleihel 
33644d8fcf21SAlaa Hleihel 	return err;
3365e8f887acSAmir Vadai }
3366e8f887acSAmir Vadai 
3367e8f887acSAmir Vadai static void _mlx5e_tc_del_flow(void *ptr, void *arg)
3368e8f887acSAmir Vadai {
3369e8f887acSAmir Vadai 	struct mlx5e_tc_flow *flow = ptr;
3370655dc3d2SOr Gerlitz 	struct mlx5e_priv *priv = flow->priv;
3371e8f887acSAmir Vadai 
3372961e8979SRoi Dayan 	mlx5e_tc_del_flow(priv, flow);
3373e8f887acSAmir Vadai 	kfree(flow);
3374e8f887acSAmir Vadai }
3375e8f887acSAmir Vadai 
3376655dc3d2SOr Gerlitz void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv)
3377e8f887acSAmir Vadai {
3378acff797cSMaor Gottlieb 	struct mlx5e_tc_table *tc = &priv->fs.tc;
3379e8f887acSAmir Vadai 
33804d8fcf21SAlaa Hleihel 	if (tc->netdevice_nb.notifier_call)
33814d8fcf21SAlaa Hleihel 		unregister_netdevice_notifier(&tc->netdevice_nb);
33824d8fcf21SAlaa Hleihel 
3383655dc3d2SOr Gerlitz 	rhashtable_free_and_destroy(&tc->ht, _mlx5e_tc_del_flow, NULL);
3384e8f887acSAmir Vadai 
3385acff797cSMaor Gottlieb 	if (!IS_ERR_OR_NULL(tc->t)) {
3386acff797cSMaor Gottlieb 		mlx5_destroy_flow_table(tc->t);
3387acff797cSMaor Gottlieb 		tc->t = NULL;
3388e8f887acSAmir Vadai 	}
3389e8f887acSAmir Vadai }
3390655dc3d2SOr Gerlitz 
3391655dc3d2SOr Gerlitz int mlx5e_tc_esw_init(struct rhashtable *tc_ht)
3392655dc3d2SOr Gerlitz {
3393655dc3d2SOr Gerlitz 	return rhashtable_init(tc_ht, &tc_ht_params);
3394655dc3d2SOr Gerlitz }
3395655dc3d2SOr Gerlitz 
3396655dc3d2SOr Gerlitz void mlx5e_tc_esw_cleanup(struct rhashtable *tc_ht)
3397655dc3d2SOr Gerlitz {
3398655dc3d2SOr Gerlitz 	rhashtable_free_and_destroy(tc_ht, _mlx5e_tc_del_flow, NULL);
3399655dc3d2SOr Gerlitz }
340001252a27SOr Gerlitz 
340101252a27SOr Gerlitz int mlx5e_tc_num_filters(struct mlx5e_priv *priv)
340201252a27SOr Gerlitz {
340301252a27SOr Gerlitz 	struct rhashtable *tc_ht = get_tc_ht(priv);
340401252a27SOr Gerlitz 
340501252a27SOr Gerlitz 	return atomic_read(&tc_ht->nelems);
340601252a27SOr Gerlitz }
3407