xref: /openbmc/linux/drivers/net/ethernet/netronome/nfp/flower/action.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
196de2506SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
296de2506SJakub Kicinski /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
31a1e586fSPieter Jansen van Vuuren 
41a1e586fSPieter Jansen van Vuuren #include <linux/bitfield.h>
5a6eb1817SJohn Hurley #include <linux/mpls.h>
61a1e586fSPieter Jansen van Vuuren #include <net/pkt_cls.h>
7ed8f2b52SPieter Jansen van Vuuren #include <net/tc_act/tc_csum.h>
81a1e586fSPieter Jansen van Vuuren #include <net/tc_act/tc_gact.h>
91a1e586fSPieter Jansen van Vuuren #include <net/tc_act/tc_mirred.h>
10a6eb1817SJohn Hurley #include <net/tc_act/tc_mpls.h>
11da83d8feSPieter Jansen van Vuuren #include <net/tc_act/tc_pedit.h>
121a1e586fSPieter Jansen van Vuuren #include <net/tc_act/tc_vlan.h>
13b27d6a95SJohn Hurley #include <net/tc_act/tc_tunnel_key.h>
141a1e586fSPieter Jansen van Vuuren 
151a1e586fSPieter Jansen van Vuuren #include "cmsg.h"
161a1e586fSPieter Jansen van Vuuren #include "main.h"
171a1e586fSPieter Jansen van Vuuren #include "../nfp_net_repr.h"
181a1e586fSPieter Jansen van Vuuren 
199e7c32feSPieter Jansen van Vuuren /* The kernel versions of TUNNEL_* are not ABI and therefore vulnerable
209e7c32feSPieter Jansen van Vuuren  * to change. Such changes will break our FW ABI.
219e7c32feSPieter Jansen van Vuuren  */
229e7c32feSPieter Jansen van Vuuren #define NFP_FL_TUNNEL_CSUM			cpu_to_be16(0x01)
239e7c32feSPieter Jansen van Vuuren #define NFP_FL_TUNNEL_KEY			cpu_to_be16(0x04)
249e7c32feSPieter Jansen van Vuuren #define NFP_FL_TUNNEL_GENEVE_OPT		cpu_to_be16(0x0800)
251922c9a4SJohn Hurley #define NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS	(IP_TUNNEL_INFO_TX | \
261922c9a4SJohn Hurley 						 IP_TUNNEL_INFO_IPV6)
271922c9a4SJohn Hurley #define NFP_FL_SUPPORTED_UDP_TUN_FLAGS		(NFP_FL_TUNNEL_CSUM | \
289e7c32feSPieter Jansen van Vuuren 						 NFP_FL_TUNNEL_KEY | \
299e7c32feSPieter Jansen van Vuuren 						 NFP_FL_TUNNEL_GENEVE_OPT)
3051a8cefcSJohn Hurley 
31a6eb1817SJohn Hurley static int
nfp_fl_push_mpls(struct nfp_fl_push_mpls * push_mpls,const struct flow_action_entry * act,struct netlink_ext_ack * extack)32a6eb1817SJohn Hurley nfp_fl_push_mpls(struct nfp_fl_push_mpls *push_mpls,
33a6eb1817SJohn Hurley 		 const struct flow_action_entry *act,
34a6eb1817SJohn Hurley 		 struct netlink_ext_ack *extack)
35a6eb1817SJohn Hurley {
36a6eb1817SJohn Hurley 	size_t act_size = sizeof(struct nfp_fl_push_mpls);
37a6eb1817SJohn Hurley 	u32 mpls_lse = 0;
38a6eb1817SJohn Hurley 
39a6eb1817SJohn Hurley 	push_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_MPLS;
40a6eb1817SJohn Hurley 	push_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ;
41a6eb1817SJohn Hurley 
42a6eb1817SJohn Hurley 	/* BOS is optional in the TC action but required for offload. */
43a6eb1817SJohn Hurley 	if (act->mpls_push.bos != ACT_MPLS_BOS_NOT_SET) {
44a6eb1817SJohn Hurley 		mpls_lse |= act->mpls_push.bos << MPLS_LS_S_SHIFT;
45a6eb1817SJohn Hurley 	} else {
46a6eb1817SJohn Hurley 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: BOS field must explicitly be set for MPLS push");
47a6eb1817SJohn Hurley 		return -EOPNOTSUPP;
48a6eb1817SJohn Hurley 	}
49a6eb1817SJohn Hurley 
50a6eb1817SJohn Hurley 	/* Leave MPLS TC as a default value of 0 if not explicitly set. */
51a6eb1817SJohn Hurley 	if (act->mpls_push.tc != ACT_MPLS_TC_NOT_SET)
52a6eb1817SJohn Hurley 		mpls_lse |= act->mpls_push.tc << MPLS_LS_TC_SHIFT;
53a6eb1817SJohn Hurley 
54a6eb1817SJohn Hurley 	/* Proto, label and TTL are enforced and verified for MPLS push. */
55a6eb1817SJohn Hurley 	mpls_lse |= act->mpls_push.label << MPLS_LS_LABEL_SHIFT;
56a6eb1817SJohn Hurley 	mpls_lse |= act->mpls_push.ttl << MPLS_LS_TTL_SHIFT;
57a6eb1817SJohn Hurley 	push_mpls->ethtype = act->mpls_push.proto;
58a6eb1817SJohn Hurley 	push_mpls->lse = cpu_to_be32(mpls_lse);
59a6eb1817SJohn Hurley 
60a6eb1817SJohn Hurley 	return 0;
61a6eb1817SJohn Hurley }
62a6eb1817SJohn Hurley 
6335b7c70cSJohn Hurley static void
nfp_fl_pop_mpls(struct nfp_fl_pop_mpls * pop_mpls,const struct flow_action_entry * act)6435b7c70cSJohn Hurley nfp_fl_pop_mpls(struct nfp_fl_pop_mpls *pop_mpls,
6535b7c70cSJohn Hurley 		const struct flow_action_entry *act)
6635b7c70cSJohn Hurley {
6735b7c70cSJohn Hurley 	size_t act_size = sizeof(struct nfp_fl_pop_mpls);
6835b7c70cSJohn Hurley 
6935b7c70cSJohn Hurley 	pop_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_POP_MPLS;
7035b7c70cSJohn Hurley 	pop_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ;
7135b7c70cSJohn Hurley 	pop_mpls->ethtype = act->mpls_pop.proto;
7235b7c70cSJohn Hurley }
7335b7c70cSJohn Hurley 
74e03e47a3SJohn Hurley static void
nfp_fl_set_mpls(struct nfp_fl_set_mpls * set_mpls,const struct flow_action_entry * act)75e03e47a3SJohn Hurley nfp_fl_set_mpls(struct nfp_fl_set_mpls *set_mpls,
76e03e47a3SJohn Hurley 		const struct flow_action_entry *act)
77e03e47a3SJohn Hurley {
78e03e47a3SJohn Hurley 	size_t act_size = sizeof(struct nfp_fl_set_mpls);
79e03e47a3SJohn Hurley 	u32 mpls_lse = 0, mpls_mask = 0;
80e03e47a3SJohn Hurley 
81e03e47a3SJohn Hurley 	set_mpls->head.jump_id = NFP_FL_ACTION_OPCODE_SET_MPLS;
82e03e47a3SJohn Hurley 	set_mpls->head.len_lw = act_size >> NFP_FL_LW_SIZ;
83e03e47a3SJohn Hurley 
84e03e47a3SJohn Hurley 	if (act->mpls_mangle.label != ACT_MPLS_LABEL_NOT_SET) {
85e03e47a3SJohn Hurley 		mpls_lse |= act->mpls_mangle.label << MPLS_LS_LABEL_SHIFT;
86e03e47a3SJohn Hurley 		mpls_mask |= MPLS_LS_LABEL_MASK;
87e03e47a3SJohn Hurley 	}
88e03e47a3SJohn Hurley 	if (act->mpls_mangle.tc != ACT_MPLS_TC_NOT_SET) {
89e03e47a3SJohn Hurley 		mpls_lse |= act->mpls_mangle.tc << MPLS_LS_TC_SHIFT;
90e03e47a3SJohn Hurley 		mpls_mask |= MPLS_LS_TC_MASK;
91e03e47a3SJohn Hurley 	}
92e03e47a3SJohn Hurley 	if (act->mpls_mangle.bos != ACT_MPLS_BOS_NOT_SET) {
93e03e47a3SJohn Hurley 		mpls_lse |= act->mpls_mangle.bos << MPLS_LS_S_SHIFT;
94e03e47a3SJohn Hurley 		mpls_mask |= MPLS_LS_S_MASK;
95e03e47a3SJohn Hurley 	}
96e03e47a3SJohn Hurley 	if (act->mpls_mangle.ttl) {
97e03e47a3SJohn Hurley 		mpls_lse |= act->mpls_mangle.ttl << MPLS_LS_TTL_SHIFT;
98e03e47a3SJohn Hurley 		mpls_mask |= MPLS_LS_TTL_MASK;
99e03e47a3SJohn Hurley 	}
100e03e47a3SJohn Hurley 
101e03e47a3SJohn Hurley 	set_mpls->lse = cpu_to_be32(mpls_lse);
102e03e47a3SJohn Hurley 	set_mpls->lse_mask = cpu_to_be32(mpls_mask);
103e03e47a3SJohn Hurley }
104e03e47a3SJohn Hurley 
nfp_fl_pop_vlan(struct nfp_fl_pop_vlan * pop_vlan)1051a1e586fSPieter Jansen van Vuuren static void nfp_fl_pop_vlan(struct nfp_fl_pop_vlan *pop_vlan)
1061a1e586fSPieter Jansen van Vuuren {
1071a1e586fSPieter Jansen van Vuuren 	size_t act_size = sizeof(struct nfp_fl_pop_vlan);
1081a1e586fSPieter Jansen van Vuuren 
10962d3f60bSPieter Jansen van Vuuren 	pop_vlan->head.jump_id = NFP_FL_ACTION_OPCODE_POP_VLAN;
11062d3f60bSPieter Jansen van Vuuren 	pop_vlan->head.len_lw = act_size >> NFP_FL_LW_SIZ;
1111a1e586fSPieter Jansen van Vuuren 	pop_vlan->reserved = 0;
1121a1e586fSPieter Jansen van Vuuren }
1131a1e586fSPieter Jansen van Vuuren 
1141a1e586fSPieter Jansen van Vuuren static void
nfp_fl_push_vlan(struct nfp_fl_push_vlan * push_vlan,const struct flow_action_entry * act)1151a1e586fSPieter Jansen van Vuuren nfp_fl_push_vlan(struct nfp_fl_push_vlan *push_vlan,
11673867881SPablo Neira Ayuso 		 const struct flow_action_entry *act)
1171a1e586fSPieter Jansen van Vuuren {
1181a1e586fSPieter Jansen van Vuuren 	size_t act_size = sizeof(struct nfp_fl_push_vlan);
1191a1e586fSPieter Jansen van Vuuren 	u16 tmp_push_vlan_tci;
1201a1e586fSPieter Jansen van Vuuren 
12162d3f60bSPieter Jansen van Vuuren 	push_vlan->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_VLAN;
12262d3f60bSPieter Jansen van Vuuren 	push_vlan->head.len_lw = act_size >> NFP_FL_LW_SIZ;
1231a1e586fSPieter Jansen van Vuuren 	push_vlan->reserved = 0;
12473867881SPablo Neira Ayuso 	push_vlan->vlan_tpid = act->vlan.proto;
1251a1e586fSPieter Jansen van Vuuren 
1261a1e586fSPieter Jansen van Vuuren 	tmp_push_vlan_tci =
12773867881SPablo Neira Ayuso 		FIELD_PREP(NFP_FL_PUSH_VLAN_PRIO, act->vlan.prio) |
12842cd5484SPieter Jansen van Vuuren 		FIELD_PREP(NFP_FL_PUSH_VLAN_VID, act->vlan.vid);
1291a1e586fSPieter Jansen van Vuuren 	push_vlan->vlan_tci = cpu_to_be16(tmp_push_vlan_tci);
1301a1e586fSPieter Jansen van Vuuren }
1311a1e586fSPieter Jansen van Vuuren 
1327e24a593SJohn Hurley static int
nfp_fl_pre_lag(struct nfp_app * app,const struct flow_action_entry * act,struct nfp_fl_payload * nfp_flow,int act_len,struct netlink_ext_ack * extack)13373867881SPablo Neira Ayuso nfp_fl_pre_lag(struct nfp_app *app, const struct flow_action_entry *act,
134bef6e97dSPieter Jansen van Vuuren 	       struct nfp_fl_payload *nfp_flow, int act_len,
135bef6e97dSPieter Jansen van Vuuren 	       struct netlink_ext_ack *extack)
1367e24a593SJohn Hurley {
1377e24a593SJohn Hurley 	size_t act_size = sizeof(struct nfp_fl_pre_lag);
1387e24a593SJohn Hurley 	struct nfp_fl_pre_lag *pre_lag;
1397e24a593SJohn Hurley 	struct net_device *out_dev;
1407e24a593SJohn Hurley 	int err;
1417e24a593SJohn Hurley 
14273867881SPablo Neira Ayuso 	out_dev = act->dev;
1437e24a593SJohn Hurley 	if (!out_dev || !netif_is_lag_master(out_dev))
1447e24a593SJohn Hurley 		return 0;
1457e24a593SJohn Hurley 
146bef6e97dSPieter Jansen van Vuuren 	if (act_len + act_size > NFP_FL_MAX_A_SIZ) {
147bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at LAG action");
1487e24a593SJohn Hurley 		return -EOPNOTSUPP;
149bef6e97dSPieter Jansen van Vuuren 	}
1507e24a593SJohn Hurley 
1517e24a593SJohn Hurley 	/* Pre_lag action must be first on action list.
1529bacb93bSWalter Heymans 	 * If other actions already exist they need to be pushed forward.
1537e24a593SJohn Hurley 	 */
1547e24a593SJohn Hurley 	if (act_len)
1557e24a593SJohn Hurley 		memmove(nfp_flow->action_data + act_size,
1567e24a593SJohn Hurley 			nfp_flow->action_data, act_len);
1577e24a593SJohn Hurley 
1587e24a593SJohn Hurley 	pre_lag = (struct nfp_fl_pre_lag *)nfp_flow->action_data;
159bef6e97dSPieter Jansen van Vuuren 	err = nfp_flower_lag_populate_pre_action(app, out_dev, pre_lag, extack);
1607e24a593SJohn Hurley 	if (err)
1617e24a593SJohn Hurley 		return err;
1627e24a593SJohn Hurley 
1637e24a593SJohn Hurley 	pre_lag->head.jump_id = NFP_FL_ACTION_OPCODE_PRE_LAG;
1647e24a593SJohn Hurley 	pre_lag->head.len_lw = act_size >> NFP_FL_LW_SIZ;
1657e24a593SJohn Hurley 
1667e24a593SJohn Hurley 	nfp_flow->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
1677e24a593SJohn Hurley 
1687e24a593SJohn Hurley 	return act_size;
1697e24a593SJohn Hurley }
1707e24a593SJohn Hurley 
1711a1e586fSPieter Jansen van Vuuren static int
nfp_fl_output(struct nfp_app * app,struct nfp_fl_output * output,const struct flow_action_entry * act,struct nfp_fl_payload * nfp_flow,bool last,struct net_device * in_dev,enum nfp_flower_tun_type tun_type,int * tun_out_cnt,bool pkt_host,struct netlink_ext_ack * extack)1727e24a593SJohn Hurley nfp_fl_output(struct nfp_app *app, struct nfp_fl_output *output,
17373867881SPablo Neira Ayuso 	      const struct flow_action_entry *act,
17473867881SPablo Neira Ayuso 	      struct nfp_fl_payload *nfp_flow,
1757e24a593SJohn Hurley 	      bool last, struct net_device *in_dev,
176bef6e97dSPieter Jansen van Vuuren 	      enum nfp_flower_tun_type tun_type, int *tun_out_cnt,
177f5c977eeSJohn Hurley 	      bool pkt_host, struct netlink_ext_ack *extack)
1781a1e586fSPieter Jansen van Vuuren {
1791a1e586fSPieter Jansen van Vuuren 	size_t act_size = sizeof(struct nfp_fl_output);
1807e24a593SJohn Hurley 	struct nfp_flower_priv *priv = app->priv;
1811a1e586fSPieter Jansen van Vuuren 	struct net_device *out_dev;
18262d3f60bSPieter Jansen van Vuuren 	u16 tmp_flags;
1831a1e586fSPieter Jansen van Vuuren 
18462d3f60bSPieter Jansen van Vuuren 	output->head.jump_id = NFP_FL_ACTION_OPCODE_OUTPUT;
18562d3f60bSPieter Jansen van Vuuren 	output->head.len_lw = act_size >> NFP_FL_LW_SIZ;
1861a1e586fSPieter Jansen van Vuuren 
18773867881SPablo Neira Ayuso 	out_dev = act->dev;
188bef6e97dSPieter Jansen van Vuuren 	if (!out_dev) {
189bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid egress interface for mirred action");
1901a1e586fSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
191bef6e97dSPieter Jansen van Vuuren 	}
1921a1e586fSPieter Jansen van Vuuren 
193b27d6a95SJohn Hurley 	tmp_flags = last ? NFP_FL_OUT_FLAGS_LAST : 0;
194b27d6a95SJohn Hurley 
195b27d6a95SJohn Hurley 	if (tun_type) {
196b27d6a95SJohn Hurley 		/* Verify the egress netdev matches the tunnel type. */
197bef6e97dSPieter Jansen van Vuuren 		if (!nfp_fl_netdev_is_tunnel_type(out_dev, tun_type)) {
198bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: egress interface does not match the required tunnel type");
199b27d6a95SJohn Hurley 			return -EOPNOTSUPP;
200bef6e97dSPieter Jansen van Vuuren 		}
201b27d6a95SJohn Hurley 
202bef6e97dSPieter Jansen van Vuuren 		if (*tun_out_cnt) {
203bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: cannot offload more than one tunnel mirred output per filter");
204b27d6a95SJohn Hurley 			return -EOPNOTSUPP;
205bef6e97dSPieter Jansen van Vuuren 		}
206b27d6a95SJohn Hurley 		(*tun_out_cnt)++;
207b27d6a95SJohn Hurley 
208b27d6a95SJohn Hurley 		output->flags = cpu_to_be16(tmp_flags |
209b27d6a95SJohn Hurley 					    NFP_FL_OUT_FLAGS_USE_TUN);
210b27d6a95SJohn Hurley 		output->port = cpu_to_be32(NFP_FL_PORT_TYPE_TUN | tun_type);
2117e24a593SJohn Hurley 	} else if (netif_is_lag_master(out_dev) &&
212e09303d3SLouis Peens 		   priv->flower_en_feats & NFP_FL_ENABLE_LAG) {
2137e24a593SJohn Hurley 		int gid;
2147e24a593SJohn Hurley 
2157e24a593SJohn Hurley 		output->flags = cpu_to_be16(tmp_flags);
2167e24a593SJohn Hurley 		gid = nfp_flower_lag_get_output_id(app, out_dev);
217bef6e97dSPieter Jansen van Vuuren 		if (gid < 0) {
218bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "invalid entry: cannot find group id for LAG action");
2197e24a593SJohn Hurley 			return gid;
220bef6e97dSPieter Jansen van Vuuren 		}
2217e24a593SJohn Hurley 		output->port = cpu_to_be32(NFP_FL_LAG_OUT | gid);
222f5c977eeSJohn Hurley 	} else if (nfp_flower_internal_port_can_offload(app, out_dev)) {
223e30b2b68SLouis Peens 		if (!(priv->flower_ext_feats & NFP_FL_FEATS_PRE_TUN_RULES) &&
224e30b2b68SLouis Peens 		    !(priv->flower_ext_feats & NFP_FL_FEATS_DECAP_V2)) {
225f5c977eeSJohn Hurley 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pre-tunnel rules not supported in loaded firmware");
226f5c977eeSJohn Hurley 			return -EOPNOTSUPP;
227f5c977eeSJohn Hurley 		}
228f5c977eeSJohn Hurley 
229f5c977eeSJohn Hurley 		if (nfp_flow->pre_tun_rule.dev || !pkt_host) {
230f5c977eeSJohn Hurley 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pre-tunnel rules require single egress dev and ptype HOST action");
231f5c977eeSJohn Hurley 			return -EOPNOTSUPP;
232f5c977eeSJohn Hurley 		}
233f5c977eeSJohn Hurley 
234f5c977eeSJohn Hurley 		nfp_flow->pre_tun_rule.dev = out_dev;
235f5c977eeSJohn Hurley 
236f5c977eeSJohn Hurley 		return 0;
237b27d6a95SJohn Hurley 	} else {
238b27d6a95SJohn Hurley 		/* Set action output parameters. */
239b27d6a95SJohn Hurley 		output->flags = cpu_to_be16(tmp_flags);
240b27d6a95SJohn Hurley 
2417885b4fcSJohn Hurley 		if (nfp_netdev_is_nfp_repr(in_dev)) {
2427885b4fcSJohn Hurley 			/* Confirm ingress and egress are on same device. */
243bef6e97dSPieter Jansen van Vuuren 			if (!netdev_port_same_parent_id(in_dev, out_dev)) {
244bef6e97dSPieter Jansen van Vuuren 				NL_SET_ERR_MSG_MOD(extack, "unsupported offload: ingress and egress interfaces are on different devices");
245bef6e97dSPieter Jansen van Vuuren 				return -EOPNOTSUPP;
246bef6e97dSPieter Jansen van Vuuren 			}
247bef6e97dSPieter Jansen van Vuuren 		}
248bef6e97dSPieter Jansen van Vuuren 
249bef6e97dSPieter Jansen van Vuuren 		if (!nfp_netdev_is_nfp_repr(out_dev)) {
250bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: egress interface is not an nfp port");
2511a1e586fSPieter Jansen van Vuuren 			return -EOPNOTSUPP;
2527885b4fcSJohn Hurley 		}
2537885b4fcSJohn Hurley 
2541a1e586fSPieter Jansen van Vuuren 		output->port = cpu_to_be32(nfp_repr_get_port_id(out_dev));
255bef6e97dSPieter Jansen van Vuuren 		if (!output->port) {
256bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid port id for egress interface");
2571a1e586fSPieter Jansen van Vuuren 			return -EOPNOTSUPP;
258b27d6a95SJohn Hurley 		}
259bef6e97dSPieter Jansen van Vuuren 	}
2601a1e586fSPieter Jansen van Vuuren 	nfp_flow->meta.shortcut = output->port;
2611a1e586fSPieter Jansen van Vuuren 
2621a1e586fSPieter Jansen van Vuuren 	return 0;
2631a1e586fSPieter Jansen van Vuuren }
2641a1e586fSPieter Jansen van Vuuren 
265fccac580SPieter Jansen van Vuuren static bool
nfp_flower_tun_is_gre(struct flow_rule * rule,int start_idx)266e75dc265SLouis Peens nfp_flower_tun_is_gre(struct flow_rule *rule, int start_idx)
267fccac580SPieter Jansen van Vuuren {
268e75dc265SLouis Peens 	struct flow_action_entry *act = rule->action.entries;
269e75dc265SLouis Peens 	int num_act = rule->action.num_entries;
270fccac580SPieter Jansen van Vuuren 	int act_idx;
271fccac580SPieter Jansen van Vuuren 
272fccac580SPieter Jansen van Vuuren 	/* Preparse action list for next mirred or redirect action */
273fccac580SPieter Jansen van Vuuren 	for (act_idx = start_idx + 1; act_idx < num_act; act_idx++)
274fccac580SPieter Jansen van Vuuren 		if (act[act_idx].id == FLOW_ACTION_REDIRECT ||
275fccac580SPieter Jansen van Vuuren 		    act[act_idx].id == FLOW_ACTION_MIRRED)
276f7536ffbSYu Xiao 			return netif_is_gretap(act[act_idx].dev) ||
277f7536ffbSYu Xiao 			       netif_is_ip6gretap(act[act_idx].dev);
278fccac580SPieter Jansen van Vuuren 
279fccac580SPieter Jansen van Vuuren 	return false;
280fccac580SPieter Jansen van Vuuren }
281fccac580SPieter Jansen van Vuuren 
2823ca3059dSJohn Hurley static enum nfp_flower_tun_type
nfp_fl_get_tun_from_act(struct nfp_app * app,struct flow_rule * rule,const struct flow_action_entry * act,int act_idx)283104dce5bSPieter Jansen van Vuuren nfp_fl_get_tun_from_act(struct nfp_app *app,
284e75dc265SLouis Peens 			struct flow_rule *rule,
285fccac580SPieter Jansen van Vuuren 			const struct flow_action_entry *act, int act_idx)
286b27d6a95SJohn Hurley {
28773867881SPablo Neira Ayuso 	const struct ip_tunnel_info *tun = act->tunnel;
2883ca3059dSJohn Hurley 	struct nfp_flower_priv *priv = app->priv;
289b27d6a95SJohn Hurley 
290fccac580SPieter Jansen van Vuuren 	/* Determine the tunnel type based on the egress netdev
291fccac580SPieter Jansen van Vuuren 	 * in the mirred action for tunnels without l4.
292fccac580SPieter Jansen van Vuuren 	 */
293e75dc265SLouis Peens 	if (nfp_flower_tun_is_gre(rule, act_idx))
294fccac580SPieter Jansen van Vuuren 		return NFP_FL_TUNNEL_GRE;
295fccac580SPieter Jansen van Vuuren 
2963ca3059dSJohn Hurley 	switch (tun->key.tp_dst) {
297bea96410SMoshe Shemesh 	case htons(IANA_VXLAN_UDP_PORT):
2983ca3059dSJohn Hurley 		return NFP_FL_TUNNEL_VXLAN;
299974eff2bSMoshe Shemesh 	case htons(GENEVE_UDP_PORT):
3003ca3059dSJohn Hurley 		if (priv->flower_ext_feats & NFP_FL_FEATS_GENEVE)
3013ca3059dSJohn Hurley 			return NFP_FL_TUNNEL_GENEVE;
302df561f66SGustavo A. R. Silva 		fallthrough;
3033ca3059dSJohn Hurley 	default:
3043ca3059dSJohn Hurley 		return NFP_FL_TUNNEL_NONE;
3053ca3059dSJohn Hurley 	}
306b27d6a95SJohn Hurley }
307b27d6a95SJohn Hurley 
nfp_fl_pre_tunnel(char * act_data,int act_len)308b27d6a95SJohn Hurley static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len)
309b27d6a95SJohn Hurley {
310b27d6a95SJohn Hurley 	size_t act_size = sizeof(struct nfp_fl_pre_tunnel);
311b27d6a95SJohn Hurley 	struct nfp_fl_pre_tunnel *pre_tun_act;
312b27d6a95SJohn Hurley 
313b27d6a95SJohn Hurley 	/* Pre_tunnel action must be first on action list.
3147e24a593SJohn Hurley 	 * If other actions already exist they need to be pushed forward.
315b27d6a95SJohn Hurley 	 */
316b27d6a95SJohn Hurley 	if (act_len)
317b27d6a95SJohn Hurley 		memmove(act_data + act_size, act_data, act_len);
318b27d6a95SJohn Hurley 
319b27d6a95SJohn Hurley 	pre_tun_act = (struct nfp_fl_pre_tunnel *)act_data;
320b27d6a95SJohn Hurley 
321b27d6a95SJohn Hurley 	memset(pre_tun_act, 0, act_size);
322b27d6a95SJohn Hurley 
32362d3f60bSPieter Jansen van Vuuren 	pre_tun_act->head.jump_id = NFP_FL_ACTION_OPCODE_PRE_TUNNEL;
32462d3f60bSPieter Jansen van Vuuren 	pre_tun_act->head.len_lw = act_size >> NFP_FL_LW_SIZ;
325b27d6a95SJohn Hurley 
326b27d6a95SJohn Hurley 	return pre_tun_act;
327b27d6a95SJohn Hurley }
328b27d6a95SJohn Hurley 
329b27d6a95SJohn Hurley static int
nfp_fl_push_geneve_options(struct nfp_fl_payload * nfp_fl,int * list_len,const struct flow_action_entry * act,struct netlink_ext_ack * extack)3309e7c32feSPieter Jansen van Vuuren nfp_fl_push_geneve_options(struct nfp_fl_payload *nfp_fl, int *list_len,
331bef6e97dSPieter Jansen van Vuuren 			   const struct flow_action_entry *act,
332bef6e97dSPieter Jansen van Vuuren 			   struct netlink_ext_ack *extack)
3339e7c32feSPieter Jansen van Vuuren {
33473867881SPablo Neira Ayuso 	struct ip_tunnel_info *ip_tun = (struct ip_tunnel_info *)act->tunnel;
3359e7c32feSPieter Jansen van Vuuren 	int opt_len, opt_cnt, act_start, tot_push_len;
3369e7c32feSPieter Jansen van Vuuren 	u8 *src = ip_tunnel_info_opts(ip_tun);
3379e7c32feSPieter Jansen van Vuuren 
3389e7c32feSPieter Jansen van Vuuren 	/* We need to populate the options in reverse order for HW.
3399e7c32feSPieter Jansen van Vuuren 	 * Therefore we go through the options, calculating the
3409e7c32feSPieter Jansen van Vuuren 	 * number of options and the total size, then we populate
3419e7c32feSPieter Jansen van Vuuren 	 * them in reverse order in the action list.
3429e7c32feSPieter Jansen van Vuuren 	 */
3439e7c32feSPieter Jansen van Vuuren 	opt_cnt = 0;
3449e7c32feSPieter Jansen van Vuuren 	tot_push_len = 0;
3459e7c32feSPieter Jansen van Vuuren 	opt_len = ip_tun->options_len;
3469e7c32feSPieter Jansen van Vuuren 	while (opt_len > 0) {
3479e7c32feSPieter Jansen van Vuuren 		struct geneve_opt *opt = (struct geneve_opt *)src;
3489e7c32feSPieter Jansen van Vuuren 
3499e7c32feSPieter Jansen van Vuuren 		opt_cnt++;
350bef6e97dSPieter Jansen van Vuuren 		if (opt_cnt > NFP_FL_MAX_GENEVE_OPT_CNT) {
351bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed number of geneve options exceeded");
3529e7c32feSPieter Jansen van Vuuren 			return -EOPNOTSUPP;
353bef6e97dSPieter Jansen van Vuuren 		}
3549e7c32feSPieter Jansen van Vuuren 
3559e7c32feSPieter Jansen van Vuuren 		tot_push_len += sizeof(struct nfp_fl_push_geneve) +
3569e7c32feSPieter Jansen van Vuuren 			       opt->length * 4;
357bef6e97dSPieter Jansen van Vuuren 		if (tot_push_len > NFP_FL_MAX_GENEVE_OPT_ACT) {
358bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push geneve options");
3599e7c32feSPieter Jansen van Vuuren 			return -EOPNOTSUPP;
360bef6e97dSPieter Jansen van Vuuren 		}
3619e7c32feSPieter Jansen van Vuuren 
3629e7c32feSPieter Jansen van Vuuren 		opt_len -= sizeof(struct geneve_opt) + opt->length * 4;
3639e7c32feSPieter Jansen van Vuuren 		src += sizeof(struct geneve_opt) + opt->length * 4;
3649e7c32feSPieter Jansen van Vuuren 	}
3659e7c32feSPieter Jansen van Vuuren 
366bef6e97dSPieter Jansen van Vuuren 	if (*list_len + tot_push_len > NFP_FL_MAX_A_SIZ) {
367bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push geneve options");
3689e7c32feSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
369bef6e97dSPieter Jansen van Vuuren 	}
3709e7c32feSPieter Jansen van Vuuren 
3719e7c32feSPieter Jansen van Vuuren 	act_start = *list_len;
3729e7c32feSPieter Jansen van Vuuren 	*list_len += tot_push_len;
3739e7c32feSPieter Jansen van Vuuren 	src = ip_tunnel_info_opts(ip_tun);
3749e7c32feSPieter Jansen van Vuuren 	while (opt_cnt) {
3759e7c32feSPieter Jansen van Vuuren 		struct geneve_opt *opt = (struct geneve_opt *)src;
3769e7c32feSPieter Jansen van Vuuren 		struct nfp_fl_push_geneve *push;
3779e7c32feSPieter Jansen van Vuuren 		size_t act_size, len;
3789e7c32feSPieter Jansen van Vuuren 
3799e7c32feSPieter Jansen van Vuuren 		opt_cnt--;
3809e7c32feSPieter Jansen van Vuuren 		act_size = sizeof(struct nfp_fl_push_geneve) + opt->length * 4;
3819e7c32feSPieter Jansen van Vuuren 		tot_push_len -= act_size;
3829e7c32feSPieter Jansen van Vuuren 		len = act_start + tot_push_len;
3839e7c32feSPieter Jansen van Vuuren 
3849e7c32feSPieter Jansen van Vuuren 		push = (struct nfp_fl_push_geneve *)&nfp_fl->action_data[len];
3859e7c32feSPieter Jansen van Vuuren 		push->head.jump_id = NFP_FL_ACTION_OPCODE_PUSH_GENEVE;
3869e7c32feSPieter Jansen van Vuuren 		push->head.len_lw = act_size >> NFP_FL_LW_SIZ;
3879e7c32feSPieter Jansen van Vuuren 		push->reserved = 0;
3889e7c32feSPieter Jansen van Vuuren 		push->class = opt->opt_class;
3899e7c32feSPieter Jansen van Vuuren 		push->type = opt->type;
3909e7c32feSPieter Jansen van Vuuren 		push->length = opt->length;
3919e7c32feSPieter Jansen van Vuuren 		memcpy(&push->opt_data, opt->opt_data, opt->length * 4);
3929e7c32feSPieter Jansen van Vuuren 
3939e7c32feSPieter Jansen van Vuuren 		src += sizeof(struct geneve_opt) + opt->length * 4;
3949e7c32feSPieter Jansen van Vuuren 	}
3959e7c32feSPieter Jansen van Vuuren 
3969e7c32feSPieter Jansen van Vuuren 	return 0;
3979e7c32feSPieter Jansen van Vuuren }
3989e7c32feSPieter Jansen van Vuuren 
3999e7c32feSPieter Jansen van Vuuren static int
nfp_fl_set_tun(struct nfp_app * app,struct nfp_fl_set_tun * set_tun,const struct flow_action_entry * act,struct nfp_fl_pre_tunnel * pre_tun,enum nfp_flower_tun_type tun_type,struct net_device * netdev,struct netlink_ext_ack * extack)4001922c9a4SJohn Hurley nfp_fl_set_tun(struct nfp_app *app, struct nfp_fl_set_tun *set_tun,
40173867881SPablo Neira Ayuso 	       const struct flow_action_entry *act,
4023ca3059dSJohn Hurley 	       struct nfp_fl_pre_tunnel *pre_tun,
40350a5852aSJohn Hurley 	       enum nfp_flower_tun_type tun_type,
404104dce5bSPieter Jansen van Vuuren 	       struct net_device *netdev, struct netlink_ext_ack *extack)
405b27d6a95SJohn Hurley {
40673867881SPablo Neira Ayuso 	const struct ip_tunnel_info *ip_tun = act->tunnel;
4071922c9a4SJohn Hurley 	bool ipv6 = ip_tunnel_info_af(ip_tun) == AF_INET6;
4081922c9a4SJohn Hurley 	size_t act_size = sizeof(struct nfp_fl_set_tun);
4099e7c32feSPieter Jansen van Vuuren 	struct nfp_flower_priv *priv = app->priv;
4103ca3059dSJohn Hurley 	u32 tmp_set_ip_tun_type_index = 0;
411b27d6a95SJohn Hurley 	/* Currently support one pre-tunnel so index is always 0. */
412b27d6a95SJohn Hurley 	int pretun_idx = 0;
413b27d6a95SJohn Hurley 
4141922c9a4SJohn Hurley 	if (!IS_ENABLED(CONFIG_IPV6) && ipv6)
4151922c9a4SJohn Hurley 		return -EOPNOTSUPP;
4161922c9a4SJohn Hurley 
4171922c9a4SJohn Hurley 	if (ipv6 && !(priv->flower_ext_feats & NFP_FL_FEATS_IPV6_TUN))
4181922c9a4SJohn Hurley 		return -EOPNOTSUPP;
4191922c9a4SJohn Hurley 
4209e7c32feSPieter Jansen van Vuuren 	BUILD_BUG_ON(NFP_FL_TUNNEL_CSUM != TUNNEL_CSUM ||
4219e7c32feSPieter Jansen van Vuuren 		     NFP_FL_TUNNEL_KEY	!= TUNNEL_KEY ||
4229e7c32feSPieter Jansen van Vuuren 		     NFP_FL_TUNNEL_GENEVE_OPT != TUNNEL_GENEVE_OPT);
4239e7c32feSPieter Jansen van Vuuren 	if (ip_tun->options_len &&
4249e7c32feSPieter Jansen van Vuuren 	    (tun_type != NFP_FL_TUNNEL_GENEVE ||
425bef6e97dSPieter Jansen van Vuuren 	    !(priv->flower_ext_feats & NFP_FL_FEATS_GENEVE_OPT))) {
426bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support geneve options offload");
427b27d6a95SJohn Hurley 		return -EOPNOTSUPP;
428bef6e97dSPieter Jansen van Vuuren 	}
429b27d6a95SJohn Hurley 
430*45490ce2SBaowen Zheng 	if (ip_tun->key.tun_flags & ~NFP_FL_SUPPORTED_UDP_TUN_FLAGS) {
431*45490ce2SBaowen Zheng 		NL_SET_ERR_MSG_MOD(extack,
432*45490ce2SBaowen Zheng 				   "unsupported offload: loaded firmware does not support tunnel flag offload");
433*45490ce2SBaowen Zheng 		return -EOPNOTSUPP;
434*45490ce2SBaowen Zheng 	}
435*45490ce2SBaowen Zheng 
4361922c9a4SJohn Hurley 	set_tun->head.jump_id = NFP_FL_ACTION_OPCODE_SET_TUNNEL;
4373ca3059dSJohn Hurley 	set_tun->head.len_lw = act_size >> NFP_FL_LW_SIZ;
438b27d6a95SJohn Hurley 
439b27d6a95SJohn Hurley 	/* Set tunnel type and pre-tunnel index. */
4403ca3059dSJohn Hurley 	tmp_set_ip_tun_type_index |=
4411922c9a4SJohn Hurley 		FIELD_PREP(NFP_FL_TUNNEL_TYPE, tun_type) |
4421922c9a4SJohn Hurley 		FIELD_PREP(NFP_FL_PRE_TUN_INDEX, pretun_idx);
443b27d6a95SJohn Hurley 
4443ca3059dSJohn Hurley 	set_tun->tun_type_index = cpu_to_be32(tmp_set_ip_tun_type_index);
445*45490ce2SBaowen Zheng 	if (ip_tun->key.tun_flags & NFP_FL_TUNNEL_KEY)
4463ca3059dSJohn Hurley 		set_tun->tun_id = ip_tun->key.tun_id;
447ed21b637SJohn Hurley 
4482a437471SJohn Hurley 	if (ip_tun->key.ttl) {
4492a437471SJohn Hurley 		set_tun->ttl = ip_tun->key.ttl;
4501922c9a4SJohn Hurley #ifdef CONFIG_IPV6
4511922c9a4SJohn Hurley 	} else if (ipv6) {
4521922c9a4SJohn Hurley 		struct net *net = dev_net(netdev);
4531922c9a4SJohn Hurley 		struct flowi6 flow = {};
4541922c9a4SJohn Hurley 		struct dst_entry *dst;
4551922c9a4SJohn Hurley 
4561922c9a4SJohn Hurley 		flow.daddr = ip_tun->key.u.ipv6.dst;
4571922c9a4SJohn Hurley 		flow.flowi4_proto = IPPROTO_UDP;
4581922c9a4SJohn Hurley 		dst = ipv6_stub->ipv6_dst_lookup_flow(net, NULL, &flow, NULL);
4591922c9a4SJohn Hurley 		if (!IS_ERR(dst)) {
4601922c9a4SJohn Hurley 			set_tun->ttl = ip6_dst_hoplimit(dst);
4611922c9a4SJohn Hurley 			dst_release(dst);
4621922c9a4SJohn Hurley 		} else {
4631922c9a4SJohn Hurley 			set_tun->ttl = net->ipv6.devconf_all->hop_limit;
4641922c9a4SJohn Hurley 		}
4651922c9a4SJohn Hurley #endif
4662a437471SJohn Hurley 	} else {
4672a437471SJohn Hurley 		struct net *net = dev_net(netdev);
4682a437471SJohn Hurley 		struct flowi4 flow = {};
4692a437471SJohn Hurley 		struct rtable *rt;
4702a437471SJohn Hurley 		int err;
4712a437471SJohn Hurley 
4722a437471SJohn Hurley 		/* Do a route lookup to determine ttl - if fails then use
4732a437471SJohn Hurley 		 * default. Note that CONFIG_INET is a requirement of
4742a437471SJohn Hurley 		 * CONFIG_NET_SWITCHDEV so must be defined here.
475ed21b637SJohn Hurley 		 */
476ed21b637SJohn Hurley 		flow.daddr = ip_tun->key.u.ipv4.dst;
477ed21b637SJohn Hurley 		flow.flowi4_proto = IPPROTO_UDP;
478ed21b637SJohn Hurley 		rt = ip_route_output_key(net, &flow);
479ed21b637SJohn Hurley 		err = PTR_ERR_OR_ZERO(rt);
480ed21b637SJohn Hurley 		if (!err) {
481ed21b637SJohn Hurley 			set_tun->ttl = ip4_dst_hoplimit(&rt->dst);
482ed21b637SJohn Hurley 			ip_rt_put(rt);
483ed21b637SJohn Hurley 		} else {
4848281b7ecSKuniyuki Iwashima 			set_tun->ttl = READ_ONCE(net->ipv4.sysctl_ip_default_ttl);
485ed21b637SJohn Hurley 		}
4862a437471SJohn Hurley 	}
487b27d6a95SJohn Hurley 
48851a8cefcSJohn Hurley 	set_tun->tos = ip_tun->key.tos;
48951a8cefcSJohn Hurley 	set_tun->tun_flags = ip_tun->key.tun_flags;
49051a8cefcSJohn Hurley 
4919e7c32feSPieter Jansen van Vuuren 	if (tun_type == NFP_FL_TUNNEL_GENEVE) {
4929e7c32feSPieter Jansen van Vuuren 		set_tun->tun_proto = htons(ETH_P_TEB);
4939e7c32feSPieter Jansen van Vuuren 		set_tun->tun_len = ip_tun->options_len / 4;
4949e7c32feSPieter Jansen van Vuuren 	}
4959e7c32feSPieter Jansen van Vuuren 
496b27d6a95SJohn Hurley 	/* Complete pre_tunnel action. */
4971922c9a4SJohn Hurley 	if (ipv6) {
4981922c9a4SJohn Hurley 		pre_tun->flags |= cpu_to_be16(NFP_FL_PRE_TUN_IPV6);
4991922c9a4SJohn Hurley 		pre_tun->ipv6_dst = ip_tun->key.u.ipv6.dst;
5001922c9a4SJohn Hurley 	} else {
5013ca3059dSJohn Hurley 		pre_tun->ipv4_dst = ip_tun->key.u.ipv4.dst;
5021922c9a4SJohn Hurley 	}
503b27d6a95SJohn Hurley 
504b27d6a95SJohn Hurley 	return 0;
505b27d6a95SJohn Hurley }
506b27d6a95SJohn Hurley 
nfp_fl_set_helper32(u32 value,u32 mask,u8 * p_exact,u8 * p_mask)507da83d8feSPieter Jansen van Vuuren static void nfp_fl_set_helper32(u32 value, u32 mask, u8 *p_exact, u8 *p_mask)
508da83d8feSPieter Jansen van Vuuren {
509da83d8feSPieter Jansen van Vuuren 	u32 oldvalue = get_unaligned((u32 *)p_exact);
510da83d8feSPieter Jansen van Vuuren 	u32 oldmask = get_unaligned((u32 *)p_mask);
511da83d8feSPieter Jansen van Vuuren 
512da83d8feSPieter Jansen van Vuuren 	value &= mask;
513da83d8feSPieter Jansen van Vuuren 	value |= oldvalue & ~mask;
514da83d8feSPieter Jansen van Vuuren 
515da83d8feSPieter Jansen van Vuuren 	put_unaligned(oldmask | mask, (u32 *)p_mask);
516da83d8feSPieter Jansen van Vuuren 	put_unaligned(value, (u32 *)p_exact);
517da83d8feSPieter Jansen van Vuuren }
518da83d8feSPieter Jansen van Vuuren 
519da83d8feSPieter Jansen van Vuuren static int
nfp_fl_set_eth(const struct flow_action_entry * act,u32 off,struct nfp_fl_set_eth * set_eth,struct netlink_ext_ack * extack)520c0bc5d8eSPablo Neira Ayuso nfp_fl_set_eth(const struct flow_action_entry *act, u32 off,
521bef6e97dSPieter Jansen van Vuuren 	       struct nfp_fl_set_eth *set_eth, struct netlink_ext_ack *extack)
522da83d8feSPieter Jansen van Vuuren {
523da83d8feSPieter Jansen van Vuuren 	u32 exact, mask;
524da83d8feSPieter Jansen van Vuuren 
525bef6e97dSPieter Jansen van Vuuren 	if (off + 4 > ETH_ALEN * 2) {
526bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit ethernet action");
527da83d8feSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
528bef6e97dSPieter Jansen van Vuuren 	}
529da83d8feSPieter Jansen van Vuuren 
53073867881SPablo Neira Ayuso 	mask = ~act->mangle.mask;
53173867881SPablo Neira Ayuso 	exact = act->mangle.val;
532da83d8feSPieter Jansen van Vuuren 
533bef6e97dSPieter Jansen van Vuuren 	if (exact & ~mask) {
534bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit ethernet action");
535da83d8feSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
536bef6e97dSPieter Jansen van Vuuren 	}
537da83d8feSPieter Jansen van Vuuren 
538da83d8feSPieter Jansen van Vuuren 	nfp_fl_set_helper32(exact, mask, &set_eth->eth_addr_val[off],
539da83d8feSPieter Jansen van Vuuren 			    &set_eth->eth_addr_mask[off]);
540da83d8feSPieter Jansen van Vuuren 
541da83d8feSPieter Jansen van Vuuren 	set_eth->reserved = cpu_to_be16(0);
54262d3f60bSPieter Jansen van Vuuren 	set_eth->head.jump_id = NFP_FL_ACTION_OPCODE_SET_ETHERNET;
54362d3f60bSPieter Jansen van Vuuren 	set_eth->head.len_lw = sizeof(*set_eth) >> NFP_FL_LW_SIZ;
544da83d8feSPieter Jansen van Vuuren 
545da83d8feSPieter Jansen van Vuuren 	return 0;
546da83d8feSPieter Jansen van Vuuren }
547da83d8feSPieter Jansen van Vuuren 
548a3c6b063SPieter Jansen van Vuuren struct ipv4_ttl_word {
549a3c6b063SPieter Jansen van Vuuren 	__u8	ttl;
550a3c6b063SPieter Jansen van Vuuren 	__u8	protocol;
551a3c6b063SPieter Jansen van Vuuren 	__sum16	check;
552a3c6b063SPieter Jansen van Vuuren };
553a3c6b063SPieter Jansen van Vuuren 
554da83d8feSPieter Jansen van Vuuren static int
nfp_fl_set_ip4(const struct flow_action_entry * act,u32 off,struct nfp_fl_set_ip4_addrs * set_ip_addr,struct nfp_fl_set_ip4_ttl_tos * set_ip_ttl_tos,struct netlink_ext_ack * extack)555c0bc5d8eSPablo Neira Ayuso nfp_fl_set_ip4(const struct flow_action_entry *act, u32 off,
556a3c6b063SPieter Jansen van Vuuren 	       struct nfp_fl_set_ip4_addrs *set_ip_addr,
557bef6e97dSPieter Jansen van Vuuren 	       struct nfp_fl_set_ip4_ttl_tos *set_ip_ttl_tos,
558bef6e97dSPieter Jansen van Vuuren 	       struct netlink_ext_ack *extack)
559c0b1bd9aSPieter Jansen van Vuuren {
560a3c6b063SPieter Jansen van Vuuren 	struct ipv4_ttl_word *ttl_word_mask;
561a3c6b063SPieter Jansen van Vuuren 	struct ipv4_ttl_word *ttl_word;
562a3c6b063SPieter Jansen van Vuuren 	struct iphdr *tos_word_mask;
563a3c6b063SPieter Jansen van Vuuren 	struct iphdr *tos_word;
564c0b1bd9aSPieter Jansen van Vuuren 	__be32 exact, mask;
565c0b1bd9aSPieter Jansen van Vuuren 
566c0b1bd9aSPieter Jansen van Vuuren 	/* We are expecting tcf_pedit to return a big endian value */
56773867881SPablo Neira Ayuso 	mask = (__force __be32)~act->mangle.mask;
56873867881SPablo Neira Ayuso 	exact = (__force __be32)act->mangle.val;
569c0b1bd9aSPieter Jansen van Vuuren 
570bef6e97dSPieter Jansen van Vuuren 	if (exact & ~mask) {
571bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv4 action");
572c0b1bd9aSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
573bef6e97dSPieter Jansen van Vuuren 	}
574c0b1bd9aSPieter Jansen van Vuuren 
575c0b1bd9aSPieter Jansen van Vuuren 	switch (off) {
576c0b1bd9aSPieter Jansen van Vuuren 	case offsetof(struct iphdr, daddr):
5778913806fSPieter Jansen van Vuuren 		set_ip_addr->ipv4_dst_mask |= mask;
5788913806fSPieter Jansen van Vuuren 		set_ip_addr->ipv4_dst &= ~mask;
5798913806fSPieter Jansen van Vuuren 		set_ip_addr->ipv4_dst |= exact & mask;
580a3c6b063SPieter Jansen van Vuuren 		set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
581a3c6b063SPieter Jansen van Vuuren 		set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >>
582a3c6b063SPieter Jansen van Vuuren 					   NFP_FL_LW_SIZ;
583c0b1bd9aSPieter Jansen van Vuuren 		break;
584c0b1bd9aSPieter Jansen van Vuuren 	case offsetof(struct iphdr, saddr):
5858913806fSPieter Jansen van Vuuren 		set_ip_addr->ipv4_src_mask |= mask;
5868913806fSPieter Jansen van Vuuren 		set_ip_addr->ipv4_src &= ~mask;
5878913806fSPieter Jansen van Vuuren 		set_ip_addr->ipv4_src |= exact & mask;
588a3c6b063SPieter Jansen van Vuuren 		set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
589a3c6b063SPieter Jansen van Vuuren 		set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >>
590a3c6b063SPieter Jansen van Vuuren 					   NFP_FL_LW_SIZ;
591a3c6b063SPieter Jansen van Vuuren 		break;
592a3c6b063SPieter Jansen van Vuuren 	case offsetof(struct iphdr, ttl):
593a3c6b063SPieter Jansen van Vuuren 		ttl_word_mask = (struct ipv4_ttl_word *)&mask;
594a3c6b063SPieter Jansen van Vuuren 		ttl_word = (struct ipv4_ttl_word *)&exact;
595a3c6b063SPieter Jansen van Vuuren 
596bef6e97dSPieter Jansen van Vuuren 		if (ttl_word_mask->protocol || ttl_word_mask->check) {
597bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv4 ttl action");
598a3c6b063SPieter Jansen van Vuuren 			return -EOPNOTSUPP;
599bef6e97dSPieter Jansen van Vuuren 		}
600a3c6b063SPieter Jansen van Vuuren 
601a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->ipv4_ttl_mask |= ttl_word_mask->ttl;
602a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->ipv4_ttl &= ~ttl_word_mask->ttl;
603a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->ipv4_ttl |= ttl_word->ttl & ttl_word_mask->ttl;
604a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->head.jump_id =
605a3c6b063SPieter Jansen van Vuuren 			NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS;
606a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >>
607a3c6b063SPieter Jansen van Vuuren 					      NFP_FL_LW_SIZ;
608a3c6b063SPieter Jansen van Vuuren 		break;
609a3c6b063SPieter Jansen van Vuuren 	case round_down(offsetof(struct iphdr, tos), 4):
610a3c6b063SPieter Jansen van Vuuren 		tos_word_mask = (struct iphdr *)&mask;
611a3c6b063SPieter Jansen van Vuuren 		tos_word = (struct iphdr *)&exact;
612a3c6b063SPieter Jansen van Vuuren 
613a3c6b063SPieter Jansen van Vuuren 		if (tos_word_mask->version || tos_word_mask->ihl ||
614bef6e97dSPieter Jansen van Vuuren 		    tos_word_mask->tot_len) {
615bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv4 tos action");
616a3c6b063SPieter Jansen van Vuuren 			return -EOPNOTSUPP;
617bef6e97dSPieter Jansen van Vuuren 		}
618a3c6b063SPieter Jansen van Vuuren 
619a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->ipv4_tos_mask |= tos_word_mask->tos;
620a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->ipv4_tos &= ~tos_word_mask->tos;
621a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->ipv4_tos |= tos_word->tos & tos_word_mask->tos;
622a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->head.jump_id =
623a3c6b063SPieter Jansen van Vuuren 			NFP_FL_ACTION_OPCODE_SET_IPV4_TTL_TOS;
624a3c6b063SPieter Jansen van Vuuren 		set_ip_ttl_tos->head.len_lw = sizeof(*set_ip_ttl_tos) >>
625a3c6b063SPieter Jansen van Vuuren 					      NFP_FL_LW_SIZ;
626c0b1bd9aSPieter Jansen van Vuuren 		break;
627c0b1bd9aSPieter Jansen van Vuuren 	default:
628bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported section of IPv4 header");
629c0b1bd9aSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
630c0b1bd9aSPieter Jansen van Vuuren 	}
631c0b1bd9aSPieter Jansen van Vuuren 
632c0b1bd9aSPieter Jansen van Vuuren 	return 0;
633c0b1bd9aSPieter Jansen van Vuuren }
634c0b1bd9aSPieter Jansen van Vuuren 
635354b82bbSPieter Jansen van Vuuren static void
nfp_fl_set_ip6_helper(int opcode_tag,u8 word,__be32 exact,__be32 mask,struct nfp_fl_set_ipv6_addr * ip6)636140b6abaSPieter Jansen van Vuuren nfp_fl_set_ip6_helper(int opcode_tag, u8 word, __be32 exact, __be32 mask,
637354b82bbSPieter Jansen van Vuuren 		      struct nfp_fl_set_ipv6_addr *ip6)
638354b82bbSPieter Jansen van Vuuren {
639140b6abaSPieter Jansen van Vuuren 	ip6->ipv6[word].mask |= mask;
640140b6abaSPieter Jansen van Vuuren 	ip6->ipv6[word].exact &= ~mask;
641140b6abaSPieter Jansen van Vuuren 	ip6->ipv6[word].exact |= exact & mask;
642354b82bbSPieter Jansen van Vuuren 
643354b82bbSPieter Jansen van Vuuren 	ip6->reserved = cpu_to_be16(0);
64462d3f60bSPieter Jansen van Vuuren 	ip6->head.jump_id = opcode_tag;
64562d3f60bSPieter Jansen van Vuuren 	ip6->head.len_lw = sizeof(*ip6) >> NFP_FL_LW_SIZ;
646354b82bbSPieter Jansen van Vuuren }
647354b82bbSPieter Jansen van Vuuren 
6484234d62cSPieter Jansen van Vuuren struct ipv6_hop_limit_word {
6494234d62cSPieter Jansen van Vuuren 	__be16 payload_len;
6504234d62cSPieter Jansen van Vuuren 	u8 nexthdr;
6514234d62cSPieter Jansen van Vuuren 	u8 hop_limit;
6524234d62cSPieter Jansen van Vuuren };
6534234d62cSPieter Jansen van Vuuren 
6544234d62cSPieter Jansen van Vuuren static int
nfp_fl_set_ip6_hop_limit_flow_label(u32 off,__be32 exact,__be32 mask,struct nfp_fl_set_ipv6_tc_hl_fl * ip_hl_fl,struct netlink_ext_ack * extack)6554234d62cSPieter Jansen van Vuuren nfp_fl_set_ip6_hop_limit_flow_label(u32 off, __be32 exact, __be32 mask,
656bef6e97dSPieter Jansen van Vuuren 				    struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl,
657bef6e97dSPieter Jansen van Vuuren 				    struct netlink_ext_ack *extack)
6584234d62cSPieter Jansen van Vuuren {
6594234d62cSPieter Jansen van Vuuren 	struct ipv6_hop_limit_word *fl_hl_mask;
6604234d62cSPieter Jansen van Vuuren 	struct ipv6_hop_limit_word *fl_hl;
6614234d62cSPieter Jansen van Vuuren 
6624234d62cSPieter Jansen van Vuuren 	switch (off) {
6634234d62cSPieter Jansen van Vuuren 	case offsetof(struct ipv6hdr, payload_len):
6644234d62cSPieter Jansen van Vuuren 		fl_hl_mask = (struct ipv6_hop_limit_word *)&mask;
6654234d62cSPieter Jansen van Vuuren 		fl_hl = (struct ipv6_hop_limit_word *)&exact;
6664234d62cSPieter Jansen van Vuuren 
667bef6e97dSPieter Jansen van Vuuren 		if (fl_hl_mask->nexthdr || fl_hl_mask->payload_len) {
668bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv6 hop limit action");
6694234d62cSPieter Jansen van Vuuren 			return -EOPNOTSUPP;
670bef6e97dSPieter Jansen van Vuuren 		}
6714234d62cSPieter Jansen van Vuuren 
6724234d62cSPieter Jansen van Vuuren 		ip_hl_fl->ipv6_hop_limit_mask |= fl_hl_mask->hop_limit;
6734234d62cSPieter Jansen van Vuuren 		ip_hl_fl->ipv6_hop_limit &= ~fl_hl_mask->hop_limit;
6744234d62cSPieter Jansen van Vuuren 		ip_hl_fl->ipv6_hop_limit |= fl_hl->hop_limit &
6754234d62cSPieter Jansen van Vuuren 					    fl_hl_mask->hop_limit;
6764234d62cSPieter Jansen van Vuuren 		break;
6774234d62cSPieter Jansen van Vuuren 	case round_down(offsetof(struct ipv6hdr, flow_lbl), 4):
67827f2533bSYinjun Zhang 		if (mask & ~IPV6_FLOWINFO_MASK ||
67927f2533bSYinjun Zhang 		    exact & ~IPV6_FLOWINFO_MASK) {
68027f2533bSYinjun Zhang 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv6 flow info action");
6814234d62cSPieter Jansen van Vuuren 			return -EOPNOTSUPP;
682bef6e97dSPieter Jansen van Vuuren 		}
6834234d62cSPieter Jansen van Vuuren 
6844234d62cSPieter Jansen van Vuuren 		ip_hl_fl->ipv6_label_mask |= mask;
6854234d62cSPieter Jansen van Vuuren 		ip_hl_fl->ipv6_label &= ~mask;
6864234d62cSPieter Jansen van Vuuren 		ip_hl_fl->ipv6_label |= exact & mask;
6874234d62cSPieter Jansen van Vuuren 		break;
6884234d62cSPieter Jansen van Vuuren 	}
6894234d62cSPieter Jansen van Vuuren 
6904234d62cSPieter Jansen van Vuuren 	ip_hl_fl->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV6_TC_HL_FL;
6914234d62cSPieter Jansen van Vuuren 	ip_hl_fl->head.len_lw = sizeof(*ip_hl_fl) >> NFP_FL_LW_SIZ;
6924234d62cSPieter Jansen van Vuuren 
6934234d62cSPieter Jansen van Vuuren 	return 0;
6944234d62cSPieter Jansen van Vuuren }
6954234d62cSPieter Jansen van Vuuren 
696354b82bbSPieter Jansen van Vuuren static int
nfp_fl_set_ip6(const struct flow_action_entry * act,u32 off,struct nfp_fl_set_ipv6_addr * ip_dst,struct nfp_fl_set_ipv6_addr * ip_src,struct nfp_fl_set_ipv6_tc_hl_fl * ip_hl_fl,struct netlink_ext_ack * extack)697c0bc5d8eSPablo Neira Ayuso nfp_fl_set_ip6(const struct flow_action_entry *act, u32 off,
698354b82bbSPieter Jansen van Vuuren 	       struct nfp_fl_set_ipv6_addr *ip_dst,
6994234d62cSPieter Jansen van Vuuren 	       struct nfp_fl_set_ipv6_addr *ip_src,
700bef6e97dSPieter Jansen van Vuuren 	       struct nfp_fl_set_ipv6_tc_hl_fl *ip_hl_fl,
701bef6e97dSPieter Jansen van Vuuren 	       struct netlink_ext_ack *extack)
702354b82bbSPieter Jansen van Vuuren {
703354b82bbSPieter Jansen van Vuuren 	__be32 exact, mask;
7044234d62cSPieter Jansen van Vuuren 	int err = 0;
705140b6abaSPieter Jansen van Vuuren 	u8 word;
706354b82bbSPieter Jansen van Vuuren 
707354b82bbSPieter Jansen van Vuuren 	/* We are expecting tcf_pedit to return a big endian value */
70873867881SPablo Neira Ayuso 	mask = (__force __be32)~act->mangle.mask;
70973867881SPablo Neira Ayuso 	exact = (__force __be32)act->mangle.val;
710354b82bbSPieter Jansen van Vuuren 
711bef6e97dSPieter Jansen van Vuuren 	if (exact & ~mask) {
712bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit IPv6 action");
713354b82bbSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
714bef6e97dSPieter Jansen van Vuuren 	}
715354b82bbSPieter Jansen van Vuuren 
716140b6abaSPieter Jansen van Vuuren 	if (off < offsetof(struct ipv6hdr, saddr)) {
7174234d62cSPieter Jansen van Vuuren 		err = nfp_fl_set_ip6_hop_limit_flow_label(off, exact, mask,
718bef6e97dSPieter Jansen van Vuuren 							  ip_hl_fl, extack);
719140b6abaSPieter Jansen van Vuuren 	} else if (off < offsetof(struct ipv6hdr, daddr)) {
720140b6abaSPieter Jansen van Vuuren 		word = (off - offsetof(struct ipv6hdr, saddr)) / sizeof(exact);
721140b6abaSPieter Jansen van Vuuren 		nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, word,
722354b82bbSPieter Jansen van Vuuren 				      exact, mask, ip_src);
723140b6abaSPieter Jansen van Vuuren 	} else if (off < offsetof(struct ipv6hdr, daddr) +
724140b6abaSPieter Jansen van Vuuren 		       sizeof(struct in6_addr)) {
725140b6abaSPieter Jansen van Vuuren 		word = (off - offsetof(struct ipv6hdr, daddr)) / sizeof(exact);
726140b6abaSPieter Jansen van Vuuren 		nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_DST, word,
727354b82bbSPieter Jansen van Vuuren 				      exact, mask, ip_dst);
728140b6abaSPieter Jansen van Vuuren 	} else {
729bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported section of IPv6 header");
730354b82bbSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
731140b6abaSPieter Jansen van Vuuren 	}
732354b82bbSPieter Jansen van Vuuren 
7334234d62cSPieter Jansen van Vuuren 	return err;
734354b82bbSPieter Jansen van Vuuren }
735354b82bbSPieter Jansen van Vuuren 
736c0b1bd9aSPieter Jansen van Vuuren static int
nfp_fl_set_tport(const struct flow_action_entry * act,u32 off,struct nfp_fl_set_tport * set_tport,int opcode,struct netlink_ext_ack * extack)737c0bc5d8eSPablo Neira Ayuso nfp_fl_set_tport(const struct flow_action_entry *act, u32 off,
738bef6e97dSPieter Jansen van Vuuren 		 struct nfp_fl_set_tport *set_tport, int opcode,
739bef6e97dSPieter Jansen van Vuuren 		 struct netlink_ext_ack *extack)
740f8b7b0a6SPieter Jansen van Vuuren {
741f8b7b0a6SPieter Jansen van Vuuren 	u32 exact, mask;
742f8b7b0a6SPieter Jansen van Vuuren 
743bef6e97dSPieter Jansen van Vuuren 	if (off) {
744bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported section of L4 header");
745f8b7b0a6SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
746bef6e97dSPieter Jansen van Vuuren 	}
747f8b7b0a6SPieter Jansen van Vuuren 
74873867881SPablo Neira Ayuso 	mask = ~act->mangle.mask;
74973867881SPablo Neira Ayuso 	exact = act->mangle.val;
750f8b7b0a6SPieter Jansen van Vuuren 
751bef6e97dSPieter Jansen van Vuuren 	if (exact & ~mask) {
752bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: invalid pedit L4 action");
753f8b7b0a6SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
754bef6e97dSPieter Jansen van Vuuren 	}
755f8b7b0a6SPieter Jansen van Vuuren 
756f8b7b0a6SPieter Jansen van Vuuren 	nfp_fl_set_helper32(exact, mask, set_tport->tp_port_val,
757f8b7b0a6SPieter Jansen van Vuuren 			    set_tport->tp_port_mask);
758f8b7b0a6SPieter Jansen van Vuuren 
759f8b7b0a6SPieter Jansen van Vuuren 	set_tport->reserved = cpu_to_be16(0);
76062d3f60bSPieter Jansen van Vuuren 	set_tport->head.jump_id = opcode;
76162d3f60bSPieter Jansen van Vuuren 	set_tport->head.len_lw = sizeof(*set_tport) >> NFP_FL_LW_SIZ;
762f8b7b0a6SPieter Jansen van Vuuren 
763f8b7b0a6SPieter Jansen van Vuuren 	return 0;
764f8b7b0a6SPieter Jansen van Vuuren }
765f8b7b0a6SPieter Jansen van Vuuren 
nfp_fl_csum_l4_to_flag(u8 ip_proto)766ed8f2b52SPieter Jansen van Vuuren static u32 nfp_fl_csum_l4_to_flag(u8 ip_proto)
767ed8f2b52SPieter Jansen van Vuuren {
768ed8f2b52SPieter Jansen van Vuuren 	switch (ip_proto) {
769ed8f2b52SPieter Jansen van Vuuren 	case 0:
770ed8f2b52SPieter Jansen van Vuuren 		/* Filter doesn't force proto match,
771ed8f2b52SPieter Jansen van Vuuren 		 * both TCP and UDP will be updated if encountered
772ed8f2b52SPieter Jansen van Vuuren 		 */
773ed8f2b52SPieter Jansen van Vuuren 		return TCA_CSUM_UPDATE_FLAG_TCP | TCA_CSUM_UPDATE_FLAG_UDP;
774ed8f2b52SPieter Jansen van Vuuren 	case IPPROTO_TCP:
775ed8f2b52SPieter Jansen van Vuuren 		return TCA_CSUM_UPDATE_FLAG_TCP;
776ed8f2b52SPieter Jansen van Vuuren 	case IPPROTO_UDP:
777ed8f2b52SPieter Jansen van Vuuren 		return TCA_CSUM_UPDATE_FLAG_UDP;
778ed8f2b52SPieter Jansen van Vuuren 	default:
779ed8f2b52SPieter Jansen van Vuuren 		/* All other protocols will be ignored by FW */
780ed8f2b52SPieter Jansen van Vuuren 		return 0;
781ed8f2b52SPieter Jansen van Vuuren 	}
782ed8f2b52SPieter Jansen van Vuuren }
783ed8f2b52SPieter Jansen van Vuuren 
784eff07b42SPieter Jansen van Vuuren struct nfp_flower_pedit_acts {
785354b82bbSPieter Jansen van Vuuren 	struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src;
7864234d62cSPieter Jansen van Vuuren 	struct nfp_fl_set_ipv6_tc_hl_fl set_ip6_tc_hl_fl;
787a3c6b063SPieter Jansen van Vuuren 	struct nfp_fl_set_ip4_ttl_tos set_ip_ttl_tos;
788c0b1bd9aSPieter Jansen van Vuuren 	struct nfp_fl_set_ip4_addrs set_ip_addr;
789f8b7b0a6SPieter Jansen van Vuuren 	struct nfp_fl_set_tport set_tport;
790da83d8feSPieter Jansen van Vuuren 	struct nfp_fl_set_eth set_eth;
791eff07b42SPieter Jansen van Vuuren };
792eff07b42SPieter Jansen van Vuuren 
793eff07b42SPieter Jansen van Vuuren static int
nfp_fl_commit_mangle(struct flow_rule * rule,char * nfp_action,int * a_len,struct nfp_flower_pedit_acts * set_act,u32 * csum_updated)794e75dc265SLouis Peens nfp_fl_commit_mangle(struct flow_rule *rule, char *nfp_action,
795eff07b42SPieter Jansen van Vuuren 		     int *a_len, struct nfp_flower_pedit_acts *set_act,
796eff07b42SPieter Jansen van Vuuren 		     u32 *csum_updated)
797eff07b42SPieter Jansen van Vuuren {
798d08c9e58SPieter Jansen van Vuuren 	size_t act_size = 0;
799ed8f2b52SPieter Jansen van Vuuren 	u8 ip_proto = 0;
800da83d8feSPieter Jansen van Vuuren 
8018f256622SPablo Neira Ayuso 	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
8028f256622SPablo Neira Ayuso 		struct flow_match_basic match;
803ed8f2b52SPieter Jansen van Vuuren 
8048f256622SPablo Neira Ayuso 		flow_rule_match_basic(rule, &match);
8058f256622SPablo Neira Ayuso 		ip_proto = match.key->ip_proto;
806ed8f2b52SPieter Jansen van Vuuren 	}
807ed8f2b52SPieter Jansen van Vuuren 
808eff07b42SPieter Jansen van Vuuren 	if (set_act->set_eth.head.len_lw) {
809eff07b42SPieter Jansen van Vuuren 		act_size = sizeof(set_act->set_eth);
810eff07b42SPieter Jansen van Vuuren 		memcpy(nfp_action, &set_act->set_eth, act_size);
811da83d8feSPieter Jansen van Vuuren 		*a_len += act_size;
812d08c9e58SPieter Jansen van Vuuren 	}
813eff07b42SPieter Jansen van Vuuren 
814eff07b42SPieter Jansen van Vuuren 	if (set_act->set_ip_ttl_tos.head.len_lw) {
815a3c6b063SPieter Jansen van Vuuren 		nfp_action += act_size;
816eff07b42SPieter Jansen van Vuuren 		act_size = sizeof(set_act->set_ip_ttl_tos);
817eff07b42SPieter Jansen van Vuuren 		memcpy(nfp_action, &set_act->set_ip_ttl_tos, act_size);
818a3c6b063SPieter Jansen van Vuuren 		*a_len += act_size;
819a3c6b063SPieter Jansen van Vuuren 
820a3c6b063SPieter Jansen van Vuuren 		/* Hardware will automatically fix IPv4 and TCP/UDP checksum. */
821a3c6b063SPieter Jansen van Vuuren 		*csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR |
822a3c6b063SPieter Jansen van Vuuren 				nfp_fl_csum_l4_to_flag(ip_proto);
823a3c6b063SPieter Jansen van Vuuren 	}
824eff07b42SPieter Jansen van Vuuren 
825eff07b42SPieter Jansen van Vuuren 	if (set_act->set_ip_addr.head.len_lw) {
826d08c9e58SPieter Jansen van Vuuren 		nfp_action += act_size;
827eff07b42SPieter Jansen van Vuuren 		act_size = sizeof(set_act->set_ip_addr);
828eff07b42SPieter Jansen van Vuuren 		memcpy(nfp_action, &set_act->set_ip_addr, act_size);
829c0b1bd9aSPieter Jansen van Vuuren 		*a_len += act_size;
830ed8f2b52SPieter Jansen van Vuuren 
831ed8f2b52SPieter Jansen van Vuuren 		/* Hardware will automatically fix IPv4 and TCP/UDP checksum. */
832ed8f2b52SPieter Jansen van Vuuren 		*csum_updated |= TCA_CSUM_UPDATE_FLAG_IPV4HDR |
833ed8f2b52SPieter Jansen van Vuuren 				nfp_fl_csum_l4_to_flag(ip_proto);
834d08c9e58SPieter Jansen van Vuuren 	}
835eff07b42SPieter Jansen van Vuuren 
836eff07b42SPieter Jansen van Vuuren 	if (set_act->set_ip6_tc_hl_fl.head.len_lw) {
8374234d62cSPieter Jansen van Vuuren 		nfp_action += act_size;
838eff07b42SPieter Jansen van Vuuren 		act_size = sizeof(set_act->set_ip6_tc_hl_fl);
839eff07b42SPieter Jansen van Vuuren 		memcpy(nfp_action, &set_act->set_ip6_tc_hl_fl, act_size);
8404234d62cSPieter Jansen van Vuuren 		*a_len += act_size;
8414234d62cSPieter Jansen van Vuuren 
8424234d62cSPieter Jansen van Vuuren 		/* Hardware will automatically fix TCP/UDP checksum. */
8434234d62cSPieter Jansen van Vuuren 		*csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
8444234d62cSPieter Jansen van Vuuren 	}
845eff07b42SPieter Jansen van Vuuren 
846eff07b42SPieter Jansen van Vuuren 	if (set_act->set_ip6_dst.head.len_lw &&
847eff07b42SPieter Jansen van Vuuren 	    set_act->set_ip6_src.head.len_lw) {
848354b82bbSPieter Jansen van Vuuren 		/* TC compiles set src and dst IPv6 address as a single action,
849354b82bbSPieter Jansen van Vuuren 		 * the hardware requires this to be 2 separate actions.
850354b82bbSPieter Jansen van Vuuren 		 */
851d08c9e58SPieter Jansen van Vuuren 		nfp_action += act_size;
852eff07b42SPieter Jansen van Vuuren 		act_size = sizeof(set_act->set_ip6_src);
853eff07b42SPieter Jansen van Vuuren 		memcpy(nfp_action, &set_act->set_ip6_src, act_size);
854354b82bbSPieter Jansen van Vuuren 		*a_len += act_size;
855354b82bbSPieter Jansen van Vuuren 
856eff07b42SPieter Jansen van Vuuren 		act_size = sizeof(set_act->set_ip6_dst);
857eff07b42SPieter Jansen van Vuuren 		memcpy(&nfp_action[sizeof(set_act->set_ip6_src)],
858eff07b42SPieter Jansen van Vuuren 		       &set_act->set_ip6_dst, act_size);
859354b82bbSPieter Jansen van Vuuren 		*a_len += act_size;
860ed8f2b52SPieter Jansen van Vuuren 
861ed8f2b52SPieter Jansen van Vuuren 		/* Hardware will automatically fix TCP/UDP checksum. */
862ed8f2b52SPieter Jansen van Vuuren 		*csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
863eff07b42SPieter Jansen van Vuuren 	} else if (set_act->set_ip6_dst.head.len_lw) {
864d08c9e58SPieter Jansen van Vuuren 		nfp_action += act_size;
865eff07b42SPieter Jansen van Vuuren 		act_size = sizeof(set_act->set_ip6_dst);
866eff07b42SPieter Jansen van Vuuren 		memcpy(nfp_action, &set_act->set_ip6_dst, act_size);
867354b82bbSPieter Jansen van Vuuren 		*a_len += act_size;
868ed8f2b52SPieter Jansen van Vuuren 
869ed8f2b52SPieter Jansen van Vuuren 		/* Hardware will automatically fix TCP/UDP checksum. */
870ed8f2b52SPieter Jansen van Vuuren 		*csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
871eff07b42SPieter Jansen van Vuuren 	} else if (set_act->set_ip6_src.head.len_lw) {
872d08c9e58SPieter Jansen van Vuuren 		nfp_action += act_size;
873eff07b42SPieter Jansen van Vuuren 		act_size = sizeof(set_act->set_ip6_src);
874eff07b42SPieter Jansen van Vuuren 		memcpy(nfp_action, &set_act->set_ip6_src, act_size);
875354b82bbSPieter Jansen van Vuuren 		*a_len += act_size;
876ed8f2b52SPieter Jansen van Vuuren 
877ed8f2b52SPieter Jansen van Vuuren 		/* Hardware will automatically fix TCP/UDP checksum. */
878ed8f2b52SPieter Jansen van Vuuren 		*csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
879d08c9e58SPieter Jansen van Vuuren 	}
880eff07b42SPieter Jansen van Vuuren 	if (set_act->set_tport.head.len_lw) {
881d08c9e58SPieter Jansen van Vuuren 		nfp_action += act_size;
882eff07b42SPieter Jansen van Vuuren 		act_size = sizeof(set_act->set_tport);
883eff07b42SPieter Jansen van Vuuren 		memcpy(nfp_action, &set_act->set_tport, act_size);
884f8b7b0a6SPieter Jansen van Vuuren 		*a_len += act_size;
885ed8f2b52SPieter Jansen van Vuuren 
886ed8f2b52SPieter Jansen van Vuuren 		/* Hardware will automatically fix TCP/UDP checksum. */
887ed8f2b52SPieter Jansen van Vuuren 		*csum_updated |= nfp_fl_csum_l4_to_flag(ip_proto);
888da83d8feSPieter Jansen van Vuuren 	}
889da83d8feSPieter Jansen van Vuuren 
890da83d8feSPieter Jansen van Vuuren 	return 0;
891da83d8feSPieter Jansen van Vuuren }
892da83d8feSPieter Jansen van Vuuren 
8931a1e586fSPieter Jansen van Vuuren static int
nfp_fl_pedit(const struct flow_action_entry * act,char * nfp_action,int * a_len,u32 * csum_updated,struct nfp_flower_pedit_acts * set_act,struct netlink_ext_ack * extack)894eff07b42SPieter Jansen van Vuuren nfp_fl_pedit(const struct flow_action_entry *act,
895e75dc265SLouis Peens 	     char *nfp_action, int *a_len,
896bef6e97dSPieter Jansen van Vuuren 	     u32 *csum_updated, struct nfp_flower_pedit_acts *set_act,
897bef6e97dSPieter Jansen van Vuuren 	     struct netlink_ext_ack *extack)
898eff07b42SPieter Jansen van Vuuren {
899eff07b42SPieter Jansen van Vuuren 	enum flow_action_mangle_base htype;
900eff07b42SPieter Jansen van Vuuren 	u32 offset;
901eff07b42SPieter Jansen van Vuuren 
902eff07b42SPieter Jansen van Vuuren 	htype = act->mangle.htype;
903eff07b42SPieter Jansen van Vuuren 	offset = act->mangle.offset;
904eff07b42SPieter Jansen van Vuuren 
905eff07b42SPieter Jansen van Vuuren 	switch (htype) {
906eff07b42SPieter Jansen van Vuuren 	case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
907bef6e97dSPieter Jansen van Vuuren 		return nfp_fl_set_eth(act, offset, &set_act->set_eth, extack);
908eff07b42SPieter Jansen van Vuuren 	case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
909eff07b42SPieter Jansen van Vuuren 		return nfp_fl_set_ip4(act, offset, &set_act->set_ip_addr,
910bef6e97dSPieter Jansen van Vuuren 				      &set_act->set_ip_ttl_tos, extack);
911eff07b42SPieter Jansen van Vuuren 	case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
912eff07b42SPieter Jansen van Vuuren 		return nfp_fl_set_ip6(act, offset, &set_act->set_ip6_dst,
913eff07b42SPieter Jansen van Vuuren 				      &set_act->set_ip6_src,
914bef6e97dSPieter Jansen van Vuuren 				      &set_act->set_ip6_tc_hl_fl, extack);
915eff07b42SPieter Jansen van Vuuren 	case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
916eff07b42SPieter Jansen van Vuuren 		return nfp_fl_set_tport(act, offset, &set_act->set_tport,
917bef6e97dSPieter Jansen van Vuuren 					NFP_FL_ACTION_OPCODE_SET_TCP, extack);
918eff07b42SPieter Jansen van Vuuren 	case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
919eff07b42SPieter Jansen van Vuuren 		return nfp_fl_set_tport(act, offset, &set_act->set_tport,
920bef6e97dSPieter Jansen van Vuuren 					NFP_FL_ACTION_OPCODE_SET_UDP, extack);
921eff07b42SPieter Jansen van Vuuren 	default:
922bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: pedit on unsupported header");
923eff07b42SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
924eff07b42SPieter Jansen van Vuuren 	}
925eff07b42SPieter Jansen van Vuuren }
926eff07b42SPieter Jansen van Vuuren 
nfp_fl_meter(char * act_data)927147747ecSBaowen Zheng static struct nfp_fl_meter *nfp_fl_meter(char *act_data)
928147747ecSBaowen Zheng {
929147747ecSBaowen Zheng 	size_t act_size = sizeof(struct nfp_fl_meter);
930147747ecSBaowen Zheng 	struct nfp_fl_meter *meter_act;
931147747ecSBaowen Zheng 
932147747ecSBaowen Zheng 	meter_act = (struct nfp_fl_meter *)act_data;
933147747ecSBaowen Zheng 
934147747ecSBaowen Zheng 	memset(meter_act, 0, act_size);
935147747ecSBaowen Zheng 
936147747ecSBaowen Zheng 	meter_act->head.jump_id = NFP_FL_ACTION_OPCODE_METER;
937147747ecSBaowen Zheng 	meter_act->head.len_lw = act_size >> NFP_FL_LW_SIZ;
938147747ecSBaowen Zheng 
939147747ecSBaowen Zheng 	return meter_act;
940147747ecSBaowen Zheng }
941147747ecSBaowen Zheng 
942147747ecSBaowen Zheng static int
nfp_flower_meter_action(struct nfp_app * app,const struct flow_action_entry * action,struct nfp_fl_payload * nfp_fl,int * a_len,struct net_device * netdev,struct netlink_ext_ack * extack)943147747ecSBaowen Zheng nfp_flower_meter_action(struct nfp_app *app,
944147747ecSBaowen Zheng 			const struct flow_action_entry *action,
945147747ecSBaowen Zheng 			struct nfp_fl_payload *nfp_fl, int *a_len,
946147747ecSBaowen Zheng 			struct net_device *netdev,
947147747ecSBaowen Zheng 			struct netlink_ext_ack *extack)
948147747ecSBaowen Zheng {
949147747ecSBaowen Zheng 	struct nfp_fl_meter *fl_meter;
950147747ecSBaowen Zheng 	u32 meter_id;
951147747ecSBaowen Zheng 
952147747ecSBaowen Zheng 	if (*a_len + sizeof(struct nfp_fl_meter) > NFP_FL_MAX_A_SIZ) {
953147747ecSBaowen Zheng 		NL_SET_ERR_MSG_MOD(extack,
954147747ecSBaowen Zheng 				   "unsupported offload:meter action size beyond the allowed maximum");
955147747ecSBaowen Zheng 		return -EOPNOTSUPP;
956147747ecSBaowen Zheng 	}
957147747ecSBaowen Zheng 
958147747ecSBaowen Zheng 	meter_id = action->hw_index;
959147747ecSBaowen Zheng 	if (!nfp_flower_search_meter_entry(app, meter_id)) {
960147747ecSBaowen Zheng 		NL_SET_ERR_MSG_MOD(extack,
961bdd6a89dSNiklas Söderlund 				   "can not offload flow table with unsupported police action.");
962147747ecSBaowen Zheng 		return -EOPNOTSUPP;
963147747ecSBaowen Zheng 	}
964147747ecSBaowen Zheng 
965147747ecSBaowen Zheng 	fl_meter = nfp_fl_meter(&nfp_fl->action_data[*a_len]);
966147747ecSBaowen Zheng 	*a_len += sizeof(struct nfp_fl_meter);
967147747ecSBaowen Zheng 	fl_meter->meter_id = cpu_to_be32(meter_id);
968147747ecSBaowen Zheng 
969147747ecSBaowen Zheng 	return 0;
970147747ecSBaowen Zheng }
971147747ecSBaowen Zheng 
972eff07b42SPieter Jansen van Vuuren static int
nfp_flower_output_action(struct nfp_app * app,const struct flow_action_entry * act,struct nfp_fl_payload * nfp_fl,int * a_len,struct net_device * netdev,bool last,enum nfp_flower_tun_type * tun_type,int * tun_out_cnt,int * out_cnt,u32 * csum_updated,bool pkt_host,struct netlink_ext_ack * extack)973eff07b42SPieter Jansen van Vuuren nfp_flower_output_action(struct nfp_app *app,
974eff07b42SPieter Jansen van Vuuren 			 const struct flow_action_entry *act,
9757e24a593SJohn Hurley 			 struct nfp_fl_payload *nfp_fl, int *a_len,
9767e24a593SJohn Hurley 			 struct net_device *netdev, bool last,
9777e24a593SJohn Hurley 			 enum nfp_flower_tun_type *tun_type, int *tun_out_cnt,
978f5c977eeSJohn Hurley 			 int *out_cnt, u32 *csum_updated, bool pkt_host,
979bef6e97dSPieter Jansen van Vuuren 			 struct netlink_ext_ack *extack)
9807e24a593SJohn Hurley {
9817e24a593SJohn Hurley 	struct nfp_flower_priv *priv = app->priv;
9827e24a593SJohn Hurley 	struct nfp_fl_output *output;
9837e24a593SJohn Hurley 	int err, prelag_size;
9847e24a593SJohn Hurley 
985ed8f2b52SPieter Jansen van Vuuren 	/* If csum_updated has not been reset by now, it means HW will
986ed8f2b52SPieter Jansen van Vuuren 	 * incorrectly update csums when they are not requested.
987ed8f2b52SPieter Jansen van Vuuren 	 */
988bef6e97dSPieter Jansen van Vuuren 	if (*csum_updated) {
989bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: set actions without updating checksums are not supported");
990ed8f2b52SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
991bef6e97dSPieter Jansen van Vuuren 	}
992ed8f2b52SPieter Jansen van Vuuren 
993bef6e97dSPieter Jansen van Vuuren 	if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ) {
994bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: mirred output increases action list size beyond the allowed maximum");
9957e24a593SJohn Hurley 		return -EOPNOTSUPP;
996bef6e97dSPieter Jansen van Vuuren 	}
9977e24a593SJohn Hurley 
9987e24a593SJohn Hurley 	output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len];
99973867881SPablo Neira Ayuso 	err = nfp_fl_output(app, output, act, nfp_fl, last, netdev, *tun_type,
1000f5c977eeSJohn Hurley 			    tun_out_cnt, pkt_host, extack);
10017e24a593SJohn Hurley 	if (err)
10027e24a593SJohn Hurley 		return err;
10037e24a593SJohn Hurley 
10047e24a593SJohn Hurley 	*a_len += sizeof(struct nfp_fl_output);
10057e24a593SJohn Hurley 
1006e09303d3SLouis Peens 	if (priv->flower_en_feats & NFP_FL_ENABLE_LAG) {
10077e24a593SJohn Hurley 		/* nfp_fl_pre_lag returns -err or size of prelag action added.
10087e24a593SJohn Hurley 		 * This will be 0 if it is not egressing to a lag dev.
10097e24a593SJohn Hurley 		 */
1010bef6e97dSPieter Jansen van Vuuren 		prelag_size = nfp_fl_pre_lag(app, act, nfp_fl, *a_len, extack);
1011bef6e97dSPieter Jansen van Vuuren 		if (prelag_size < 0) {
10127e24a593SJohn Hurley 			return prelag_size;
1013bef6e97dSPieter Jansen van Vuuren 		} else if (prelag_size > 0 && (!last || *out_cnt)) {
1014bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: LAG action has to be last action in action list");
10157e24a593SJohn Hurley 			return -EOPNOTSUPP;
1016bef6e97dSPieter Jansen van Vuuren 		}
10177e24a593SJohn Hurley 
10187e24a593SJohn Hurley 		*a_len += prelag_size;
10197e24a593SJohn Hurley 	}
10207e24a593SJohn Hurley 	(*out_cnt)++;
10217e24a593SJohn Hurley 
10227e24a593SJohn Hurley 	return 0;
10237e24a593SJohn Hurley }
10247e24a593SJohn Hurley 
10257e24a593SJohn Hurley static int
nfp_flower_loop_action(struct nfp_app * app,const struct flow_action_entry * act,struct flow_rule * rule,struct nfp_fl_payload * nfp_fl,int * a_len,struct net_device * netdev,enum nfp_flower_tun_type * tun_type,int * tun_out_cnt,int * out_cnt,u32 * csum_updated,struct nfp_flower_pedit_acts * set_act,bool * pkt_host,struct netlink_ext_ack * extack,int act_idx)102673867881SPablo Neira Ayuso nfp_flower_loop_action(struct nfp_app *app, const struct flow_action_entry *act,
1027e75dc265SLouis Peens 		       struct flow_rule *rule,
10281a1e586fSPieter Jansen van Vuuren 		       struct nfp_fl_payload *nfp_fl, int *a_len,
1029b27d6a95SJohn Hurley 		       struct net_device *netdev,
10307e24a593SJohn Hurley 		       enum nfp_flower_tun_type *tun_type, int *tun_out_cnt,
1031eff07b42SPieter Jansen van Vuuren 		       int *out_cnt, u32 *csum_updated,
1032f5c977eeSJohn Hurley 		       struct nfp_flower_pedit_acts *set_act, bool *pkt_host,
1033fccac580SPieter Jansen van Vuuren 		       struct netlink_ext_ack *extack, int act_idx)
10341a1e586fSPieter Jansen van Vuuren {
1035147747ecSBaowen Zheng 	struct nfp_flower_priv *fl_priv = app->priv;
1036b27d6a95SJohn Hurley 	struct nfp_fl_pre_tunnel *pre_tun;
10371922c9a4SJohn Hurley 	struct nfp_fl_set_tun *set_tun;
10381a1e586fSPieter Jansen van Vuuren 	struct nfp_fl_push_vlan *psh_v;
1039a6eb1817SJohn Hurley 	struct nfp_fl_push_mpls *psh_m;
10401a1e586fSPieter Jansen van Vuuren 	struct nfp_fl_pop_vlan *pop_v;
104135b7c70cSJohn Hurley 	struct nfp_fl_pop_mpls *pop_m;
1042e03e47a3SJohn Hurley 	struct nfp_fl_set_mpls *set_m;
10431a1e586fSPieter Jansen van Vuuren 	int err;
10441a1e586fSPieter Jansen van Vuuren 
104573867881SPablo Neira Ayuso 	switch (act->id) {
104673867881SPablo Neira Ayuso 	case FLOW_ACTION_DROP:
10471a1e586fSPieter Jansen van Vuuren 		nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_DROP);
104873867881SPablo Neira Ayuso 		break;
1049f5c977eeSJohn Hurley 	case FLOW_ACTION_REDIRECT_INGRESS:
105073867881SPablo Neira Ayuso 	case FLOW_ACTION_REDIRECT:
105173867881SPablo Neira Ayuso 		err = nfp_flower_output_action(app, act, nfp_fl, a_len, netdev,
10527e24a593SJohn Hurley 					       true, tun_type, tun_out_cnt,
1053f5c977eeSJohn Hurley 					       out_cnt, csum_updated, *pkt_host,
1054f5c977eeSJohn Hurley 					       extack);
10551a1e586fSPieter Jansen van Vuuren 		if (err)
10561a1e586fSPieter Jansen van Vuuren 			return err;
105773867881SPablo Neira Ayuso 		break;
1058f5c977eeSJohn Hurley 	case FLOW_ACTION_MIRRED_INGRESS:
105973867881SPablo Neira Ayuso 	case FLOW_ACTION_MIRRED:
106073867881SPablo Neira Ayuso 		err = nfp_flower_output_action(app, act, nfp_fl, a_len, netdev,
10617e24a593SJohn Hurley 					       false, tun_type, tun_out_cnt,
1062f5c977eeSJohn Hurley 					       out_cnt, csum_updated, *pkt_host,
1063f5c977eeSJohn Hurley 					       extack);
10641a1e586fSPieter Jansen van Vuuren 		if (err)
10651a1e586fSPieter Jansen van Vuuren 			return err;
106673867881SPablo Neira Ayuso 		break;
106773867881SPablo Neira Ayuso 	case FLOW_ACTION_VLAN_POP:
1068bef6e97dSPieter Jansen van Vuuren 		if (*a_len +
1069bef6e97dSPieter Jansen van Vuuren 		    sizeof(struct nfp_fl_pop_vlan) > NFP_FL_MAX_A_SIZ) {
1070bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at pop vlan");
10711a1e586fSPieter Jansen van Vuuren 			return -EOPNOTSUPP;
1072bef6e97dSPieter Jansen van Vuuren 		}
10731a1e586fSPieter Jansen van Vuuren 
10741a1e586fSPieter Jansen van Vuuren 		pop_v = (struct nfp_fl_pop_vlan *)&nfp_fl->action_data[*a_len];
10751a1e586fSPieter Jansen van Vuuren 		nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_POPV);
10761a1e586fSPieter Jansen van Vuuren 
10771a1e586fSPieter Jansen van Vuuren 		nfp_fl_pop_vlan(pop_v);
10781a1e586fSPieter Jansen van Vuuren 		*a_len += sizeof(struct nfp_fl_pop_vlan);
107973867881SPablo Neira Ayuso 		break;
108073867881SPablo Neira Ayuso 	case FLOW_ACTION_VLAN_PUSH:
1081bef6e97dSPieter Jansen van Vuuren 		if (*a_len +
1082bef6e97dSPieter Jansen van Vuuren 		    sizeof(struct nfp_fl_push_vlan) > NFP_FL_MAX_A_SIZ) {
1083bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push vlan");
10841a1e586fSPieter Jansen van Vuuren 			return -EOPNOTSUPP;
1085bef6e97dSPieter Jansen van Vuuren 		}
10861a1e586fSPieter Jansen van Vuuren 
10871a1e586fSPieter Jansen van Vuuren 		psh_v = (struct nfp_fl_push_vlan *)&nfp_fl->action_data[*a_len];
10881a1e586fSPieter Jansen van Vuuren 		nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
10891a1e586fSPieter Jansen van Vuuren 
109073867881SPablo Neira Ayuso 		nfp_fl_push_vlan(psh_v, act);
10911a1e586fSPieter Jansen van Vuuren 		*a_len += sizeof(struct nfp_fl_push_vlan);
109273867881SPablo Neira Ayuso 		break;
109373867881SPablo Neira Ayuso 	case FLOW_ACTION_TUNNEL_ENCAP: {
109473867881SPablo Neira Ayuso 		const struct ip_tunnel_info *ip_tun = act->tunnel;
1095224de549SLouis Peens 
1096e75dc265SLouis Peens 		*tun_type = nfp_fl_get_tun_from_act(app, rule, act, act_idx);
1097bef6e97dSPieter Jansen van Vuuren 		if (*tun_type == NFP_FL_TUNNEL_NONE) {
1098bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported tunnel type in action list");
10993ca3059dSJohn Hurley 			return -EOPNOTSUPP;
1100bef6e97dSPieter Jansen van Vuuren 		}
11013ca3059dSJohn Hurley 
1102bef6e97dSPieter Jansen van Vuuren 		if (ip_tun->mode & ~NFP_FL_SUPPORTED_TUNNEL_INFO_FLAGS) {
1103bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported tunnel flags in action list");
1104224de549SLouis Peens 			return -EOPNOTSUPP;
1105bef6e97dSPieter Jansen van Vuuren 		}
1106224de549SLouis Peens 
1107b27d6a95SJohn Hurley 		/* Pre-tunnel action is required for tunnel encap.
1108b27d6a95SJohn Hurley 		 * This checks for next hop entries on NFP.
1109b27d6a95SJohn Hurley 		 * If none, the packet falls back before applying other actions.
1110b27d6a95SJohn Hurley 		 */
1111b27d6a95SJohn Hurley 		if (*a_len + sizeof(struct nfp_fl_pre_tunnel) +
11121922c9a4SJohn Hurley 		    sizeof(struct nfp_fl_set_tun) > NFP_FL_MAX_A_SIZ) {
1113bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at tunnel encap");
1114b27d6a95SJohn Hurley 			return -EOPNOTSUPP;
1115bef6e97dSPieter Jansen van Vuuren 		}
1116b27d6a95SJohn Hurley 
1117b27d6a95SJohn Hurley 		pre_tun = nfp_fl_pre_tunnel(nfp_fl->action_data, *a_len);
1118b27d6a95SJohn Hurley 		nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
1119b27d6a95SJohn Hurley 		*a_len += sizeof(struct nfp_fl_pre_tunnel);
1120b27d6a95SJohn Hurley 
1121bef6e97dSPieter Jansen van Vuuren 		err = nfp_fl_push_geneve_options(nfp_fl, a_len, act, extack);
11229e7c32feSPieter Jansen van Vuuren 		if (err)
11239e7c32feSPieter Jansen van Vuuren 			return err;
11249e7c32feSPieter Jansen van Vuuren 
11253ca3059dSJohn Hurley 		set_tun = (void *)&nfp_fl->action_data[*a_len];
11261922c9a4SJohn Hurley 		err = nfp_fl_set_tun(app, set_tun, act, pre_tun, *tun_type,
11271922c9a4SJohn Hurley 				     netdev, extack);
1128b27d6a95SJohn Hurley 		if (err)
1129b27d6a95SJohn Hurley 			return err;
11301922c9a4SJohn Hurley 		*a_len += sizeof(struct nfp_fl_set_tun);
113173867881SPablo Neira Ayuso 		}
113273867881SPablo Neira Ayuso 		break;
113373867881SPablo Neira Ayuso 	case FLOW_ACTION_TUNNEL_DECAP:
1134b27d6a95SJohn Hurley 		/* Tunnel decap is handled by default so accept action. */
1135b27d6a95SJohn Hurley 		return 0;
113673867881SPablo Neira Ayuso 	case FLOW_ACTION_MANGLE:
1137e75dc265SLouis Peens 		if (nfp_fl_pedit(act, &nfp_fl->action_data[*a_len],
1138bef6e97dSPieter Jansen van Vuuren 				 a_len, csum_updated, set_act, extack))
1139da83d8feSPieter Jansen van Vuuren 			return -EOPNOTSUPP;
114073867881SPablo Neira Ayuso 		break;
114173867881SPablo Neira Ayuso 	case FLOW_ACTION_CSUM:
1142ed8f2b52SPieter Jansen van Vuuren 		/* csum action requests recalc of something we have not fixed */
1143bef6e97dSPieter Jansen van Vuuren 		if (act->csum_flags & ~*csum_updated) {
1144bef6e97dSPieter Jansen van Vuuren 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported csum update action in action list");
1145ed8f2b52SPieter Jansen van Vuuren 			return -EOPNOTSUPP;
1146bef6e97dSPieter Jansen van Vuuren 		}
1147ed8f2b52SPieter Jansen van Vuuren 		/* If we will correctly fix the csum we can remove it from the
1148ed8f2b52SPieter Jansen van Vuuren 		 * csum update list. Which will later be used to check support.
1149ed8f2b52SPieter Jansen van Vuuren 		 */
115073867881SPablo Neira Ayuso 		*csum_updated &= ~act->csum_flags;
115173867881SPablo Neira Ayuso 		break;
1152a6eb1817SJohn Hurley 	case FLOW_ACTION_MPLS_PUSH:
1153a6eb1817SJohn Hurley 		if (*a_len +
1154a6eb1817SJohn Hurley 		    sizeof(struct nfp_fl_push_mpls) > NFP_FL_MAX_A_SIZ) {
1155a6eb1817SJohn Hurley 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at push MPLS");
1156a6eb1817SJohn Hurley 			return -EOPNOTSUPP;
1157a6eb1817SJohn Hurley 		}
1158a6eb1817SJohn Hurley 
1159a6eb1817SJohn Hurley 		psh_m = (struct nfp_fl_push_mpls *)&nfp_fl->action_data[*a_len];
1160a6eb1817SJohn Hurley 		nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
1161a6eb1817SJohn Hurley 
1162a6eb1817SJohn Hurley 		err = nfp_fl_push_mpls(psh_m, act, extack);
1163a6eb1817SJohn Hurley 		if (err)
1164a6eb1817SJohn Hurley 			return err;
1165a6eb1817SJohn Hurley 		*a_len += sizeof(struct nfp_fl_push_mpls);
1166a6eb1817SJohn Hurley 		break;
116735b7c70cSJohn Hurley 	case FLOW_ACTION_MPLS_POP:
116835b7c70cSJohn Hurley 		if (*a_len +
116935b7c70cSJohn Hurley 		    sizeof(struct nfp_fl_pop_mpls) > NFP_FL_MAX_A_SIZ) {
117035b7c70cSJohn Hurley 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at pop MPLS");
117135b7c70cSJohn Hurley 			return -EOPNOTSUPP;
117235b7c70cSJohn Hurley 		}
117335b7c70cSJohn Hurley 
117435b7c70cSJohn Hurley 		pop_m = (struct nfp_fl_pop_mpls *)&nfp_fl->action_data[*a_len];
117535b7c70cSJohn Hurley 		nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
117635b7c70cSJohn Hurley 
117735b7c70cSJohn Hurley 		nfp_fl_pop_mpls(pop_m, act);
117835b7c70cSJohn Hurley 		*a_len += sizeof(struct nfp_fl_pop_mpls);
117935b7c70cSJohn Hurley 		break;
1180e03e47a3SJohn Hurley 	case FLOW_ACTION_MPLS_MANGLE:
1181e03e47a3SJohn Hurley 		if (*a_len +
1182e03e47a3SJohn Hurley 		    sizeof(struct nfp_fl_set_mpls) > NFP_FL_MAX_A_SIZ) {
1183e03e47a3SJohn Hurley 			NL_SET_ERR_MSG_MOD(extack, "unsupported offload: maximum allowed action list size exceeded at set MPLS");
1184e03e47a3SJohn Hurley 			return -EOPNOTSUPP;
1185e03e47a3SJohn Hurley 		}
1186e03e47a3SJohn Hurley 
1187e03e47a3SJohn Hurley 		set_m = (struct nfp_fl_set_mpls *)&nfp_fl->action_data[*a_len];
1188e03e47a3SJohn Hurley 		nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
1189e03e47a3SJohn Hurley 
1190e03e47a3SJohn Hurley 		nfp_fl_set_mpls(set_m, act);
1191e03e47a3SJohn Hurley 		*a_len += sizeof(struct nfp_fl_set_mpls);
1192e03e47a3SJohn Hurley 		break;
1193f5c977eeSJohn Hurley 	case FLOW_ACTION_PTYPE:
1194f5c977eeSJohn Hurley 		/* TC ptype skbedit sets PACKET_HOST for ingress redirect. */
1195f5c977eeSJohn Hurley 		if (act->ptype != PACKET_HOST)
1196f5c977eeSJohn Hurley 			return -EOPNOTSUPP;
1197f5c977eeSJohn Hurley 
1198f5c977eeSJohn Hurley 		*pkt_host = true;
1199f5c977eeSJohn Hurley 		break;
1200147747ecSBaowen Zheng 	case FLOW_ACTION_POLICE:
1201147747ecSBaowen Zheng 		if (!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_METER)) {
1202147747ecSBaowen Zheng 			NL_SET_ERR_MSG_MOD(extack,
1203147747ecSBaowen Zheng 					   "unsupported offload: unsupported police action in action list");
1204147747ecSBaowen Zheng 			return -EOPNOTSUPP;
1205147747ecSBaowen Zheng 		}
1206147747ecSBaowen Zheng 
1207147747ecSBaowen Zheng 		err = nfp_flower_meter_action(app, act, nfp_fl, a_len, netdev,
1208147747ecSBaowen Zheng 					      extack);
1209147747ecSBaowen Zheng 		if (err)
1210147747ecSBaowen Zheng 			return err;
1211147747ecSBaowen Zheng 		break;
121273867881SPablo Neira Ayuso 	default:
12131a1e586fSPieter Jansen van Vuuren 		/* Currently we do not handle any other actions. */
1214bef6e97dSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: unsupported action in action list");
12151a1e586fSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
12161a1e586fSPieter Jansen van Vuuren 	}
12171a1e586fSPieter Jansen van Vuuren 
12181a1e586fSPieter Jansen van Vuuren 	return 0;
12191a1e586fSPieter Jansen van Vuuren }
12201a1e586fSPieter Jansen van Vuuren 
nfp_fl_check_mangle_start(struct flow_action * flow_act,int current_act_idx)1221eff07b42SPieter Jansen van Vuuren static bool nfp_fl_check_mangle_start(struct flow_action *flow_act,
1222eff07b42SPieter Jansen van Vuuren 				      int current_act_idx)
1223eff07b42SPieter Jansen van Vuuren {
1224eff07b42SPieter Jansen van Vuuren 	struct flow_action_entry current_act;
1225eff07b42SPieter Jansen van Vuuren 	struct flow_action_entry prev_act;
1226eff07b42SPieter Jansen van Vuuren 
1227eff07b42SPieter Jansen van Vuuren 	current_act = flow_act->entries[current_act_idx];
1228eff07b42SPieter Jansen van Vuuren 	if (current_act.id != FLOW_ACTION_MANGLE)
1229eff07b42SPieter Jansen van Vuuren 		return false;
1230eff07b42SPieter Jansen van Vuuren 
1231eff07b42SPieter Jansen van Vuuren 	if (current_act_idx == 0)
1232eff07b42SPieter Jansen van Vuuren 		return true;
1233eff07b42SPieter Jansen van Vuuren 
1234eff07b42SPieter Jansen van Vuuren 	prev_act = flow_act->entries[current_act_idx - 1];
1235eff07b42SPieter Jansen van Vuuren 
1236eff07b42SPieter Jansen van Vuuren 	return prev_act.id != FLOW_ACTION_MANGLE;
1237eff07b42SPieter Jansen van Vuuren }
1238eff07b42SPieter Jansen van Vuuren 
nfp_fl_check_mangle_end(struct flow_action * flow_act,int current_act_idx)1239eff07b42SPieter Jansen van Vuuren static bool nfp_fl_check_mangle_end(struct flow_action *flow_act,
1240eff07b42SPieter Jansen van Vuuren 				    int current_act_idx)
1241eff07b42SPieter Jansen van Vuuren {
1242eff07b42SPieter Jansen van Vuuren 	struct flow_action_entry current_act;
1243eff07b42SPieter Jansen van Vuuren 	struct flow_action_entry next_act;
1244eff07b42SPieter Jansen van Vuuren 
1245eff07b42SPieter Jansen van Vuuren 	current_act = flow_act->entries[current_act_idx];
1246eff07b42SPieter Jansen van Vuuren 	if (current_act.id != FLOW_ACTION_MANGLE)
1247eff07b42SPieter Jansen van Vuuren 		return false;
1248eff07b42SPieter Jansen van Vuuren 
1249eff07b42SPieter Jansen van Vuuren 	if (current_act_idx == flow_act->num_entries)
1250eff07b42SPieter Jansen van Vuuren 		return true;
1251eff07b42SPieter Jansen van Vuuren 
1252eff07b42SPieter Jansen van Vuuren 	next_act = flow_act->entries[current_act_idx + 1];
1253eff07b42SPieter Jansen van Vuuren 
1254eff07b42SPieter Jansen van Vuuren 	return next_act.id != FLOW_ACTION_MANGLE;
1255eff07b42SPieter Jansen van Vuuren }
1256eff07b42SPieter Jansen van Vuuren 
nfp_flower_compile_action(struct nfp_app * app,struct flow_rule * rule,struct net_device * netdev,struct nfp_fl_payload * nfp_flow,struct netlink_ext_ack * extack)12577e24a593SJohn Hurley int nfp_flower_compile_action(struct nfp_app *app,
1258e75dc265SLouis Peens 			      struct flow_rule *rule,
12591a1e586fSPieter Jansen van Vuuren 			      struct net_device *netdev,
1260bef6e97dSPieter Jansen van Vuuren 			      struct nfp_fl_payload *nfp_flow,
1261bef6e97dSPieter Jansen van Vuuren 			      struct netlink_ext_ack *extack)
12621a1e586fSPieter Jansen van Vuuren {
1263244cd96aSCong Wang 	int act_len, act_cnt, err, tun_out_cnt, out_cnt, i;
1264eff07b42SPieter Jansen van Vuuren 	struct nfp_flower_pedit_acts set_act;
1265b27d6a95SJohn Hurley 	enum nfp_flower_tun_type tun_type;
126673867881SPablo Neira Ayuso 	struct flow_action_entry *act;
1267f5c977eeSJohn Hurley 	bool pkt_host = false;
1268ed8f2b52SPieter Jansen van Vuuren 	u32 csum_updated = 0;
12691a1e586fSPieter Jansen van Vuuren 
1270e75dc265SLouis Peens 	if (!flow_action_hw_stats_check(&rule->action, extack,
127136b4b92bSJakub Kicinski 					FLOW_ACTION_HW_STATS_DELAYED_BIT))
1272319a1d19SJiri Pirko 		return -EOPNOTSUPP;
1273319a1d19SJiri Pirko 
12741a1e586fSPieter Jansen van Vuuren 	memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ);
12751a1e586fSPieter Jansen van Vuuren 	nfp_flow->meta.act_len = 0;
1276b27d6a95SJohn Hurley 	tun_type = NFP_FL_TUNNEL_NONE;
12771a1e586fSPieter Jansen van Vuuren 	act_len = 0;
12781a1e586fSPieter Jansen van Vuuren 	act_cnt = 0;
1279b27d6a95SJohn Hurley 	tun_out_cnt = 0;
12807e24a593SJohn Hurley 	out_cnt = 0;
12811a1e586fSPieter Jansen van Vuuren 
1282e75dc265SLouis Peens 	flow_action_for_each(i, act, &rule->action) {
1283e75dc265SLouis Peens 		if (nfp_fl_check_mangle_start(&rule->action, i))
1284eff07b42SPieter Jansen van Vuuren 			memset(&set_act, 0, sizeof(set_act));
1285e75dc265SLouis Peens 		err = nfp_flower_loop_action(app, act, rule, nfp_flow, &act_len,
1286ed8f2b52SPieter Jansen van Vuuren 					     netdev, &tun_type, &tun_out_cnt,
1287fccac580SPieter Jansen van Vuuren 					     &out_cnt, &csum_updated,
1288f5c977eeSJohn Hurley 					     &set_act, &pkt_host, extack, i);
12891a1e586fSPieter Jansen van Vuuren 		if (err)
12901a1e586fSPieter Jansen van Vuuren 			return err;
12911a1e586fSPieter Jansen van Vuuren 		act_cnt++;
1292e75dc265SLouis Peens 		if (nfp_fl_check_mangle_end(&rule->action, i))
1293e75dc265SLouis Peens 			nfp_fl_commit_mangle(rule,
1294eff07b42SPieter Jansen van Vuuren 					     &nfp_flow->action_data[act_len],
1295eff07b42SPieter Jansen van Vuuren 					     &act_len, &set_act, &csum_updated);
12961a1e586fSPieter Jansen van Vuuren 	}
12971a1e586fSPieter Jansen van Vuuren 
12981a1e586fSPieter Jansen van Vuuren 	/* We optimise when the action list is small, this can unfortunately
12991a1e586fSPieter Jansen van Vuuren 	 * not happen once we have more than one action in the action list.
13001a1e586fSPieter Jansen van Vuuren 	 */
13011a1e586fSPieter Jansen van Vuuren 	if (act_cnt > 1)
13021a1e586fSPieter Jansen van Vuuren 		nfp_flow->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL);
13031a1e586fSPieter Jansen van Vuuren 
13041a1e586fSPieter Jansen van Vuuren 	nfp_flow->meta.act_len = act_len;
13051a1e586fSPieter Jansen van Vuuren 
13061a1e586fSPieter Jansen van Vuuren 	return 0;
13071a1e586fSPieter Jansen van Vuuren }
1308