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>
415a7e5bcbSVlad Buslov #include <linux/refcount.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>
47f6dfb4c3SHadar Hen Zion #include <net/arp.h>
483616d08bSDavid Ahern #include <net/ipv6_stubs.h>
49e8f887acSAmir Vadai #include "en.h"
501d447a39SSaeed Mahameed #include "en_rep.h"
51232c0013SHadar Hen Zion #include "en_tc.h"
5203a9d11eSOr Gerlitz #include "eswitch.h"
533f6d08d1SOr Gerlitz #include "fs_core.h"
542c81bfd5SHuy Nguyen #include "en/port.h"
55101f4de9SOz Shlomo #include "en/tc_tun.h"
5604de7ddaSRoi Dayan #include "lib/devcom.h"
579272e3dfSYevgeny Kliteynik #include "lib/geneve.h"
58e8f887acSAmir Vadai 
593bc4b7bfSOr Gerlitz struct mlx5_nic_flow_attr {
603bc4b7bfSOr Gerlitz 	u32 action;
613bc4b7bfSOr Gerlitz 	u32 flow_tag;
622f4fe4caSOr Gerlitz 	u32 mod_hdr_id;
635c65c564SOr Gerlitz 	u32 hairpin_tirn;
6438aa51c1SOr Gerlitz 	u8 match_level;
653f6d08d1SOr Gerlitz 	struct mlx5_flow_table	*hairpin_ft;
66b8aee822SMark Bloch 	struct mlx5_fc		*counter;
673bc4b7bfSOr Gerlitz };
683bc4b7bfSOr Gerlitz 
6960bd4af8SOr Gerlitz #define MLX5E_TC_FLOW_BASE (MLX5E_TC_LAST_EXPORTED_BIT + 1)
7060bd4af8SOr Gerlitz 
7165ba8fb7SOr Gerlitz enum {
7260bd4af8SOr Gerlitz 	MLX5E_TC_FLOW_INGRESS	= MLX5E_TC_INGRESS,
7360bd4af8SOr Gerlitz 	MLX5E_TC_FLOW_EGRESS	= MLX5E_TC_EGRESS,
74d9ee0491SOr Gerlitz 	MLX5E_TC_FLOW_ESWITCH	= MLX5E_TC_ESW_OFFLOAD,
75d9ee0491SOr Gerlitz 	MLX5E_TC_FLOW_NIC	= MLX5E_TC_NIC_OFFLOAD,
76d9ee0491SOr Gerlitz 	MLX5E_TC_FLOW_OFFLOADED	= BIT(MLX5E_TC_FLOW_BASE),
77d9ee0491SOr Gerlitz 	MLX5E_TC_FLOW_HAIRPIN	= BIT(MLX5E_TC_FLOW_BASE + 1),
78d9ee0491SOr Gerlitz 	MLX5E_TC_FLOW_HAIRPIN_RSS = BIT(MLX5E_TC_FLOW_BASE + 2),
79d9ee0491SOr Gerlitz 	MLX5E_TC_FLOW_SLOW	  = BIT(MLX5E_TC_FLOW_BASE + 3),
80d9ee0491SOr Gerlitz 	MLX5E_TC_FLOW_DUP         = BIT(MLX5E_TC_FLOW_BASE + 4),
81ef06c9eeSRoi Dayan 	MLX5E_TC_FLOW_NOT_READY   = BIT(MLX5E_TC_FLOW_BASE + 5),
8265ba8fb7SOr Gerlitz };
8365ba8fb7SOr Gerlitz 
84e4ad91f2SChris Mi #define MLX5E_TC_MAX_SPLITS 1
85e4ad91f2SChris Mi 
8679baaec7SEli Britstein /* Helper struct for accessing a struct containing list_head array.
8779baaec7SEli Britstein  * Containing struct
8879baaec7SEli Britstein  *   |- Helper array
8979baaec7SEli Britstein  *      [0] Helper item 0
9079baaec7SEli Britstein  *          |- list_head item 0
9179baaec7SEli Britstein  *          |- index (0)
9279baaec7SEli Britstein  *      [1] Helper item 1
9379baaec7SEli Britstein  *          |- list_head item 1
9479baaec7SEli Britstein  *          |- index (1)
9579baaec7SEli Britstein  * To access the containing struct from one of the list_head items:
9679baaec7SEli Britstein  * 1. Get the helper item from the list_head item using
9779baaec7SEli Britstein  *    helper item =
9879baaec7SEli Britstein  *        container_of(list_head item, helper struct type, list_head field)
9979baaec7SEli Britstein  * 2. Get the contining struct from the helper item and its index in the array:
10079baaec7SEli Britstein  *    containing struct =
10179baaec7SEli Britstein  *        container_of(helper item, containing struct type, helper field[index])
10279baaec7SEli Britstein  */
10379baaec7SEli Britstein struct encap_flow_item {
10479baaec7SEli Britstein 	struct list_head list;
10579baaec7SEli Britstein 	int index;
10679baaec7SEli Britstein };
10779baaec7SEli Britstein 
108e8f887acSAmir Vadai struct mlx5e_tc_flow {
109e8f887acSAmir Vadai 	struct rhash_head	node;
110655dc3d2SOr Gerlitz 	struct mlx5e_priv	*priv;
111e8f887acSAmir Vadai 	u64			cookie;
1125dbe906fSPaul Blakey 	u16			flags;
113e4ad91f2SChris Mi 	struct mlx5_flow_handle *rule[MLX5E_TC_MAX_SPLITS + 1];
11479baaec7SEli Britstein 	/* Flow can be associated with multiple encap IDs.
11579baaec7SEli Britstein 	 * The number of encaps is bounded by the number of supported
11679baaec7SEli Britstein 	 * destinations.
11779baaec7SEli Britstein 	 */
11879baaec7SEli Britstein 	struct encap_flow_item encaps[MLX5_MAX_FLOW_FWD_VPORTS];
11904de7ddaSRoi Dayan 	struct mlx5e_tc_flow    *peer_flow;
12011c9c548SOr Gerlitz 	struct list_head	mod_hdr; /* flows sharing the same mod hdr ID */
1215c65c564SOr Gerlitz 	struct list_head	hairpin; /* flows sharing the same hairpin */
12204de7ddaSRoi Dayan 	struct list_head	peer;    /* flows with peer flow */
123b4a23329SRoi Dayan 	struct list_head	unready; /* flows not ready to be offloaded (e.g due to missing route) */
1245a7e5bcbSVlad Buslov 	refcount_t		refcnt;
1253bc4b7bfSOr Gerlitz 	union {
126ecf5bb79SOr Gerlitz 		struct mlx5_esw_flow_attr esw_attr[0];
1273bc4b7bfSOr Gerlitz 		struct mlx5_nic_flow_attr nic_attr[0];
1283bc4b7bfSOr Gerlitz 	};
129e8f887acSAmir Vadai };
130e8f887acSAmir Vadai 
13117091853SOr Gerlitz struct mlx5e_tc_flow_parse_attr {
1321f6da306SYevgeny Kliteynik 	const struct ip_tunnel_info *tun_info[MLX5_MAX_FLOW_FWD_VPORTS];
133d11afc26SOz Shlomo 	struct net_device *filter_dev;
13417091853SOr Gerlitz 	struct mlx5_flow_spec spec;
135d79b6df6SOr Gerlitz 	int num_mod_hdr_actions;
136218d05ceSTonghao Zhang 	int max_mod_hdr_actions;
137d79b6df6SOr Gerlitz 	void *mod_hdr_actions;
13898b66cb1SEli Britstein 	int mirred_ifindex[MLX5_MAX_FLOW_FWD_VPORTS];
13917091853SOr Gerlitz };
14017091853SOr Gerlitz 
141acff797cSMaor Gottlieb #define MLX5E_TC_TABLE_NUM_GROUPS 4
142b3a433deSOr Gerlitz #define MLX5E_TC_TABLE_MAX_GROUP_SIZE BIT(16)
143e8f887acSAmir Vadai 
14477ab67b7SOr Gerlitz struct mlx5e_hairpin {
14577ab67b7SOr Gerlitz 	struct mlx5_hairpin *pair;
14677ab67b7SOr Gerlitz 
14777ab67b7SOr Gerlitz 	struct mlx5_core_dev *func_mdev;
1483f6d08d1SOr Gerlitz 	struct mlx5e_priv *func_priv;
14977ab67b7SOr Gerlitz 	u32 tdn;
15077ab67b7SOr Gerlitz 	u32 tirn;
1513f6d08d1SOr Gerlitz 
1523f6d08d1SOr Gerlitz 	int num_channels;
1533f6d08d1SOr Gerlitz 	struct mlx5e_rqt indir_rqt;
1543f6d08d1SOr Gerlitz 	u32 indir_tirn[MLX5E_NUM_INDIR_TIRS];
1553f6d08d1SOr Gerlitz 	struct mlx5e_ttc_table ttc;
15677ab67b7SOr Gerlitz };
15777ab67b7SOr Gerlitz 
1585c65c564SOr Gerlitz struct mlx5e_hairpin_entry {
1595c65c564SOr Gerlitz 	/* a node of a hash table which keeps all the  hairpin entries */
1605c65c564SOr Gerlitz 	struct hlist_node hairpin_hlist;
1615c65c564SOr Gerlitz 
1625c65c564SOr Gerlitz 	/* flows sharing the same hairpin */
1635c65c564SOr Gerlitz 	struct list_head flows;
1645c65c564SOr Gerlitz 
165d8822868SOr Gerlitz 	u16 peer_vhca_id;
166106be53bSOr Gerlitz 	u8 prio;
1675c65c564SOr Gerlitz 	struct mlx5e_hairpin *hp;
1685c65c564SOr Gerlitz };
1695c65c564SOr Gerlitz 
17011c9c548SOr Gerlitz struct mod_hdr_key {
17111c9c548SOr Gerlitz 	int num_actions;
17211c9c548SOr Gerlitz 	void *actions;
17311c9c548SOr Gerlitz };
17411c9c548SOr Gerlitz 
17511c9c548SOr Gerlitz struct mlx5e_mod_hdr_entry {
17611c9c548SOr Gerlitz 	/* a node of a hash table which keeps all the mod_hdr entries */
17711c9c548SOr Gerlitz 	struct hlist_node mod_hdr_hlist;
17811c9c548SOr Gerlitz 
17911c9c548SOr Gerlitz 	/* flows sharing the same mod_hdr entry */
18011c9c548SOr Gerlitz 	struct list_head flows;
18111c9c548SOr Gerlitz 
18211c9c548SOr Gerlitz 	struct mod_hdr_key key;
18311c9c548SOr Gerlitz 
18411c9c548SOr Gerlitz 	u32 mod_hdr_id;
18511c9c548SOr Gerlitz };
18611c9c548SOr Gerlitz 
18711c9c548SOr Gerlitz #define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto)
18811c9c548SOr Gerlitz 
1895a7e5bcbSVlad Buslov static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
1905a7e5bcbSVlad Buslov 			      struct mlx5e_tc_flow *flow);
1915a7e5bcbSVlad Buslov 
1925a7e5bcbSVlad Buslov static struct mlx5e_tc_flow *mlx5e_flow_get(struct mlx5e_tc_flow *flow)
1935a7e5bcbSVlad Buslov {
1945a7e5bcbSVlad Buslov 	if (!flow || !refcount_inc_not_zero(&flow->refcnt))
1955a7e5bcbSVlad Buslov 		return ERR_PTR(-EINVAL);
1965a7e5bcbSVlad Buslov 	return flow;
1975a7e5bcbSVlad Buslov }
1985a7e5bcbSVlad Buslov 
1995a7e5bcbSVlad Buslov static void mlx5e_flow_put(struct mlx5e_priv *priv,
2005a7e5bcbSVlad Buslov 			   struct mlx5e_tc_flow *flow)
2015a7e5bcbSVlad Buslov {
2025a7e5bcbSVlad Buslov 	if (refcount_dec_and_test(&flow->refcnt)) {
2035a7e5bcbSVlad Buslov 		mlx5e_tc_del_flow(priv, flow);
2045a7e5bcbSVlad Buslov 		kfree(flow);
2055a7e5bcbSVlad Buslov 	}
2065a7e5bcbSVlad Buslov }
2075a7e5bcbSVlad Buslov 
20811c9c548SOr Gerlitz static inline u32 hash_mod_hdr_info(struct mod_hdr_key *key)
20911c9c548SOr Gerlitz {
21011c9c548SOr Gerlitz 	return jhash(key->actions,
21111c9c548SOr Gerlitz 		     key->num_actions * MLX5_MH_ACT_SZ, 0);
21211c9c548SOr Gerlitz }
21311c9c548SOr Gerlitz 
21411c9c548SOr Gerlitz static inline int cmp_mod_hdr_info(struct mod_hdr_key *a,
21511c9c548SOr Gerlitz 				   struct mod_hdr_key *b)
21611c9c548SOr Gerlitz {
21711c9c548SOr Gerlitz 	if (a->num_actions != b->num_actions)
21811c9c548SOr Gerlitz 		return 1;
21911c9c548SOr Gerlitz 
22011c9c548SOr Gerlitz 	return memcmp(a->actions, b->actions, a->num_actions * MLX5_MH_ACT_SZ);
22111c9c548SOr Gerlitz }
22211c9c548SOr Gerlitz 
22311c9c548SOr Gerlitz static int mlx5e_attach_mod_hdr(struct mlx5e_priv *priv,
22411c9c548SOr Gerlitz 				struct mlx5e_tc_flow *flow,
22511c9c548SOr Gerlitz 				struct mlx5e_tc_flow_parse_attr *parse_attr)
22611c9c548SOr Gerlitz {
22711c9c548SOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
22811c9c548SOr Gerlitz 	int num_actions, actions_size, namespace, err;
22911c9c548SOr Gerlitz 	struct mlx5e_mod_hdr_entry *mh;
23011c9c548SOr Gerlitz 	struct mod_hdr_key key;
23111c9c548SOr Gerlitz 	bool found = false;
23211c9c548SOr Gerlitz 	u32 hash_key;
23311c9c548SOr Gerlitz 
23411c9c548SOr Gerlitz 	num_actions  = parse_attr->num_mod_hdr_actions;
23511c9c548SOr Gerlitz 	actions_size = MLX5_MH_ACT_SZ * num_actions;
23611c9c548SOr Gerlitz 
23711c9c548SOr Gerlitz 	key.actions = parse_attr->mod_hdr_actions;
23811c9c548SOr Gerlitz 	key.num_actions = num_actions;
23911c9c548SOr Gerlitz 
24011c9c548SOr Gerlitz 	hash_key = hash_mod_hdr_info(&key);
24111c9c548SOr Gerlitz 
24211c9c548SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH) {
24311c9c548SOr Gerlitz 		namespace = MLX5_FLOW_NAMESPACE_FDB;
24411c9c548SOr Gerlitz 		hash_for_each_possible(esw->offloads.mod_hdr_tbl, mh,
24511c9c548SOr Gerlitz 				       mod_hdr_hlist, hash_key) {
24611c9c548SOr Gerlitz 			if (!cmp_mod_hdr_info(&mh->key, &key)) {
24711c9c548SOr Gerlitz 				found = true;
24811c9c548SOr Gerlitz 				break;
24911c9c548SOr Gerlitz 			}
25011c9c548SOr Gerlitz 		}
25111c9c548SOr Gerlitz 	} else {
25211c9c548SOr Gerlitz 		namespace = MLX5_FLOW_NAMESPACE_KERNEL;
25311c9c548SOr Gerlitz 		hash_for_each_possible(priv->fs.tc.mod_hdr_tbl, mh,
25411c9c548SOr Gerlitz 				       mod_hdr_hlist, hash_key) {
25511c9c548SOr Gerlitz 			if (!cmp_mod_hdr_info(&mh->key, &key)) {
25611c9c548SOr Gerlitz 				found = true;
25711c9c548SOr Gerlitz 				break;
25811c9c548SOr Gerlitz 			}
25911c9c548SOr Gerlitz 		}
26011c9c548SOr Gerlitz 	}
26111c9c548SOr Gerlitz 
26211c9c548SOr Gerlitz 	if (found)
26311c9c548SOr Gerlitz 		goto attach_flow;
26411c9c548SOr Gerlitz 
26511c9c548SOr Gerlitz 	mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL);
26611c9c548SOr Gerlitz 	if (!mh)
26711c9c548SOr Gerlitz 		return -ENOMEM;
26811c9c548SOr Gerlitz 
26911c9c548SOr Gerlitz 	mh->key.actions = (void *)mh + sizeof(*mh);
27011c9c548SOr Gerlitz 	memcpy(mh->key.actions, key.actions, actions_size);
27111c9c548SOr Gerlitz 	mh->key.num_actions = num_actions;
27211c9c548SOr Gerlitz 	INIT_LIST_HEAD(&mh->flows);
27311c9c548SOr Gerlitz 
27411c9c548SOr Gerlitz 	err = mlx5_modify_header_alloc(priv->mdev, namespace,
27511c9c548SOr Gerlitz 				       mh->key.num_actions,
27611c9c548SOr Gerlitz 				       mh->key.actions,
27711c9c548SOr Gerlitz 				       &mh->mod_hdr_id);
27811c9c548SOr Gerlitz 	if (err)
27911c9c548SOr Gerlitz 		goto out_err;
28011c9c548SOr Gerlitz 
28111c9c548SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
28211c9c548SOr Gerlitz 		hash_add(esw->offloads.mod_hdr_tbl, &mh->mod_hdr_hlist, hash_key);
28311c9c548SOr Gerlitz 	else
28411c9c548SOr Gerlitz 		hash_add(priv->fs.tc.mod_hdr_tbl, &mh->mod_hdr_hlist, hash_key);
28511c9c548SOr Gerlitz 
28611c9c548SOr Gerlitz attach_flow:
28711c9c548SOr Gerlitz 	list_add(&flow->mod_hdr, &mh->flows);
28811c9c548SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
28911c9c548SOr Gerlitz 		flow->esw_attr->mod_hdr_id = mh->mod_hdr_id;
29011c9c548SOr Gerlitz 	else
29111c9c548SOr Gerlitz 		flow->nic_attr->mod_hdr_id = mh->mod_hdr_id;
29211c9c548SOr Gerlitz 
29311c9c548SOr Gerlitz 	return 0;
29411c9c548SOr Gerlitz 
29511c9c548SOr Gerlitz out_err:
29611c9c548SOr Gerlitz 	kfree(mh);
29711c9c548SOr Gerlitz 	return err;
29811c9c548SOr Gerlitz }
29911c9c548SOr Gerlitz 
30011c9c548SOr Gerlitz static void mlx5e_detach_mod_hdr(struct mlx5e_priv *priv,
30111c9c548SOr Gerlitz 				 struct mlx5e_tc_flow *flow)
30211c9c548SOr Gerlitz {
30311c9c548SOr Gerlitz 	struct list_head *next = flow->mod_hdr.next;
30411c9c548SOr Gerlitz 
3055a7e5bcbSVlad Buslov 	/* flow wasn't fully initialized */
3065a7e5bcbSVlad Buslov 	if (list_empty(&flow->mod_hdr))
3075a7e5bcbSVlad Buslov 		return;
3085a7e5bcbSVlad Buslov 
30911c9c548SOr Gerlitz 	list_del(&flow->mod_hdr);
31011c9c548SOr Gerlitz 
31111c9c548SOr Gerlitz 	if (list_empty(next)) {
31211c9c548SOr Gerlitz 		struct mlx5e_mod_hdr_entry *mh;
31311c9c548SOr Gerlitz 
31411c9c548SOr Gerlitz 		mh = list_entry(next, struct mlx5e_mod_hdr_entry, flows);
31511c9c548SOr Gerlitz 
31611c9c548SOr Gerlitz 		mlx5_modify_header_dealloc(priv->mdev, mh->mod_hdr_id);
31711c9c548SOr Gerlitz 		hash_del(&mh->mod_hdr_hlist);
31811c9c548SOr Gerlitz 		kfree(mh);
31911c9c548SOr Gerlitz 	}
32011c9c548SOr Gerlitz }
32111c9c548SOr Gerlitz 
32277ab67b7SOr Gerlitz static
32377ab67b7SOr Gerlitz struct mlx5_core_dev *mlx5e_hairpin_get_mdev(struct net *net, int ifindex)
32477ab67b7SOr Gerlitz {
32577ab67b7SOr Gerlitz 	struct net_device *netdev;
32677ab67b7SOr Gerlitz 	struct mlx5e_priv *priv;
32777ab67b7SOr Gerlitz 
32877ab67b7SOr Gerlitz 	netdev = __dev_get_by_index(net, ifindex);
32977ab67b7SOr Gerlitz 	priv = netdev_priv(netdev);
33077ab67b7SOr Gerlitz 	return priv->mdev;
33177ab67b7SOr Gerlitz }
33277ab67b7SOr Gerlitz 
33377ab67b7SOr Gerlitz static int mlx5e_hairpin_create_transport(struct mlx5e_hairpin *hp)
33477ab67b7SOr Gerlitz {
33577ab67b7SOr Gerlitz 	u32 in[MLX5_ST_SZ_DW(create_tir_in)] = {0};
33677ab67b7SOr Gerlitz 	void *tirc;
33777ab67b7SOr Gerlitz 	int err;
33877ab67b7SOr Gerlitz 
33977ab67b7SOr Gerlitz 	err = mlx5_core_alloc_transport_domain(hp->func_mdev, &hp->tdn);
34077ab67b7SOr Gerlitz 	if (err)
34177ab67b7SOr Gerlitz 		goto alloc_tdn_err;
34277ab67b7SOr Gerlitz 
34377ab67b7SOr Gerlitz 	tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
34477ab67b7SOr Gerlitz 
34577ab67b7SOr Gerlitz 	MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_DIRECT);
346ddae74acSOr Gerlitz 	MLX5_SET(tirc, tirc, inline_rqn, hp->pair->rqn[0]);
34777ab67b7SOr Gerlitz 	MLX5_SET(tirc, tirc, transport_domain, hp->tdn);
34877ab67b7SOr Gerlitz 
34977ab67b7SOr Gerlitz 	err = mlx5_core_create_tir(hp->func_mdev, in, MLX5_ST_SZ_BYTES(create_tir_in), &hp->tirn);
35077ab67b7SOr Gerlitz 	if (err)
35177ab67b7SOr Gerlitz 		goto create_tir_err;
35277ab67b7SOr Gerlitz 
35377ab67b7SOr Gerlitz 	return 0;
35477ab67b7SOr Gerlitz 
35577ab67b7SOr Gerlitz create_tir_err:
35677ab67b7SOr Gerlitz 	mlx5_core_dealloc_transport_domain(hp->func_mdev, hp->tdn);
35777ab67b7SOr Gerlitz alloc_tdn_err:
35877ab67b7SOr Gerlitz 	return err;
35977ab67b7SOr Gerlitz }
36077ab67b7SOr Gerlitz 
36177ab67b7SOr Gerlitz static void mlx5e_hairpin_destroy_transport(struct mlx5e_hairpin *hp)
36277ab67b7SOr Gerlitz {
36377ab67b7SOr Gerlitz 	mlx5_core_destroy_tir(hp->func_mdev, hp->tirn);
36477ab67b7SOr Gerlitz 	mlx5_core_dealloc_transport_domain(hp->func_mdev, hp->tdn);
36577ab67b7SOr Gerlitz }
36677ab67b7SOr Gerlitz 
3673f6d08d1SOr Gerlitz static void mlx5e_hairpin_fill_rqt_rqns(struct mlx5e_hairpin *hp, void *rqtc)
3683f6d08d1SOr Gerlitz {
3693f6d08d1SOr Gerlitz 	u32 indirection_rqt[MLX5E_INDIR_RQT_SIZE], rqn;
3703f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
3713f6d08d1SOr Gerlitz 	int i, ix, sz = MLX5E_INDIR_RQT_SIZE;
3723f6d08d1SOr Gerlitz 
3733f6d08d1SOr Gerlitz 	mlx5e_build_default_indir_rqt(indirection_rqt, sz,
3743f6d08d1SOr Gerlitz 				      hp->num_channels);
3753f6d08d1SOr Gerlitz 
3763f6d08d1SOr Gerlitz 	for (i = 0; i < sz; i++) {
3773f6d08d1SOr Gerlitz 		ix = i;
378bbeb53b8SAya Levin 		if (priv->rss_params.hfunc == ETH_RSS_HASH_XOR)
3793f6d08d1SOr Gerlitz 			ix = mlx5e_bits_invert(i, ilog2(sz));
3803f6d08d1SOr Gerlitz 		ix = indirection_rqt[ix];
3813f6d08d1SOr Gerlitz 		rqn = hp->pair->rqn[ix];
3823f6d08d1SOr Gerlitz 		MLX5_SET(rqtc, rqtc, rq_num[i], rqn);
3833f6d08d1SOr Gerlitz 	}
3843f6d08d1SOr Gerlitz }
3853f6d08d1SOr Gerlitz 
3863f6d08d1SOr Gerlitz static int mlx5e_hairpin_create_indirect_rqt(struct mlx5e_hairpin *hp)
3873f6d08d1SOr Gerlitz {
3883f6d08d1SOr Gerlitz 	int inlen, err, sz = MLX5E_INDIR_RQT_SIZE;
3893f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
3903f6d08d1SOr Gerlitz 	struct mlx5_core_dev *mdev = priv->mdev;
3913f6d08d1SOr Gerlitz 	void *rqtc;
3923f6d08d1SOr Gerlitz 	u32 *in;
3933f6d08d1SOr Gerlitz 
3943f6d08d1SOr Gerlitz 	inlen = MLX5_ST_SZ_BYTES(create_rqt_in) + sizeof(u32) * sz;
3953f6d08d1SOr Gerlitz 	in = kvzalloc(inlen, GFP_KERNEL);
3963f6d08d1SOr Gerlitz 	if (!in)
3973f6d08d1SOr Gerlitz 		return -ENOMEM;
3983f6d08d1SOr Gerlitz 
3993f6d08d1SOr Gerlitz 	rqtc = MLX5_ADDR_OF(create_rqt_in, in, rqt_context);
4003f6d08d1SOr Gerlitz 
4013f6d08d1SOr Gerlitz 	MLX5_SET(rqtc, rqtc, rqt_actual_size, sz);
4023f6d08d1SOr Gerlitz 	MLX5_SET(rqtc, rqtc, rqt_max_size, sz);
4033f6d08d1SOr Gerlitz 
4043f6d08d1SOr Gerlitz 	mlx5e_hairpin_fill_rqt_rqns(hp, rqtc);
4053f6d08d1SOr Gerlitz 
4063f6d08d1SOr Gerlitz 	err = mlx5_core_create_rqt(mdev, in, inlen, &hp->indir_rqt.rqtn);
4073f6d08d1SOr Gerlitz 	if (!err)
4083f6d08d1SOr Gerlitz 		hp->indir_rqt.enabled = true;
4093f6d08d1SOr Gerlitz 
4103f6d08d1SOr Gerlitz 	kvfree(in);
4113f6d08d1SOr Gerlitz 	return err;
4123f6d08d1SOr Gerlitz }
4133f6d08d1SOr Gerlitz 
4143f6d08d1SOr Gerlitz static int mlx5e_hairpin_create_indirect_tirs(struct mlx5e_hairpin *hp)
4153f6d08d1SOr Gerlitz {
4163f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
4173f6d08d1SOr Gerlitz 	u32 in[MLX5_ST_SZ_DW(create_tir_in)];
4183f6d08d1SOr Gerlitz 	int tt, i, err;
4193f6d08d1SOr Gerlitz 	void *tirc;
4203f6d08d1SOr Gerlitz 
4213f6d08d1SOr Gerlitz 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
422d930ac79SAya Levin 		struct mlx5e_tirc_config ttconfig = mlx5e_tirc_get_default_config(tt);
423d930ac79SAya Levin 
4243f6d08d1SOr Gerlitz 		memset(in, 0, MLX5_ST_SZ_BYTES(create_tir_in));
4253f6d08d1SOr Gerlitz 		tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
4263f6d08d1SOr Gerlitz 
4273f6d08d1SOr Gerlitz 		MLX5_SET(tirc, tirc, transport_domain, hp->tdn);
4283f6d08d1SOr Gerlitz 		MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT);
4293f6d08d1SOr Gerlitz 		MLX5_SET(tirc, tirc, indirect_table, hp->indir_rqt.rqtn);
430bbeb53b8SAya Levin 		mlx5e_build_indir_tir_ctx_hash(&priv->rss_params, &ttconfig, tirc, false);
431bbeb53b8SAya Levin 
4323f6d08d1SOr Gerlitz 		err = mlx5_core_create_tir(hp->func_mdev, in,
4333f6d08d1SOr Gerlitz 					   MLX5_ST_SZ_BYTES(create_tir_in), &hp->indir_tirn[tt]);
4343f6d08d1SOr Gerlitz 		if (err) {
4353f6d08d1SOr Gerlitz 			mlx5_core_warn(hp->func_mdev, "create indirect tirs failed, %d\n", err);
4363f6d08d1SOr Gerlitz 			goto err_destroy_tirs;
4373f6d08d1SOr Gerlitz 		}
4383f6d08d1SOr Gerlitz 	}
4393f6d08d1SOr Gerlitz 	return 0;
4403f6d08d1SOr Gerlitz 
4413f6d08d1SOr Gerlitz err_destroy_tirs:
4423f6d08d1SOr Gerlitz 	for (i = 0; i < tt; i++)
4433f6d08d1SOr Gerlitz 		mlx5_core_destroy_tir(hp->func_mdev, hp->indir_tirn[i]);
4443f6d08d1SOr Gerlitz 	return err;
4453f6d08d1SOr Gerlitz }
4463f6d08d1SOr Gerlitz 
4473f6d08d1SOr Gerlitz static void mlx5e_hairpin_destroy_indirect_tirs(struct mlx5e_hairpin *hp)
4483f6d08d1SOr Gerlitz {
4493f6d08d1SOr Gerlitz 	int tt;
4503f6d08d1SOr Gerlitz 
4513f6d08d1SOr Gerlitz 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
4523f6d08d1SOr Gerlitz 		mlx5_core_destroy_tir(hp->func_mdev, hp->indir_tirn[tt]);
4533f6d08d1SOr Gerlitz }
4543f6d08d1SOr Gerlitz 
4553f6d08d1SOr Gerlitz static void mlx5e_hairpin_set_ttc_params(struct mlx5e_hairpin *hp,
4563f6d08d1SOr Gerlitz 					 struct ttc_params *ttc_params)
4573f6d08d1SOr Gerlitz {
4583f6d08d1SOr Gerlitz 	struct mlx5_flow_table_attr *ft_attr = &ttc_params->ft_attr;
4593f6d08d1SOr Gerlitz 	int tt;
4603f6d08d1SOr Gerlitz 
4613f6d08d1SOr Gerlitz 	memset(ttc_params, 0, sizeof(*ttc_params));
4623f6d08d1SOr Gerlitz 
4633f6d08d1SOr Gerlitz 	ttc_params->any_tt_tirn = hp->tirn;
4643f6d08d1SOr Gerlitz 
4653f6d08d1SOr Gerlitz 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
4663f6d08d1SOr Gerlitz 		ttc_params->indir_tirn[tt] = hp->indir_tirn[tt];
4673f6d08d1SOr Gerlitz 
4683f6d08d1SOr Gerlitz 	ft_attr->max_fte = MLX5E_NUM_TT;
4693f6d08d1SOr Gerlitz 	ft_attr->level = MLX5E_TC_TTC_FT_LEVEL;
4703f6d08d1SOr Gerlitz 	ft_attr->prio = MLX5E_TC_PRIO;
4713f6d08d1SOr Gerlitz }
4723f6d08d1SOr Gerlitz 
4733f6d08d1SOr Gerlitz static int mlx5e_hairpin_rss_init(struct mlx5e_hairpin *hp)
4743f6d08d1SOr Gerlitz {
4753f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
4763f6d08d1SOr Gerlitz 	struct ttc_params ttc_params;
4773f6d08d1SOr Gerlitz 	int err;
4783f6d08d1SOr Gerlitz 
4793f6d08d1SOr Gerlitz 	err = mlx5e_hairpin_create_indirect_rqt(hp);
4803f6d08d1SOr Gerlitz 	if (err)
4813f6d08d1SOr Gerlitz 		return err;
4823f6d08d1SOr Gerlitz 
4833f6d08d1SOr Gerlitz 	err = mlx5e_hairpin_create_indirect_tirs(hp);
4843f6d08d1SOr Gerlitz 	if (err)
4853f6d08d1SOr Gerlitz 		goto err_create_indirect_tirs;
4863f6d08d1SOr Gerlitz 
4873f6d08d1SOr Gerlitz 	mlx5e_hairpin_set_ttc_params(hp, &ttc_params);
4883f6d08d1SOr Gerlitz 	err = mlx5e_create_ttc_table(priv, &ttc_params, &hp->ttc);
4893f6d08d1SOr Gerlitz 	if (err)
4903f6d08d1SOr Gerlitz 		goto err_create_ttc_table;
4913f6d08d1SOr Gerlitz 
4923f6d08d1SOr Gerlitz 	netdev_dbg(priv->netdev, "add hairpin: using %d channels rss ttc table id %x\n",
4933f6d08d1SOr Gerlitz 		   hp->num_channels, hp->ttc.ft.t->id);
4943f6d08d1SOr Gerlitz 
4953f6d08d1SOr Gerlitz 	return 0;
4963f6d08d1SOr Gerlitz 
4973f6d08d1SOr Gerlitz err_create_ttc_table:
4983f6d08d1SOr Gerlitz 	mlx5e_hairpin_destroy_indirect_tirs(hp);
4993f6d08d1SOr Gerlitz err_create_indirect_tirs:
5003f6d08d1SOr Gerlitz 	mlx5e_destroy_rqt(priv, &hp->indir_rqt);
5013f6d08d1SOr Gerlitz 
5023f6d08d1SOr Gerlitz 	return err;
5033f6d08d1SOr Gerlitz }
5043f6d08d1SOr Gerlitz 
5053f6d08d1SOr Gerlitz static void mlx5e_hairpin_rss_cleanup(struct mlx5e_hairpin *hp)
5063f6d08d1SOr Gerlitz {
5073f6d08d1SOr Gerlitz 	struct mlx5e_priv *priv = hp->func_priv;
5083f6d08d1SOr Gerlitz 
5093f6d08d1SOr Gerlitz 	mlx5e_destroy_ttc_table(priv, &hp->ttc);
5103f6d08d1SOr Gerlitz 	mlx5e_hairpin_destroy_indirect_tirs(hp);
5113f6d08d1SOr Gerlitz 	mlx5e_destroy_rqt(priv, &hp->indir_rqt);
5123f6d08d1SOr Gerlitz }
5133f6d08d1SOr Gerlitz 
51477ab67b7SOr Gerlitz static struct mlx5e_hairpin *
51577ab67b7SOr Gerlitz mlx5e_hairpin_create(struct mlx5e_priv *priv, struct mlx5_hairpin_params *params,
51677ab67b7SOr Gerlitz 		     int peer_ifindex)
51777ab67b7SOr Gerlitz {
51877ab67b7SOr Gerlitz 	struct mlx5_core_dev *func_mdev, *peer_mdev;
51977ab67b7SOr Gerlitz 	struct mlx5e_hairpin *hp;
52077ab67b7SOr Gerlitz 	struct mlx5_hairpin *pair;
52177ab67b7SOr Gerlitz 	int err;
52277ab67b7SOr Gerlitz 
52377ab67b7SOr Gerlitz 	hp = kzalloc(sizeof(*hp), GFP_KERNEL);
52477ab67b7SOr Gerlitz 	if (!hp)
52577ab67b7SOr Gerlitz 		return ERR_PTR(-ENOMEM);
52677ab67b7SOr Gerlitz 
52777ab67b7SOr Gerlitz 	func_mdev = priv->mdev;
52877ab67b7SOr Gerlitz 	peer_mdev = mlx5e_hairpin_get_mdev(dev_net(priv->netdev), peer_ifindex);
52977ab67b7SOr Gerlitz 
53077ab67b7SOr Gerlitz 	pair = mlx5_core_hairpin_create(func_mdev, peer_mdev, params);
53177ab67b7SOr Gerlitz 	if (IS_ERR(pair)) {
53277ab67b7SOr Gerlitz 		err = PTR_ERR(pair);
53377ab67b7SOr Gerlitz 		goto create_pair_err;
53477ab67b7SOr Gerlitz 	}
53577ab67b7SOr Gerlitz 	hp->pair = pair;
53677ab67b7SOr Gerlitz 	hp->func_mdev = func_mdev;
5373f6d08d1SOr Gerlitz 	hp->func_priv = priv;
5383f6d08d1SOr Gerlitz 	hp->num_channels = params->num_channels;
53977ab67b7SOr Gerlitz 
54077ab67b7SOr Gerlitz 	err = mlx5e_hairpin_create_transport(hp);
54177ab67b7SOr Gerlitz 	if (err)
54277ab67b7SOr Gerlitz 		goto create_transport_err;
54377ab67b7SOr Gerlitz 
5443f6d08d1SOr Gerlitz 	if (hp->num_channels > 1) {
5453f6d08d1SOr Gerlitz 		err = mlx5e_hairpin_rss_init(hp);
5463f6d08d1SOr Gerlitz 		if (err)
5473f6d08d1SOr Gerlitz 			goto rss_init_err;
5483f6d08d1SOr Gerlitz 	}
5493f6d08d1SOr Gerlitz 
55077ab67b7SOr Gerlitz 	return hp;
55177ab67b7SOr Gerlitz 
5523f6d08d1SOr Gerlitz rss_init_err:
5533f6d08d1SOr Gerlitz 	mlx5e_hairpin_destroy_transport(hp);
55477ab67b7SOr Gerlitz create_transport_err:
55577ab67b7SOr Gerlitz 	mlx5_core_hairpin_destroy(hp->pair);
55677ab67b7SOr Gerlitz create_pair_err:
55777ab67b7SOr Gerlitz 	kfree(hp);
55877ab67b7SOr Gerlitz 	return ERR_PTR(err);
55977ab67b7SOr Gerlitz }
56077ab67b7SOr Gerlitz 
56177ab67b7SOr Gerlitz static void mlx5e_hairpin_destroy(struct mlx5e_hairpin *hp)
56277ab67b7SOr Gerlitz {
5633f6d08d1SOr Gerlitz 	if (hp->num_channels > 1)
5643f6d08d1SOr Gerlitz 		mlx5e_hairpin_rss_cleanup(hp);
56577ab67b7SOr Gerlitz 	mlx5e_hairpin_destroy_transport(hp);
56677ab67b7SOr Gerlitz 	mlx5_core_hairpin_destroy(hp->pair);
56777ab67b7SOr Gerlitz 	kvfree(hp);
56877ab67b7SOr Gerlitz }
56977ab67b7SOr Gerlitz 
570106be53bSOr Gerlitz static inline u32 hash_hairpin_info(u16 peer_vhca_id, u8 prio)
571106be53bSOr Gerlitz {
572106be53bSOr Gerlitz 	return (peer_vhca_id << 16 | prio);
573106be53bSOr Gerlitz }
574106be53bSOr Gerlitz 
5755c65c564SOr Gerlitz static struct mlx5e_hairpin_entry *mlx5e_hairpin_get(struct mlx5e_priv *priv,
576106be53bSOr Gerlitz 						     u16 peer_vhca_id, u8 prio)
5775c65c564SOr Gerlitz {
5785c65c564SOr Gerlitz 	struct mlx5e_hairpin_entry *hpe;
579106be53bSOr Gerlitz 	u32 hash_key = hash_hairpin_info(peer_vhca_id, prio);
5805c65c564SOr Gerlitz 
5815c65c564SOr Gerlitz 	hash_for_each_possible(priv->fs.tc.hairpin_tbl, hpe,
582106be53bSOr Gerlitz 			       hairpin_hlist, hash_key) {
583106be53bSOr Gerlitz 		if (hpe->peer_vhca_id == peer_vhca_id && hpe->prio == prio)
5845c65c564SOr Gerlitz 			return hpe;
5855c65c564SOr Gerlitz 	}
5865c65c564SOr Gerlitz 
5875c65c564SOr Gerlitz 	return NULL;
5885c65c564SOr Gerlitz }
5895c65c564SOr Gerlitz 
590106be53bSOr Gerlitz #define UNKNOWN_MATCH_PRIO 8
591106be53bSOr Gerlitz 
592106be53bSOr Gerlitz static int mlx5e_hairpin_get_prio(struct mlx5e_priv *priv,
593e98bedf5SEli Britstein 				  struct mlx5_flow_spec *spec, u8 *match_prio,
594e98bedf5SEli Britstein 				  struct netlink_ext_ack *extack)
595106be53bSOr Gerlitz {
596106be53bSOr Gerlitz 	void *headers_c, *headers_v;
597106be53bSOr Gerlitz 	u8 prio_val, prio_mask = 0;
598106be53bSOr Gerlitz 	bool vlan_present;
599106be53bSOr Gerlitz 
600106be53bSOr Gerlitz #ifdef CONFIG_MLX5_CORE_EN_DCB
601106be53bSOr Gerlitz 	if (priv->dcbx_dp.trust_state != MLX5_QPTS_TRUST_PCP) {
602e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
603e98bedf5SEli Britstein 				   "only PCP trust state supported for hairpin");
604106be53bSOr Gerlitz 		return -EOPNOTSUPP;
605106be53bSOr Gerlitz 	}
606106be53bSOr Gerlitz #endif
607106be53bSOr Gerlitz 	headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, outer_headers);
608106be53bSOr Gerlitz 	headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, outer_headers);
609106be53bSOr Gerlitz 
610106be53bSOr Gerlitz 	vlan_present = MLX5_GET(fte_match_set_lyr_2_4, headers_v, cvlan_tag);
611106be53bSOr Gerlitz 	if (vlan_present) {
612106be53bSOr Gerlitz 		prio_mask = MLX5_GET(fte_match_set_lyr_2_4, headers_c, first_prio);
613106be53bSOr Gerlitz 		prio_val = MLX5_GET(fte_match_set_lyr_2_4, headers_v, first_prio);
614106be53bSOr Gerlitz 	}
615106be53bSOr Gerlitz 
616106be53bSOr Gerlitz 	if (!vlan_present || !prio_mask) {
617106be53bSOr Gerlitz 		prio_val = UNKNOWN_MATCH_PRIO;
618106be53bSOr Gerlitz 	} else if (prio_mask != 0x7) {
619e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
620e98bedf5SEli Britstein 				   "masked priority match not supported for hairpin");
621106be53bSOr Gerlitz 		return -EOPNOTSUPP;
622106be53bSOr Gerlitz 	}
623106be53bSOr Gerlitz 
624106be53bSOr Gerlitz 	*match_prio = prio_val;
625106be53bSOr Gerlitz 	return 0;
626106be53bSOr Gerlitz }
627106be53bSOr Gerlitz 
6285c65c564SOr Gerlitz static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv,
6295c65c564SOr Gerlitz 				  struct mlx5e_tc_flow *flow,
630e98bedf5SEli Britstein 				  struct mlx5e_tc_flow_parse_attr *parse_attr,
631e98bedf5SEli Britstein 				  struct netlink_ext_ack *extack)
6325c65c564SOr Gerlitz {
63398b66cb1SEli Britstein 	int peer_ifindex = parse_attr->mirred_ifindex[0];
6345c65c564SOr Gerlitz 	struct mlx5_hairpin_params params;
635d8822868SOr Gerlitz 	struct mlx5_core_dev *peer_mdev;
6365c65c564SOr Gerlitz 	struct mlx5e_hairpin_entry *hpe;
6375c65c564SOr Gerlitz 	struct mlx5e_hairpin *hp;
6383f6d08d1SOr Gerlitz 	u64 link_speed64;
6393f6d08d1SOr Gerlitz 	u32 link_speed;
640106be53bSOr Gerlitz 	u8 match_prio;
641d8822868SOr Gerlitz 	u16 peer_id;
6425c65c564SOr Gerlitz 	int err;
6435c65c564SOr Gerlitz 
644d8822868SOr Gerlitz 	peer_mdev = mlx5e_hairpin_get_mdev(dev_net(priv->netdev), peer_ifindex);
645d8822868SOr Gerlitz 	if (!MLX5_CAP_GEN(priv->mdev, hairpin) || !MLX5_CAP_GEN(peer_mdev, hairpin)) {
646e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack, "hairpin is not supported");
6475c65c564SOr Gerlitz 		return -EOPNOTSUPP;
6485c65c564SOr Gerlitz 	}
6495c65c564SOr Gerlitz 
650d8822868SOr Gerlitz 	peer_id = MLX5_CAP_GEN(peer_mdev, vhca_id);
651e98bedf5SEli Britstein 	err = mlx5e_hairpin_get_prio(priv, &parse_attr->spec, &match_prio,
652e98bedf5SEli Britstein 				     extack);
653106be53bSOr Gerlitz 	if (err)
654106be53bSOr Gerlitz 		return err;
655106be53bSOr Gerlitz 	hpe = mlx5e_hairpin_get(priv, peer_id, match_prio);
6565c65c564SOr Gerlitz 	if (hpe)
6575c65c564SOr Gerlitz 		goto attach_flow;
6585c65c564SOr Gerlitz 
6595c65c564SOr Gerlitz 	hpe = kzalloc(sizeof(*hpe), GFP_KERNEL);
6605c65c564SOr Gerlitz 	if (!hpe)
6615c65c564SOr Gerlitz 		return -ENOMEM;
6625c65c564SOr Gerlitz 
6635c65c564SOr Gerlitz 	INIT_LIST_HEAD(&hpe->flows);
664d8822868SOr Gerlitz 	hpe->peer_vhca_id = peer_id;
665106be53bSOr Gerlitz 	hpe->prio = match_prio;
6665c65c564SOr Gerlitz 
6675c65c564SOr Gerlitz 	params.log_data_size = 15;
6685c65c564SOr Gerlitz 	params.log_data_size = min_t(u8, params.log_data_size,
6695c65c564SOr Gerlitz 				     MLX5_CAP_GEN(priv->mdev, log_max_hairpin_wq_data_sz));
6705c65c564SOr Gerlitz 	params.log_data_size = max_t(u8, params.log_data_size,
6715c65c564SOr Gerlitz 				     MLX5_CAP_GEN(priv->mdev, log_min_hairpin_wq_data_sz));
6725c65c564SOr Gerlitz 
673eb9180f7SOr Gerlitz 	params.log_num_packets = params.log_data_size -
674eb9180f7SOr Gerlitz 				 MLX5_MPWRQ_MIN_LOG_STRIDE_SZ(priv->mdev);
675eb9180f7SOr Gerlitz 	params.log_num_packets = min_t(u8, params.log_num_packets,
676eb9180f7SOr Gerlitz 				       MLX5_CAP_GEN(priv->mdev, log_max_hairpin_num_packets));
677eb9180f7SOr Gerlitz 
678eb9180f7SOr Gerlitz 	params.q_counter = priv->q_counter;
6793f6d08d1SOr Gerlitz 	/* set hairpin pair per each 50Gbs share of the link */
6802c81bfd5SHuy Nguyen 	mlx5e_port_max_linkspeed(priv->mdev, &link_speed);
6813f6d08d1SOr Gerlitz 	link_speed = max_t(u32, link_speed, 50000);
6823f6d08d1SOr Gerlitz 	link_speed64 = link_speed;
6833f6d08d1SOr Gerlitz 	do_div(link_speed64, 50000);
6843f6d08d1SOr Gerlitz 	params.num_channels = link_speed64;
6853f6d08d1SOr Gerlitz 
6865c65c564SOr Gerlitz 	hp = mlx5e_hairpin_create(priv, &params, peer_ifindex);
6875c65c564SOr Gerlitz 	if (IS_ERR(hp)) {
6885c65c564SOr Gerlitz 		err = PTR_ERR(hp);
6895c65c564SOr Gerlitz 		goto create_hairpin_err;
6905c65c564SOr Gerlitz 	}
6915c65c564SOr Gerlitz 
692eb9180f7SOr Gerlitz 	netdev_dbg(priv->netdev, "add hairpin: tirn %x rqn %x peer %s sqn %x prio %d (log) data %d packets %d\n",
69327b942fbSParav Pandit 		   hp->tirn, hp->pair->rqn[0],
69427b942fbSParav Pandit 		   dev_name(hp->pair->peer_mdev->device),
695eb9180f7SOr Gerlitz 		   hp->pair->sqn[0], match_prio, params.log_data_size, params.log_num_packets);
6965c65c564SOr Gerlitz 
6975c65c564SOr Gerlitz 	hpe->hp = hp;
698106be53bSOr Gerlitz 	hash_add(priv->fs.tc.hairpin_tbl, &hpe->hairpin_hlist,
699106be53bSOr Gerlitz 		 hash_hairpin_info(peer_id, match_prio));
7005c65c564SOr Gerlitz 
7015c65c564SOr Gerlitz attach_flow:
7023f6d08d1SOr Gerlitz 	if (hpe->hp->num_channels > 1) {
7033f6d08d1SOr Gerlitz 		flow->flags |= MLX5E_TC_FLOW_HAIRPIN_RSS;
7043f6d08d1SOr Gerlitz 		flow->nic_attr->hairpin_ft = hpe->hp->ttc.ft.t;
7053f6d08d1SOr Gerlitz 	} else {
7065c65c564SOr Gerlitz 		flow->nic_attr->hairpin_tirn = hpe->hp->tirn;
7073f6d08d1SOr Gerlitz 	}
7085c65c564SOr Gerlitz 	list_add(&flow->hairpin, &hpe->flows);
7093f6d08d1SOr Gerlitz 
7105c65c564SOr Gerlitz 	return 0;
7115c65c564SOr Gerlitz 
7125c65c564SOr Gerlitz create_hairpin_err:
7135c65c564SOr Gerlitz 	kfree(hpe);
7145c65c564SOr Gerlitz 	return err;
7155c65c564SOr Gerlitz }
7165c65c564SOr Gerlitz 
7175c65c564SOr Gerlitz static void mlx5e_hairpin_flow_del(struct mlx5e_priv *priv,
7185c65c564SOr Gerlitz 				   struct mlx5e_tc_flow *flow)
7195c65c564SOr Gerlitz {
7205c65c564SOr Gerlitz 	struct list_head *next = flow->hairpin.next;
7215c65c564SOr Gerlitz 
7225a7e5bcbSVlad Buslov 	/* flow wasn't fully initialized */
7235a7e5bcbSVlad Buslov 	if (list_empty(&flow->hairpin))
7245a7e5bcbSVlad Buslov 		return;
7255a7e5bcbSVlad Buslov 
7265c65c564SOr Gerlitz 	list_del(&flow->hairpin);
7275c65c564SOr Gerlitz 
7285c65c564SOr Gerlitz 	/* no more hairpin flows for us, release the hairpin pair */
7295c65c564SOr Gerlitz 	if (list_empty(next)) {
7305c65c564SOr Gerlitz 		struct mlx5e_hairpin_entry *hpe;
7315c65c564SOr Gerlitz 
7325c65c564SOr Gerlitz 		hpe = list_entry(next, struct mlx5e_hairpin_entry, flows);
7335c65c564SOr Gerlitz 
7345c65c564SOr Gerlitz 		netdev_dbg(priv->netdev, "del hairpin: peer %s\n",
73527b942fbSParav Pandit 			   dev_name(hpe->hp->pair->peer_mdev->device));
7365c65c564SOr Gerlitz 
7375c65c564SOr Gerlitz 		mlx5e_hairpin_destroy(hpe->hp);
7385c65c564SOr Gerlitz 		hash_del(&hpe->hairpin_hlist);
7395c65c564SOr Gerlitz 		kfree(hpe);
7405c65c564SOr Gerlitz 	}
7415c65c564SOr Gerlitz }
7425c65c564SOr Gerlitz 
743c83954abSRabie Loulou static int
74474491de9SMark Bloch mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
74517091853SOr Gerlitz 		      struct mlx5e_tc_flow_parse_attr *parse_attr,
746e98bedf5SEli Britstein 		      struct mlx5e_tc_flow *flow,
747e98bedf5SEli Britstein 		      struct netlink_ext_ack *extack)
748e8f887acSAmir Vadai {
749bb0ee7dcSJianbo Liu 	struct mlx5_flow_context *flow_context = &parse_attr->spec.flow_context;
750aa0cbbaeSOr Gerlitz 	struct mlx5_nic_flow_attr *attr = flow->nic_attr;
751aad7e08dSAmir Vadai 	struct mlx5_core_dev *dev = priv->mdev;
7525c65c564SOr Gerlitz 	struct mlx5_flow_destination dest[2] = {};
75366958ed9SHadar Hen Zion 	struct mlx5_flow_act flow_act = {
7543bc4b7bfSOr Gerlitz 		.action = attr->action,
75560786f09SMark Bloch 		.reformat_id = 0,
756bb0ee7dcSJianbo Liu 		.flags    = FLOW_ACT_NO_APPEND,
75766958ed9SHadar Hen Zion 	};
758aad7e08dSAmir Vadai 	struct mlx5_fc *counter = NULL;
7595c65c564SOr Gerlitz 	int err, dest_ix = 0;
760e8f887acSAmir Vadai 
761bb0ee7dcSJianbo Liu 	flow_context->flags |= FLOW_CONTEXT_HAS_TAG;
762bb0ee7dcSJianbo Liu 	flow_context->flow_tag = attr->flow_tag;
763bb0ee7dcSJianbo Liu 
7645c65c564SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_HAIRPIN) {
765e98bedf5SEli Britstein 		err = mlx5e_hairpin_flow_add(priv, flow, parse_attr, extack);
7665a7e5bcbSVlad Buslov 		if (err)
7675a7e5bcbSVlad Buslov 			return err;
7685a7e5bcbSVlad Buslov 
7693f6d08d1SOr Gerlitz 		if (flow->flags & MLX5E_TC_FLOW_HAIRPIN_RSS) {
7703f6d08d1SOr Gerlitz 			dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
7713f6d08d1SOr Gerlitz 			dest[dest_ix].ft = attr->hairpin_ft;
7723f6d08d1SOr Gerlitz 		} else {
7735c65c564SOr Gerlitz 			dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_TIR;
7745c65c564SOr Gerlitz 			dest[dest_ix].tir_num = attr->hairpin_tirn;
7753f6d08d1SOr Gerlitz 		}
7763f6d08d1SOr Gerlitz 		dest_ix++;
7773f6d08d1SOr Gerlitz 	} else if (attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
7785c65c564SOr Gerlitz 		dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
7795c65c564SOr Gerlitz 		dest[dest_ix].ft = priv->fs.vlan.ft.t;
7805c65c564SOr Gerlitz 		dest_ix++;
7815c65c564SOr Gerlitz 	}
782aad7e08dSAmir Vadai 
7835c65c564SOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
7845c65c564SOr Gerlitz 		counter = mlx5_fc_create(dev, true);
7855a7e5bcbSVlad Buslov 		if (IS_ERR(counter))
7865a7e5bcbSVlad Buslov 			return PTR_ERR(counter);
7875a7e5bcbSVlad Buslov 
7885c65c564SOr Gerlitz 		dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
789171c7625SMark Bloch 		dest[dest_ix].counter_id = mlx5_fc_id(counter);
7905c65c564SOr Gerlitz 		dest_ix++;
791b8aee822SMark Bloch 		attr->counter = counter;
792aad7e08dSAmir Vadai 	}
793aad7e08dSAmir Vadai 
7942f4fe4caSOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
7953099eb5aSOr Gerlitz 		err = mlx5e_attach_mod_hdr(priv, flow, parse_attr);
796d7e75a32SOr Gerlitz 		flow_act.modify_id = attr->mod_hdr_id;
7972f4fe4caSOr Gerlitz 		kfree(parse_attr->mod_hdr_actions);
798c83954abSRabie Loulou 		if (err)
7995a7e5bcbSVlad Buslov 			return err;
8002f4fe4caSOr Gerlitz 	}
8012f4fe4caSOr Gerlitz 
802acff797cSMaor Gottlieb 	if (IS_ERR_OR_NULL(priv->fs.tc.t)) {
80321b9c144SOr Gerlitz 		int tc_grp_size, tc_tbl_size;
80421b9c144SOr Gerlitz 		u32 max_flow_counter;
80521b9c144SOr Gerlitz 
80621b9c144SOr Gerlitz 		max_flow_counter = (MLX5_CAP_GEN(dev, max_flow_counter_31_16) << 16) |
80721b9c144SOr Gerlitz 				    MLX5_CAP_GEN(dev, max_flow_counter_15_0);
80821b9c144SOr Gerlitz 
80921b9c144SOr Gerlitz 		tc_grp_size = min_t(int, max_flow_counter, MLX5E_TC_TABLE_MAX_GROUP_SIZE);
81021b9c144SOr Gerlitz 
81121b9c144SOr Gerlitz 		tc_tbl_size = min_t(int, tc_grp_size * MLX5E_TC_TABLE_NUM_GROUPS,
81221b9c144SOr Gerlitz 				    BIT(MLX5_CAP_FLOWTABLE_NIC_RX(dev, log_max_ft_size)));
81321b9c144SOr Gerlitz 
814acff797cSMaor Gottlieb 		priv->fs.tc.t =
815acff797cSMaor Gottlieb 			mlx5_create_auto_grouped_flow_table(priv->fs.ns,
816acff797cSMaor Gottlieb 							    MLX5E_TC_PRIO,
81721b9c144SOr Gerlitz 							    tc_tbl_size,
818acff797cSMaor Gottlieb 							    MLX5E_TC_TABLE_NUM_GROUPS,
8193f6d08d1SOr Gerlitz 							    MLX5E_TC_FT_LEVEL, 0);
820acff797cSMaor Gottlieb 		if (IS_ERR(priv->fs.tc.t)) {
821e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
822e98bedf5SEli Britstein 					   "Failed to create tc offload table\n");
823e8f887acSAmir Vadai 			netdev_err(priv->netdev,
824e8f887acSAmir Vadai 				   "Failed to create tc offload table\n");
8255a7e5bcbSVlad Buslov 			return PTR_ERR(priv->fs.tc.t);
826e8f887acSAmir Vadai 		}
827e8f887acSAmir Vadai 	}
828e8f887acSAmir Vadai 
82938aa51c1SOr Gerlitz 	if (attr->match_level != MLX5_MATCH_NONE)
830d4a18e16SYevgeny Kliteynik 		parse_attr->spec.match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS;
83138aa51c1SOr Gerlitz 
832c83954abSRabie Loulou 	flow->rule[0] = mlx5_add_flow_rules(priv->fs.tc.t, &parse_attr->spec,
8335c65c564SOr Gerlitz 					    &flow_act, dest, dest_ix);
834e8f887acSAmir Vadai 
8355a7e5bcbSVlad Buslov 	if (IS_ERR(flow->rule[0]))
8365a7e5bcbSVlad Buslov 		return PTR_ERR(flow->rule[0]);
837aad7e08dSAmir Vadai 
838c83954abSRabie Loulou 	return 0;
839e8f887acSAmir Vadai }
840e8f887acSAmir Vadai 
841d85cdccbSOr Gerlitz static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
842d85cdccbSOr Gerlitz 				  struct mlx5e_tc_flow *flow)
843d85cdccbSOr Gerlitz {
844513f8f7fSOr Gerlitz 	struct mlx5_nic_flow_attr *attr = flow->nic_attr;
845d85cdccbSOr Gerlitz 	struct mlx5_fc *counter = NULL;
846d85cdccbSOr Gerlitz 
847b8aee822SMark Bloch 	counter = attr->counter;
8485a7e5bcbSVlad Buslov 	if (!IS_ERR_OR_NULL(flow->rule[0]))
849e4ad91f2SChris Mi 		mlx5_del_flow_rules(flow->rule[0]);
850d85cdccbSOr Gerlitz 	mlx5_fc_destroy(priv->mdev, counter);
851d85cdccbSOr Gerlitz 
852d9ee0491SOr Gerlitz 	if (!mlx5e_tc_num_filters(priv, MLX5E_TC_NIC_OFFLOAD)  && priv->fs.tc.t) {
853d85cdccbSOr Gerlitz 		mlx5_destroy_flow_table(priv->fs.tc.t);
854d85cdccbSOr Gerlitz 		priv->fs.tc.t = NULL;
855d85cdccbSOr Gerlitz 	}
8562f4fe4caSOr Gerlitz 
857513f8f7fSOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
8583099eb5aSOr Gerlitz 		mlx5e_detach_mod_hdr(priv, flow);
8595c65c564SOr Gerlitz 
8605c65c564SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_HAIRPIN)
8615c65c564SOr Gerlitz 		mlx5e_hairpin_flow_del(priv, flow);
862d85cdccbSOr Gerlitz }
863d85cdccbSOr Gerlitz 
864aa0cbbaeSOr Gerlitz static void mlx5e_detach_encap(struct mlx5e_priv *priv,
8658c4dc42bSEli Britstein 			       struct mlx5e_tc_flow *flow, int out_index);
866aa0cbbaeSOr Gerlitz 
8673c37745eSOr Gerlitz static int mlx5e_attach_encap(struct mlx5e_priv *priv,
868e98bedf5SEli Britstein 			      struct mlx5e_tc_flow *flow,
869733d4f36SRoi Dayan 			      struct net_device *mirred_dev,
870733d4f36SRoi Dayan 			      int out_index,
8718c4dc42bSEli Britstein 			      struct netlink_ext_ack *extack,
8720ad060eeSRoi Dayan 			      struct net_device **encap_dev,
8730ad060eeSRoi Dayan 			      bool *encap_valid);
8743c37745eSOr Gerlitz 
8756d2a3ed0SOr Gerlitz static struct mlx5_flow_handle *
8766d2a3ed0SOr Gerlitz mlx5e_tc_offload_fdb_rules(struct mlx5_eswitch *esw,
8776d2a3ed0SOr Gerlitz 			   struct mlx5e_tc_flow *flow,
8786d2a3ed0SOr Gerlitz 			   struct mlx5_flow_spec *spec,
8796d2a3ed0SOr Gerlitz 			   struct mlx5_esw_flow_attr *attr)
8806d2a3ed0SOr Gerlitz {
8816d2a3ed0SOr Gerlitz 	struct mlx5_flow_handle *rule;
8826d2a3ed0SOr Gerlitz 
8836d2a3ed0SOr Gerlitz 	rule = mlx5_eswitch_add_offloaded_rule(esw, spec, attr);
8846d2a3ed0SOr Gerlitz 	if (IS_ERR(rule))
8856d2a3ed0SOr Gerlitz 		return rule;
8866d2a3ed0SOr Gerlitz 
887e85e02baSEli Britstein 	if (attr->split_count) {
8886d2a3ed0SOr Gerlitz 		flow->rule[1] = mlx5_eswitch_add_fwd_rule(esw, spec, attr);
8896d2a3ed0SOr Gerlitz 		if (IS_ERR(flow->rule[1])) {
8906d2a3ed0SOr Gerlitz 			mlx5_eswitch_del_offloaded_rule(esw, rule, attr);
8916d2a3ed0SOr Gerlitz 			return flow->rule[1];
8926d2a3ed0SOr Gerlitz 		}
8936d2a3ed0SOr Gerlitz 	}
8946d2a3ed0SOr Gerlitz 
8956d2a3ed0SOr Gerlitz 	flow->flags |= MLX5E_TC_FLOW_OFFLOADED;
8966d2a3ed0SOr Gerlitz 	return rule;
8976d2a3ed0SOr Gerlitz }
8986d2a3ed0SOr Gerlitz 
8996d2a3ed0SOr Gerlitz static void
9006d2a3ed0SOr Gerlitz mlx5e_tc_unoffload_fdb_rules(struct mlx5_eswitch *esw,
9016d2a3ed0SOr Gerlitz 			     struct mlx5e_tc_flow *flow,
9026d2a3ed0SOr Gerlitz 			   struct mlx5_esw_flow_attr *attr)
9036d2a3ed0SOr Gerlitz {
9046d2a3ed0SOr Gerlitz 	flow->flags &= ~MLX5E_TC_FLOW_OFFLOADED;
9056d2a3ed0SOr Gerlitz 
906e85e02baSEli Britstein 	if (attr->split_count)
9076d2a3ed0SOr Gerlitz 		mlx5_eswitch_del_fwd_rule(esw, flow->rule[1], attr);
9086d2a3ed0SOr Gerlitz 
9096d2a3ed0SOr Gerlitz 	mlx5_eswitch_del_offloaded_rule(esw, flow->rule[0], attr);
9106d2a3ed0SOr Gerlitz }
9116d2a3ed0SOr Gerlitz 
9125dbe906fSPaul Blakey static struct mlx5_flow_handle *
9135dbe906fSPaul Blakey mlx5e_tc_offload_to_slow_path(struct mlx5_eswitch *esw,
9145dbe906fSPaul Blakey 			      struct mlx5e_tc_flow *flow,
9155dbe906fSPaul Blakey 			      struct mlx5_flow_spec *spec,
9165dbe906fSPaul Blakey 			      struct mlx5_esw_flow_attr *slow_attr)
9175dbe906fSPaul Blakey {
9185dbe906fSPaul Blakey 	struct mlx5_flow_handle *rule;
9195dbe906fSPaul Blakey 
9205dbe906fSPaul Blakey 	memcpy(slow_attr, flow->esw_attr, sizeof(*slow_attr));
921154e62abSOr Gerlitz 	slow_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
9222be09de7SDavid S. Miller 	slow_attr->split_count = 0;
923154e62abSOr Gerlitz 	slow_attr->dest_chain = FDB_SLOW_PATH_CHAIN;
9245dbe906fSPaul Blakey 
9255dbe906fSPaul Blakey 	rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, slow_attr);
9265dbe906fSPaul Blakey 	if (!IS_ERR(rule))
9275dbe906fSPaul Blakey 		flow->flags |= MLX5E_TC_FLOW_SLOW;
9285dbe906fSPaul Blakey 
9295dbe906fSPaul Blakey 	return rule;
9305dbe906fSPaul Blakey }
9315dbe906fSPaul Blakey 
9325dbe906fSPaul Blakey static void
9335dbe906fSPaul Blakey mlx5e_tc_unoffload_from_slow_path(struct mlx5_eswitch *esw,
9345dbe906fSPaul Blakey 				  struct mlx5e_tc_flow *flow,
9355dbe906fSPaul Blakey 				  struct mlx5_esw_flow_attr *slow_attr)
9365dbe906fSPaul Blakey {
9375dbe906fSPaul Blakey 	memcpy(slow_attr, flow->esw_attr, sizeof(*slow_attr));
938154e62abSOr Gerlitz 	slow_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
9392be09de7SDavid S. Miller 	slow_attr->split_count = 0;
940154e62abSOr Gerlitz 	slow_attr->dest_chain = FDB_SLOW_PATH_CHAIN;
9415dbe906fSPaul Blakey 	mlx5e_tc_unoffload_fdb_rules(esw, flow, slow_attr);
9425dbe906fSPaul Blakey 	flow->flags &= ~MLX5E_TC_FLOW_SLOW;
9435dbe906fSPaul Blakey }
9445dbe906fSPaul Blakey 
945b4a23329SRoi Dayan static void add_unready_flow(struct mlx5e_tc_flow *flow)
946b4a23329SRoi Dayan {
947b4a23329SRoi Dayan 	struct mlx5_rep_uplink_priv *uplink_priv;
948b4a23329SRoi Dayan 	struct mlx5e_rep_priv *rpriv;
949b4a23329SRoi Dayan 	struct mlx5_eswitch *esw;
950b4a23329SRoi Dayan 
951b4a23329SRoi Dayan 	esw = flow->priv->mdev->priv.eswitch;
952b4a23329SRoi Dayan 	rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
953b4a23329SRoi Dayan 	uplink_priv = &rpriv->uplink_priv;
954b4a23329SRoi Dayan 
955b4a23329SRoi Dayan 	flow->flags |= MLX5E_TC_FLOW_NOT_READY;
956b4a23329SRoi Dayan 	list_add_tail(&flow->unready, &uplink_priv->unready_flows);
957b4a23329SRoi Dayan }
958b4a23329SRoi Dayan 
959b4a23329SRoi Dayan static void remove_unready_flow(struct mlx5e_tc_flow *flow)
960b4a23329SRoi Dayan {
961b4a23329SRoi Dayan 	list_del(&flow->unready);
962b4a23329SRoi Dayan 	flow->flags &= ~MLX5E_TC_FLOW_NOT_READY;
963b4a23329SRoi Dayan }
964b4a23329SRoi Dayan 
965c83954abSRabie Loulou static int
96674491de9SMark Bloch mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
967e98bedf5SEli Britstein 		      struct mlx5e_tc_flow *flow,
968e98bedf5SEli Britstein 		      struct netlink_ext_ack *extack)
969adb4c123SOr Gerlitz {
970adb4c123SOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
971bf07aa73SPaul Blakey 	u32 max_chain = mlx5_eswitch_get_chain_range(esw);
972aa0cbbaeSOr Gerlitz 	struct mlx5_esw_flow_attr *attr = flow->esw_attr;
9737040632dSTonghao Zhang 	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
974bf07aa73SPaul Blakey 	u16 max_prio = mlx5_eswitch_get_prio_range(esw);
9753c37745eSOr Gerlitz 	struct net_device *out_dev, *encap_dev = NULL;
976b8aee822SMark Bloch 	struct mlx5_fc *counter = NULL;
9773c37745eSOr Gerlitz 	struct mlx5e_rep_priv *rpriv;
9783c37745eSOr Gerlitz 	struct mlx5e_priv *out_priv;
9790ad060eeSRoi Dayan 	bool encap_valid = true;
9800ad060eeSRoi Dayan 	int err = 0;
981f493f155SEli Britstein 	int out_index;
9828b32580dSOr Gerlitz 
983d14f6f2aSOr Gerlitz 	if (!mlx5_eswitch_prios_supported(esw) && attr->prio != 1) {
984d14f6f2aSOr Gerlitz 		NL_SET_ERR_MSG(extack, "E-switch priorities unsupported, upgrade FW");
985d14f6f2aSOr Gerlitz 		return -EOPNOTSUPP;
986d14f6f2aSOr Gerlitz 	}
987e52c2802SPaul Blakey 
988bf07aa73SPaul Blakey 	if (attr->chain > max_chain) {
989bf07aa73SPaul Blakey 		NL_SET_ERR_MSG(extack, "Requested chain is out of supported range");
9905a7e5bcbSVlad Buslov 		return -EOPNOTSUPP;
991bf07aa73SPaul Blakey 	}
992bf07aa73SPaul Blakey 
993bf07aa73SPaul Blakey 	if (attr->prio > max_prio) {
994bf07aa73SPaul Blakey 		NL_SET_ERR_MSG(extack, "Requested priority is out of supported range");
9955a7e5bcbSVlad Buslov 		return -EOPNOTSUPP;
996bf07aa73SPaul Blakey 	}
997bf07aa73SPaul Blakey 
998f493f155SEli Britstein 	for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++) {
9998c4dc42bSEli Britstein 		int mirred_ifindex;
10008c4dc42bSEli Britstein 
1001f493f155SEli Britstein 		if (!(attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP))
1002f493f155SEli Britstein 			continue;
1003f493f155SEli Britstein 
10047040632dSTonghao Zhang 		mirred_ifindex = parse_attr->mirred_ifindex[out_index];
10053c37745eSOr Gerlitz 		out_dev = __dev_get_by_index(dev_net(priv->netdev),
10068c4dc42bSEli Britstein 					     mirred_ifindex);
1007733d4f36SRoi Dayan 		err = mlx5e_attach_encap(priv, flow, out_dev, out_index,
10080ad060eeSRoi Dayan 					 extack, &encap_dev, &encap_valid);
10090ad060eeSRoi Dayan 		if (err)
10105a7e5bcbSVlad Buslov 			return err;
10110ad060eeSRoi Dayan 
10123c37745eSOr Gerlitz 		out_priv = netdev_priv(encap_dev);
10133c37745eSOr Gerlitz 		rpriv = out_priv->ppriv;
10141cc26d74SEli Britstein 		attr->dests[out_index].rep = rpriv->rep;
10151cc26d74SEli Britstein 		attr->dests[out_index].mdev = out_priv->mdev;
10163c37745eSOr Gerlitz 	}
10173c37745eSOr Gerlitz 
10188b32580dSOr Gerlitz 	err = mlx5_eswitch_add_vlan_action(esw, attr);
1019c83954abSRabie Loulou 	if (err)
10205a7e5bcbSVlad Buslov 		return err;
1021adb4c123SOr Gerlitz 
1022d7e75a32SOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
10231a9527bbSOr Gerlitz 		err = mlx5e_attach_mod_hdr(priv, flow, parse_attr);
1024d7e75a32SOr Gerlitz 		kfree(parse_attr->mod_hdr_actions);
1025c83954abSRabie Loulou 		if (err)
10265a7e5bcbSVlad Buslov 			return err;
1027d7e75a32SOr Gerlitz 	}
1028d7e75a32SOr Gerlitz 
1029b8aee822SMark Bloch 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
1030f9392795SShahar Klein 		counter = mlx5_fc_create(attr->counter_dev, true);
10315a7e5bcbSVlad Buslov 		if (IS_ERR(counter))
10325a7e5bcbSVlad Buslov 			return PTR_ERR(counter);
1033b8aee822SMark Bloch 
1034b8aee822SMark Bloch 		attr->counter = counter;
1035b8aee822SMark Bloch 	}
1036b8aee822SMark Bloch 
10370ad060eeSRoi Dayan 	/* we get here if one of the following takes place:
10380ad060eeSRoi Dayan 	 * (1) there's no error
10390ad060eeSRoi Dayan 	 * (2) there's an encap action and we don't have valid neigh
10403c37745eSOr Gerlitz 	 */
10410ad060eeSRoi Dayan 	if (!encap_valid) {
10425dbe906fSPaul Blakey 		/* continue with goto slow path rule instead */
10435dbe906fSPaul Blakey 		struct mlx5_esw_flow_attr slow_attr;
10445dbe906fSPaul Blakey 
10455dbe906fSPaul Blakey 		flow->rule[0] = mlx5e_tc_offload_to_slow_path(esw, flow, &parse_attr->spec, &slow_attr);
10465dbe906fSPaul Blakey 	} else {
10476d2a3ed0SOr Gerlitz 		flow->rule[0] = mlx5e_tc_offload_fdb_rules(esw, flow, &parse_attr->spec, attr);
10485dbe906fSPaul Blakey 	}
10495dbe906fSPaul Blakey 
10505a7e5bcbSVlad Buslov 	if (IS_ERR(flow->rule[0]))
10515a7e5bcbSVlad Buslov 		return PTR_ERR(flow->rule[0]);
1052c83954abSRabie Loulou 
10535dbe906fSPaul Blakey 	return 0;
1054aa0cbbaeSOr Gerlitz }
1055d85cdccbSOr Gerlitz 
10569272e3dfSYevgeny Kliteynik static bool mlx5_flow_has_geneve_opt(struct mlx5e_tc_flow *flow)
10579272e3dfSYevgeny Kliteynik {
10589272e3dfSYevgeny Kliteynik 	struct mlx5_flow_spec *spec = &flow->esw_attr->parse_attr->spec;
10599272e3dfSYevgeny Kliteynik 	void *headers_v = MLX5_ADDR_OF(fte_match_param,
10609272e3dfSYevgeny Kliteynik 				       spec->match_value,
10619272e3dfSYevgeny Kliteynik 				       misc_parameters_3);
10629272e3dfSYevgeny Kliteynik 	u32 geneve_tlv_opt_0_data = MLX5_GET(fte_match_set_misc3,
10639272e3dfSYevgeny Kliteynik 					     headers_v,
10649272e3dfSYevgeny Kliteynik 					     geneve_tlv_option_0_data);
10659272e3dfSYevgeny Kliteynik 
10669272e3dfSYevgeny Kliteynik 	return !!geneve_tlv_opt_0_data;
10679272e3dfSYevgeny Kliteynik }
10689272e3dfSYevgeny Kliteynik 
1069d85cdccbSOr Gerlitz static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
1070d85cdccbSOr Gerlitz 				  struct mlx5e_tc_flow *flow)
1071d85cdccbSOr Gerlitz {
1072d85cdccbSOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
1073d7e75a32SOr Gerlitz 	struct mlx5_esw_flow_attr *attr = flow->esw_attr;
10745dbe906fSPaul Blakey 	struct mlx5_esw_flow_attr slow_attr;
1075f493f155SEli Britstein 	int out_index;
1076d85cdccbSOr Gerlitz 
1077ef06c9eeSRoi Dayan 	if (flow->flags & MLX5E_TC_FLOW_NOT_READY) {
1078b4a23329SRoi Dayan 		remove_unready_flow(flow);
1079ef06c9eeSRoi Dayan 		kvfree(attr->parse_attr);
1080ef06c9eeSRoi Dayan 		return;
1081ef06c9eeSRoi Dayan 	}
1082ef06c9eeSRoi Dayan 
10835dbe906fSPaul Blakey 	if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
10845dbe906fSPaul Blakey 		if (flow->flags & MLX5E_TC_FLOW_SLOW)
10855dbe906fSPaul Blakey 			mlx5e_tc_unoffload_from_slow_path(esw, flow, &slow_attr);
10865dbe906fSPaul Blakey 		else
10875dbe906fSPaul Blakey 			mlx5e_tc_unoffload_fdb_rules(esw, flow, attr);
10885dbe906fSPaul Blakey 	}
1089d85cdccbSOr Gerlitz 
10909272e3dfSYevgeny Kliteynik 	if (mlx5_flow_has_geneve_opt(flow))
10919272e3dfSYevgeny Kliteynik 		mlx5_geneve_tlv_option_del(priv->mdev->geneve);
10929272e3dfSYevgeny Kliteynik 
1093513f8f7fSOr Gerlitz 	mlx5_eswitch_del_vlan_action(esw, attr);
1094d85cdccbSOr Gerlitz 
1095f493f155SEli Britstein 	for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++)
10968c4dc42bSEli Britstein 		if (attr->dests[out_index].flags & MLX5_ESW_DEST_ENCAP)
10978c4dc42bSEli Britstein 			mlx5e_detach_encap(priv, flow, out_index);
1098f493f155SEli Britstein 	kvfree(attr->parse_attr);
1099d7e75a32SOr Gerlitz 
1100513f8f7fSOr Gerlitz 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
11011a9527bbSOr Gerlitz 		mlx5e_detach_mod_hdr(priv, flow);
1102b8aee822SMark Bloch 
1103b8aee822SMark Bloch 	if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT)
1104f9392795SShahar Klein 		mlx5_fc_destroy(attr->counter_dev, attr->counter);
1105d85cdccbSOr Gerlitz }
1106d85cdccbSOr Gerlitz 
1107232c0013SHadar Hen Zion void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv,
1108232c0013SHadar Hen Zion 			      struct mlx5e_encap_entry *e)
1109232c0013SHadar Hen Zion {
11103c37745eSOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
11115dbe906fSPaul Blakey 	struct mlx5_esw_flow_attr slow_attr, *esw_attr;
11125a7e5bcbSVlad Buslov 	struct encap_flow_item *efi, *tmp;
11136d2a3ed0SOr Gerlitz 	struct mlx5_flow_handle *rule;
11146d2a3ed0SOr Gerlitz 	struct mlx5_flow_spec *spec;
1115232c0013SHadar Hen Zion 	struct mlx5e_tc_flow *flow;
1116232c0013SHadar Hen Zion 	int err;
1117232c0013SHadar Hen Zion 
111854c177caSOz Shlomo 	err = mlx5_packet_reformat_alloc(priv->mdev,
111954c177caSOz Shlomo 					 e->reformat_type,
1120232c0013SHadar Hen Zion 					 e->encap_size, e->encap_header,
112131ca3648SMark Bloch 					 MLX5_FLOW_NAMESPACE_FDB,
1122232c0013SHadar Hen Zion 					 &e->encap_id);
1123232c0013SHadar Hen Zion 	if (err) {
1124232c0013SHadar Hen Zion 		mlx5_core_warn(priv->mdev, "Failed to offload cached encapsulation header, %d\n",
1125232c0013SHadar Hen Zion 			       err);
1126232c0013SHadar Hen Zion 		return;
1127232c0013SHadar Hen Zion 	}
1128232c0013SHadar Hen Zion 	e->flags |= MLX5_ENCAP_ENTRY_VALID;
1129f6dfb4c3SHadar Hen Zion 	mlx5e_rep_queue_neigh_stats_work(priv);
1130232c0013SHadar Hen Zion 
11315a7e5bcbSVlad Buslov 	list_for_each_entry_safe(efi, tmp, &e->flows, list) {
11328c4dc42bSEli Britstein 		bool all_flow_encaps_valid = true;
11338c4dc42bSEli Britstein 		int i;
11348c4dc42bSEli Britstein 
113579baaec7SEli Britstein 		flow = container_of(efi, struct mlx5e_tc_flow, encaps[efi->index]);
11365a7e5bcbSVlad Buslov 		if (IS_ERR(mlx5e_flow_get(flow)))
11375a7e5bcbSVlad Buslov 			continue;
11385a7e5bcbSVlad Buslov 
11393c37745eSOr Gerlitz 		esw_attr = flow->esw_attr;
11406d2a3ed0SOr Gerlitz 		spec = &esw_attr->parse_attr->spec;
11416d2a3ed0SOr Gerlitz 
11428c4dc42bSEli Britstein 		esw_attr->dests[efi->index].encap_id = e->encap_id;
11438c4dc42bSEli Britstein 		esw_attr->dests[efi->index].flags |= MLX5_ESW_DEST_ENCAP_VALID;
11448c4dc42bSEli Britstein 		/* Flow can be associated with multiple encap entries.
11458c4dc42bSEli Britstein 		 * Before offloading the flow verify that all of them have
11468c4dc42bSEli Britstein 		 * a valid neighbour.
11478c4dc42bSEli Britstein 		 */
11488c4dc42bSEli Britstein 		for (i = 0; i < MLX5_MAX_FLOW_FWD_VPORTS; i++) {
11498c4dc42bSEli Britstein 			if (!(esw_attr->dests[i].flags & MLX5_ESW_DEST_ENCAP))
11508c4dc42bSEli Britstein 				continue;
11518c4dc42bSEli Britstein 			if (!(esw_attr->dests[i].flags & MLX5_ESW_DEST_ENCAP_VALID)) {
11528c4dc42bSEli Britstein 				all_flow_encaps_valid = false;
11538c4dc42bSEli Britstein 				break;
11548c4dc42bSEli Britstein 			}
11558c4dc42bSEli Britstein 		}
11568c4dc42bSEli Britstein 		/* Do not offload flows with unresolved neighbors */
11578c4dc42bSEli Britstein 		if (!all_flow_encaps_valid)
11585a7e5bcbSVlad Buslov 			goto loop_cont;
11595dbe906fSPaul Blakey 		/* update from slow path rule to encap rule */
11606d2a3ed0SOr Gerlitz 		rule = mlx5e_tc_offload_fdb_rules(esw, flow, spec, esw_attr);
11616d2a3ed0SOr Gerlitz 		if (IS_ERR(rule)) {
11626d2a3ed0SOr Gerlitz 			err = PTR_ERR(rule);
1163232c0013SHadar Hen Zion 			mlx5_core_warn(priv->mdev, "Failed to update cached encapsulation flow, %d\n",
1164232c0013SHadar Hen Zion 				       err);
11655a7e5bcbSVlad Buslov 			goto loop_cont;
1166232c0013SHadar Hen Zion 		}
11675dbe906fSPaul Blakey 
11685dbe906fSPaul Blakey 		mlx5e_tc_unoffload_from_slow_path(esw, flow, &slow_attr);
11695dbe906fSPaul Blakey 		flow->flags |= MLX5E_TC_FLOW_OFFLOADED; /* was unset when slow path rule removed */
11706d2a3ed0SOr Gerlitz 		flow->rule[0] = rule;
11715a7e5bcbSVlad Buslov 
11725a7e5bcbSVlad Buslov loop_cont:
11735a7e5bcbSVlad Buslov 		mlx5e_flow_put(priv, flow);
1174232c0013SHadar Hen Zion 	}
1175232c0013SHadar Hen Zion }
1176232c0013SHadar Hen Zion 
1177232c0013SHadar Hen Zion void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv,
1178232c0013SHadar Hen Zion 			      struct mlx5e_encap_entry *e)
1179232c0013SHadar Hen Zion {
11803c37745eSOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
11815dbe906fSPaul Blakey 	struct mlx5_esw_flow_attr slow_attr;
11825a7e5bcbSVlad Buslov 	struct encap_flow_item *efi, *tmp;
11835dbe906fSPaul Blakey 	struct mlx5_flow_handle *rule;
11845dbe906fSPaul Blakey 	struct mlx5_flow_spec *spec;
1185232c0013SHadar Hen Zion 	struct mlx5e_tc_flow *flow;
11865dbe906fSPaul Blakey 	int err;
1187232c0013SHadar Hen Zion 
11885a7e5bcbSVlad Buslov 	list_for_each_entry_safe(efi, tmp, &e->flows, list) {
118979baaec7SEli Britstein 		flow = container_of(efi, struct mlx5e_tc_flow, encaps[efi->index]);
11905a7e5bcbSVlad Buslov 		if (IS_ERR(mlx5e_flow_get(flow)))
11915a7e5bcbSVlad Buslov 			continue;
11925a7e5bcbSVlad Buslov 
11935dbe906fSPaul Blakey 		spec = &flow->esw_attr->parse_attr->spec;
11945dbe906fSPaul Blakey 
11955dbe906fSPaul Blakey 		/* update from encap rule to slow path rule */
11965dbe906fSPaul Blakey 		rule = mlx5e_tc_offload_to_slow_path(esw, flow, spec, &slow_attr);
11978c4dc42bSEli Britstein 		/* mark the flow's encap dest as non-valid */
11988c4dc42bSEli Britstein 		flow->esw_attr->dests[efi->index].flags &= ~MLX5_ESW_DEST_ENCAP_VALID;
11995dbe906fSPaul Blakey 
12005dbe906fSPaul Blakey 		if (IS_ERR(rule)) {
12015dbe906fSPaul Blakey 			err = PTR_ERR(rule);
12025dbe906fSPaul Blakey 			mlx5_core_warn(priv->mdev, "Failed to update slow path (encap) flow, %d\n",
12035dbe906fSPaul Blakey 				       err);
12045a7e5bcbSVlad Buslov 			goto loop_cont;
12055dbe906fSPaul Blakey 		}
12065dbe906fSPaul Blakey 
12076d2a3ed0SOr Gerlitz 		mlx5e_tc_unoffload_fdb_rules(esw, flow, flow->esw_attr);
12085dbe906fSPaul Blakey 		flow->flags |= MLX5E_TC_FLOW_OFFLOADED; /* was unset when fast path rule removed */
12095dbe906fSPaul Blakey 		flow->rule[0] = rule;
12105a7e5bcbSVlad Buslov 
12115a7e5bcbSVlad Buslov loop_cont:
12125a7e5bcbSVlad Buslov 		mlx5e_flow_put(priv, flow);
1213232c0013SHadar Hen Zion 	}
1214232c0013SHadar Hen Zion 
121561c806daSOr Gerlitz 	/* we know that the encap is valid */
1216232c0013SHadar Hen Zion 	e->flags &= ~MLX5_ENCAP_ENTRY_VALID;
121760786f09SMark Bloch 	mlx5_packet_reformat_dealloc(priv->mdev, e->encap_id);
1218232c0013SHadar Hen Zion }
1219232c0013SHadar Hen Zion 
1220b8aee822SMark Bloch static struct mlx5_fc *mlx5e_tc_get_counter(struct mlx5e_tc_flow *flow)
1221b8aee822SMark Bloch {
1222b8aee822SMark Bloch 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
1223b8aee822SMark Bloch 		return flow->esw_attr->counter;
1224b8aee822SMark Bloch 	else
1225b8aee822SMark Bloch 		return flow->nic_attr->counter;
1226b8aee822SMark Bloch }
1227b8aee822SMark Bloch 
1228f6dfb4c3SHadar Hen Zion void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
1229f6dfb4c3SHadar Hen Zion {
1230f6dfb4c3SHadar Hen Zion 	struct mlx5e_neigh *m_neigh = &nhe->m_neigh;
1231f6dfb4c3SHadar Hen Zion 	u64 bytes, packets, lastuse = 0;
1232f6dfb4c3SHadar Hen Zion 	struct mlx5e_tc_flow *flow;
1233f6dfb4c3SHadar Hen Zion 	struct mlx5e_encap_entry *e;
1234f6dfb4c3SHadar Hen Zion 	struct mlx5_fc *counter;
1235f6dfb4c3SHadar Hen Zion 	struct neigh_table *tbl;
1236f6dfb4c3SHadar Hen Zion 	bool neigh_used = false;
1237f6dfb4c3SHadar Hen Zion 	struct neighbour *n;
1238f6dfb4c3SHadar Hen Zion 
1239f6dfb4c3SHadar Hen Zion 	if (m_neigh->family == AF_INET)
1240f6dfb4c3SHadar Hen Zion 		tbl = &arp_tbl;
1241f6dfb4c3SHadar Hen Zion #if IS_ENABLED(CONFIG_IPV6)
1242f6dfb4c3SHadar Hen Zion 	else if (m_neigh->family == AF_INET6)
1243423c9db2SOr Gerlitz 		tbl = &nd_tbl;
1244f6dfb4c3SHadar Hen Zion #endif
1245f6dfb4c3SHadar Hen Zion 	else
1246f6dfb4c3SHadar Hen Zion 		return;
1247f6dfb4c3SHadar Hen Zion 
1248f6dfb4c3SHadar Hen Zion 	list_for_each_entry(e, &nhe->encap_list, encap_list) {
12495a7e5bcbSVlad Buslov 		struct encap_flow_item *efi, *tmp;
1250f6dfb4c3SHadar Hen Zion 		if (!(e->flags & MLX5_ENCAP_ENTRY_VALID))
1251f6dfb4c3SHadar Hen Zion 			continue;
12525a7e5bcbSVlad Buslov 		list_for_each_entry_safe(efi, tmp, &e->flows, list) {
125379baaec7SEli Britstein 			flow = container_of(efi, struct mlx5e_tc_flow,
125479baaec7SEli Britstein 					    encaps[efi->index]);
12555a7e5bcbSVlad Buslov 			if (IS_ERR(mlx5e_flow_get(flow)))
12565a7e5bcbSVlad Buslov 				continue;
12575a7e5bcbSVlad Buslov 
1258f6dfb4c3SHadar Hen Zion 			if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
1259b8aee822SMark Bloch 				counter = mlx5e_tc_get_counter(flow);
1260f6dfb4c3SHadar Hen Zion 				mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
1261f6dfb4c3SHadar Hen Zion 				if (time_after((unsigned long)lastuse, nhe->reported_lastuse)) {
12625a7e5bcbSVlad Buslov 					mlx5e_flow_put(netdev_priv(e->out_dev), flow);
1263f6dfb4c3SHadar Hen Zion 					neigh_used = true;
1264f6dfb4c3SHadar Hen Zion 					break;
1265f6dfb4c3SHadar Hen Zion 				}
1266f6dfb4c3SHadar Hen Zion 			}
12675a7e5bcbSVlad Buslov 
12685a7e5bcbSVlad Buslov 			mlx5e_flow_put(netdev_priv(e->out_dev), flow);
1269f6dfb4c3SHadar Hen Zion 		}
1270e36d4810SRoi Dayan 		if (neigh_used)
1271e36d4810SRoi Dayan 			break;
1272f6dfb4c3SHadar Hen Zion 	}
1273f6dfb4c3SHadar Hen Zion 
1274f6dfb4c3SHadar Hen Zion 	if (neigh_used) {
1275f6dfb4c3SHadar Hen Zion 		nhe->reported_lastuse = jiffies;
1276f6dfb4c3SHadar Hen Zion 
1277f6dfb4c3SHadar Hen Zion 		/* find the relevant neigh according to the cached device and
1278f6dfb4c3SHadar Hen Zion 		 * dst ip pair
1279f6dfb4c3SHadar Hen Zion 		 */
1280f6dfb4c3SHadar Hen Zion 		n = neigh_lookup(tbl, &m_neigh->dst_ip, m_neigh->dev);
1281c7f7ba8dSRoi Dayan 		if (!n)
1282f6dfb4c3SHadar Hen Zion 			return;
1283f6dfb4c3SHadar Hen Zion 
1284f6dfb4c3SHadar Hen Zion 		neigh_event_send(n, NULL);
1285f6dfb4c3SHadar Hen Zion 		neigh_release(n);
1286f6dfb4c3SHadar Hen Zion 	}
1287f6dfb4c3SHadar Hen Zion }
1288f6dfb4c3SHadar Hen Zion 
1289d85cdccbSOr Gerlitz static void mlx5e_detach_encap(struct mlx5e_priv *priv,
12908c4dc42bSEli Britstein 			       struct mlx5e_tc_flow *flow, int out_index)
1291d85cdccbSOr Gerlitz {
12928c4dc42bSEli Britstein 	struct list_head *next = flow->encaps[out_index].list.next;
12935067b602SRoi Dayan 
12945a7e5bcbSVlad Buslov 	/* flow wasn't fully initialized */
12955a7e5bcbSVlad Buslov 	if (list_empty(&flow->encaps[out_index].list))
12965a7e5bcbSVlad Buslov 		return;
12975a7e5bcbSVlad Buslov 
12988c4dc42bSEli Britstein 	list_del(&flow->encaps[out_index].list);
12995067b602SRoi Dayan 	if (list_empty(next)) {
1300c1ae1152SOr Gerlitz 		struct mlx5e_encap_entry *e;
13015067b602SRoi Dayan 
1302c1ae1152SOr Gerlitz 		e = list_entry(next, struct mlx5e_encap_entry, flows);
1303232c0013SHadar Hen Zion 		mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e);
1304232c0013SHadar Hen Zion 
1305232c0013SHadar Hen Zion 		if (e->flags & MLX5_ENCAP_ENTRY_VALID)
130660786f09SMark Bloch 			mlx5_packet_reformat_dealloc(priv->mdev, e->encap_id);
1307232c0013SHadar Hen Zion 
1308cdc5a7f3SOr Gerlitz 		hash_del_rcu(&e->encap_hlist);
1309232c0013SHadar Hen Zion 		kfree(e->encap_header);
13105067b602SRoi Dayan 		kfree(e);
13115067b602SRoi Dayan 	}
13125067b602SRoi Dayan }
13135067b602SRoi Dayan 
131404de7ddaSRoi Dayan static void __mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow)
131504de7ddaSRoi Dayan {
131604de7ddaSRoi Dayan 	struct mlx5_eswitch *esw = flow->priv->mdev->priv.eswitch;
131704de7ddaSRoi Dayan 
131804de7ddaSRoi Dayan 	if (!(flow->flags & MLX5E_TC_FLOW_ESWITCH) ||
131904de7ddaSRoi Dayan 	    !(flow->flags & MLX5E_TC_FLOW_DUP))
132004de7ddaSRoi Dayan 		return;
132104de7ddaSRoi Dayan 
132204de7ddaSRoi Dayan 	mutex_lock(&esw->offloads.peer_mutex);
132304de7ddaSRoi Dayan 	list_del(&flow->peer);
132404de7ddaSRoi Dayan 	mutex_unlock(&esw->offloads.peer_mutex);
132504de7ddaSRoi Dayan 
132604de7ddaSRoi Dayan 	flow->flags &= ~MLX5E_TC_FLOW_DUP;
132704de7ddaSRoi Dayan 
132804de7ddaSRoi Dayan 	mlx5e_tc_del_fdb_flow(flow->peer_flow->priv, flow->peer_flow);
132904de7ddaSRoi Dayan 	kvfree(flow->peer_flow);
133004de7ddaSRoi Dayan 	flow->peer_flow = NULL;
133104de7ddaSRoi Dayan }
133204de7ddaSRoi Dayan 
133304de7ddaSRoi Dayan static void mlx5e_tc_del_fdb_peer_flow(struct mlx5e_tc_flow *flow)
133404de7ddaSRoi Dayan {
133504de7ddaSRoi Dayan 	struct mlx5_core_dev *dev = flow->priv->mdev;
133604de7ddaSRoi Dayan 	struct mlx5_devcom *devcom = dev->priv.devcom;
133704de7ddaSRoi Dayan 	struct mlx5_eswitch *peer_esw;
133804de7ddaSRoi Dayan 
133904de7ddaSRoi Dayan 	peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
134004de7ddaSRoi Dayan 	if (!peer_esw)
134104de7ddaSRoi Dayan 		return;
134204de7ddaSRoi Dayan 
134304de7ddaSRoi Dayan 	__mlx5e_tc_del_fdb_peer_flow(flow);
134404de7ddaSRoi Dayan 	mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
134504de7ddaSRoi Dayan }
134604de7ddaSRoi Dayan 
1347e8f887acSAmir Vadai static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
1348961e8979SRoi Dayan 			      struct mlx5e_tc_flow *flow)
1349e8f887acSAmir Vadai {
135004de7ddaSRoi Dayan 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH) {
135104de7ddaSRoi Dayan 		mlx5e_tc_del_fdb_peer_flow(flow);
1352d85cdccbSOr Gerlitz 		mlx5e_tc_del_fdb_flow(priv, flow);
135304de7ddaSRoi Dayan 	} else {
1354d85cdccbSOr Gerlitz 		mlx5e_tc_del_nic_flow(priv, flow);
1355e8f887acSAmir Vadai 	}
135604de7ddaSRoi Dayan }
1357e8f887acSAmir Vadai 
1358bbd00f7eSHadar Hen Zion 
1359bbd00f7eSHadar Hen Zion static int parse_tunnel_attr(struct mlx5e_priv *priv,
1360bbd00f7eSHadar Hen Zion 			     struct mlx5_flow_spec *spec,
1361f9e30088SPablo Neira Ayuso 			     struct flow_cls_offload *f,
13626363651dSOr Gerlitz 			     struct net_device *filter_dev, u8 *match_level)
1363bbd00f7eSHadar Hen Zion {
1364e98bedf5SEli Britstein 	struct netlink_ext_ack *extack = f->common.extack;
1365bbd00f7eSHadar Hen Zion 	void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
1366bbd00f7eSHadar Hen Zion 				       outer_headers);
1367bbd00f7eSHadar Hen Zion 	void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
1368bbd00f7eSHadar Hen Zion 				       outer_headers);
1369f9e30088SPablo Neira Ayuso 	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
13708f256622SPablo Neira Ayuso 	int err;
1371bbd00f7eSHadar Hen Zion 
1372101f4de9SOz Shlomo 	err = mlx5e_tc_tun_parse(filter_dev, priv, spec, f,
13736363651dSOr Gerlitz 				 headers_c, headers_v, match_level);
137454c177caSOz Shlomo 	if (err) {
137554c177caSOz Shlomo 		NL_SET_ERR_MSG_MOD(extack,
137654c177caSOz Shlomo 				   "failed to parse tunnel attributes");
1377101f4de9SOz Shlomo 		return err;
1378bbd00f7eSHadar Hen Zion 	}
1379bbd00f7eSHadar Hen Zion 
1380d1bda7eeSTonghao Zhang 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
13818f256622SPablo Neira Ayuso 		struct flow_match_ipv4_addrs match;
13828f256622SPablo Neira Ayuso 
13838f256622SPablo Neira Ayuso 		flow_rule_match_enc_ipv4_addrs(rule, &match);
1384bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1385bbd00f7eSHadar Hen Zion 			 src_ipv4_src_ipv6.ipv4_layout.ipv4,
13868f256622SPablo Neira Ayuso 			 ntohl(match.mask->src));
1387bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1388bbd00f7eSHadar Hen Zion 			 src_ipv4_src_ipv6.ipv4_layout.ipv4,
13898f256622SPablo Neira Ayuso 			 ntohl(match.key->src));
1390bbd00f7eSHadar Hen Zion 
1391bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1392bbd00f7eSHadar Hen Zion 			 dst_ipv4_dst_ipv6.ipv4_layout.ipv4,
13938f256622SPablo Neira Ayuso 			 ntohl(match.mask->dst));
1394bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1395bbd00f7eSHadar Hen Zion 			 dst_ipv4_dst_ipv6.ipv4_layout.ipv4,
13968f256622SPablo Neira Ayuso 			 ntohl(match.key->dst));
1397bbd00f7eSHadar Hen Zion 
1398bbd00f7eSHadar Hen Zion 		MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ethertype);
1399bbd00f7eSHadar Hen Zion 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, ETH_P_IP);
1400d1bda7eeSTonghao Zhang 	} else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) {
14018f256622SPablo Neira Ayuso 		struct flow_match_ipv6_addrs match;
140219f44401SOr Gerlitz 
14038f256622SPablo Neira Ayuso 		flow_rule_match_enc_ipv6_addrs(rule, &match);
140419f44401SOr Gerlitz 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
140519f44401SOr Gerlitz 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
14068f256622SPablo Neira Ayuso 		       &match.mask->src, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
140719f44401SOr Gerlitz 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
140819f44401SOr Gerlitz 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
14098f256622SPablo Neira Ayuso 		       &match.key->src, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
141019f44401SOr Gerlitz 
141119f44401SOr Gerlitz 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
141219f44401SOr Gerlitz 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
14138f256622SPablo Neira Ayuso 		       &match.mask->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
141419f44401SOr Gerlitz 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
141519f44401SOr Gerlitz 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
14168f256622SPablo Neira Ayuso 		       &match.key->dst, MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6));
141719f44401SOr Gerlitz 
141819f44401SOr Gerlitz 		MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, ethertype);
141919f44401SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, ETH_P_IPV6);
14202e72eb43SOr Gerlitz 	}
1421bbd00f7eSHadar Hen Zion 
14228f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IP)) {
14238f256622SPablo Neira Ayuso 		struct flow_match_ip match;
1424bcef735cSOr Gerlitz 
14258f256622SPablo Neira Ayuso 		flow_rule_match_enc_ip(rule, &match);
14268f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn,
14278f256622SPablo Neira Ayuso 			 match.mask->tos & 0x3);
14288f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn,
14298f256622SPablo Neira Ayuso 			 match.key->tos & 0x3);
1430bcef735cSOr Gerlitz 
14318f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp,
14328f256622SPablo Neira Ayuso 			 match.mask->tos >> 2);
14338f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp,
14348f256622SPablo Neira Ayuso 			 match.key->tos  >> 2);
1435bcef735cSOr Gerlitz 
14368f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit,
14378f256622SPablo Neira Ayuso 			 match.mask->ttl);
14388f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit,
14398f256622SPablo Neira Ayuso 			 match.key->ttl);
1440e98bedf5SEli Britstein 
14418f256622SPablo Neira Ayuso 		if (match.mask->ttl &&
1442e98bedf5SEli Britstein 		    !MLX5_CAP_ESW_FLOWTABLE_FDB
1443e98bedf5SEli Britstein 			(priv->mdev,
1444e98bedf5SEli Britstein 			 ft_field_support.outer_ipv4_ttl)) {
1445e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1446e98bedf5SEli Britstein 					   "Matching on TTL is not supported");
1447e98bedf5SEli Britstein 			return -EOPNOTSUPP;
1448e98bedf5SEli Britstein 		}
1449e98bedf5SEli Britstein 
1450bcef735cSOr Gerlitz 	}
1451bcef735cSOr Gerlitz 
1452bbd00f7eSHadar Hen Zion 	/* Enforce DMAC when offloading incoming tunneled flows.
1453bbd00f7eSHadar Hen Zion 	 * Flow counters require a match on the DMAC.
1454bbd00f7eSHadar Hen Zion 	 */
1455bbd00f7eSHadar Hen Zion 	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, dmac_47_16);
1456bbd00f7eSHadar Hen Zion 	MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, dmac_15_0);
1457bbd00f7eSHadar Hen Zion 	ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1458bbd00f7eSHadar Hen Zion 				     dmac_47_16), priv->netdev->dev_addr);
1459bbd00f7eSHadar Hen Zion 
1460bbd00f7eSHadar Hen Zion 	/* let software handle IP fragments */
1461bbd00f7eSHadar Hen Zion 	MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1);
1462bbd00f7eSHadar Hen Zion 	MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag, 0);
1463bbd00f7eSHadar Hen Zion 
1464bbd00f7eSHadar Hen Zion 	return 0;
1465bbd00f7eSHadar Hen Zion }
1466bbd00f7eSHadar Hen Zion 
14678377629eSEli Britstein static void *get_match_headers_criteria(u32 flags,
14688377629eSEli Britstein 					struct mlx5_flow_spec *spec)
14698377629eSEli Britstein {
14708377629eSEli Britstein 	return (flags & MLX5_FLOW_CONTEXT_ACTION_DECAP) ?
14718377629eSEli Britstein 		MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
14728377629eSEli Britstein 			     inner_headers) :
14738377629eSEli Britstein 		MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
14748377629eSEli Britstein 			     outer_headers);
14758377629eSEli Britstein }
14768377629eSEli Britstein 
14778377629eSEli Britstein static void *get_match_headers_value(u32 flags,
14788377629eSEli Britstein 				     struct mlx5_flow_spec *spec)
14798377629eSEli Britstein {
14808377629eSEli Britstein 	return (flags & MLX5_FLOW_CONTEXT_ACTION_DECAP) ?
14818377629eSEli Britstein 		MLX5_ADDR_OF(fte_match_param, spec->match_value,
14828377629eSEli Britstein 			     inner_headers) :
14838377629eSEli Britstein 		MLX5_ADDR_OF(fte_match_param, spec->match_value,
14848377629eSEli Britstein 			     outer_headers);
14858377629eSEli Britstein }
14868377629eSEli Britstein 
1487de0af0bfSRoi Dayan static int __parse_cls_flower(struct mlx5e_priv *priv,
1488de0af0bfSRoi Dayan 			      struct mlx5_flow_spec *spec,
1489f9e30088SPablo Neira Ayuso 			      struct flow_cls_offload *f,
149054c177caSOz Shlomo 			      struct net_device *filter_dev,
14916363651dSOr Gerlitz 			      u8 *match_level, u8 *tunnel_match_level)
1492e3a2b7edSAmir Vadai {
1493e98bedf5SEli Britstein 	struct netlink_ext_ack *extack = f->common.extack;
1494c5bb1730SMaor Gottlieb 	void *headers_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
1495c5bb1730SMaor Gottlieb 				       outer_headers);
1496c5bb1730SMaor Gottlieb 	void *headers_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
1497c5bb1730SMaor Gottlieb 				       outer_headers);
1498699e96ddSJianbo Liu 	void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
1499699e96ddSJianbo Liu 				    misc_parameters);
1500699e96ddSJianbo Liu 	void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
1501699e96ddSJianbo Liu 				    misc_parameters);
1502f9e30088SPablo Neira Ayuso 	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
15038f256622SPablo Neira Ayuso 	struct flow_dissector *dissector = rule->match.dissector;
1504e3a2b7edSAmir Vadai 	u16 addr_type = 0;
1505e3a2b7edSAmir Vadai 	u8 ip_proto = 0;
1506e3a2b7edSAmir Vadai 
1507d708f902SOr Gerlitz 	*match_level = MLX5_MATCH_NONE;
1508de0af0bfSRoi Dayan 
15098f256622SPablo Neira Ayuso 	if (dissector->used_keys &
15103d144578SVlad Buslov 	    ~(BIT(FLOW_DISSECTOR_KEY_META) |
15113d144578SVlad Buslov 	      BIT(FLOW_DISSECTOR_KEY_CONTROL) |
1512e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
1513e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
1514095b6cfdSOr Gerlitz 	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
1515699e96ddSJianbo Liu 	      BIT(FLOW_DISSECTOR_KEY_CVLAN) |
1516e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
1517e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
1518bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_PORTS) |
1519bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
1520bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) |
1521bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) |
1522bbd00f7eSHadar Hen Zion 	      BIT(FLOW_DISSECTOR_KEY_ENC_PORTS)	|
1523e77834ecSOr Gerlitz 	      BIT(FLOW_DISSECTOR_KEY_ENC_CONTROL) |
1524fd7da28bSOr Gerlitz 	      BIT(FLOW_DISSECTOR_KEY_TCP) |
1525bcef735cSOr Gerlitz 	      BIT(FLOW_DISSECTOR_KEY_IP)  |
15269272e3dfSYevgeny Kliteynik 	      BIT(FLOW_DISSECTOR_KEY_ENC_IP) |
15279272e3dfSYevgeny Kliteynik 	      BIT(FLOW_DISSECTOR_KEY_ENC_OPTS))) {
1528e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack, "Unsupported key");
1529e3a2b7edSAmir Vadai 		netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
15308f256622SPablo Neira Ayuso 			    dissector->used_keys);
1531e3a2b7edSAmir Vadai 		return -EOPNOTSUPP;
1532e3a2b7edSAmir Vadai 	}
1533e3a2b7edSAmir Vadai 
1534075973c7SVlad Buslov 	if (mlx5e_get_tc_tun(filter_dev)) {
15356363651dSOr Gerlitz 		if (parse_tunnel_attr(priv, spec, f, filter_dev, tunnel_match_level))
1536bbd00f7eSHadar Hen Zion 			return -EOPNOTSUPP;
1537bbd00f7eSHadar Hen Zion 
1538bbd00f7eSHadar Hen Zion 		/* In decap flow, header pointers should point to the inner
1539bbd00f7eSHadar Hen Zion 		 * headers, outer header were already set by parse_tunnel_attr
1540bbd00f7eSHadar Hen Zion 		 */
15418377629eSEli Britstein 		headers_c = get_match_headers_criteria(MLX5_FLOW_CONTEXT_ACTION_DECAP,
15428377629eSEli Britstein 						       spec);
15438377629eSEli Britstein 		headers_v = get_match_headers_value(MLX5_FLOW_CONTEXT_ACTION_DECAP,
15448377629eSEli Britstein 						    spec);
1545bbd00f7eSHadar Hen Zion 	}
1546bbd00f7eSHadar Hen Zion 
15478f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
15488f256622SPablo Neira Ayuso 		struct flow_match_basic match;
1549e3a2b7edSAmir Vadai 
15508f256622SPablo Neira Ayuso 		flow_rule_match_basic(rule, &match);
15518f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype,
15528f256622SPablo Neira Ayuso 			 ntohs(match.mask->n_proto));
15538f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype,
15548f256622SPablo Neira Ayuso 			 ntohs(match.key->n_proto));
15558f256622SPablo Neira Ayuso 
15568f256622SPablo Neira Ayuso 		if (match.mask->n_proto)
1557d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L2;
1558e3a2b7edSAmir Vadai 	}
155935a605dbSEli Britstein 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN) ||
156035a605dbSEli Britstein 	    is_vlan_dev(filter_dev)) {
156135a605dbSEli Britstein 		struct flow_dissector_key_vlan filter_dev_mask;
156235a605dbSEli Britstein 		struct flow_dissector_key_vlan filter_dev_key;
15638f256622SPablo Neira Ayuso 		struct flow_match_vlan match;
15648f256622SPablo Neira Ayuso 
156535a605dbSEli Britstein 		if (is_vlan_dev(filter_dev)) {
156635a605dbSEli Britstein 			match.key = &filter_dev_key;
156735a605dbSEli Britstein 			match.key->vlan_id = vlan_dev_vlan_id(filter_dev);
156835a605dbSEli Britstein 			match.key->vlan_tpid = vlan_dev_vlan_proto(filter_dev);
156935a605dbSEli Britstein 			match.key->vlan_priority = 0;
157035a605dbSEli Britstein 			match.mask = &filter_dev_mask;
157135a605dbSEli Britstein 			memset(match.mask, 0xff, sizeof(*match.mask));
157235a605dbSEli Britstein 			match.mask->vlan_priority = 0;
157335a605dbSEli Britstein 		} else {
15748f256622SPablo Neira Ayuso 			flow_rule_match_vlan(rule, &match);
157535a605dbSEli Britstein 		}
15768f256622SPablo Neira Ayuso 		if (match.mask->vlan_id ||
15778f256622SPablo Neira Ayuso 		    match.mask->vlan_priority ||
15788f256622SPablo Neira Ayuso 		    match.mask->vlan_tpid) {
15798f256622SPablo Neira Ayuso 			if (match.key->vlan_tpid == htons(ETH_P_8021AD)) {
1580699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1581699e96ddSJianbo Liu 					 svlan_tag, 1);
1582699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1583699e96ddSJianbo Liu 					 svlan_tag, 1);
1584699e96ddSJianbo Liu 			} else {
1585699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_lyr_2_4, headers_c,
1586699e96ddSJianbo Liu 					 cvlan_tag, 1);
1587699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_lyr_2_4, headers_v,
1588699e96ddSJianbo Liu 					 cvlan_tag, 1);
1589699e96ddSJianbo Liu 			}
1590095b6cfdSOr Gerlitz 
15918f256622SPablo Neira Ayuso 			MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_vid,
15928f256622SPablo Neira Ayuso 				 match.mask->vlan_id);
15938f256622SPablo Neira Ayuso 			MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_vid,
15948f256622SPablo Neira Ayuso 				 match.key->vlan_id);
1595358d79a4SOr Gerlitz 
15968f256622SPablo Neira Ayuso 			MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_prio,
15978f256622SPablo Neira Ayuso 				 match.mask->vlan_priority);
15988f256622SPablo Neira Ayuso 			MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_prio,
15998f256622SPablo Neira Ayuso 				 match.key->vlan_priority);
160054782900SOr Gerlitz 
1601d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L2;
1602095b6cfdSOr Gerlitz 		}
1603d3a80bb5SOr Gerlitz 	} else if (*match_level != MLX5_MATCH_NONE) {
1604cee26487SJianbo Liu 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, svlan_tag, 1);
1605cee26487SJianbo Liu 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1);
1606d3a80bb5SOr Gerlitz 		*match_level = MLX5_MATCH_L2;
1607095b6cfdSOr Gerlitz 	}
1608095b6cfdSOr Gerlitz 
16098f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CVLAN)) {
16108f256622SPablo Neira Ayuso 		struct flow_match_vlan match;
16118f256622SPablo Neira Ayuso 
161212d5cbf8SJianbo Liu 		flow_rule_match_cvlan(rule, &match);
16138f256622SPablo Neira Ayuso 		if (match.mask->vlan_id ||
16148f256622SPablo Neira Ayuso 		    match.mask->vlan_priority ||
16158f256622SPablo Neira Ayuso 		    match.mask->vlan_tpid) {
16168f256622SPablo Neira Ayuso 			if (match.key->vlan_tpid == htons(ETH_P_8021AD)) {
1617699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_misc, misc_c,
1618699e96ddSJianbo Liu 					 outer_second_svlan_tag, 1);
1619699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_misc, misc_v,
1620699e96ddSJianbo Liu 					 outer_second_svlan_tag, 1);
1621699e96ddSJianbo Liu 			} else {
1622699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_misc, misc_c,
1623699e96ddSJianbo Liu 					 outer_second_cvlan_tag, 1);
1624699e96ddSJianbo Liu 				MLX5_SET(fte_match_set_misc, misc_v,
1625699e96ddSJianbo Liu 					 outer_second_cvlan_tag, 1);
1626699e96ddSJianbo Liu 			}
1627699e96ddSJianbo Liu 
1628699e96ddSJianbo Liu 			MLX5_SET(fte_match_set_misc, misc_c, outer_second_vid,
16298f256622SPablo Neira Ayuso 				 match.mask->vlan_id);
1630699e96ddSJianbo Liu 			MLX5_SET(fte_match_set_misc, misc_v, outer_second_vid,
16318f256622SPablo Neira Ayuso 				 match.key->vlan_id);
1632699e96ddSJianbo Liu 			MLX5_SET(fte_match_set_misc, misc_c, outer_second_prio,
16338f256622SPablo Neira Ayuso 				 match.mask->vlan_priority);
1634699e96ddSJianbo Liu 			MLX5_SET(fte_match_set_misc, misc_v, outer_second_prio,
16358f256622SPablo Neira Ayuso 				 match.key->vlan_priority);
1636699e96ddSJianbo Liu 
1637699e96ddSJianbo Liu 			*match_level = MLX5_MATCH_L2;
1638699e96ddSJianbo Liu 		}
1639699e96ddSJianbo Liu 	}
1640699e96ddSJianbo Liu 
16418f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
16428f256622SPablo Neira Ayuso 		struct flow_match_eth_addrs match;
164354782900SOr Gerlitz 
16448f256622SPablo Neira Ayuso 		flow_rule_match_eth_addrs(rule, &match);
1645d3a80bb5SOr Gerlitz 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1646d3a80bb5SOr Gerlitz 					     dmac_47_16),
16478f256622SPablo Neira Ayuso 				match.mask->dst);
1648d3a80bb5SOr Gerlitz 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1649d3a80bb5SOr Gerlitz 					     dmac_47_16),
16508f256622SPablo Neira Ayuso 				match.key->dst);
1651d3a80bb5SOr Gerlitz 
1652d3a80bb5SOr Gerlitz 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1653d3a80bb5SOr Gerlitz 					     smac_47_16),
16548f256622SPablo Neira Ayuso 				match.mask->src);
1655d3a80bb5SOr Gerlitz 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1656d3a80bb5SOr Gerlitz 					     smac_47_16),
16578f256622SPablo Neira Ayuso 				match.key->src);
1658d3a80bb5SOr Gerlitz 
16598f256622SPablo Neira Ayuso 		if (!is_zero_ether_addr(match.mask->src) ||
16608f256622SPablo Neira Ayuso 		    !is_zero_ether_addr(match.mask->dst))
1661d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L2;
166254782900SOr Gerlitz 	}
166354782900SOr Gerlitz 
16648f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
16658f256622SPablo Neira Ayuso 		struct flow_match_control match;
166654782900SOr Gerlitz 
16678f256622SPablo Neira Ayuso 		flow_rule_match_control(rule, &match);
16688f256622SPablo Neira Ayuso 		addr_type = match.key->addr_type;
166954782900SOr Gerlitz 
167054782900SOr Gerlitz 		/* the HW doesn't support frag first/later */
16718f256622SPablo Neira Ayuso 		if (match.mask->flags & FLOW_DIS_FIRST_FRAG)
167254782900SOr Gerlitz 			return -EOPNOTSUPP;
167354782900SOr Gerlitz 
16748f256622SPablo Neira Ayuso 		if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
167554782900SOr Gerlitz 			MLX5_SET(fte_match_set_lyr_2_4, headers_c, frag, 1);
167654782900SOr Gerlitz 			MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag,
16778f256622SPablo Neira Ayuso 				 match.key->flags & FLOW_DIS_IS_FRAGMENT);
167854782900SOr Gerlitz 
167954782900SOr Gerlitz 			/* the HW doesn't need L3 inline to match on frag=no */
16808f256622SPablo Neira Ayuso 			if (!(match.key->flags & FLOW_DIS_IS_FRAGMENT))
168183621b7dSOr Gerlitz 				*match_level = MLX5_MATCH_L2;
168254782900SOr Gerlitz 	/* ***  L2 attributes parsing up to here *** */
168354782900SOr Gerlitz 			else
168483621b7dSOr Gerlitz 				*match_level = MLX5_MATCH_L3;
168554782900SOr Gerlitz 		}
168654782900SOr Gerlitz 	}
168754782900SOr Gerlitz 
16888f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
16898f256622SPablo Neira Ayuso 		struct flow_match_basic match;
16908f256622SPablo Neira Ayuso 
16918f256622SPablo Neira Ayuso 		flow_rule_match_basic(rule, &match);
16928f256622SPablo Neira Ayuso 		ip_proto = match.key->ip_proto;
169354782900SOr Gerlitz 
169454782900SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
16958f256622SPablo Neira Ayuso 			 match.mask->ip_proto);
169654782900SOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
16978f256622SPablo Neira Ayuso 			 match.key->ip_proto);
169854782900SOr Gerlitz 
16998f256622SPablo Neira Ayuso 		if (match.mask->ip_proto)
1700d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L3;
170154782900SOr Gerlitz 	}
170254782900SOr Gerlitz 
1703e3a2b7edSAmir Vadai 	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
17048f256622SPablo Neira Ayuso 		struct flow_match_ipv4_addrs match;
1705e3a2b7edSAmir Vadai 
17068f256622SPablo Neira Ayuso 		flow_rule_match_ipv4_addrs(rule, &match);
1707e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1708e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv4_layout.ipv4),
17098f256622SPablo Neira Ayuso 		       &match.mask->src, sizeof(match.mask->src));
1710e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1711e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv4_layout.ipv4),
17128f256622SPablo Neira Ayuso 		       &match.key->src, sizeof(match.key->src));
1713e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1714e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
17158f256622SPablo Neira Ayuso 		       &match.mask->dst, sizeof(match.mask->dst));
1716e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1717e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
17188f256622SPablo Neira Ayuso 		       &match.key->dst, sizeof(match.key->dst));
1719de0af0bfSRoi Dayan 
17208f256622SPablo Neira Ayuso 		if (match.mask->src || match.mask->dst)
1721d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L3;
1722e3a2b7edSAmir Vadai 	}
1723e3a2b7edSAmir Vadai 
1724e3a2b7edSAmir Vadai 	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
17258f256622SPablo Neira Ayuso 		struct flow_match_ipv6_addrs match;
1726e3a2b7edSAmir Vadai 
17278f256622SPablo Neira Ayuso 		flow_rule_match_ipv6_addrs(rule, &match);
1728e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1729e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
17308f256622SPablo Neira Ayuso 		       &match.mask->src, sizeof(match.mask->src));
1731e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1732e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
17338f256622SPablo Neira Ayuso 		       &match.key->src, sizeof(match.key->src));
1734e3a2b7edSAmir Vadai 
1735e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
1736e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
17378f256622SPablo Neira Ayuso 		       &match.mask->dst, sizeof(match.mask->dst));
1738e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
1739e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
17408f256622SPablo Neira Ayuso 		       &match.key->dst, sizeof(match.key->dst));
1741de0af0bfSRoi Dayan 
17428f256622SPablo Neira Ayuso 		if (ipv6_addr_type(&match.mask->src) != IPV6_ADDR_ANY ||
17438f256622SPablo Neira Ayuso 		    ipv6_addr_type(&match.mask->dst) != IPV6_ADDR_ANY)
1744d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L3;
1745e3a2b7edSAmir Vadai 	}
1746e3a2b7edSAmir Vadai 
17478f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
17488f256622SPablo Neira Ayuso 		struct flow_match_ip match;
17491f97a526SOr Gerlitz 
17508f256622SPablo Neira Ayuso 		flow_rule_match_ip(rule, &match);
17518f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_ecn,
17528f256622SPablo Neira Ayuso 			 match.mask->tos & 0x3);
17538f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_ecn,
17548f256622SPablo Neira Ayuso 			 match.key->tos & 0x3);
17551f97a526SOr Gerlitz 
17568f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_dscp,
17578f256622SPablo Neira Ayuso 			 match.mask->tos >> 2);
17588f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_dscp,
17598f256622SPablo Neira Ayuso 			 match.key->tos  >> 2);
17601f97a526SOr Gerlitz 
17618f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ttl_hoplimit,
17628f256622SPablo Neira Ayuso 			 match.mask->ttl);
17638f256622SPablo Neira Ayuso 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ttl_hoplimit,
17648f256622SPablo Neira Ayuso 			 match.key->ttl);
17651f97a526SOr Gerlitz 
17668f256622SPablo Neira Ayuso 		if (match.mask->ttl &&
1767a8ade55fSOr Gerlitz 		    !MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev,
1768e98bedf5SEli Britstein 						ft_field_support.outer_ipv4_ttl)) {
1769e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1770e98bedf5SEli Britstein 					   "Matching on TTL is not supported");
17711f97a526SOr Gerlitz 			return -EOPNOTSUPP;
1772e98bedf5SEli Britstein 		}
1773a8ade55fSOr Gerlitz 
17748f256622SPablo Neira Ayuso 		if (match.mask->tos || match.mask->ttl)
1775d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L3;
17761f97a526SOr Gerlitz 	}
17771f97a526SOr Gerlitz 
177854782900SOr Gerlitz 	/* ***  L3 attributes parsing up to here *** */
177954782900SOr Gerlitz 
17808f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
17818f256622SPablo Neira Ayuso 		struct flow_match_ports match;
17828f256622SPablo Neira Ayuso 
17838f256622SPablo Neira Ayuso 		flow_rule_match_ports(rule, &match);
1784e3a2b7edSAmir Vadai 		switch (ip_proto) {
1785e3a2b7edSAmir Vadai 		case IPPROTO_TCP:
1786e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
17878f256622SPablo Neira Ayuso 				 tcp_sport, ntohs(match.mask->src));
1788e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
17898f256622SPablo Neira Ayuso 				 tcp_sport, ntohs(match.key->src));
1790e3a2b7edSAmir Vadai 
1791e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
17928f256622SPablo Neira Ayuso 				 tcp_dport, ntohs(match.mask->dst));
1793e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
17948f256622SPablo Neira Ayuso 				 tcp_dport, ntohs(match.key->dst));
1795e3a2b7edSAmir Vadai 			break;
1796e3a2b7edSAmir Vadai 
1797e3a2b7edSAmir Vadai 		case IPPROTO_UDP:
1798e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
17998f256622SPablo Neira Ayuso 				 udp_sport, ntohs(match.mask->src));
1800e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
18018f256622SPablo Neira Ayuso 				 udp_sport, ntohs(match.key->src));
1802e3a2b7edSAmir Vadai 
1803e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
18048f256622SPablo Neira Ayuso 				 udp_dport, ntohs(match.mask->dst));
1805e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
18068f256622SPablo Neira Ayuso 				 udp_dport, ntohs(match.key->dst));
1807e3a2b7edSAmir Vadai 			break;
1808e3a2b7edSAmir Vadai 		default:
1809e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1810e98bedf5SEli Britstein 					   "Only UDP and TCP transports are supported for L4 matching");
1811e3a2b7edSAmir Vadai 			netdev_err(priv->netdev,
1812e3a2b7edSAmir Vadai 				   "Only UDP and TCP transport are supported\n");
1813e3a2b7edSAmir Vadai 			return -EINVAL;
1814e3a2b7edSAmir Vadai 		}
1815de0af0bfSRoi Dayan 
18168f256622SPablo Neira Ayuso 		if (match.mask->src || match.mask->dst)
1817d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L4;
1818e3a2b7edSAmir Vadai 	}
1819e3a2b7edSAmir Vadai 
18208f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) {
18218f256622SPablo Neira Ayuso 		struct flow_match_tcp match;
1822e77834ecSOr Gerlitz 
18238f256622SPablo Neira Ayuso 		flow_rule_match_tcp(rule, &match);
1824e77834ecSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, tcp_flags,
18258f256622SPablo Neira Ayuso 			 ntohs(match.mask->flags));
1826e77834ecSOr Gerlitz 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, tcp_flags,
18278f256622SPablo Neira Ayuso 			 ntohs(match.key->flags));
1828e77834ecSOr Gerlitz 
18298f256622SPablo Neira Ayuso 		if (match.mask->flags)
1830d708f902SOr Gerlitz 			*match_level = MLX5_MATCH_L4;
1831e77834ecSOr Gerlitz 	}
1832e77834ecSOr Gerlitz 
1833e3a2b7edSAmir Vadai 	return 0;
1834e3a2b7edSAmir Vadai }
1835e3a2b7edSAmir Vadai 
1836de0af0bfSRoi Dayan static int parse_cls_flower(struct mlx5e_priv *priv,
183765ba8fb7SOr Gerlitz 			    struct mlx5e_tc_flow *flow,
1838de0af0bfSRoi Dayan 			    struct mlx5_flow_spec *spec,
1839f9e30088SPablo Neira Ayuso 			    struct flow_cls_offload *f,
184054c177caSOz Shlomo 			    struct net_device *filter_dev)
1841de0af0bfSRoi Dayan {
1842e98bedf5SEli Britstein 	struct netlink_ext_ack *extack = f->common.extack;
1843de0af0bfSRoi Dayan 	struct mlx5_core_dev *dev = priv->mdev;
1844de0af0bfSRoi Dayan 	struct mlx5_eswitch *esw = dev->priv.eswitch;
18451d447a39SSaeed Mahameed 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
18466363651dSOr Gerlitz 	u8 match_level, tunnel_match_level = MLX5_MATCH_NONE;
18471d447a39SSaeed Mahameed 	struct mlx5_eswitch_rep *rep;
1848de0af0bfSRoi Dayan 	int err;
1849de0af0bfSRoi Dayan 
18506363651dSOr Gerlitz 	err = __parse_cls_flower(priv, spec, f, filter_dev, &match_level, &tunnel_match_level);
1851de0af0bfSRoi Dayan 
18521d447a39SSaeed Mahameed 	if (!err && (flow->flags & MLX5E_TC_FLOW_ESWITCH)) {
18531d447a39SSaeed Mahameed 		rep = rpriv->rep;
1854b05af6aaSBodong Wang 		if (rep->vport != MLX5_VPORT_UPLINK &&
18551d447a39SSaeed Mahameed 		    (esw->offloads.inline_mode != MLX5_INLINE_MODE_NONE &&
1856d708f902SOr Gerlitz 		    esw->offloads.inline_mode < match_level)) {
1857e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
1858e98bedf5SEli Britstein 					   "Flow is not offloaded due to min inline setting");
1859de0af0bfSRoi Dayan 			netdev_warn(priv->netdev,
1860de0af0bfSRoi Dayan 				    "Flow is not offloaded due to min inline setting, required %d actual %d\n",
1861d708f902SOr Gerlitz 				    match_level, esw->offloads.inline_mode);
1862de0af0bfSRoi Dayan 			return -EOPNOTSUPP;
1863de0af0bfSRoi Dayan 		}
1864de0af0bfSRoi Dayan 	}
1865de0af0bfSRoi Dayan 
18666363651dSOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH) {
186738aa51c1SOr Gerlitz 		flow->esw_attr->match_level = match_level;
18686363651dSOr Gerlitz 		flow->esw_attr->tunnel_match_level = tunnel_match_level;
18696363651dSOr Gerlitz 	} else {
187038aa51c1SOr Gerlitz 		flow->nic_attr->match_level = match_level;
18716363651dSOr Gerlitz 	}
187238aa51c1SOr Gerlitz 
1873de0af0bfSRoi Dayan 	return err;
1874de0af0bfSRoi Dayan }
1875de0af0bfSRoi Dayan 
1876d79b6df6SOr Gerlitz struct pedit_headers {
1877d79b6df6SOr Gerlitz 	struct ethhdr  eth;
18780eb69bb9SEli Britstein 	struct vlan_hdr vlan;
1879d79b6df6SOr Gerlitz 	struct iphdr   ip4;
1880d79b6df6SOr Gerlitz 	struct ipv6hdr ip6;
1881d79b6df6SOr Gerlitz 	struct tcphdr  tcp;
1882d79b6df6SOr Gerlitz 	struct udphdr  udp;
1883d79b6df6SOr Gerlitz };
1884d79b6df6SOr Gerlitz 
1885c500c86bSPablo Neira Ayuso struct pedit_headers_action {
1886c500c86bSPablo Neira Ayuso 	struct pedit_headers	vals;
1887c500c86bSPablo Neira Ayuso 	struct pedit_headers	masks;
1888c500c86bSPablo Neira Ayuso 	u32			pedits;
1889c500c86bSPablo Neira Ayuso };
1890c500c86bSPablo Neira Ayuso 
1891d79b6df6SOr Gerlitz static int pedit_header_offsets[] = {
189273867881SPablo Neira Ayuso 	[FLOW_ACT_MANGLE_HDR_TYPE_ETH] = offsetof(struct pedit_headers, eth),
189373867881SPablo Neira Ayuso 	[FLOW_ACT_MANGLE_HDR_TYPE_IP4] = offsetof(struct pedit_headers, ip4),
189473867881SPablo Neira Ayuso 	[FLOW_ACT_MANGLE_HDR_TYPE_IP6] = offsetof(struct pedit_headers, ip6),
189573867881SPablo Neira Ayuso 	[FLOW_ACT_MANGLE_HDR_TYPE_TCP] = offsetof(struct pedit_headers, tcp),
189673867881SPablo Neira Ayuso 	[FLOW_ACT_MANGLE_HDR_TYPE_UDP] = offsetof(struct pedit_headers, udp),
1897d79b6df6SOr Gerlitz };
1898d79b6df6SOr Gerlitz 
1899d79b6df6SOr Gerlitz #define pedit_header(_ph, _htype) ((void *)(_ph) + pedit_header_offsets[_htype])
1900d79b6df6SOr Gerlitz 
1901d79b6df6SOr Gerlitz static int set_pedit_val(u8 hdr_type, u32 mask, u32 val, u32 offset,
1902c500c86bSPablo Neira Ayuso 			 struct pedit_headers_action *hdrs)
1903d79b6df6SOr Gerlitz {
1904d79b6df6SOr Gerlitz 	u32 *curr_pmask, *curr_pval;
1905d79b6df6SOr Gerlitz 
1906c500c86bSPablo Neira Ayuso 	curr_pmask = (u32 *)(pedit_header(&hdrs->masks, hdr_type) + offset);
1907c500c86bSPablo Neira Ayuso 	curr_pval  = (u32 *)(pedit_header(&hdrs->vals, hdr_type) + offset);
1908d79b6df6SOr Gerlitz 
1909d79b6df6SOr Gerlitz 	if (*curr_pmask & mask)  /* disallow acting twice on the same location */
1910d79b6df6SOr Gerlitz 		goto out_err;
1911d79b6df6SOr Gerlitz 
1912d79b6df6SOr Gerlitz 	*curr_pmask |= mask;
1913d79b6df6SOr Gerlitz 	*curr_pval  |= (val & mask);
1914d79b6df6SOr Gerlitz 
1915d79b6df6SOr Gerlitz 	return 0;
1916d79b6df6SOr Gerlitz 
1917d79b6df6SOr Gerlitz out_err:
1918d79b6df6SOr Gerlitz 	return -EOPNOTSUPP;
1919d79b6df6SOr Gerlitz }
1920d79b6df6SOr Gerlitz 
1921d79b6df6SOr Gerlitz struct mlx5_fields {
1922d79b6df6SOr Gerlitz 	u8  field;
1923d79b6df6SOr Gerlitz 	u8  size;
1924d79b6df6SOr Gerlitz 	u32 offset;
192527c11b6bSEli Britstein 	u32 match_offset;
1926d79b6df6SOr Gerlitz };
1927d79b6df6SOr Gerlitz 
192827c11b6bSEli Britstein #define OFFLOAD(fw_field, size, field, off, match_field) \
192927c11b6bSEli Britstein 		{MLX5_ACTION_IN_FIELD_OUT_ ## fw_field, size, \
193027c11b6bSEli Britstein 		 offsetof(struct pedit_headers, field) + (off), \
193127c11b6bSEli Britstein 		 MLX5_BYTE_OFF(fte_match_set_lyr_2_4, match_field)}
193227c11b6bSEli Britstein 
19332ef86872SEli Britstein /* masked values are the same and there are no rewrites that do not have a
19342ef86872SEli Britstein  * match.
19352ef86872SEli Britstein  */
19362ef86872SEli Britstein #define SAME_VAL_MASK(type, valp, maskp, matchvalp, matchmaskp) ({ \
19372ef86872SEli Britstein 	type matchmaskx = *(type *)(matchmaskp); \
19382ef86872SEli Britstein 	type matchvalx = *(type *)(matchvalp); \
19392ef86872SEli Britstein 	type maskx = *(type *)(maskp); \
19402ef86872SEli Britstein 	type valx = *(type *)(valp); \
19412ef86872SEli Britstein 	\
19422ef86872SEli Britstein 	(valx & maskx) == (matchvalx & matchmaskx) && !(maskx & (maskx ^ \
19432ef86872SEli Britstein 								 matchmaskx)); \
19442ef86872SEli Britstein })
19452ef86872SEli Britstein 
194627c11b6bSEli Britstein static bool cmp_val_mask(void *valp, void *maskp, void *matchvalp,
194727c11b6bSEli Britstein 			 void *matchmaskp, int size)
194827c11b6bSEli Britstein {
194927c11b6bSEli Britstein 	bool same = false;
195027c11b6bSEli Britstein 
195127c11b6bSEli Britstein 	switch (size) {
195227c11b6bSEli Britstein 	case sizeof(u8):
19532ef86872SEli Britstein 		same = SAME_VAL_MASK(u8, valp, maskp, matchvalp, matchmaskp);
195427c11b6bSEli Britstein 		break;
195527c11b6bSEli Britstein 	case sizeof(u16):
19562ef86872SEli Britstein 		same = SAME_VAL_MASK(u16, valp, maskp, matchvalp, matchmaskp);
195727c11b6bSEli Britstein 		break;
195827c11b6bSEli Britstein 	case sizeof(u32):
19592ef86872SEli Britstein 		same = SAME_VAL_MASK(u32, valp, maskp, matchvalp, matchmaskp);
196027c11b6bSEli Britstein 		break;
196127c11b6bSEli Britstein 	}
196227c11b6bSEli Britstein 
196327c11b6bSEli Britstein 	return same;
196427c11b6bSEli Britstein }
1965a8e4f0c4SOr Gerlitz 
1966d79b6df6SOr Gerlitz static struct mlx5_fields fields[] = {
196727c11b6bSEli Britstein 	OFFLOAD(DMAC_47_16, 4, eth.h_dest[0], 0, dmac_47_16),
196827c11b6bSEli Britstein 	OFFLOAD(DMAC_15_0,  2, eth.h_dest[4], 0, dmac_15_0),
196927c11b6bSEli Britstein 	OFFLOAD(SMAC_47_16, 4, eth.h_source[0], 0, smac_47_16),
197027c11b6bSEli Britstein 	OFFLOAD(SMAC_15_0,  2, eth.h_source[4], 0, smac_15_0),
197127c11b6bSEli Britstein 	OFFLOAD(ETHERTYPE,  2, eth.h_proto, 0, ethertype),
197227c11b6bSEli Britstein 	OFFLOAD(FIRST_VID,  2, vlan.h_vlan_TCI, 0, first_vid),
1973d79b6df6SOr Gerlitz 
197427c11b6bSEli Britstein 	OFFLOAD(IP_TTL, 1, ip4.ttl,   0, ttl_hoplimit),
197527c11b6bSEli Britstein 	OFFLOAD(SIPV4,  4, ip4.saddr, 0, src_ipv4_src_ipv6.ipv4_layout.ipv4),
197627c11b6bSEli Britstein 	OFFLOAD(DIPV4,  4, ip4.daddr, 0, dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
1977d79b6df6SOr Gerlitz 
197827c11b6bSEli Britstein 	OFFLOAD(SIPV6_127_96, 4, ip6.saddr.s6_addr32[0], 0,
197927c11b6bSEli Britstein 		src_ipv4_src_ipv6.ipv6_layout.ipv6[0]),
198027c11b6bSEli Britstein 	OFFLOAD(SIPV6_95_64,  4, ip6.saddr.s6_addr32[1], 0,
198127c11b6bSEli Britstein 		src_ipv4_src_ipv6.ipv6_layout.ipv6[4]),
198227c11b6bSEli Britstein 	OFFLOAD(SIPV6_63_32,  4, ip6.saddr.s6_addr32[2], 0,
198327c11b6bSEli Britstein 		src_ipv4_src_ipv6.ipv6_layout.ipv6[8]),
198427c11b6bSEli Britstein 	OFFLOAD(SIPV6_31_0,   4, ip6.saddr.s6_addr32[3], 0,
198527c11b6bSEli Britstein 		src_ipv4_src_ipv6.ipv6_layout.ipv6[12]),
198627c11b6bSEli Britstein 	OFFLOAD(DIPV6_127_96, 4, ip6.daddr.s6_addr32[0], 0,
198727c11b6bSEli Britstein 		dst_ipv4_dst_ipv6.ipv6_layout.ipv6[0]),
198827c11b6bSEli Britstein 	OFFLOAD(DIPV6_95_64,  4, ip6.daddr.s6_addr32[1], 0,
198927c11b6bSEli Britstein 		dst_ipv4_dst_ipv6.ipv6_layout.ipv6[4]),
199027c11b6bSEli Britstein 	OFFLOAD(DIPV6_63_32,  4, ip6.daddr.s6_addr32[2], 0,
199127c11b6bSEli Britstein 		dst_ipv4_dst_ipv6.ipv6_layout.ipv6[8]),
199227c11b6bSEli Britstein 	OFFLOAD(DIPV6_31_0,   4, ip6.daddr.s6_addr32[3], 0,
199327c11b6bSEli Britstein 		dst_ipv4_dst_ipv6.ipv6_layout.ipv6[12]),
199427c11b6bSEli Britstein 	OFFLOAD(IPV6_HOPLIMIT, 1, ip6.hop_limit, 0, ttl_hoplimit),
1995d79b6df6SOr Gerlitz 
199627c11b6bSEli Britstein 	OFFLOAD(TCP_SPORT, 2, tcp.source,  0, tcp_sport),
199727c11b6bSEli Britstein 	OFFLOAD(TCP_DPORT, 2, tcp.dest,    0, tcp_dport),
199827c11b6bSEli Britstein 	OFFLOAD(TCP_FLAGS, 1, tcp.ack_seq, 5, tcp_flags),
1999d79b6df6SOr Gerlitz 
200027c11b6bSEli Britstein 	OFFLOAD(UDP_SPORT, 2, udp.source, 0, udp_sport),
200127c11b6bSEli Britstein 	OFFLOAD(UDP_DPORT, 2, udp.dest,   0, udp_dport),
2002d79b6df6SOr Gerlitz };
2003d79b6df6SOr Gerlitz 
2004218d05ceSTonghao Zhang /* On input attr->max_mod_hdr_actions tells how many HW actions can be parsed at
2005218d05ceSTonghao Zhang  * max from the SW pedit action. On success, attr->num_mod_hdr_actions
2006218d05ceSTonghao Zhang  * says how many HW actions were actually parsed.
2007d79b6df6SOr Gerlitz  */
2008c500c86bSPablo Neira Ayuso static int offload_pedit_fields(struct pedit_headers_action *hdrs,
2009e98bedf5SEli Britstein 				struct mlx5e_tc_flow_parse_attr *parse_attr,
201027c11b6bSEli Britstein 				u32 *action_flags,
2011e98bedf5SEli Britstein 				struct netlink_ext_ack *extack)
2012d79b6df6SOr Gerlitz {
2013d79b6df6SOr Gerlitz 	struct pedit_headers *set_masks, *add_masks, *set_vals, *add_vals;
201427c11b6bSEli Britstein 	void *headers_c = get_match_headers_criteria(*action_flags,
201527c11b6bSEli Britstein 						     &parse_attr->spec);
201627c11b6bSEli Britstein 	void *headers_v = get_match_headers_value(*action_flags,
201727c11b6bSEli Britstein 						  &parse_attr->spec);
20182b64bebaSOr Gerlitz 	int i, action_size, nactions, max_actions, first, last, next_z;
2019d79b6df6SOr Gerlitz 	void *s_masks_p, *a_masks_p, *vals_p;
2020d79b6df6SOr Gerlitz 	struct mlx5_fields *f;
2021d79b6df6SOr Gerlitz 	u8 cmd, field_bsize;
2022e3ca4e05SOr Gerlitz 	u32 s_mask, a_mask;
2023d79b6df6SOr Gerlitz 	unsigned long mask;
20242b64bebaSOr Gerlitz 	__be32 mask_be32;
20252b64bebaSOr Gerlitz 	__be16 mask_be16;
2026d79b6df6SOr Gerlitz 	void *action;
2027d79b6df6SOr Gerlitz 
202873867881SPablo Neira Ayuso 	set_masks = &hdrs[0].masks;
202973867881SPablo Neira Ayuso 	add_masks = &hdrs[1].masks;
203073867881SPablo Neira Ayuso 	set_vals = &hdrs[0].vals;
203173867881SPablo Neira Ayuso 	add_vals = &hdrs[1].vals;
2032d79b6df6SOr Gerlitz 
2033d79b6df6SOr Gerlitz 	action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto);
2034218d05ceSTonghao Zhang 	action = parse_attr->mod_hdr_actions +
2035218d05ceSTonghao Zhang 		 parse_attr->num_mod_hdr_actions * action_size;
2036218d05ceSTonghao Zhang 
2037218d05ceSTonghao Zhang 	max_actions = parse_attr->max_mod_hdr_actions;
2038218d05ceSTonghao Zhang 	nactions = parse_attr->num_mod_hdr_actions;
2039d79b6df6SOr Gerlitz 
2040d79b6df6SOr Gerlitz 	for (i = 0; i < ARRAY_SIZE(fields); i++) {
204127c11b6bSEli Britstein 		bool skip;
204227c11b6bSEli Britstein 
2043d79b6df6SOr Gerlitz 		f = &fields[i];
2044d79b6df6SOr Gerlitz 		/* avoid seeing bits set from previous iterations */
2045e3ca4e05SOr Gerlitz 		s_mask = 0;
2046e3ca4e05SOr Gerlitz 		a_mask = 0;
2047d79b6df6SOr Gerlitz 
2048d79b6df6SOr Gerlitz 		s_masks_p = (void *)set_masks + f->offset;
2049d79b6df6SOr Gerlitz 		a_masks_p = (void *)add_masks + f->offset;
2050d79b6df6SOr Gerlitz 
2051d79b6df6SOr Gerlitz 		memcpy(&s_mask, s_masks_p, f->size);
2052d79b6df6SOr Gerlitz 		memcpy(&a_mask, a_masks_p, f->size);
2053d79b6df6SOr Gerlitz 
2054d79b6df6SOr Gerlitz 		if (!s_mask && !a_mask) /* nothing to offload here */
2055d79b6df6SOr Gerlitz 			continue;
2056d79b6df6SOr Gerlitz 
2057d79b6df6SOr Gerlitz 		if (s_mask && a_mask) {
2058e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
2059e98bedf5SEli Britstein 					   "can't set and add to the same HW field");
2060d79b6df6SOr Gerlitz 			printk(KERN_WARNING "mlx5: can't set and add to the same HW field (%x)\n", f->field);
2061d79b6df6SOr Gerlitz 			return -EOPNOTSUPP;
2062d79b6df6SOr Gerlitz 		}
2063d79b6df6SOr Gerlitz 
2064d79b6df6SOr Gerlitz 		if (nactions == max_actions) {
2065e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
2066e98bedf5SEli Britstein 					   "too many pedit actions, can't offload");
2067d79b6df6SOr Gerlitz 			printk(KERN_WARNING "mlx5: parsed %d pedit actions, can't do more\n", nactions);
2068d79b6df6SOr Gerlitz 			return -EOPNOTSUPP;
2069d79b6df6SOr Gerlitz 		}
2070d79b6df6SOr Gerlitz 
207127c11b6bSEli Britstein 		skip = false;
2072d79b6df6SOr Gerlitz 		if (s_mask) {
207327c11b6bSEli Britstein 			void *match_mask = headers_c + f->match_offset;
207427c11b6bSEli Britstein 			void *match_val = headers_v + f->match_offset;
207527c11b6bSEli Britstein 
2076d79b6df6SOr Gerlitz 			cmd  = MLX5_ACTION_TYPE_SET;
2077d79b6df6SOr Gerlitz 			mask = s_mask;
2078d79b6df6SOr Gerlitz 			vals_p = (void *)set_vals + f->offset;
207927c11b6bSEli Britstein 			/* don't rewrite if we have a match on the same value */
208027c11b6bSEli Britstein 			if (cmp_val_mask(vals_p, s_masks_p, match_val,
208127c11b6bSEli Britstein 					 match_mask, f->size))
208227c11b6bSEli Britstein 				skip = true;
2083d79b6df6SOr Gerlitz 			/* clear to denote we consumed this field */
2084d79b6df6SOr Gerlitz 			memset(s_masks_p, 0, f->size);
2085d79b6df6SOr Gerlitz 		} else {
208627c11b6bSEli Britstein 			u32 zero = 0;
208727c11b6bSEli Britstein 
2088d79b6df6SOr Gerlitz 			cmd  = MLX5_ACTION_TYPE_ADD;
2089d79b6df6SOr Gerlitz 			mask = a_mask;
2090d79b6df6SOr Gerlitz 			vals_p = (void *)add_vals + f->offset;
209127c11b6bSEli Britstein 			/* add 0 is no change */
209227c11b6bSEli Britstein 			if (!memcmp(vals_p, &zero, f->size))
209327c11b6bSEli Britstein 				skip = true;
2094d79b6df6SOr Gerlitz 			/* clear to denote we consumed this field */
2095d79b6df6SOr Gerlitz 			memset(a_masks_p, 0, f->size);
2096d79b6df6SOr Gerlitz 		}
209727c11b6bSEli Britstein 		if (skip)
209827c11b6bSEli Britstein 			continue;
2099d79b6df6SOr Gerlitz 
2100d79b6df6SOr Gerlitz 		field_bsize = f->size * BITS_PER_BYTE;
2101e3ca4e05SOr Gerlitz 
21022b64bebaSOr Gerlitz 		if (field_bsize == 32) {
21032b64bebaSOr Gerlitz 			mask_be32 = *(__be32 *)&mask;
21042b64bebaSOr Gerlitz 			mask = (__force unsigned long)cpu_to_le32(be32_to_cpu(mask_be32));
21052b64bebaSOr Gerlitz 		} else if (field_bsize == 16) {
21062b64bebaSOr Gerlitz 			mask_be16 = *(__be16 *)&mask;
21072b64bebaSOr Gerlitz 			mask = (__force unsigned long)cpu_to_le16(be16_to_cpu(mask_be16));
21082b64bebaSOr Gerlitz 		}
21092b64bebaSOr Gerlitz 
2110d79b6df6SOr Gerlitz 		first = find_first_bit(&mask, field_bsize);
21112b64bebaSOr Gerlitz 		next_z = find_next_zero_bit(&mask, field_bsize, first);
2112d79b6df6SOr Gerlitz 		last  = find_last_bit(&mask, field_bsize);
21132b64bebaSOr Gerlitz 		if (first < next_z && next_z < last) {
2114e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
2115e98bedf5SEli Britstein 					   "rewrite of few sub-fields isn't supported");
21162b64bebaSOr Gerlitz 			printk(KERN_WARNING "mlx5: rewrite of few sub-fields (mask %lx) isn't offloaded\n",
2117d79b6df6SOr Gerlitz 			       mask);
2118d79b6df6SOr Gerlitz 			return -EOPNOTSUPP;
2119d79b6df6SOr Gerlitz 		}
2120d79b6df6SOr Gerlitz 
2121d79b6df6SOr Gerlitz 		MLX5_SET(set_action_in, action, action_type, cmd);
2122d79b6df6SOr Gerlitz 		MLX5_SET(set_action_in, action, field, f->field);
2123d79b6df6SOr Gerlitz 
2124d79b6df6SOr Gerlitz 		if (cmd == MLX5_ACTION_TYPE_SET) {
21252b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, offset, first);
2126d79b6df6SOr Gerlitz 			/* length is num of bits to be written, zero means length of 32 */
21272b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, length, (last - first + 1));
2128d79b6df6SOr Gerlitz 		}
2129d79b6df6SOr Gerlitz 
2130d79b6df6SOr Gerlitz 		if (field_bsize == 32)
21312b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, data, ntohl(*(__be32 *)vals_p) >> first);
2132d79b6df6SOr Gerlitz 		else if (field_bsize == 16)
21332b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, data, ntohs(*(__be16 *)vals_p) >> first);
2134d79b6df6SOr Gerlitz 		else if (field_bsize == 8)
21352b64bebaSOr Gerlitz 			MLX5_SET(set_action_in, action, data, *(u8 *)vals_p >> first);
2136d79b6df6SOr Gerlitz 
2137d79b6df6SOr Gerlitz 		action += action_size;
2138d79b6df6SOr Gerlitz 		nactions++;
2139d79b6df6SOr Gerlitz 	}
2140d79b6df6SOr Gerlitz 
2141d79b6df6SOr Gerlitz 	parse_attr->num_mod_hdr_actions = nactions;
2142d79b6df6SOr Gerlitz 	return 0;
2143d79b6df6SOr Gerlitz }
2144d79b6df6SOr Gerlitz 
21452cc1cb1dSTonghao Zhang static int mlx5e_flow_namespace_max_modify_action(struct mlx5_core_dev *mdev,
21462cc1cb1dSTonghao Zhang 						  int namespace)
21472cc1cb1dSTonghao Zhang {
21482cc1cb1dSTonghao Zhang 	if (namespace == MLX5_FLOW_NAMESPACE_FDB) /* FDB offloading */
21492cc1cb1dSTonghao Zhang 		return MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, max_modify_header_actions);
21502cc1cb1dSTonghao Zhang 	else /* namespace is MLX5_FLOW_NAMESPACE_KERNEL - NIC offloading */
21512cc1cb1dSTonghao Zhang 		return MLX5_CAP_FLOWTABLE_NIC_RX(mdev, max_modify_header_actions);
21522cc1cb1dSTonghao Zhang }
21532cc1cb1dSTonghao Zhang 
2154d79b6df6SOr Gerlitz static int alloc_mod_hdr_actions(struct mlx5e_priv *priv,
2155c500c86bSPablo Neira Ayuso 				 struct pedit_headers_action *hdrs,
2156c500c86bSPablo Neira Ayuso 				 int namespace,
2157d79b6df6SOr Gerlitz 				 struct mlx5e_tc_flow_parse_attr *parse_attr)
2158d79b6df6SOr Gerlitz {
2159d79b6df6SOr Gerlitz 	int nkeys, action_size, max_actions;
2160d79b6df6SOr Gerlitz 
2161c500c86bSPablo Neira Ayuso 	nkeys = hdrs[TCA_PEDIT_KEY_EX_CMD_SET].pedits +
2162c500c86bSPablo Neira Ayuso 		hdrs[TCA_PEDIT_KEY_EX_CMD_ADD].pedits;
2163d79b6df6SOr Gerlitz 	action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto);
2164d79b6df6SOr Gerlitz 
21652cc1cb1dSTonghao Zhang 	max_actions = mlx5e_flow_namespace_max_modify_action(priv->mdev, namespace);
2166d79b6df6SOr Gerlitz 	/* can get up to crazingly 16 HW actions in 32 bits pedit SW key */
2167d79b6df6SOr Gerlitz 	max_actions = min(max_actions, nkeys * 16);
2168d79b6df6SOr Gerlitz 
2169d79b6df6SOr Gerlitz 	parse_attr->mod_hdr_actions = kcalloc(max_actions, action_size, GFP_KERNEL);
2170d79b6df6SOr Gerlitz 	if (!parse_attr->mod_hdr_actions)
2171d79b6df6SOr Gerlitz 		return -ENOMEM;
2172d79b6df6SOr Gerlitz 
2173218d05ceSTonghao Zhang 	parse_attr->max_mod_hdr_actions = max_actions;
2174d79b6df6SOr Gerlitz 	return 0;
2175d79b6df6SOr Gerlitz }
2176d79b6df6SOr Gerlitz 
2177d79b6df6SOr Gerlitz static const struct pedit_headers zero_masks = {};
2178d79b6df6SOr Gerlitz 
2179d79b6df6SOr Gerlitz static int parse_tc_pedit_action(struct mlx5e_priv *priv,
218073867881SPablo Neira Ayuso 				 const struct flow_action_entry *act, int namespace,
2181e98bedf5SEli Britstein 				 struct mlx5e_tc_flow_parse_attr *parse_attr,
2182c500c86bSPablo Neira Ayuso 				 struct pedit_headers_action *hdrs,
2183e98bedf5SEli Britstein 				 struct netlink_ext_ack *extack)
2184d79b6df6SOr Gerlitz {
218573867881SPablo Neira Ayuso 	u8 cmd = (act->id == FLOW_ACTION_MANGLE) ? 0 : 1;
218673867881SPablo Neira Ayuso 	int err = -EOPNOTSUPP;
2187d79b6df6SOr Gerlitz 	u32 mask, val, offset;
218873867881SPablo Neira Ayuso 	u8 htype;
2189d79b6df6SOr Gerlitz 
219073867881SPablo Neira Ayuso 	htype = act->mangle.htype;
2191d79b6df6SOr Gerlitz 	err = -EOPNOTSUPP; /* can't be all optimistic */
2192d79b6df6SOr Gerlitz 
219373867881SPablo Neira Ayuso 	if (htype == FLOW_ACT_MANGLE_UNSPEC) {
219473867881SPablo Neira Ayuso 		NL_SET_ERR_MSG_MOD(extack, "legacy pedit isn't offloaded");
2195d79b6df6SOr Gerlitz 		goto out_err;
2196d79b6df6SOr Gerlitz 	}
2197d79b6df6SOr Gerlitz 
21982cc1cb1dSTonghao Zhang 	if (!mlx5e_flow_namespace_max_modify_action(priv->mdev, namespace)) {
21992cc1cb1dSTonghao Zhang 		NL_SET_ERR_MSG_MOD(extack,
22002cc1cb1dSTonghao Zhang 				   "The pedit offload action is not supported");
22012cc1cb1dSTonghao Zhang 		goto out_err;
22022cc1cb1dSTonghao Zhang 	}
22032cc1cb1dSTonghao Zhang 
220473867881SPablo Neira Ayuso 	mask = act->mangle.mask;
220573867881SPablo Neira Ayuso 	val = act->mangle.val;
220673867881SPablo Neira Ayuso 	offset = act->mangle.offset;
2207d79b6df6SOr Gerlitz 
2208c500c86bSPablo Neira Ayuso 	err = set_pedit_val(htype, ~mask, val, offset, &hdrs[cmd]);
2209d79b6df6SOr Gerlitz 	if (err)
2210d79b6df6SOr Gerlitz 		goto out_err;
2211c500c86bSPablo Neira Ayuso 
2212c500c86bSPablo Neira Ayuso 	hdrs[cmd].pedits++;
2213d79b6df6SOr Gerlitz 
2214c500c86bSPablo Neira Ayuso 	return 0;
2215c500c86bSPablo Neira Ayuso out_err:
2216c500c86bSPablo Neira Ayuso 	return err;
2217c500c86bSPablo Neira Ayuso }
2218c500c86bSPablo Neira Ayuso 
2219c500c86bSPablo Neira Ayuso static int alloc_tc_pedit_action(struct mlx5e_priv *priv, int namespace,
2220c500c86bSPablo Neira Ayuso 				 struct mlx5e_tc_flow_parse_attr *parse_attr,
2221c500c86bSPablo Neira Ayuso 				 struct pedit_headers_action *hdrs,
222227c11b6bSEli Britstein 				 u32 *action_flags,
2223c500c86bSPablo Neira Ayuso 				 struct netlink_ext_ack *extack)
2224c500c86bSPablo Neira Ayuso {
2225c500c86bSPablo Neira Ayuso 	struct pedit_headers *cmd_masks;
2226c500c86bSPablo Neira Ayuso 	int err;
2227c500c86bSPablo Neira Ayuso 	u8 cmd;
2228c500c86bSPablo Neira Ayuso 
2229218d05ceSTonghao Zhang 	if (!parse_attr->mod_hdr_actions) {
2230c500c86bSPablo Neira Ayuso 		err = alloc_mod_hdr_actions(priv, hdrs, namespace, parse_attr);
2231d79b6df6SOr Gerlitz 		if (err)
2232d79b6df6SOr Gerlitz 			goto out_err;
2233218d05ceSTonghao Zhang 	}
2234d79b6df6SOr Gerlitz 
223527c11b6bSEli Britstein 	err = offload_pedit_fields(hdrs, parse_attr, action_flags, extack);
2236d79b6df6SOr Gerlitz 	if (err < 0)
2237d79b6df6SOr Gerlitz 		goto out_dealloc_parsed_actions;
2238d79b6df6SOr Gerlitz 
2239d79b6df6SOr Gerlitz 	for (cmd = 0; cmd < __PEDIT_CMD_MAX; cmd++) {
2240c500c86bSPablo Neira Ayuso 		cmd_masks = &hdrs[cmd].masks;
2241d79b6df6SOr Gerlitz 		if (memcmp(cmd_masks, &zero_masks, sizeof(zero_masks))) {
2242e98bedf5SEli Britstein 			NL_SET_ERR_MSG_MOD(extack,
2243e98bedf5SEli Britstein 					   "attempt to offload an unsupported field");
2244b3a433deSOr Gerlitz 			netdev_warn(priv->netdev, "attempt to offload an unsupported field (cmd %d)\n", cmd);
2245d79b6df6SOr Gerlitz 			print_hex_dump(KERN_WARNING, "mask: ", DUMP_PREFIX_ADDRESS,
2246d79b6df6SOr Gerlitz 				       16, 1, cmd_masks, sizeof(zero_masks), true);
2247d79b6df6SOr Gerlitz 			err = -EOPNOTSUPP;
2248d79b6df6SOr Gerlitz 			goto out_dealloc_parsed_actions;
2249d79b6df6SOr Gerlitz 		}
2250d79b6df6SOr Gerlitz 	}
2251d79b6df6SOr Gerlitz 
2252d79b6df6SOr Gerlitz 	return 0;
2253d79b6df6SOr Gerlitz 
2254d79b6df6SOr Gerlitz out_dealloc_parsed_actions:
2255d79b6df6SOr Gerlitz 	kfree(parse_attr->mod_hdr_actions);
2256d79b6df6SOr Gerlitz out_err:
2257d79b6df6SOr Gerlitz 	return err;
2258d79b6df6SOr Gerlitz }
2259d79b6df6SOr Gerlitz 
2260e98bedf5SEli Britstein static bool csum_offload_supported(struct mlx5e_priv *priv,
2261e98bedf5SEli Britstein 				   u32 action,
2262e98bedf5SEli Britstein 				   u32 update_flags,
2263e98bedf5SEli Britstein 				   struct netlink_ext_ack *extack)
226426c02749SOr Gerlitz {
226526c02749SOr Gerlitz 	u32 prot_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR | TCA_CSUM_UPDATE_FLAG_TCP |
226626c02749SOr Gerlitz 			 TCA_CSUM_UPDATE_FLAG_UDP;
226726c02749SOr Gerlitz 
226826c02749SOr Gerlitz 	/*  The HW recalcs checksums only if re-writing headers */
226926c02749SOr Gerlitz 	if (!(action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)) {
2270e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
2271e98bedf5SEli Britstein 				   "TC csum action is only offloaded with pedit");
227226c02749SOr Gerlitz 		netdev_warn(priv->netdev,
227326c02749SOr Gerlitz 			    "TC csum action is only offloaded with pedit\n");
227426c02749SOr Gerlitz 		return false;
227526c02749SOr Gerlitz 	}
227626c02749SOr Gerlitz 
227726c02749SOr Gerlitz 	if (update_flags & ~prot_flags) {
2278e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
2279e98bedf5SEli Britstein 				   "can't offload TC csum action for some header/s");
228026c02749SOr Gerlitz 		netdev_warn(priv->netdev,
228126c02749SOr Gerlitz 			    "can't offload TC csum action for some header/s - flags %#x\n",
228226c02749SOr Gerlitz 			    update_flags);
228326c02749SOr Gerlitz 		return false;
228426c02749SOr Gerlitz 	}
228526c02749SOr Gerlitz 
228626c02749SOr Gerlitz 	return true;
228726c02749SOr Gerlitz }
228826c02749SOr Gerlitz 
22898998576bSDmytro Linkin struct ip_ttl_word {
22908998576bSDmytro Linkin 	__u8	ttl;
22918998576bSDmytro Linkin 	__u8	protocol;
22928998576bSDmytro Linkin 	__sum16	check;
22938998576bSDmytro Linkin };
22948998576bSDmytro Linkin 
22958998576bSDmytro Linkin struct ipv6_hoplimit_word {
22968998576bSDmytro Linkin 	__be16	payload_len;
22978998576bSDmytro Linkin 	__u8	nexthdr;
22988998576bSDmytro Linkin 	__u8	hop_limit;
22998998576bSDmytro Linkin };
23008998576bSDmytro Linkin 
23018998576bSDmytro Linkin static bool is_action_keys_supported(const struct flow_action_entry *act)
23028998576bSDmytro Linkin {
23038998576bSDmytro Linkin 	u32 mask, offset;
23048998576bSDmytro Linkin 	u8 htype;
23058998576bSDmytro Linkin 
23068998576bSDmytro Linkin 	htype = act->mangle.htype;
23078998576bSDmytro Linkin 	offset = act->mangle.offset;
23088998576bSDmytro Linkin 	mask = ~act->mangle.mask;
23098998576bSDmytro Linkin 	/* For IPv4 & IPv6 header check 4 byte word,
23108998576bSDmytro Linkin 	 * to determine that modified fields
23118998576bSDmytro Linkin 	 * are NOT ttl & hop_limit only.
23128998576bSDmytro Linkin 	 */
23138998576bSDmytro Linkin 	if (htype == FLOW_ACT_MANGLE_HDR_TYPE_IP4) {
23148998576bSDmytro Linkin 		struct ip_ttl_word *ttl_word =
23158998576bSDmytro Linkin 			(struct ip_ttl_word *)&mask;
23168998576bSDmytro Linkin 
23178998576bSDmytro Linkin 		if (offset != offsetof(struct iphdr, ttl) ||
23188998576bSDmytro Linkin 		    ttl_word->protocol ||
23198998576bSDmytro Linkin 		    ttl_word->check) {
23208998576bSDmytro Linkin 			return true;
23218998576bSDmytro Linkin 		}
23228998576bSDmytro Linkin 	} else if (htype == FLOW_ACT_MANGLE_HDR_TYPE_IP6) {
23238998576bSDmytro Linkin 		struct ipv6_hoplimit_word *hoplimit_word =
23248998576bSDmytro Linkin 			(struct ipv6_hoplimit_word *)&mask;
23258998576bSDmytro Linkin 
23268998576bSDmytro Linkin 		if (offset != offsetof(struct ipv6hdr, payload_len) ||
23278998576bSDmytro Linkin 		    hoplimit_word->payload_len ||
23288998576bSDmytro Linkin 		    hoplimit_word->nexthdr) {
23298998576bSDmytro Linkin 			return true;
23308998576bSDmytro Linkin 		}
23318998576bSDmytro Linkin 	}
23328998576bSDmytro Linkin 	return false;
23338998576bSDmytro Linkin }
23348998576bSDmytro Linkin 
2335bdd66ac0SOr Gerlitz static bool modify_header_match_supported(struct mlx5_flow_spec *spec,
233673867881SPablo Neira Ayuso 					  struct flow_action *flow_action,
23371651925dSGuy Shattah 					  u32 actions,
2338e98bedf5SEli Britstein 					  struct netlink_ext_ack *extack)
2339bdd66ac0SOr Gerlitz {
234073867881SPablo Neira Ayuso 	const struct flow_action_entry *act;
2341bdd66ac0SOr Gerlitz 	bool modify_ip_header;
2342bdd66ac0SOr Gerlitz 	void *headers_v;
2343bdd66ac0SOr Gerlitz 	u16 ethertype;
23448998576bSDmytro Linkin 	u8 ip_proto;
234573867881SPablo Neira Ayuso 	int i;
2346bdd66ac0SOr Gerlitz 
23478377629eSEli Britstein 	headers_v = get_match_headers_value(actions, spec);
2348bdd66ac0SOr Gerlitz 	ethertype = MLX5_GET(fte_match_set_lyr_2_4, headers_v, ethertype);
2349bdd66ac0SOr Gerlitz 
2350bdd66ac0SOr Gerlitz 	/* for non-IP we only re-write MACs, so we're okay */
2351bdd66ac0SOr Gerlitz 	if (ethertype != ETH_P_IP && ethertype != ETH_P_IPV6)
2352bdd66ac0SOr Gerlitz 		goto out_ok;
2353bdd66ac0SOr Gerlitz 
2354bdd66ac0SOr Gerlitz 	modify_ip_header = false;
235573867881SPablo Neira Ayuso 	flow_action_for_each(i, act, flow_action) {
235673867881SPablo Neira Ayuso 		if (act->id != FLOW_ACTION_MANGLE &&
235773867881SPablo Neira Ayuso 		    act->id != FLOW_ACTION_ADD)
2358bdd66ac0SOr Gerlitz 			continue;
2359bdd66ac0SOr Gerlitz 
23608998576bSDmytro Linkin 		if (is_action_keys_supported(act)) {
2361bdd66ac0SOr Gerlitz 			modify_ip_header = true;
2362bdd66ac0SOr Gerlitz 			break;
2363bdd66ac0SOr Gerlitz 		}
2364bdd66ac0SOr Gerlitz 	}
2365bdd66ac0SOr Gerlitz 
2366bdd66ac0SOr Gerlitz 	ip_proto = MLX5_GET(fte_match_set_lyr_2_4, headers_v, ip_protocol);
23671ccef350SJianbo Liu 	if (modify_ip_header && ip_proto != IPPROTO_TCP &&
23681ccef350SJianbo Liu 	    ip_proto != IPPROTO_UDP && ip_proto != IPPROTO_ICMP) {
2369e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
2370e98bedf5SEli Britstein 				   "can't offload re-write of non TCP/UDP");
2371bdd66ac0SOr Gerlitz 		pr_info("can't offload re-write of ip proto %d\n", ip_proto);
2372bdd66ac0SOr Gerlitz 		return false;
2373bdd66ac0SOr Gerlitz 	}
2374bdd66ac0SOr Gerlitz 
2375bdd66ac0SOr Gerlitz out_ok:
2376bdd66ac0SOr Gerlitz 	return true;
2377bdd66ac0SOr Gerlitz }
2378bdd66ac0SOr Gerlitz 
2379bdd66ac0SOr Gerlitz static bool actions_match_supported(struct mlx5e_priv *priv,
238073867881SPablo Neira Ayuso 				    struct flow_action *flow_action,
2381bdd66ac0SOr Gerlitz 				    struct mlx5e_tc_flow_parse_attr *parse_attr,
2382e98bedf5SEli Britstein 				    struct mlx5e_tc_flow *flow,
2383e98bedf5SEli Britstein 				    struct netlink_ext_ack *extack)
2384bdd66ac0SOr Gerlitz {
2385bdd66ac0SOr Gerlitz 	u32 actions;
2386bdd66ac0SOr Gerlitz 
2387bdd66ac0SOr Gerlitz 	if (flow->flags & MLX5E_TC_FLOW_ESWITCH)
2388bdd66ac0SOr Gerlitz 		actions = flow->esw_attr->action;
2389bdd66ac0SOr Gerlitz 	else
2390bdd66ac0SOr Gerlitz 		actions = flow->nic_attr->action;
2391bdd66ac0SOr Gerlitz 
23927e29392eSRoi Dayan 	if (flow->flags & MLX5E_TC_FLOW_EGRESS &&
239335a605dbSEli Britstein 	    !((actions & MLX5_FLOW_CONTEXT_ACTION_DECAP) ||
239435a605dbSEli Britstein 	      (actions & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP)))
23957e29392eSRoi Dayan 		return false;
23967e29392eSRoi Dayan 
2397bdd66ac0SOr Gerlitz 	if (actions & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
239873867881SPablo Neira Ayuso 		return modify_header_match_supported(&parse_attr->spec,
2399a655fe9fSDavid S. Miller 						     flow_action, actions,
2400e98bedf5SEli Britstein 						     extack);
2401bdd66ac0SOr Gerlitz 
2402bdd66ac0SOr Gerlitz 	return true;
2403bdd66ac0SOr Gerlitz }
2404bdd66ac0SOr Gerlitz 
24055c65c564SOr Gerlitz static bool same_hw_devs(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv)
24065c65c564SOr Gerlitz {
24075c65c564SOr Gerlitz 	struct mlx5_core_dev *fmdev, *pmdev;
2408816f6706SOr Gerlitz 	u64 fsystem_guid, psystem_guid;
24095c65c564SOr Gerlitz 
24105c65c564SOr Gerlitz 	fmdev = priv->mdev;
24115c65c564SOr Gerlitz 	pmdev = peer_priv->mdev;
24125c65c564SOr Gerlitz 
241359c9d35eSAlaa Hleihel 	fsystem_guid = mlx5_query_nic_system_image_guid(fmdev);
241459c9d35eSAlaa Hleihel 	psystem_guid = mlx5_query_nic_system_image_guid(pmdev);
24155c65c564SOr Gerlitz 
2416816f6706SOr Gerlitz 	return (fsystem_guid == psystem_guid);
24175c65c564SOr Gerlitz }
24185c65c564SOr Gerlitz 
2419bdc837eeSEli Britstein static int add_vlan_rewrite_action(struct mlx5e_priv *priv, int namespace,
2420bdc837eeSEli Britstein 				   const struct flow_action_entry *act,
2421bdc837eeSEli Britstein 				   struct mlx5e_tc_flow_parse_attr *parse_attr,
2422bdc837eeSEli Britstein 				   struct pedit_headers_action *hdrs,
2423bdc837eeSEli Britstein 				   u32 *action, struct netlink_ext_ack *extack)
2424bdc837eeSEli Britstein {
2425bdc837eeSEli Britstein 	u16 mask16 = VLAN_VID_MASK;
2426bdc837eeSEli Britstein 	u16 val16 = act->vlan.vid & VLAN_VID_MASK;
2427bdc837eeSEli Britstein 	const struct flow_action_entry pedit_act = {
2428bdc837eeSEli Britstein 		.id = FLOW_ACTION_MANGLE,
2429bdc837eeSEli Britstein 		.mangle.htype = FLOW_ACT_MANGLE_HDR_TYPE_ETH,
2430bdc837eeSEli Britstein 		.mangle.offset = offsetof(struct vlan_ethhdr, h_vlan_TCI),
2431bdc837eeSEli Britstein 		.mangle.mask = ~(u32)be16_to_cpu(*(__be16 *)&mask16),
2432bdc837eeSEli Britstein 		.mangle.val = (u32)be16_to_cpu(*(__be16 *)&val16),
2433bdc837eeSEli Britstein 	};
24346fca9d1eSEli Britstein 	u8 match_prio_mask, match_prio_val;
2435bf2f3bcaSEli Britstein 	void *headers_c, *headers_v;
2436bdc837eeSEli Britstein 	int err;
2437bdc837eeSEli Britstein 
2438bf2f3bcaSEli Britstein 	headers_c = get_match_headers_criteria(*action, &parse_attr->spec);
2439bf2f3bcaSEli Britstein 	headers_v = get_match_headers_value(*action, &parse_attr->spec);
2440bf2f3bcaSEli Britstein 
2441bf2f3bcaSEli Britstein 	if (!(MLX5_GET(fte_match_set_lyr_2_4, headers_c, cvlan_tag) &&
2442bf2f3bcaSEli Britstein 	      MLX5_GET(fte_match_set_lyr_2_4, headers_v, cvlan_tag))) {
2443bf2f3bcaSEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
2444bf2f3bcaSEli Britstein 				   "VLAN rewrite action must have VLAN protocol match");
2445bf2f3bcaSEli Britstein 		return -EOPNOTSUPP;
2446bf2f3bcaSEli Britstein 	}
2447bf2f3bcaSEli Britstein 
24486fca9d1eSEli Britstein 	match_prio_mask = MLX5_GET(fte_match_set_lyr_2_4, headers_c, first_prio);
24496fca9d1eSEli Britstein 	match_prio_val = MLX5_GET(fte_match_set_lyr_2_4, headers_v, first_prio);
24506fca9d1eSEli Britstein 	if (act->vlan.prio != (match_prio_val & match_prio_mask)) {
24516fca9d1eSEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
24526fca9d1eSEli Britstein 				   "Changing VLAN prio is not supported");
2453bdc837eeSEli Britstein 		return -EOPNOTSUPP;
2454bdc837eeSEli Britstein 	}
2455bdc837eeSEli Britstein 
2456bdc837eeSEli Britstein 	err = parse_tc_pedit_action(priv, &pedit_act, namespace, parse_attr,
2457bdc837eeSEli Britstein 				    hdrs, NULL);
2458bdc837eeSEli Britstein 	*action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
2459bdc837eeSEli Britstein 
2460bdc837eeSEli Britstein 	return err;
2461bdc837eeSEli Britstein }
2462bdc837eeSEli Britstein 
24630bac1194SEli Britstein static int
24640bac1194SEli Britstein add_vlan_prio_tag_rewrite_action(struct mlx5e_priv *priv,
24650bac1194SEli Britstein 				 struct mlx5e_tc_flow_parse_attr *parse_attr,
24660bac1194SEli Britstein 				 struct pedit_headers_action *hdrs,
24670bac1194SEli Britstein 				 u32 *action, struct netlink_ext_ack *extack)
24680bac1194SEli Britstein {
24690bac1194SEli Britstein 	const struct flow_action_entry prio_tag_act = {
24700bac1194SEli Britstein 		.vlan.vid = 0,
24710bac1194SEli Britstein 		.vlan.prio =
24720bac1194SEli Britstein 			MLX5_GET(fte_match_set_lyr_2_4,
24730bac1194SEli Britstein 				 get_match_headers_value(*action,
24740bac1194SEli Britstein 							 &parse_attr->spec),
24750bac1194SEli Britstein 				 first_prio) &
24760bac1194SEli Britstein 			MLX5_GET(fte_match_set_lyr_2_4,
24770bac1194SEli Britstein 				 get_match_headers_criteria(*action,
24780bac1194SEli Britstein 							    &parse_attr->spec),
24790bac1194SEli Britstein 				 first_prio),
24800bac1194SEli Britstein 	};
24810bac1194SEli Britstein 
24820bac1194SEli Britstein 	return add_vlan_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB,
24830bac1194SEli Britstein 				       &prio_tag_act, parse_attr, hdrs, action,
24840bac1194SEli Britstein 				       extack);
24850bac1194SEli Britstein }
24860bac1194SEli Britstein 
248773867881SPablo Neira Ayuso static int parse_tc_nic_actions(struct mlx5e_priv *priv,
248873867881SPablo Neira Ayuso 				struct flow_action *flow_action,
2489aa0cbbaeSOr Gerlitz 				struct mlx5e_tc_flow_parse_attr *parse_attr,
2490e98bedf5SEli Britstein 				struct mlx5e_tc_flow *flow,
2491e98bedf5SEli Britstein 				struct netlink_ext_ack *extack)
2492e3a2b7edSAmir Vadai {
2493aa0cbbaeSOr Gerlitz 	struct mlx5_nic_flow_attr *attr = flow->nic_attr;
249473867881SPablo Neira Ayuso 	struct pedit_headers_action hdrs[2] = {};
249573867881SPablo Neira Ayuso 	const struct flow_action_entry *act;
24961cab1cd7SOr Gerlitz 	u32 action = 0;
2497244cd96aSCong Wang 	int err, i;
2498e3a2b7edSAmir Vadai 
249973867881SPablo Neira Ayuso 	if (!flow_action_has_entries(flow_action))
2500e3a2b7edSAmir Vadai 		return -EINVAL;
2501e3a2b7edSAmir Vadai 
25023bc4b7bfSOr Gerlitz 	attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
2503e3a2b7edSAmir Vadai 
250473867881SPablo Neira Ayuso 	flow_action_for_each(i, act, flow_action) {
250573867881SPablo Neira Ayuso 		switch (act->id) {
250673867881SPablo Neira Ayuso 		case FLOW_ACTION_DROP:
25071cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
2508aad7e08dSAmir Vadai 			if (MLX5_CAP_FLOWTABLE(priv->mdev,
2509aad7e08dSAmir Vadai 					       flow_table_properties_nic_receive.flow_counter))
25101cab1cd7SOr Gerlitz 				action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
251173867881SPablo Neira Ayuso 			break;
251273867881SPablo Neira Ayuso 		case FLOW_ACTION_MANGLE:
251373867881SPablo Neira Ayuso 		case FLOW_ACTION_ADD:
251473867881SPablo Neira Ayuso 			err = parse_tc_pedit_action(priv, act, MLX5_FLOW_NAMESPACE_KERNEL,
2515c500c86bSPablo Neira Ayuso 						    parse_attr, hdrs, extack);
25162f4fe4caSOr Gerlitz 			if (err)
25172f4fe4caSOr Gerlitz 				return err;
25182f4fe4caSOr Gerlitz 
25191cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
25202f4fe4caSOr Gerlitz 				  MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
252173867881SPablo Neira Ayuso 			break;
2522bdc837eeSEli Britstein 		case FLOW_ACTION_VLAN_MANGLE:
2523bdc837eeSEli Britstein 			err = add_vlan_rewrite_action(priv,
2524bdc837eeSEli Britstein 						      MLX5_FLOW_NAMESPACE_KERNEL,
2525bdc837eeSEli Britstein 						      act, parse_attr, hdrs,
2526bdc837eeSEli Britstein 						      &action, extack);
2527bdc837eeSEli Britstein 			if (err)
2528bdc837eeSEli Britstein 				return err;
2529bdc837eeSEli Britstein 
2530bdc837eeSEli Britstein 			break;
253173867881SPablo Neira Ayuso 		case FLOW_ACTION_CSUM:
25321cab1cd7SOr Gerlitz 			if (csum_offload_supported(priv, action,
253373867881SPablo Neira Ayuso 						   act->csum_flags,
2534e98bedf5SEli Britstein 						   extack))
253573867881SPablo Neira Ayuso 				break;
253626c02749SOr Gerlitz 
253726c02749SOr Gerlitz 			return -EOPNOTSUPP;
253873867881SPablo Neira Ayuso 		case FLOW_ACTION_REDIRECT: {
253973867881SPablo Neira Ayuso 			struct net_device *peer_dev = act->dev;
25405c65c564SOr Gerlitz 
25415c65c564SOr Gerlitz 			if (priv->netdev->netdev_ops == peer_dev->netdev_ops &&
25425c65c564SOr Gerlitz 			    same_hw_devs(priv, netdev_priv(peer_dev))) {
254398b66cb1SEli Britstein 				parse_attr->mirred_ifindex[0] = peer_dev->ifindex;
25445c65c564SOr Gerlitz 				flow->flags |= MLX5E_TC_FLOW_HAIRPIN;
25451cab1cd7SOr Gerlitz 				action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
25465c65c564SOr Gerlitz 					  MLX5_FLOW_CONTEXT_ACTION_COUNT;
25475c65c564SOr Gerlitz 			} else {
2548e98bedf5SEli Britstein 				NL_SET_ERR_MSG_MOD(extack,
2549e98bedf5SEli Britstein 						   "device is not on same HW, can't offload");
25505c65c564SOr Gerlitz 				netdev_warn(priv->netdev, "device %s not on same HW, can't offload\n",
25515c65c564SOr Gerlitz 					    peer_dev->name);
25525c65c564SOr Gerlitz 				return -EINVAL;
25535c65c564SOr Gerlitz 			}
25545c65c564SOr Gerlitz 			}
255573867881SPablo Neira Ayuso 			break;
255673867881SPablo Neira Ayuso 		case FLOW_ACTION_MARK: {
255773867881SPablo Neira Ayuso 			u32 mark = act->mark;
2558e3a2b7edSAmir Vadai 
2559e3a2b7edSAmir Vadai 			if (mark & ~MLX5E_TC_FLOW_ID_MASK) {
2560e98bedf5SEli Britstein 				NL_SET_ERR_MSG_MOD(extack,
2561e98bedf5SEli Britstein 						   "Bad flow mark - only 16 bit is supported");
2562e3a2b7edSAmir Vadai 				return -EINVAL;
2563e3a2b7edSAmir Vadai 			}
2564e3a2b7edSAmir Vadai 
25653bc4b7bfSOr Gerlitz 			attr->flow_tag = mark;
25661cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
2567e3a2b7edSAmir Vadai 			}
256873867881SPablo Neira Ayuso 			break;
256973867881SPablo Neira Ayuso 		default:
25702cc1cb1dSTonghao Zhang 			NL_SET_ERR_MSG_MOD(extack, "The offload action is not supported");
25712cc1cb1dSTonghao Zhang 			return -EOPNOTSUPP;
2572e3a2b7edSAmir Vadai 		}
257373867881SPablo Neira Ayuso 	}
2574e3a2b7edSAmir Vadai 
2575c500c86bSPablo Neira Ayuso 	if (hdrs[TCA_PEDIT_KEY_EX_CMD_SET].pedits ||
2576c500c86bSPablo Neira Ayuso 	    hdrs[TCA_PEDIT_KEY_EX_CMD_ADD].pedits) {
2577c500c86bSPablo Neira Ayuso 		err = alloc_tc_pedit_action(priv, MLX5_FLOW_NAMESPACE_KERNEL,
257827c11b6bSEli Britstein 					    parse_attr, hdrs, &action, extack);
2579c500c86bSPablo Neira Ayuso 		if (err)
2580c500c86bSPablo Neira Ayuso 			return err;
258127c11b6bSEli Britstein 		/* in case all pedit actions are skipped, remove the MOD_HDR
258227c11b6bSEli Britstein 		 * flag.
258327c11b6bSEli Britstein 		 */
2584e7739a60SEli Britstein 		if (parse_attr->num_mod_hdr_actions == 0) {
258527c11b6bSEli Britstein 			action &= ~MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
2586e7739a60SEli Britstein 			kfree(parse_attr->mod_hdr_actions);
2587e7739a60SEli Britstein 		}
2588c500c86bSPablo Neira Ayuso 	}
2589c500c86bSPablo Neira Ayuso 
25901cab1cd7SOr Gerlitz 	attr->action = action;
259173867881SPablo Neira Ayuso 	if (!actions_match_supported(priv, flow_action, parse_attr, flow, extack))
2592bdd66ac0SOr Gerlitz 		return -EOPNOTSUPP;
2593bdd66ac0SOr Gerlitz 
2594e3a2b7edSAmir Vadai 	return 0;
2595e3a2b7edSAmir Vadai }
2596e3a2b7edSAmir Vadai 
25977f1a546eSEli Britstein struct encap_key {
25981f6da306SYevgeny Kliteynik 	const struct ip_tunnel_key *ip_tun_key;
2599d386939aSYevgeny Kliteynik 	struct mlx5e_tc_tunnel *tc_tunnel;
26007f1a546eSEli Britstein };
26017f1a546eSEli Britstein 
26027f1a546eSEli Britstein static inline int cmp_encap_info(struct encap_key *a,
26037f1a546eSEli Britstein 				 struct encap_key *b)
2604a54e20b4SHadar Hen Zion {
26057f1a546eSEli Britstein 	return memcmp(a->ip_tun_key, b->ip_tun_key, sizeof(*a->ip_tun_key)) ||
2606d386939aSYevgeny Kliteynik 	       a->tc_tunnel->tunnel_type != b->tc_tunnel->tunnel_type;
2607a54e20b4SHadar Hen Zion }
2608a54e20b4SHadar Hen Zion 
26097f1a546eSEli Britstein static inline int hash_encap_info(struct encap_key *key)
2610a54e20b4SHadar Hen Zion {
26117f1a546eSEli Britstein 	return jhash(key->ip_tun_key, sizeof(*key->ip_tun_key),
2612d386939aSYevgeny Kliteynik 		     key->tc_tunnel->tunnel_type);
2613a54e20b4SHadar Hen Zion }
2614a54e20b4SHadar Hen Zion 
2615a54e20b4SHadar Hen Zion 
2616b1d90e6bSRabie Loulou static bool is_merged_eswitch_dev(struct mlx5e_priv *priv,
2617b1d90e6bSRabie Loulou 				  struct net_device *peer_netdev)
2618b1d90e6bSRabie Loulou {
2619b1d90e6bSRabie Loulou 	struct mlx5e_priv *peer_priv;
2620b1d90e6bSRabie Loulou 
2621b1d90e6bSRabie Loulou 	peer_priv = netdev_priv(peer_netdev);
2622b1d90e6bSRabie Loulou 
2623b1d90e6bSRabie Loulou 	return (MLX5_CAP_ESW(priv->mdev, merged_eswitch) &&
262468931c7dSRoi Dayan 		mlx5e_eswitch_rep(priv->netdev) &&
262568931c7dSRoi Dayan 		mlx5e_eswitch_rep(peer_netdev) &&
262668931c7dSRoi Dayan 		same_hw_devs(priv, peer_priv));
2627b1d90e6bSRabie Loulou }
2628b1d90e6bSRabie Loulou 
2629ce99f6b9SOr Gerlitz 
263054c177caSOz Shlomo 
2631a54e20b4SHadar Hen Zion static int mlx5e_attach_encap(struct mlx5e_priv *priv,
2632e98bedf5SEli Britstein 			      struct mlx5e_tc_flow *flow,
2633733d4f36SRoi Dayan 			      struct net_device *mirred_dev,
2634733d4f36SRoi Dayan 			      int out_index,
26358c4dc42bSEli Britstein 			      struct netlink_ext_ack *extack,
26360ad060eeSRoi Dayan 			      struct net_device **encap_dev,
26370ad060eeSRoi Dayan 			      bool *encap_valid)
263803a9d11eSOr Gerlitz {
2639a54e20b4SHadar Hen Zion 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
264045247bf2SOr Gerlitz 	struct mlx5_esw_flow_attr *attr = flow->esw_attr;
2641733d4f36SRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr;
26421f6da306SYevgeny Kliteynik 	const struct ip_tunnel_info *tun_info;
26437f1a546eSEli Britstein 	struct encap_key key, e_key;
2644c1ae1152SOr Gerlitz 	struct mlx5e_encap_entry *e;
2645733d4f36SRoi Dayan 	unsigned short family;
2646a54e20b4SHadar Hen Zion 	uintptr_t hash_key;
2647a54e20b4SHadar Hen Zion 	bool found = false;
264854c177caSOz Shlomo 	int err = 0;
2649a54e20b4SHadar Hen Zion 
2650733d4f36SRoi Dayan 	parse_attr = attr->parse_attr;
26511f6da306SYevgeny Kliteynik 	tun_info = parse_attr->tun_info[out_index];
2652733d4f36SRoi Dayan 	family = ip_tunnel_info_af(tun_info);
26537f1a546eSEli Britstein 	key.ip_tun_key = &tun_info->key;
2654d386939aSYevgeny Kliteynik 	key.tc_tunnel = mlx5e_get_tc_tun(mirred_dev);
2655d71f895cSEli Cohen 	if (!key.tc_tunnel) {
2656d71f895cSEli Cohen 		NL_SET_ERR_MSG_MOD(extack, "Unsupported tunnel");
2657d71f895cSEli Cohen 		return -EOPNOTSUPP;
2658d71f895cSEli Cohen 	}
2659733d4f36SRoi Dayan 
26607f1a546eSEli Britstein 	hash_key = hash_encap_info(&key);
2661a54e20b4SHadar Hen Zion 
2662a54e20b4SHadar Hen Zion 	hash_for_each_possible_rcu(esw->offloads.encap_tbl, e,
2663a54e20b4SHadar Hen Zion 				   encap_hlist, hash_key) {
26641f6da306SYevgeny Kliteynik 		e_key.ip_tun_key = &e->tun_info->key;
2665d386939aSYevgeny Kliteynik 		e_key.tc_tunnel = e->tunnel;
26667f1a546eSEli Britstein 		if (!cmp_encap_info(&e_key, &key)) {
2667a54e20b4SHadar Hen Zion 			found = true;
2668a54e20b4SHadar Hen Zion 			break;
2669a54e20b4SHadar Hen Zion 		}
2670a54e20b4SHadar Hen Zion 	}
2671a54e20b4SHadar Hen Zion 
2672b2812089SVlad Buslov 	/* must verify if encap is valid or not */
267345247bf2SOr Gerlitz 	if (found)
267445247bf2SOr Gerlitz 		goto attach_flow;
2675a54e20b4SHadar Hen Zion 
2676a54e20b4SHadar Hen Zion 	e = kzalloc(sizeof(*e), GFP_KERNEL);
2677a54e20b4SHadar Hen Zion 	if (!e)
2678a54e20b4SHadar Hen Zion 		return -ENOMEM;
2679a54e20b4SHadar Hen Zion 
26801f6da306SYevgeny Kliteynik 	e->tun_info = tun_info;
2681101f4de9SOz Shlomo 	err = mlx5e_tc_tun_init_encap_attr(mirred_dev, priv, e, extack);
268254c177caSOz Shlomo 	if (err)
268354c177caSOz Shlomo 		goto out_err;
268454c177caSOz Shlomo 
2685a54e20b4SHadar Hen Zion 	INIT_LIST_HEAD(&e->flows);
2686a54e20b4SHadar Hen Zion 
2687ce99f6b9SOr Gerlitz 	if (family == AF_INET)
2688101f4de9SOz Shlomo 		err = mlx5e_tc_tun_create_header_ipv4(priv, mirred_dev, e);
2689ce99f6b9SOr Gerlitz 	else if (family == AF_INET6)
2690101f4de9SOz Shlomo 		err = mlx5e_tc_tun_create_header_ipv6(priv, mirred_dev, e);
2691ce99f6b9SOr Gerlitz 
26920ad060eeSRoi Dayan 	if (err)
2693a54e20b4SHadar Hen Zion 		goto out_err;
2694a54e20b4SHadar Hen Zion 
2695a54e20b4SHadar Hen Zion 	hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key);
2696a54e20b4SHadar Hen Zion 
269745247bf2SOr Gerlitz attach_flow:
26988c4dc42bSEli Britstein 	list_add(&flow->encaps[out_index].list, &e->flows);
26998c4dc42bSEli Britstein 	flow->encaps[out_index].index = out_index;
270045247bf2SOr Gerlitz 	*encap_dev = e->out_dev;
27018c4dc42bSEli Britstein 	if (e->flags & MLX5_ENCAP_ENTRY_VALID) {
27028c4dc42bSEli Britstein 		attr->dests[out_index].encap_id = e->encap_id;
27038c4dc42bSEli Britstein 		attr->dests[out_index].flags |= MLX5_ESW_DEST_ENCAP_VALID;
27040ad060eeSRoi Dayan 		*encap_valid = true;
27058c4dc42bSEli Britstein 	} else {
27060ad060eeSRoi Dayan 		*encap_valid = false;
27078c4dc42bSEli Britstein 	}
270845247bf2SOr Gerlitz 
2709232c0013SHadar Hen Zion 	return err;
2710a54e20b4SHadar Hen Zion 
2711a54e20b4SHadar Hen Zion out_err:
2712a54e20b4SHadar Hen Zion 	kfree(e);
2713a54e20b4SHadar Hen Zion 	return err;
2714a54e20b4SHadar Hen Zion }
2715a54e20b4SHadar Hen Zion 
27161482bd3dSJianbo Liu static int parse_tc_vlan_action(struct mlx5e_priv *priv,
271773867881SPablo Neira Ayuso 				const struct flow_action_entry *act,
27181482bd3dSJianbo Liu 				struct mlx5_esw_flow_attr *attr,
27191482bd3dSJianbo Liu 				u32 *action)
27201482bd3dSJianbo Liu {
2721cc495188SJianbo Liu 	u8 vlan_idx = attr->total_vlan;
2722cc495188SJianbo Liu 
2723cc495188SJianbo Liu 	if (vlan_idx >= MLX5_FS_VLAN_DEPTH)
27241482bd3dSJianbo Liu 		return -EOPNOTSUPP;
2725cc495188SJianbo Liu 
272673867881SPablo Neira Ayuso 	switch (act->id) {
272773867881SPablo Neira Ayuso 	case FLOW_ACTION_VLAN_POP:
2728cc495188SJianbo Liu 		if (vlan_idx) {
2729cc495188SJianbo Liu 			if (!mlx5_eswitch_vlan_actions_supported(priv->mdev,
2730cc495188SJianbo Liu 								 MLX5_FS_VLAN_DEPTH))
2731cc495188SJianbo Liu 				return -EOPNOTSUPP;
2732cc495188SJianbo Liu 
2733cc495188SJianbo Liu 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2;
2734cc495188SJianbo Liu 		} else {
2735cc495188SJianbo Liu 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
2736cc495188SJianbo Liu 		}
273773867881SPablo Neira Ayuso 		break;
273873867881SPablo Neira Ayuso 	case FLOW_ACTION_VLAN_PUSH:
273973867881SPablo Neira Ayuso 		attr->vlan_vid[vlan_idx] = act->vlan.vid;
274073867881SPablo Neira Ayuso 		attr->vlan_prio[vlan_idx] = act->vlan.prio;
274173867881SPablo Neira Ayuso 		attr->vlan_proto[vlan_idx] = act->vlan.proto;
2742cc495188SJianbo Liu 		if (!attr->vlan_proto[vlan_idx])
2743cc495188SJianbo Liu 			attr->vlan_proto[vlan_idx] = htons(ETH_P_8021Q);
2744cc495188SJianbo Liu 
2745cc495188SJianbo Liu 		if (vlan_idx) {
2746cc495188SJianbo Liu 			if (!mlx5_eswitch_vlan_actions_supported(priv->mdev,
2747cc495188SJianbo Liu 								 MLX5_FS_VLAN_DEPTH))
2748cc495188SJianbo Liu 				return -EOPNOTSUPP;
2749cc495188SJianbo Liu 
2750cc495188SJianbo Liu 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2;
2751cc495188SJianbo Liu 		} else {
2752cc495188SJianbo Liu 			if (!mlx5_eswitch_vlan_actions_supported(priv->mdev, 1) &&
275373867881SPablo Neira Ayuso 			    (act->vlan.proto != htons(ETH_P_8021Q) ||
275473867881SPablo Neira Ayuso 			     act->vlan.prio))
2755cc495188SJianbo Liu 				return -EOPNOTSUPP;
2756cc495188SJianbo Liu 
2757cc495188SJianbo Liu 			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
27581482bd3dSJianbo Liu 		}
275973867881SPablo Neira Ayuso 		break;
276073867881SPablo Neira Ayuso 	default:
2761bdc837eeSEli Britstein 		return -EINVAL;
27621482bd3dSJianbo Liu 	}
27631482bd3dSJianbo Liu 
2764cc495188SJianbo Liu 	attr->total_vlan = vlan_idx + 1;
2765cc495188SJianbo Liu 
27661482bd3dSJianbo Liu 	return 0;
27671482bd3dSJianbo Liu }
27681482bd3dSJianbo Liu 
2769278748a9SEli Britstein static int add_vlan_push_action(struct mlx5e_priv *priv,
2770278748a9SEli Britstein 				struct mlx5_esw_flow_attr *attr,
2771278748a9SEli Britstein 				struct net_device **out_dev,
2772278748a9SEli Britstein 				u32 *action)
2773278748a9SEli Britstein {
2774278748a9SEli Britstein 	struct net_device *vlan_dev = *out_dev;
2775278748a9SEli Britstein 	struct flow_action_entry vlan_act = {
2776278748a9SEli Britstein 		.id = FLOW_ACTION_VLAN_PUSH,
2777278748a9SEli Britstein 		.vlan.vid = vlan_dev_vlan_id(vlan_dev),
2778278748a9SEli Britstein 		.vlan.proto = vlan_dev_vlan_proto(vlan_dev),
2779278748a9SEli Britstein 		.vlan.prio = 0,
2780278748a9SEli Britstein 	};
2781278748a9SEli Britstein 	int err;
2782278748a9SEli Britstein 
2783278748a9SEli Britstein 	err = parse_tc_vlan_action(priv, &vlan_act, attr, action);
2784278748a9SEli Britstein 	if (err)
2785278748a9SEli Britstein 		return err;
2786278748a9SEli Britstein 
2787278748a9SEli Britstein 	*out_dev = dev_get_by_index_rcu(dev_net(vlan_dev),
2788278748a9SEli Britstein 					dev_get_iflink(vlan_dev));
2789278748a9SEli Britstein 	if (is_vlan_dev(*out_dev))
2790278748a9SEli Britstein 		err = add_vlan_push_action(priv, attr, out_dev, action);
2791278748a9SEli Britstein 
2792278748a9SEli Britstein 	return err;
2793278748a9SEli Britstein }
2794278748a9SEli Britstein 
279535a605dbSEli Britstein static int add_vlan_pop_action(struct mlx5e_priv *priv,
279635a605dbSEli Britstein 			       struct mlx5_esw_flow_attr *attr,
279735a605dbSEli Britstein 			       u32 *action)
279835a605dbSEli Britstein {
279935a605dbSEli Britstein 	int nest_level = vlan_get_encap_level(attr->parse_attr->filter_dev);
280035a605dbSEli Britstein 	struct flow_action_entry vlan_act = {
280135a605dbSEli Britstein 		.id = FLOW_ACTION_VLAN_POP,
280235a605dbSEli Britstein 	};
280335a605dbSEli Britstein 	int err = 0;
280435a605dbSEli Britstein 
280535a605dbSEli Britstein 	while (nest_level--) {
280635a605dbSEli Britstein 		err = parse_tc_vlan_action(priv, &vlan_act, attr, action);
280735a605dbSEli Britstein 		if (err)
280835a605dbSEli Britstein 			return err;
280935a605dbSEli Britstein 	}
281035a605dbSEli Britstein 
281135a605dbSEli Britstein 	return err;
281235a605dbSEli Britstein }
281335a605dbSEli Britstein 
2814f6dc1264SPaul Blakey bool mlx5e_is_valid_eswitch_fwd_dev(struct mlx5e_priv *priv,
2815f6dc1264SPaul Blakey 				    struct net_device *out_dev)
2816f6dc1264SPaul Blakey {
2817f6dc1264SPaul Blakey 	if (is_merged_eswitch_dev(priv, out_dev))
2818f6dc1264SPaul Blakey 		return true;
2819f6dc1264SPaul Blakey 
2820f6dc1264SPaul Blakey 	return mlx5e_eswitch_rep(out_dev) &&
2821f6dc1264SPaul Blakey 	       same_hw_devs(priv, netdev_priv(out_dev));
2822f6dc1264SPaul Blakey }
2823f6dc1264SPaul Blakey 
282473867881SPablo Neira Ayuso static int parse_tc_fdb_actions(struct mlx5e_priv *priv,
282573867881SPablo Neira Ayuso 				struct flow_action *flow_action,
2826e98bedf5SEli Britstein 				struct mlx5e_tc_flow *flow,
2827e98bedf5SEli Britstein 				struct netlink_ext_ack *extack)
2828a54e20b4SHadar Hen Zion {
282973867881SPablo Neira Ayuso 	struct pedit_headers_action hdrs[2] = {};
2830bf07aa73SPaul Blakey 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
2831ecf5bb79SOr Gerlitz 	struct mlx5_esw_flow_attr *attr = flow->esw_attr;
28326f9af8ffSTonghao Zhang 	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
28331d447a39SSaeed Mahameed 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
283473867881SPablo Neira Ayuso 	const struct ip_tunnel_info *info = NULL;
283573867881SPablo Neira Ayuso 	const struct flow_action_entry *act;
2836a54e20b4SHadar Hen Zion 	bool encap = false;
28371cab1cd7SOr Gerlitz 	u32 action = 0;
2838244cd96aSCong Wang 	int err, i;
283903a9d11eSOr Gerlitz 
284073867881SPablo Neira Ayuso 	if (!flow_action_has_entries(flow_action))
284103a9d11eSOr Gerlitz 		return -EINVAL;
284203a9d11eSOr Gerlitz 
284373867881SPablo Neira Ayuso 	flow_action_for_each(i, act, flow_action) {
284473867881SPablo Neira Ayuso 		switch (act->id) {
284573867881SPablo Neira Ayuso 		case FLOW_ACTION_DROP:
28461cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_DROP |
284703a9d11eSOr Gerlitz 				  MLX5_FLOW_CONTEXT_ACTION_COUNT;
284873867881SPablo Neira Ayuso 			break;
284973867881SPablo Neira Ayuso 		case FLOW_ACTION_MANGLE:
285073867881SPablo Neira Ayuso 		case FLOW_ACTION_ADD:
285173867881SPablo Neira Ayuso 			err = parse_tc_pedit_action(priv, act, MLX5_FLOW_NAMESPACE_FDB,
2852c500c86bSPablo Neira Ayuso 						    parse_attr, hdrs, extack);
2853d7e75a32SOr Gerlitz 			if (err)
2854d7e75a32SOr Gerlitz 				return err;
2855d7e75a32SOr Gerlitz 
28561cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
2857e85e02baSEli Britstein 			attr->split_count = attr->out_count;
285873867881SPablo Neira Ayuso 			break;
285973867881SPablo Neira Ayuso 		case FLOW_ACTION_CSUM:
28601cab1cd7SOr Gerlitz 			if (csum_offload_supported(priv, action,
286173867881SPablo Neira Ayuso 						   act->csum_flags, extack))
286273867881SPablo Neira Ayuso 				break;
286326c02749SOr Gerlitz 
286426c02749SOr Gerlitz 			return -EOPNOTSUPP;
286573867881SPablo Neira Ayuso 		case FLOW_ACTION_REDIRECT:
286673867881SPablo Neira Ayuso 		case FLOW_ACTION_MIRRED: {
286703a9d11eSOr Gerlitz 			struct mlx5e_priv *out_priv;
2868592d3651SChris Mi 			struct net_device *out_dev;
286903a9d11eSOr Gerlitz 
287073867881SPablo Neira Ayuso 			out_dev = act->dev;
2871ef381359SOz Shlomo 			if (!out_dev) {
2872ef381359SOz Shlomo 				/* out_dev is NULL when filters with
2873ef381359SOz Shlomo 				 * non-existing mirred device are replayed to
2874ef381359SOz Shlomo 				 * the driver.
2875ef381359SOz Shlomo 				 */
2876ef381359SOz Shlomo 				return -EINVAL;
2877ef381359SOz Shlomo 			}
287803a9d11eSOr Gerlitz 
2879592d3651SChris Mi 			if (attr->out_count >= MLX5_MAX_FLOW_FWD_VPORTS) {
2880e98bedf5SEli Britstein 				NL_SET_ERR_MSG_MOD(extack,
2881e98bedf5SEli Britstein 						   "can't support more output ports, can't offload forwarding");
2882592d3651SChris Mi 				pr_err("can't support more than %d output ports, can't offload forwarding\n",
2883592d3651SChris Mi 				       attr->out_count);
2884592d3651SChris Mi 				return -EOPNOTSUPP;
2885592d3651SChris Mi 			}
2886592d3651SChris Mi 
2887f493f155SEli Britstein 			action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
2888f493f155SEli Britstein 				  MLX5_FLOW_CONTEXT_ACTION_COUNT;
2889f6dc1264SPaul Blakey 			if (netdev_port_same_parent_id(priv->netdev, out_dev)) {
28907ba58ba7SRabie Loulou 				struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
28917ba58ba7SRabie Loulou 				struct net_device *uplink_dev = mlx5_eswitch_uplink_get_proto_dev(esw, REP_ETH);
28927ba58ba7SRabie Loulou 				struct net_device *uplink_upper = netdev_master_upper_dev_get(uplink_dev);
28937ba58ba7SRabie Loulou 
28947ba58ba7SRabie Loulou 				if (uplink_upper &&
28957ba58ba7SRabie Loulou 				    netif_is_lag_master(uplink_upper) &&
28967ba58ba7SRabie Loulou 				    uplink_upper == out_dev)
28977ba58ba7SRabie Loulou 					out_dev = uplink_dev;
28987ba58ba7SRabie Loulou 
2899278748a9SEli Britstein 				if (is_vlan_dev(out_dev)) {
2900278748a9SEli Britstein 					err = add_vlan_push_action(priv, attr,
2901278748a9SEli Britstein 								   &out_dev,
2902278748a9SEli Britstein 								   &action);
2903278748a9SEli Britstein 					if (err)
2904278748a9SEli Britstein 						return err;
2905278748a9SEli Britstein 				}
2906f6dc1264SPaul Blakey 
290735a605dbSEli Britstein 				if (is_vlan_dev(parse_attr->filter_dev)) {
290835a605dbSEli Britstein 					err = add_vlan_pop_action(priv, attr,
290935a605dbSEli Britstein 								  &action);
291035a605dbSEli Britstein 					if (err)
291135a605dbSEli Britstein 						return err;
291235a605dbSEli Britstein 				}
2913278748a9SEli Britstein 
2914f6dc1264SPaul Blakey 				if (!mlx5e_is_valid_eswitch_fwd_dev(priv, out_dev)) {
2915f6dc1264SPaul Blakey 					NL_SET_ERR_MSG_MOD(extack,
2916f6dc1264SPaul Blakey 							   "devices are not on same switch HW, can't offload forwarding");
2917f6dc1264SPaul Blakey 					pr_err("devices %s %s not on same switch HW, can't offload forwarding\n",
2918f6dc1264SPaul Blakey 					       priv->netdev->name, out_dev->name);
2919a0646c88SEli Britstein 					return -EOPNOTSUPP;
2920f6dc1264SPaul Blakey 				}
2921a0646c88SEli Britstein 
292203a9d11eSOr Gerlitz 				out_priv = netdev_priv(out_dev);
29231d447a39SSaeed Mahameed 				rpriv = out_priv->ppriv;
2924df65a573SEli Britstein 				attr->dests[attr->out_count].rep = rpriv->rep;
2925df65a573SEli Britstein 				attr->dests[attr->out_count].mdev = out_priv->mdev;
2926df65a573SEli Britstein 				attr->out_count++;
2927a54e20b4SHadar Hen Zion 			} else if (encap) {
29288c4dc42bSEli Britstein 				parse_attr->mirred_ifindex[attr->out_count] =
29298c4dc42bSEli Britstein 					out_dev->ifindex;
29301f6da306SYevgeny Kliteynik 				parse_attr->tun_info[attr->out_count] = info;
29318c4dc42bSEli Britstein 				encap = false;
2932f493f155SEli Britstein 				attr->dests[attr->out_count].flags |=
2933f493f155SEli Britstein 					MLX5_ESW_DEST_ENCAP;
29341cc26d74SEli Britstein 				attr->out_count++;
2935df65a573SEli Britstein 				/* attr->dests[].rep is resolved when we
2936df65a573SEli Britstein 				 * handle encap
2937df65a573SEli Britstein 				 */
2938ef381359SOz Shlomo 			} else if (parse_attr->filter_dev != priv->netdev) {
2939ef381359SOz Shlomo 				/* All mlx5 devices are called to configure
2940ef381359SOz Shlomo 				 * high level device filters. Therefore, the
2941ef381359SOz Shlomo 				 * *attempt* to  install a filter on invalid
2942ef381359SOz Shlomo 				 * eswitch should not trigger an explicit error
2943ef381359SOz Shlomo 				 */
2944ef381359SOz Shlomo 				return -EINVAL;
2945a54e20b4SHadar Hen Zion 			} else {
2946e98bedf5SEli Britstein 				NL_SET_ERR_MSG_MOD(extack,
2947e98bedf5SEli Britstein 						   "devices are not on same switch HW, can't offload forwarding");
2948a54e20b4SHadar Hen Zion 				pr_err("devices %s %s not on same switch HW, can't offload forwarding\n",
2949a54e20b4SHadar Hen Zion 				       priv->netdev->name, out_dev->name);
2950a54e20b4SHadar Hen Zion 				return -EINVAL;
2951a54e20b4SHadar Hen Zion 			}
2952a54e20b4SHadar Hen Zion 			}
295373867881SPablo Neira Ayuso 			break;
295473867881SPablo Neira Ayuso 		case FLOW_ACTION_TUNNEL_ENCAP:
295573867881SPablo Neira Ayuso 			info = act->tunnel;
2956a54e20b4SHadar Hen Zion 			if (info)
2957a54e20b4SHadar Hen Zion 				encap = true;
2958a54e20b4SHadar Hen Zion 			else
2959a54e20b4SHadar Hen Zion 				return -EOPNOTSUPP;
296003a9d11eSOr Gerlitz 
296173867881SPablo Neira Ayuso 			break;
296273867881SPablo Neira Ayuso 		case FLOW_ACTION_VLAN_PUSH:
296373867881SPablo Neira Ayuso 		case FLOW_ACTION_VLAN_POP:
296476b496b1SEli Britstein 			if (act->id == FLOW_ACTION_VLAN_PUSH &&
296576b496b1SEli Britstein 			    (action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP)) {
296676b496b1SEli Britstein 				/* Replace vlan pop+push with vlan modify */
296776b496b1SEli Britstein 				action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
296876b496b1SEli Britstein 				err = add_vlan_rewrite_action(priv,
296976b496b1SEli Britstein 							      MLX5_FLOW_NAMESPACE_FDB,
297076b496b1SEli Britstein 							      act, parse_attr, hdrs,
297176b496b1SEli Britstein 							      &action, extack);
297276b496b1SEli Britstein 			} else {
297373867881SPablo Neira Ayuso 				err = parse_tc_vlan_action(priv, act, attr, &action);
297476b496b1SEli Britstein 			}
29751482bd3dSJianbo Liu 			if (err)
29761482bd3dSJianbo Liu 				return err;
29771482bd3dSJianbo Liu 
2978e85e02baSEli Britstein 			attr->split_count = attr->out_count;
297973867881SPablo Neira Ayuso 			break;
2980bdc837eeSEli Britstein 		case FLOW_ACTION_VLAN_MANGLE:
2981bdc837eeSEli Britstein 			err = add_vlan_rewrite_action(priv,
2982bdc837eeSEli Britstein 						      MLX5_FLOW_NAMESPACE_FDB,
2983bdc837eeSEli Britstein 						      act, parse_attr, hdrs,
2984bdc837eeSEli Britstein 						      &action, extack);
2985bdc837eeSEli Britstein 			if (err)
2986bdc837eeSEli Britstein 				return err;
2987bdc837eeSEli Britstein 
2988bdc837eeSEli Britstein 			attr->split_count = attr->out_count;
2989bdc837eeSEli Britstein 			break;
299073867881SPablo Neira Ayuso 		case FLOW_ACTION_TUNNEL_DECAP:
29911cab1cd7SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_DECAP;
299273867881SPablo Neira Ayuso 			break;
299373867881SPablo Neira Ayuso 		case FLOW_ACTION_GOTO: {
299473867881SPablo Neira Ayuso 			u32 dest_chain = act->chain_index;
2995bf07aa73SPaul Blakey 			u32 max_chain = mlx5_eswitch_get_chain_range(esw);
2996bf07aa73SPaul Blakey 
2997bf07aa73SPaul Blakey 			if (dest_chain <= attr->chain) {
2998bf07aa73SPaul Blakey 				NL_SET_ERR_MSG(extack, "Goto earlier chain isn't supported");
2999bf07aa73SPaul Blakey 				return -EOPNOTSUPP;
3000bf07aa73SPaul Blakey 			}
3001bf07aa73SPaul Blakey 			if (dest_chain > max_chain) {
3002bf07aa73SPaul Blakey 				NL_SET_ERR_MSG(extack, "Requested destination chain is out of supported range");
3003bf07aa73SPaul Blakey 				return -EOPNOTSUPP;
3004bf07aa73SPaul Blakey 			}
3005e88afe75SOr Gerlitz 			action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
3006bf07aa73SPaul Blakey 			attr->dest_chain = dest_chain;
300773867881SPablo Neira Ayuso 			break;
3008bf07aa73SPaul Blakey 			}
300973867881SPablo Neira Ayuso 		default:
30102cc1cb1dSTonghao Zhang 			NL_SET_ERR_MSG_MOD(extack, "The offload action is not supported");
30112cc1cb1dSTonghao Zhang 			return -EOPNOTSUPP;
301203a9d11eSOr Gerlitz 		}
301373867881SPablo Neira Ayuso 	}
3014bdd66ac0SOr Gerlitz 
30150bac1194SEli Britstein 	if (MLX5_CAP_GEN(esw->dev, prio_tag_required) &&
30160bac1194SEli Britstein 	    action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) {
30170bac1194SEli Britstein 		/* For prio tag mode, replace vlan pop with rewrite vlan prio
30180bac1194SEli Britstein 		 * tag rewrite.
30190bac1194SEli Britstein 		 */
30200bac1194SEli Britstein 		action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
30210bac1194SEli Britstein 		err = add_vlan_prio_tag_rewrite_action(priv, parse_attr, hdrs,
30220bac1194SEli Britstein 						       &action, extack);
30230bac1194SEli Britstein 		if (err)
30240bac1194SEli Britstein 			return err;
30250bac1194SEli Britstein 	}
30260bac1194SEli Britstein 
3027c500c86bSPablo Neira Ayuso 	if (hdrs[TCA_PEDIT_KEY_EX_CMD_SET].pedits ||
3028c500c86bSPablo Neira Ayuso 	    hdrs[TCA_PEDIT_KEY_EX_CMD_ADD].pedits) {
302984be899fSTonghao Zhang 		err = alloc_tc_pedit_action(priv, MLX5_FLOW_NAMESPACE_FDB,
303027c11b6bSEli Britstein 					    parse_attr, hdrs, &action, extack);
3031c500c86bSPablo Neira Ayuso 		if (err)
3032c500c86bSPablo Neira Ayuso 			return err;
303327c11b6bSEli Britstein 		/* in case all pedit actions are skipped, remove the MOD_HDR
303427c11b6bSEli Britstein 		 * flag. we might have set split_count either by pedit or
303527c11b6bSEli Britstein 		 * pop/push. if there is no pop/push either, reset it too.
303627c11b6bSEli Britstein 		 */
303727c11b6bSEli Britstein 		if (parse_attr->num_mod_hdr_actions == 0) {
303827c11b6bSEli Britstein 			action &= ~MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
3039e7739a60SEli Britstein 			kfree(parse_attr->mod_hdr_actions);
304027c11b6bSEli Britstein 			if (!((action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) ||
304127c11b6bSEli Britstein 			      (action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH)))
304227c11b6bSEli Britstein 				attr->split_count = 0;
304327c11b6bSEli Britstein 		}
3044c500c86bSPablo Neira Ayuso 	}
3045c500c86bSPablo Neira Ayuso 
30461cab1cd7SOr Gerlitz 	attr->action = action;
304773867881SPablo Neira Ayuso 	if (!actions_match_supported(priv, flow_action, parse_attr, flow, extack))
3048bdd66ac0SOr Gerlitz 		return -EOPNOTSUPP;
3049bdd66ac0SOr Gerlitz 
3050e88afe75SOr Gerlitz 	if (attr->dest_chain) {
3051e88afe75SOr Gerlitz 		if (attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
3052e88afe75SOr Gerlitz 			NL_SET_ERR_MSG(extack, "Mirroring goto chain rules isn't supported");
3053e88afe75SOr Gerlitz 			return -EOPNOTSUPP;
3054e88afe75SOr Gerlitz 		}
3055e88afe75SOr Gerlitz 		attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
3056e88afe75SOr Gerlitz 	}
3057e88afe75SOr Gerlitz 
3058e85e02baSEli Britstein 	if (attr->split_count > 0 && !mlx5_esw_has_fwd_fdb(priv->mdev)) {
3059e98bedf5SEli Britstein 		NL_SET_ERR_MSG_MOD(extack,
3060e98bedf5SEli Britstein 				   "current firmware doesn't support split rule for port mirroring");
3061592d3651SChris Mi 		netdev_warn_once(priv->netdev, "current firmware doesn't support split rule for port mirroring\n");
3062592d3651SChris Mi 		return -EOPNOTSUPP;
3063592d3651SChris Mi 	}
3064592d3651SChris Mi 
306531c8eba5SOr Gerlitz 	return 0;
306603a9d11eSOr Gerlitz }
306703a9d11eSOr Gerlitz 
30685dbe906fSPaul Blakey static void get_flags(int flags, u16 *flow_flags)
306960bd4af8SOr Gerlitz {
30705dbe906fSPaul Blakey 	u16 __flow_flags = 0;
307160bd4af8SOr Gerlitz 
307260bd4af8SOr Gerlitz 	if (flags & MLX5E_TC_INGRESS)
307360bd4af8SOr Gerlitz 		__flow_flags |= MLX5E_TC_FLOW_INGRESS;
307460bd4af8SOr Gerlitz 	if (flags & MLX5E_TC_EGRESS)
307560bd4af8SOr Gerlitz 		__flow_flags |= MLX5E_TC_FLOW_EGRESS;
307660bd4af8SOr Gerlitz 
3077d9ee0491SOr Gerlitz 	if (flags & MLX5E_TC_ESW_OFFLOAD)
3078d9ee0491SOr Gerlitz 		__flow_flags |= MLX5E_TC_FLOW_ESWITCH;
3079d9ee0491SOr Gerlitz 	if (flags & MLX5E_TC_NIC_OFFLOAD)
3080d9ee0491SOr Gerlitz 		__flow_flags |= MLX5E_TC_FLOW_NIC;
3081d9ee0491SOr Gerlitz 
308260bd4af8SOr Gerlitz 	*flow_flags = __flow_flags;
308360bd4af8SOr Gerlitz }
308460bd4af8SOr Gerlitz 
308505866c82SOr Gerlitz static const struct rhashtable_params tc_ht_params = {
308605866c82SOr Gerlitz 	.head_offset = offsetof(struct mlx5e_tc_flow, node),
308705866c82SOr Gerlitz 	.key_offset = offsetof(struct mlx5e_tc_flow, cookie),
308805866c82SOr Gerlitz 	.key_len = sizeof(((struct mlx5e_tc_flow *)0)->cookie),
308905866c82SOr Gerlitz 	.automatic_shrinking = true,
309005866c82SOr Gerlitz };
309105866c82SOr Gerlitz 
3092d9ee0491SOr Gerlitz static struct rhashtable *get_tc_ht(struct mlx5e_priv *priv, int flags)
309305866c82SOr Gerlitz {
3094655dc3d2SOr Gerlitz 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
3095655dc3d2SOr Gerlitz 	struct mlx5e_rep_priv *uplink_rpriv;
3096655dc3d2SOr Gerlitz 
3097d9ee0491SOr Gerlitz 	if (flags & MLX5E_TC_ESW_OFFLOAD) {
3098655dc3d2SOr Gerlitz 		uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
3099ec1366c2SOz Shlomo 		return &uplink_rpriv->uplink_priv.tc_ht;
3100d9ee0491SOr Gerlitz 	} else /* NIC offload */
310105866c82SOr Gerlitz 		return &priv->fs.tc.ht;
310205866c82SOr Gerlitz }
310305866c82SOr Gerlitz 
310404de7ddaSRoi Dayan static bool is_peer_flow_needed(struct mlx5e_tc_flow *flow)
310504de7ddaSRoi Dayan {
31061418ddd9SAviv Heller 	struct mlx5_esw_flow_attr *attr = flow->esw_attr;
3107b05af6aaSBodong Wang 	bool is_rep_ingress = attr->in_rep->vport != MLX5_VPORT_UPLINK &&
31081418ddd9SAviv Heller 			      flow->flags & MLX5E_TC_FLOW_INGRESS;
31091418ddd9SAviv Heller 	bool act_is_encap = !!(attr->action &
31101418ddd9SAviv Heller 			       MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT);
31111418ddd9SAviv Heller 	bool esw_paired = mlx5_devcom_is_paired(attr->in_mdev->priv.devcom,
31121418ddd9SAviv Heller 						MLX5_DEVCOM_ESW_OFFLOADS);
31131418ddd9SAviv Heller 
311410fbb1cdSRoi Dayan 	if (!esw_paired)
311510fbb1cdSRoi Dayan 		return false;
311610fbb1cdSRoi Dayan 
311710fbb1cdSRoi Dayan 	if ((mlx5_lag_is_sriov(attr->in_mdev) ||
311810fbb1cdSRoi Dayan 	     mlx5_lag_is_multipath(attr->in_mdev)) &&
311910fbb1cdSRoi Dayan 	    (is_rep_ingress || act_is_encap))
312010fbb1cdSRoi Dayan 		return true;
312110fbb1cdSRoi Dayan 
312210fbb1cdSRoi Dayan 	return false;
312304de7ddaSRoi Dayan }
312404de7ddaSRoi Dayan 
3125a88780a9SRoi Dayan static int
3126a88780a9SRoi Dayan mlx5e_alloc_flow(struct mlx5e_priv *priv, int attr_size,
3127f9e30088SPablo Neira Ayuso 		 struct flow_cls_offload *f, u16 flow_flags,
3128a88780a9SRoi Dayan 		 struct mlx5e_tc_flow_parse_attr **__parse_attr,
3129a88780a9SRoi Dayan 		 struct mlx5e_tc_flow **__flow)
3130e3a2b7edSAmir Vadai {
313117091853SOr Gerlitz 	struct mlx5e_tc_flow_parse_attr *parse_attr;
31323bc4b7bfSOr Gerlitz 	struct mlx5e_tc_flow *flow;
31335a7e5bcbSVlad Buslov 	int out_index, err;
3134776b12b6SOr Gerlitz 
313565ba8fb7SOr Gerlitz 	flow = kzalloc(sizeof(*flow) + attr_size, GFP_KERNEL);
31361b9a07eeSLeon Romanovsky 	parse_attr = kvzalloc(sizeof(*parse_attr), GFP_KERNEL);
313717091853SOr Gerlitz 	if (!parse_attr || !flow) {
3138e3a2b7edSAmir Vadai 		err = -ENOMEM;
3139e3a2b7edSAmir Vadai 		goto err_free;
3140e3a2b7edSAmir Vadai 	}
3141e3a2b7edSAmir Vadai 
3142e3a2b7edSAmir Vadai 	flow->cookie = f->cookie;
314365ba8fb7SOr Gerlitz 	flow->flags = flow_flags;
3144655dc3d2SOr Gerlitz 	flow->priv = priv;
31455a7e5bcbSVlad Buslov 	for (out_index = 0; out_index < MLX5_MAX_FLOW_FWD_VPORTS; out_index++)
31465a7e5bcbSVlad Buslov 		INIT_LIST_HEAD(&flow->encaps[out_index].list);
31475a7e5bcbSVlad Buslov 	INIT_LIST_HEAD(&flow->mod_hdr);
31485a7e5bcbSVlad Buslov 	INIT_LIST_HEAD(&flow->hairpin);
31495a7e5bcbSVlad Buslov 	refcount_set(&flow->refcnt, 1);
3150e3a2b7edSAmir Vadai 
3151a88780a9SRoi Dayan 	*__flow = flow;
3152a88780a9SRoi Dayan 	*__parse_attr = parse_attr;
3153a88780a9SRoi Dayan 
3154a88780a9SRoi Dayan 	return 0;
3155a88780a9SRoi Dayan 
3156a88780a9SRoi Dayan err_free:
3157a88780a9SRoi Dayan 	kfree(flow);
3158a88780a9SRoi Dayan 	kvfree(parse_attr);
3159a88780a9SRoi Dayan 	return err;
3160adb4c123SOr Gerlitz }
3161adb4c123SOr Gerlitz 
3162988ab9c7STonghao Zhang static void
3163988ab9c7STonghao Zhang mlx5e_flow_esw_attr_init(struct mlx5_esw_flow_attr *esw_attr,
3164988ab9c7STonghao Zhang 			 struct mlx5e_priv *priv,
3165988ab9c7STonghao Zhang 			 struct mlx5e_tc_flow_parse_attr *parse_attr,
3166f9e30088SPablo Neira Ayuso 			 struct flow_cls_offload *f,
3167988ab9c7STonghao Zhang 			 struct mlx5_eswitch_rep *in_rep,
3168988ab9c7STonghao Zhang 			 struct mlx5_core_dev *in_mdev)
3169988ab9c7STonghao Zhang {
3170988ab9c7STonghao Zhang 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
3171988ab9c7STonghao Zhang 
3172988ab9c7STonghao Zhang 	esw_attr->parse_attr = parse_attr;
3173988ab9c7STonghao Zhang 	esw_attr->chain = f->common.chain_index;
3174988ab9c7STonghao Zhang 	esw_attr->prio = TC_H_MAJ(f->common.prio) >> 16;
3175988ab9c7STonghao Zhang 
3176988ab9c7STonghao Zhang 	esw_attr->in_rep = in_rep;
3177988ab9c7STonghao Zhang 	esw_attr->in_mdev = in_mdev;
3178988ab9c7STonghao Zhang 
3179988ab9c7STonghao Zhang 	if (MLX5_CAP_ESW(esw->dev, counter_eswitch_affinity) ==
3180988ab9c7STonghao Zhang 	    MLX5_COUNTER_SOURCE_ESWITCH)
3181988ab9c7STonghao Zhang 		esw_attr->counter_dev = in_mdev;
3182988ab9c7STonghao Zhang 	else
3183988ab9c7STonghao Zhang 		esw_attr->counter_dev = priv->mdev;
3184988ab9c7STonghao Zhang }
3185988ab9c7STonghao Zhang 
318671129676SJason Gunthorpe static struct mlx5e_tc_flow *
318704de7ddaSRoi Dayan __mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
3188f9e30088SPablo Neira Ayuso 		     struct flow_cls_offload *f,
31895dbe906fSPaul Blakey 		     u16 flow_flags,
3190d11afc26SOz Shlomo 		     struct net_device *filter_dev,
319104de7ddaSRoi Dayan 		     struct mlx5_eswitch_rep *in_rep,
319271129676SJason Gunthorpe 		     struct mlx5_core_dev *in_mdev)
3193a88780a9SRoi Dayan {
3194f9e30088SPablo Neira Ayuso 	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
3195a88780a9SRoi Dayan 	struct netlink_ext_ack *extack = f->common.extack;
3196a88780a9SRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr;
3197a88780a9SRoi Dayan 	struct mlx5e_tc_flow *flow;
3198a88780a9SRoi Dayan 	int attr_size, err;
3199a88780a9SRoi Dayan 
3200a88780a9SRoi Dayan 	flow_flags |= MLX5E_TC_FLOW_ESWITCH;
3201a88780a9SRoi Dayan 	attr_size  = sizeof(struct mlx5_esw_flow_attr);
3202a88780a9SRoi Dayan 	err = mlx5e_alloc_flow(priv, attr_size, f, flow_flags,
3203a88780a9SRoi Dayan 			       &parse_attr, &flow);
3204a88780a9SRoi Dayan 	if (err)
3205a88780a9SRoi Dayan 		goto out;
3206988ab9c7STonghao Zhang 
3207d11afc26SOz Shlomo 	parse_attr->filter_dev = filter_dev;
3208988ab9c7STonghao Zhang 	mlx5e_flow_esw_attr_init(flow->esw_attr,
3209988ab9c7STonghao Zhang 				 priv, parse_attr,
3210988ab9c7STonghao Zhang 				 f, in_rep, in_mdev);
3211988ab9c7STonghao Zhang 
321254c177caSOz Shlomo 	err = parse_cls_flower(flow->priv, flow, &parse_attr->spec,
321354c177caSOz Shlomo 			       f, filter_dev);
3214d11afc26SOz Shlomo 	if (err)
3215d11afc26SOz Shlomo 		goto err_free;
3216a88780a9SRoi Dayan 
32176f9af8ffSTonghao Zhang 	err = parse_tc_fdb_actions(priv, &rule->action, flow, extack);
3218a88780a9SRoi Dayan 	if (err)
3219a88780a9SRoi Dayan 		goto err_free;
3220a88780a9SRoi Dayan 
32217040632dSTonghao Zhang 	err = mlx5e_tc_add_fdb_flow(priv, flow, extack);
3222ef06c9eeSRoi Dayan 	if (err) {
3223ef06c9eeSRoi Dayan 		if (!(err == -ENETUNREACH && mlx5_lag_is_multipath(in_mdev)))
3224aa0cbbaeSOr Gerlitz 			goto err_free;
32255c40348cSOr Gerlitz 
3226b4a23329SRoi Dayan 		add_unready_flow(flow);
3227ef06c9eeSRoi Dayan 	}
3228ef06c9eeSRoi Dayan 
322971129676SJason Gunthorpe 	return flow;
3230e3a2b7edSAmir Vadai 
3231e3a2b7edSAmir Vadai err_free:
32325a7e5bcbSVlad Buslov 	mlx5e_flow_put(priv, flow);
3233a88780a9SRoi Dayan out:
323471129676SJason Gunthorpe 	return ERR_PTR(err);
3235a88780a9SRoi Dayan }
3236a88780a9SRoi Dayan 
3237f9e30088SPablo Neira Ayuso static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f,
323895dc1902SRoi Dayan 				      struct mlx5e_tc_flow *flow,
323995dc1902SRoi Dayan 				      u16 flow_flags)
324004de7ddaSRoi Dayan {
324104de7ddaSRoi Dayan 	struct mlx5e_priv *priv = flow->priv, *peer_priv;
324204de7ddaSRoi Dayan 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch, *peer_esw;
324304de7ddaSRoi Dayan 	struct mlx5_devcom *devcom = priv->mdev->priv.devcom;
324404de7ddaSRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr;
324504de7ddaSRoi Dayan 	struct mlx5e_rep_priv *peer_urpriv;
324604de7ddaSRoi Dayan 	struct mlx5e_tc_flow *peer_flow;
324704de7ddaSRoi Dayan 	struct mlx5_core_dev *in_mdev;
324804de7ddaSRoi Dayan 	int err = 0;
324904de7ddaSRoi Dayan 
325004de7ddaSRoi Dayan 	peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
325104de7ddaSRoi Dayan 	if (!peer_esw)
325204de7ddaSRoi Dayan 		return -ENODEV;
325304de7ddaSRoi Dayan 
325404de7ddaSRoi Dayan 	peer_urpriv = mlx5_eswitch_get_uplink_priv(peer_esw, REP_ETH);
325504de7ddaSRoi Dayan 	peer_priv = netdev_priv(peer_urpriv->netdev);
325604de7ddaSRoi Dayan 
325704de7ddaSRoi Dayan 	/* in_mdev is assigned of which the packet originated from.
325804de7ddaSRoi Dayan 	 * So packets redirected to uplink use the same mdev of the
325904de7ddaSRoi Dayan 	 * original flow and packets redirected from uplink use the
326004de7ddaSRoi Dayan 	 * peer mdev.
326104de7ddaSRoi Dayan 	 */
3262b05af6aaSBodong Wang 	if (flow->esw_attr->in_rep->vport == MLX5_VPORT_UPLINK)
326304de7ddaSRoi Dayan 		in_mdev = peer_priv->mdev;
326404de7ddaSRoi Dayan 	else
326504de7ddaSRoi Dayan 		in_mdev = priv->mdev;
326604de7ddaSRoi Dayan 
326704de7ddaSRoi Dayan 	parse_attr = flow->esw_attr->parse_attr;
326895dc1902SRoi Dayan 	peer_flow = __mlx5e_add_fdb_flow(peer_priv, f, flow_flags,
326904de7ddaSRoi Dayan 					 parse_attr->filter_dev,
327071129676SJason Gunthorpe 					 flow->esw_attr->in_rep, in_mdev);
327171129676SJason Gunthorpe 	if (IS_ERR(peer_flow)) {
327271129676SJason Gunthorpe 		err = PTR_ERR(peer_flow);
327304de7ddaSRoi Dayan 		goto out;
327471129676SJason Gunthorpe 	}
327504de7ddaSRoi Dayan 
327604de7ddaSRoi Dayan 	flow->peer_flow = peer_flow;
327704de7ddaSRoi Dayan 	flow->flags |= MLX5E_TC_FLOW_DUP;
327804de7ddaSRoi Dayan 	mutex_lock(&esw->offloads.peer_mutex);
327904de7ddaSRoi Dayan 	list_add_tail(&flow->peer, &esw->offloads.peer_flows);
328004de7ddaSRoi Dayan 	mutex_unlock(&esw->offloads.peer_mutex);
328104de7ddaSRoi Dayan 
328204de7ddaSRoi Dayan out:
328304de7ddaSRoi Dayan 	mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
328404de7ddaSRoi Dayan 	return err;
328504de7ddaSRoi Dayan }
328604de7ddaSRoi Dayan 
328704de7ddaSRoi Dayan static int
328804de7ddaSRoi Dayan mlx5e_add_fdb_flow(struct mlx5e_priv *priv,
3289f9e30088SPablo Neira Ayuso 		   struct flow_cls_offload *f,
329004de7ddaSRoi Dayan 		   u16 flow_flags,
329104de7ddaSRoi Dayan 		   struct net_device *filter_dev,
329204de7ddaSRoi Dayan 		   struct mlx5e_tc_flow **__flow)
329304de7ddaSRoi Dayan {
329404de7ddaSRoi Dayan 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
329504de7ddaSRoi Dayan 	struct mlx5_eswitch_rep *in_rep = rpriv->rep;
329604de7ddaSRoi Dayan 	struct mlx5_core_dev *in_mdev = priv->mdev;
329704de7ddaSRoi Dayan 	struct mlx5e_tc_flow *flow;
329804de7ddaSRoi Dayan 	int err;
329904de7ddaSRoi Dayan 
330071129676SJason Gunthorpe 	flow = __mlx5e_add_fdb_flow(priv, f, flow_flags, filter_dev, in_rep,
330171129676SJason Gunthorpe 				    in_mdev);
330271129676SJason Gunthorpe 	if (IS_ERR(flow))
330371129676SJason Gunthorpe 		return PTR_ERR(flow);
330404de7ddaSRoi Dayan 
330504de7ddaSRoi Dayan 	if (is_peer_flow_needed(flow)) {
330695dc1902SRoi Dayan 		err = mlx5e_tc_add_fdb_peer_flow(f, flow, flow_flags);
330704de7ddaSRoi Dayan 		if (err) {
330804de7ddaSRoi Dayan 			mlx5e_tc_del_fdb_flow(priv, flow);
330904de7ddaSRoi Dayan 			goto out;
331004de7ddaSRoi Dayan 		}
331104de7ddaSRoi Dayan 	}
331204de7ddaSRoi Dayan 
331304de7ddaSRoi Dayan 	*__flow = flow;
331404de7ddaSRoi Dayan 
331504de7ddaSRoi Dayan 	return 0;
331604de7ddaSRoi Dayan 
331704de7ddaSRoi Dayan out:
331804de7ddaSRoi Dayan 	return err;
331904de7ddaSRoi Dayan }
332004de7ddaSRoi Dayan 
3321a88780a9SRoi Dayan static int
3322a88780a9SRoi Dayan mlx5e_add_nic_flow(struct mlx5e_priv *priv,
3323f9e30088SPablo Neira Ayuso 		   struct flow_cls_offload *f,
33245dbe906fSPaul Blakey 		   u16 flow_flags,
3325d11afc26SOz Shlomo 		   struct net_device *filter_dev,
3326a88780a9SRoi Dayan 		   struct mlx5e_tc_flow **__flow)
3327a88780a9SRoi Dayan {
3328f9e30088SPablo Neira Ayuso 	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
3329a88780a9SRoi Dayan 	struct netlink_ext_ack *extack = f->common.extack;
3330a88780a9SRoi Dayan 	struct mlx5e_tc_flow_parse_attr *parse_attr;
3331a88780a9SRoi Dayan 	struct mlx5e_tc_flow *flow;
3332a88780a9SRoi Dayan 	int attr_size, err;
3333a88780a9SRoi Dayan 
3334bf07aa73SPaul Blakey 	/* multi-chain not supported for NIC rules */
3335bf07aa73SPaul Blakey 	if (!tc_cls_can_offload_and_chain0(priv->netdev, &f->common))
3336bf07aa73SPaul Blakey 		return -EOPNOTSUPP;
3337bf07aa73SPaul Blakey 
3338a88780a9SRoi Dayan 	flow_flags |= MLX5E_TC_FLOW_NIC;
3339a88780a9SRoi Dayan 	attr_size  = sizeof(struct mlx5_nic_flow_attr);
3340a88780a9SRoi Dayan 	err = mlx5e_alloc_flow(priv, attr_size, f, flow_flags,
3341a88780a9SRoi Dayan 			       &parse_attr, &flow);
3342a88780a9SRoi Dayan 	if (err)
3343a88780a9SRoi Dayan 		goto out;
3344a88780a9SRoi Dayan 
3345d11afc26SOz Shlomo 	parse_attr->filter_dev = filter_dev;
334654c177caSOz Shlomo 	err = parse_cls_flower(flow->priv, flow, &parse_attr->spec,
334754c177caSOz Shlomo 			       f, filter_dev);
3348d11afc26SOz Shlomo 	if (err)
3349d11afc26SOz Shlomo 		goto err_free;
3350d11afc26SOz Shlomo 
335173867881SPablo Neira Ayuso 	err = parse_tc_nic_actions(priv, &rule->action, parse_attr, flow, extack);
3352a88780a9SRoi Dayan 	if (err)
3353a88780a9SRoi Dayan 		goto err_free;
3354a88780a9SRoi Dayan 
3355a88780a9SRoi Dayan 	err = mlx5e_tc_add_nic_flow(priv, parse_attr, flow, extack);
3356a88780a9SRoi Dayan 	if (err)
3357a88780a9SRoi Dayan 		goto err_free;
3358a88780a9SRoi Dayan 
3359a88780a9SRoi Dayan 	flow->flags |= MLX5E_TC_FLOW_OFFLOADED;
3360a88780a9SRoi Dayan 	kvfree(parse_attr);
3361a88780a9SRoi Dayan 	*__flow = flow;
3362a88780a9SRoi Dayan 
3363a88780a9SRoi Dayan 	return 0;
3364a88780a9SRoi Dayan 
3365a88780a9SRoi Dayan err_free:
33665a7e5bcbSVlad Buslov 	mlx5e_flow_put(priv, flow);
3367a88780a9SRoi Dayan 	kvfree(parse_attr);
3368a88780a9SRoi Dayan out:
3369a88780a9SRoi Dayan 	return err;
3370a88780a9SRoi Dayan }
3371a88780a9SRoi Dayan 
3372a88780a9SRoi Dayan static int
3373a88780a9SRoi Dayan mlx5e_tc_add_flow(struct mlx5e_priv *priv,
3374f9e30088SPablo Neira Ayuso 		  struct flow_cls_offload *f,
3375a88780a9SRoi Dayan 		  int flags,
3376d11afc26SOz Shlomo 		  struct net_device *filter_dev,
3377a88780a9SRoi Dayan 		  struct mlx5e_tc_flow **flow)
3378a88780a9SRoi Dayan {
3379a88780a9SRoi Dayan 	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
33805dbe906fSPaul Blakey 	u16 flow_flags;
3381a88780a9SRoi Dayan 	int err;
3382a88780a9SRoi Dayan 
3383a88780a9SRoi Dayan 	get_flags(flags, &flow_flags);
3384a88780a9SRoi Dayan 
3385bf07aa73SPaul Blakey 	if (!tc_can_offload_extack(priv->netdev, f->common.extack))
3386bf07aa73SPaul Blakey 		return -EOPNOTSUPP;
3387bf07aa73SPaul Blakey 
3388f6455de0SBodong Wang 	if (esw && esw->mode == MLX5_ESWITCH_OFFLOADS)
3389d11afc26SOz Shlomo 		err = mlx5e_add_fdb_flow(priv, f, flow_flags,
3390d11afc26SOz Shlomo 					 filter_dev, flow);
3391a88780a9SRoi Dayan 	else
3392d11afc26SOz Shlomo 		err = mlx5e_add_nic_flow(priv, f, flow_flags,
3393d11afc26SOz Shlomo 					 filter_dev, flow);
3394a88780a9SRoi Dayan 
3395a88780a9SRoi Dayan 	return err;
3396a88780a9SRoi Dayan }
3397a88780a9SRoi Dayan 
339871d82d2aSOz Shlomo int mlx5e_configure_flower(struct net_device *dev, struct mlx5e_priv *priv,
3399f9e30088SPablo Neira Ayuso 			   struct flow_cls_offload *f, int flags)
3400a88780a9SRoi Dayan {
3401a88780a9SRoi Dayan 	struct netlink_ext_ack *extack = f->common.extack;
3402d9ee0491SOr Gerlitz 	struct rhashtable *tc_ht = get_tc_ht(priv, flags);
3403a88780a9SRoi Dayan 	struct mlx5e_tc_flow *flow;
3404a88780a9SRoi Dayan 	int err = 0;
3405a88780a9SRoi Dayan 
3406a88780a9SRoi Dayan 	flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
3407a88780a9SRoi Dayan 	if (flow) {
3408a88780a9SRoi Dayan 		NL_SET_ERR_MSG_MOD(extack,
3409a88780a9SRoi Dayan 				   "flow cookie already exists, ignoring");
3410a88780a9SRoi Dayan 		netdev_warn_once(priv->netdev,
3411a88780a9SRoi Dayan 				 "flow cookie %lx already exists, ignoring\n",
3412a88780a9SRoi Dayan 				 f->cookie);
34130e1c1a2fSVlad Buslov 		err = -EEXIST;
3414a88780a9SRoi Dayan 		goto out;
3415a88780a9SRoi Dayan 	}
3416a88780a9SRoi Dayan 
3417d11afc26SOz Shlomo 	err = mlx5e_tc_add_flow(priv, f, flags, dev, &flow);
3418a88780a9SRoi Dayan 	if (err)
3419a88780a9SRoi Dayan 		goto out;
3420a88780a9SRoi Dayan 
3421a88780a9SRoi Dayan 	err = rhashtable_insert_fast(tc_ht, &flow->node, tc_ht_params);
3422a88780a9SRoi Dayan 	if (err)
3423a88780a9SRoi Dayan 		goto err_free;
3424a88780a9SRoi Dayan 
3425a88780a9SRoi Dayan 	return 0;
3426a88780a9SRoi Dayan 
3427a88780a9SRoi Dayan err_free:
34285a7e5bcbSVlad Buslov 	mlx5e_flow_put(priv, flow);
3429a88780a9SRoi Dayan out:
3430e3a2b7edSAmir Vadai 	return err;
3431e3a2b7edSAmir Vadai }
3432e3a2b7edSAmir Vadai 
34338f8ae895SOr Gerlitz #define DIRECTION_MASK (MLX5E_TC_INGRESS | MLX5E_TC_EGRESS)
34348f8ae895SOr Gerlitz #define FLOW_DIRECTION_MASK (MLX5E_TC_FLOW_INGRESS | MLX5E_TC_FLOW_EGRESS)
34358f8ae895SOr Gerlitz 
34368f8ae895SOr Gerlitz static bool same_flow_direction(struct mlx5e_tc_flow *flow, int flags)
34378f8ae895SOr Gerlitz {
34388f8ae895SOr Gerlitz 	if ((flow->flags & FLOW_DIRECTION_MASK) == (flags & DIRECTION_MASK))
34398f8ae895SOr Gerlitz 		return true;
34408f8ae895SOr Gerlitz 
34418f8ae895SOr Gerlitz 	return false;
34428f8ae895SOr Gerlitz }
34438f8ae895SOr Gerlitz 
344471d82d2aSOz Shlomo int mlx5e_delete_flower(struct net_device *dev, struct mlx5e_priv *priv,
3445f9e30088SPablo Neira Ayuso 			struct flow_cls_offload *f, int flags)
3446e3a2b7edSAmir Vadai {
3447d9ee0491SOr Gerlitz 	struct rhashtable *tc_ht = get_tc_ht(priv, flags);
3448e3a2b7edSAmir Vadai 	struct mlx5e_tc_flow *flow;
3449e3a2b7edSAmir Vadai 
345005866c82SOr Gerlitz 	flow = rhashtable_lookup_fast(tc_ht, &f->cookie, tc_ht_params);
34518f8ae895SOr Gerlitz 	if (!flow || !same_flow_direction(flow, flags))
3452e3a2b7edSAmir Vadai 		return -EINVAL;
3453e3a2b7edSAmir Vadai 
345405866c82SOr Gerlitz 	rhashtable_remove_fast(tc_ht, &flow->node, tc_ht_params);
3455e3a2b7edSAmir Vadai 
34565a7e5bcbSVlad Buslov 	mlx5e_flow_put(priv, flow);
3457e3a2b7edSAmir Vadai 
3458e3a2b7edSAmir Vadai 	return 0;
3459e3a2b7edSAmir Vadai }
3460e3a2b7edSAmir Vadai 
346171d82d2aSOz Shlomo int mlx5e_stats_flower(struct net_device *dev, struct mlx5e_priv *priv,
3462f9e30088SPablo Neira Ayuso 		       struct flow_cls_offload *f, int flags)
3463aad7e08dSAmir Vadai {
346404de7ddaSRoi Dayan 	struct mlx5_devcom *devcom = priv->mdev->priv.devcom;
3465d9ee0491SOr Gerlitz 	struct rhashtable *tc_ht = get_tc_ht(priv, flags);
346604de7ddaSRoi Dayan 	struct mlx5_eswitch *peer_esw;
3467aad7e08dSAmir Vadai 	struct mlx5e_tc_flow *flow;
3468aad7e08dSAmir Vadai 	struct mlx5_fc *counter;
3469316d5f72SRoi Dayan 	u64 lastuse = 0;
3470316d5f72SRoi Dayan 	u64 packets = 0;
3471316d5f72SRoi Dayan 	u64 bytes = 0;
34725a7e5bcbSVlad Buslov 	int err = 0;
3473aad7e08dSAmir Vadai 
34745a7e5bcbSVlad Buslov 	flow = mlx5e_flow_get(rhashtable_lookup_fast(tc_ht, &f->cookie,
34755a7e5bcbSVlad Buslov 						     tc_ht_params));
34765a7e5bcbSVlad Buslov 	if (IS_ERR(flow))
34775a7e5bcbSVlad Buslov 		return PTR_ERR(flow);
34785a7e5bcbSVlad Buslov 
34795a7e5bcbSVlad Buslov 	if (!same_flow_direction(flow, flags)) {
34805a7e5bcbSVlad Buslov 		err = -EINVAL;
34815a7e5bcbSVlad Buslov 		goto errout;
34825a7e5bcbSVlad Buslov 	}
3483aad7e08dSAmir Vadai 
3484316d5f72SRoi Dayan 	if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) {
3485b8aee822SMark Bloch 		counter = mlx5e_tc_get_counter(flow);
3486aad7e08dSAmir Vadai 		if (!counter)
34875a7e5bcbSVlad Buslov 			goto errout;
3488aad7e08dSAmir Vadai 
3489aad7e08dSAmir Vadai 		mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse);
3490316d5f72SRoi Dayan 	}
3491aad7e08dSAmir Vadai 
3492316d5f72SRoi Dayan 	/* Under multipath it's possible for one rule to be currently
3493316d5f72SRoi Dayan 	 * un-offloaded while the other rule is offloaded.
3494316d5f72SRoi Dayan 	 */
349504de7ddaSRoi Dayan 	peer_esw = mlx5_devcom_get_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
349604de7ddaSRoi Dayan 	if (!peer_esw)
349704de7ddaSRoi Dayan 		goto out;
349804de7ddaSRoi Dayan 
349904de7ddaSRoi Dayan 	if ((flow->flags & MLX5E_TC_FLOW_DUP) &&
350004de7ddaSRoi Dayan 	    (flow->peer_flow->flags & MLX5E_TC_FLOW_OFFLOADED)) {
350104de7ddaSRoi Dayan 		u64 bytes2;
350204de7ddaSRoi Dayan 		u64 packets2;
350304de7ddaSRoi Dayan 		u64 lastuse2;
350404de7ddaSRoi Dayan 
350504de7ddaSRoi Dayan 		counter = mlx5e_tc_get_counter(flow->peer_flow);
3506316d5f72SRoi Dayan 		if (!counter)
3507316d5f72SRoi Dayan 			goto no_peer_counter;
350804de7ddaSRoi Dayan 		mlx5_fc_query_cached(counter, &bytes2, &packets2, &lastuse2);
350904de7ddaSRoi Dayan 
351004de7ddaSRoi Dayan 		bytes += bytes2;
351104de7ddaSRoi Dayan 		packets += packets2;
351204de7ddaSRoi Dayan 		lastuse = max_t(u64, lastuse, lastuse2);
351304de7ddaSRoi Dayan 	}
351404de7ddaSRoi Dayan 
3515316d5f72SRoi Dayan no_peer_counter:
351604de7ddaSRoi Dayan 	mlx5_devcom_release_peer_data(devcom, MLX5_DEVCOM_ESW_OFFLOADS);
351704de7ddaSRoi Dayan out:
35183b1903efSPablo Neira Ayuso 	flow_stats_update(&f->stats, bytes, packets, lastuse);
35195a7e5bcbSVlad Buslov errout:
35205a7e5bcbSVlad Buslov 	mlx5e_flow_put(priv, flow);
35215a7e5bcbSVlad Buslov 	return err;
3522aad7e08dSAmir Vadai }
3523aad7e08dSAmir Vadai 
35244d8fcf21SAlaa Hleihel static void mlx5e_tc_hairpin_update_dead_peer(struct mlx5e_priv *priv,
35254d8fcf21SAlaa Hleihel 					      struct mlx5e_priv *peer_priv)
35264d8fcf21SAlaa Hleihel {
35274d8fcf21SAlaa Hleihel 	struct mlx5_core_dev *peer_mdev = peer_priv->mdev;
35284d8fcf21SAlaa Hleihel 	struct mlx5e_hairpin_entry *hpe;
35294d8fcf21SAlaa Hleihel 	u16 peer_vhca_id;
35304d8fcf21SAlaa Hleihel 	int bkt;
35314d8fcf21SAlaa Hleihel 
35324d8fcf21SAlaa Hleihel 	if (!same_hw_devs(priv, peer_priv))
35334d8fcf21SAlaa Hleihel 		return;
35344d8fcf21SAlaa Hleihel 
35354d8fcf21SAlaa Hleihel 	peer_vhca_id = MLX5_CAP_GEN(peer_mdev, vhca_id);
35364d8fcf21SAlaa Hleihel 
35374d8fcf21SAlaa Hleihel 	hash_for_each(priv->fs.tc.hairpin_tbl, bkt, hpe, hairpin_hlist) {
35384d8fcf21SAlaa Hleihel 		if (hpe->peer_vhca_id == peer_vhca_id)
35394d8fcf21SAlaa Hleihel 			hpe->hp->pair->peer_gone = true;
35404d8fcf21SAlaa Hleihel 	}
35414d8fcf21SAlaa Hleihel }
35424d8fcf21SAlaa Hleihel 
35434d8fcf21SAlaa Hleihel static int mlx5e_tc_netdev_event(struct notifier_block *this,
35444d8fcf21SAlaa Hleihel 				 unsigned long event, void *ptr)
35454d8fcf21SAlaa Hleihel {
35464d8fcf21SAlaa Hleihel 	struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
35474d8fcf21SAlaa Hleihel 	struct mlx5e_flow_steering *fs;
35484d8fcf21SAlaa Hleihel 	struct mlx5e_priv *peer_priv;
35494d8fcf21SAlaa Hleihel 	struct mlx5e_tc_table *tc;
35504d8fcf21SAlaa Hleihel 	struct mlx5e_priv *priv;
35514d8fcf21SAlaa Hleihel 
35524d8fcf21SAlaa Hleihel 	if (ndev->netdev_ops != &mlx5e_netdev_ops ||
35534d8fcf21SAlaa Hleihel 	    event != NETDEV_UNREGISTER ||
35544d8fcf21SAlaa Hleihel 	    ndev->reg_state == NETREG_REGISTERED)
35554d8fcf21SAlaa Hleihel 		return NOTIFY_DONE;
35564d8fcf21SAlaa Hleihel 
35574d8fcf21SAlaa Hleihel 	tc = container_of(this, struct mlx5e_tc_table, netdevice_nb);
35584d8fcf21SAlaa Hleihel 	fs = container_of(tc, struct mlx5e_flow_steering, tc);
35594d8fcf21SAlaa Hleihel 	priv = container_of(fs, struct mlx5e_priv, fs);
35604d8fcf21SAlaa Hleihel 	peer_priv = netdev_priv(ndev);
35614d8fcf21SAlaa Hleihel 	if (priv == peer_priv ||
35624d8fcf21SAlaa Hleihel 	    !(priv->netdev->features & NETIF_F_HW_TC))
35634d8fcf21SAlaa Hleihel 		return NOTIFY_DONE;
35644d8fcf21SAlaa Hleihel 
35654d8fcf21SAlaa Hleihel 	mlx5e_tc_hairpin_update_dead_peer(priv, peer_priv);
35664d8fcf21SAlaa Hleihel 
35674d8fcf21SAlaa Hleihel 	return NOTIFY_DONE;
35684d8fcf21SAlaa Hleihel }
35694d8fcf21SAlaa Hleihel 
3570655dc3d2SOr Gerlitz int mlx5e_tc_nic_init(struct mlx5e_priv *priv)
3571e8f887acSAmir Vadai {
3572acff797cSMaor Gottlieb 	struct mlx5e_tc_table *tc = &priv->fs.tc;
35734d8fcf21SAlaa Hleihel 	int err;
3574e8f887acSAmir Vadai 
357511c9c548SOr Gerlitz 	hash_init(tc->mod_hdr_tbl);
35765c65c564SOr Gerlitz 	hash_init(tc->hairpin_tbl);
357711c9c548SOr Gerlitz 
35784d8fcf21SAlaa Hleihel 	err = rhashtable_init(&tc->ht, &tc_ht_params);
35794d8fcf21SAlaa Hleihel 	if (err)
35804d8fcf21SAlaa Hleihel 		return err;
35814d8fcf21SAlaa Hleihel 
35824d8fcf21SAlaa Hleihel 	tc->netdevice_nb.notifier_call = mlx5e_tc_netdev_event;
35834d8fcf21SAlaa Hleihel 	if (register_netdevice_notifier(&tc->netdevice_nb)) {
35844d8fcf21SAlaa Hleihel 		tc->netdevice_nb.notifier_call = NULL;
35854d8fcf21SAlaa Hleihel 		mlx5_core_warn(priv->mdev, "Failed to register netdev notifier\n");
35864d8fcf21SAlaa Hleihel 	}
35874d8fcf21SAlaa Hleihel 
35884d8fcf21SAlaa Hleihel 	return err;
3589e8f887acSAmir Vadai }
3590e8f887acSAmir Vadai 
3591e8f887acSAmir Vadai static void _mlx5e_tc_del_flow(void *ptr, void *arg)
3592e8f887acSAmir Vadai {
3593e8f887acSAmir Vadai 	struct mlx5e_tc_flow *flow = ptr;
3594655dc3d2SOr Gerlitz 	struct mlx5e_priv *priv = flow->priv;
3595e8f887acSAmir Vadai 
3596961e8979SRoi Dayan 	mlx5e_tc_del_flow(priv, flow);
3597e8f887acSAmir Vadai 	kfree(flow);
3598e8f887acSAmir Vadai }
3599e8f887acSAmir Vadai 
3600655dc3d2SOr Gerlitz void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv)
3601e8f887acSAmir Vadai {
3602acff797cSMaor Gottlieb 	struct mlx5e_tc_table *tc = &priv->fs.tc;
3603e8f887acSAmir Vadai 
36044d8fcf21SAlaa Hleihel 	if (tc->netdevice_nb.notifier_call)
36054d8fcf21SAlaa Hleihel 		unregister_netdevice_notifier(&tc->netdevice_nb);
36064d8fcf21SAlaa Hleihel 
3607d9ee0491SOr Gerlitz 	rhashtable_destroy(&tc->ht);
3608e8f887acSAmir Vadai 
3609acff797cSMaor Gottlieb 	if (!IS_ERR_OR_NULL(tc->t)) {
3610acff797cSMaor Gottlieb 		mlx5_destroy_flow_table(tc->t);
3611acff797cSMaor Gottlieb 		tc->t = NULL;
3612e8f887acSAmir Vadai 	}
3613e8f887acSAmir Vadai }
3614655dc3d2SOr Gerlitz 
3615655dc3d2SOr Gerlitz int mlx5e_tc_esw_init(struct rhashtable *tc_ht)
3616655dc3d2SOr Gerlitz {
3617655dc3d2SOr Gerlitz 	return rhashtable_init(tc_ht, &tc_ht_params);
3618655dc3d2SOr Gerlitz }
3619655dc3d2SOr Gerlitz 
3620655dc3d2SOr Gerlitz void mlx5e_tc_esw_cleanup(struct rhashtable *tc_ht)
3621655dc3d2SOr Gerlitz {
3622655dc3d2SOr Gerlitz 	rhashtable_free_and_destroy(tc_ht, _mlx5e_tc_del_flow, NULL);
3623655dc3d2SOr Gerlitz }
362401252a27SOr Gerlitz 
3625d9ee0491SOr Gerlitz int mlx5e_tc_num_filters(struct mlx5e_priv *priv, int flags)
362601252a27SOr Gerlitz {
3627d9ee0491SOr Gerlitz 	struct rhashtable *tc_ht = get_tc_ht(priv, flags);
362801252a27SOr Gerlitz 
362901252a27SOr Gerlitz 	return atomic_read(&tc_ht->nelems);
363001252a27SOr Gerlitz }
363104de7ddaSRoi Dayan 
363204de7ddaSRoi Dayan void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw)
363304de7ddaSRoi Dayan {
363404de7ddaSRoi Dayan 	struct mlx5e_tc_flow *flow, *tmp;
363504de7ddaSRoi Dayan 
363604de7ddaSRoi Dayan 	list_for_each_entry_safe(flow, tmp, &esw->offloads.peer_flows, peer)
363704de7ddaSRoi Dayan 		__mlx5e_tc_del_fdb_peer_flow(flow);
363804de7ddaSRoi Dayan }
3639b4a23329SRoi Dayan 
3640b4a23329SRoi Dayan void mlx5e_tc_reoffload_flows_work(struct work_struct *work)
3641b4a23329SRoi Dayan {
3642b4a23329SRoi Dayan 	struct mlx5_rep_uplink_priv *rpriv =
3643b4a23329SRoi Dayan 		container_of(work, struct mlx5_rep_uplink_priv,
3644b4a23329SRoi Dayan 			     reoffload_flows_work);
3645b4a23329SRoi Dayan 	struct mlx5e_tc_flow *flow, *tmp;
3646b4a23329SRoi Dayan 
3647b4a23329SRoi Dayan 	rtnl_lock();
3648b4a23329SRoi Dayan 	list_for_each_entry_safe(flow, tmp, &rpriv->unready_flows, unready) {
3649b4a23329SRoi Dayan 		if (!mlx5e_tc_add_fdb_flow(flow->priv, flow, NULL))
3650b4a23329SRoi Dayan 			remove_unready_flow(flow);
3651b4a23329SRoi Dayan 	}
3652b4a23329SRoi Dayan 	rtnl_unlock();
3653b4a23329SRoi Dayan }
3654