1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 
4 #include "act.h"
5 #include "en/tc_priv.h"
6 #include "eswitch.h"
7 
8 static int
9 validate_goto_chain(struct mlx5e_priv *priv,
10 		    struct mlx5e_tc_flow *flow,
11 		    struct mlx5_flow_attr *attr,
12 		    const struct flow_action_entry *act,
13 		    struct netlink_ext_ack *extack)
14 {
15 	struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs);
16 	bool is_esw = mlx5e_is_eswitch_flow(flow);
17 	bool ft_flow = mlx5e_is_ft_flow(flow);
18 	u32 dest_chain = act->chain_index;
19 	struct mlx5_fs_chains *chains;
20 	struct mlx5_eswitch *esw;
21 	u32 reformat_and_fwd;
22 	u32 max_chain;
23 
24 	esw = priv->mdev->priv.eswitch;
25 	chains = is_esw ? esw_chains(esw) : mlx5e_nic_chains(tc);
26 	max_chain = mlx5_chains_get_chain_range(chains);
27 	reformat_and_fwd = is_esw ?
28 			   MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, reformat_and_fwd_to_table) :
29 			   MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, reformat_and_fwd_to_table);
30 
31 	if (ft_flow) {
32 		NL_SET_ERR_MSG_MOD(extack, "Goto action is not supported");
33 		return -EOPNOTSUPP;
34 	}
35 
36 	if (!mlx5_chains_backwards_supported(chains) &&
37 	    dest_chain <= attr->chain) {
38 		NL_SET_ERR_MSG_MOD(extack, "Goto lower numbered chain isn't supported");
39 		return -EOPNOTSUPP;
40 	}
41 
42 	if (dest_chain > max_chain) {
43 		NL_SET_ERR_MSG_MOD(extack,
44 				   "Requested destination chain is out of supported range");
45 		return -EOPNOTSUPP;
46 	}
47 
48 	if (attr->action & (MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT |
49 			    MLX5_FLOW_CONTEXT_ACTION_DECAP) &&
50 	    !reformat_and_fwd) {
51 		NL_SET_ERR_MSG_MOD(extack,
52 				   "Goto chain is not allowed if action has reformat or decap");
53 		return -EOPNOTSUPP;
54 	}
55 
56 	return 0;
57 }
58 
59 static bool
60 tc_act_can_offload_goto(struct mlx5e_tc_act_parse_state *parse_state,
61 			const struct flow_action_entry *act,
62 			int act_index,
63 			struct mlx5_flow_attr *attr)
64 {
65 	struct netlink_ext_ack *extack = parse_state->extack;
66 	struct mlx5e_tc_flow *flow = parse_state->flow;
67 
68 	if (validate_goto_chain(flow->priv, flow, attr, act, extack))
69 		return false;
70 
71 	return true;
72 }
73 
74 static int
75 tc_act_parse_goto(struct mlx5e_tc_act_parse_state *parse_state,
76 		  const struct flow_action_entry *act,
77 		  struct mlx5e_priv *priv,
78 		  struct mlx5_flow_attr *attr)
79 {
80 	attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
81 	attr->dest_chain = act->chain_index;
82 
83 	return 0;
84 }
85 
86 static int
87 tc_act_post_parse_goto(struct mlx5e_tc_act_parse_state *parse_state,
88 		       struct mlx5e_priv *priv,
89 		       struct mlx5_flow_attr *attr)
90 {
91 	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
92 	struct netlink_ext_ack *extack = parse_state->extack;
93 	struct mlx5e_tc_flow *flow = parse_state->flow;
94 
95 	if (!attr->dest_chain)
96 		return 0;
97 
98 	if (parse_state->decap) {
99 		/* It can be supported if we'll create a mapping for
100 		 * the tunnel device only (without tunnel), and set
101 		 * this tunnel id with this decap flow.
102 		 *
103 		 * On restore (miss), we'll just set this saved tunnel
104 		 * device.
105 		 */
106 
107 		NL_SET_ERR_MSG_MOD(extack, "Decap with goto isn't supported");
108 		netdev_warn(priv->netdev, "Decap with goto isn't supported");
109 		return -EOPNOTSUPP;
110 	}
111 
112 	if (!mlx5e_is_eswitch_flow(flow) && parse_attr->mirred_ifindex[0]) {
113 		NL_SET_ERR_MSG_MOD(extack, "Mirroring goto chain rules isn't supported");
114 		return -EOPNOTSUPP;
115 	}
116 
117 	return 0;
118 }
119 
120 struct mlx5e_tc_act mlx5e_tc_act_goto = {
121 	.can_offload = tc_act_can_offload_goto,
122 	.parse_action = tc_act_parse_goto,
123 	.post_parse = tc_act_post_parse_goto,
124 	.is_terminating_action = true,
125 };
126