1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2// Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
4#include <linux/if_vlan.h>
5#include "act.h"
6#include "vlan.h"
7#include "en/tc_priv.h"
8
9static int
10add_vlan_prio_tag_rewrite_action(struct mlx5e_priv *priv,
11				 struct mlx5e_tc_flow_parse_attr *parse_attr,
12				 u32 *action, struct netlink_ext_ack *extack)
13{
14	const struct flow_action_entry prio_tag_act = {
15		.vlan.vid = 0,
16		.vlan.prio =
17			MLX5_GET(fte_match_set_lyr_2_4,
18				 mlx5e_get_match_headers_value(*action,
19							       &parse_attr->spec),
20				 first_prio) &
21			MLX5_GET(fte_match_set_lyr_2_4,
22				 mlx5e_get_match_headers_criteria(*action,
23								  &parse_attr->spec),
24				 first_prio),
25	};
26
27	return mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB,
28						    &prio_tag_act, parse_attr, action,
29						    extack);
30}
31
32static int
33parse_tc_vlan_action(struct mlx5e_priv *priv,
34		     const struct flow_action_entry *act,
35		     struct mlx5_esw_flow_attr *attr,
36		     u32 *action,
37		     struct netlink_ext_ack *extack,
38		     struct mlx5e_tc_act_parse_state *parse_state)
39{
40	u8 vlan_idx = attr->total_vlan;
41
42	if (vlan_idx >= MLX5_FS_VLAN_DEPTH) {
43		NL_SET_ERR_MSG_MOD(extack, "Total vlans used is greater than supported");
44		return -EOPNOTSUPP;
45	}
46
47	if (!mlx5_eswitch_vlan_actions_supported(priv->mdev, vlan_idx)) {
48		NL_SET_ERR_MSG_MOD(extack, "firmware vlan actions is not supported");
49		return -EOPNOTSUPP;
50	}
51
52	switch (act->id) {
53	case FLOW_ACTION_VLAN_POP:
54		if (vlan_idx)
55			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP_2;
56		else
57			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
58		break;
59	case FLOW_ACTION_VLAN_PUSH:
60		attr->vlan_vid[vlan_idx] = act->vlan.vid;
61		attr->vlan_prio[vlan_idx] = act->vlan.prio;
62		attr->vlan_proto[vlan_idx] = act->vlan.proto;
63		if (!attr->vlan_proto[vlan_idx])
64			attr->vlan_proto[vlan_idx] = htons(ETH_P_8021Q);
65
66		if (vlan_idx)
67			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH_2;
68		else
69			*action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
70		break;
71	case FLOW_ACTION_VLAN_POP_ETH:
72		parse_state->eth_pop = true;
73		break;
74	case FLOW_ACTION_VLAN_PUSH_ETH:
75		if (!flow_flag_test(parse_state->flow, L3_TO_L2_DECAP))
76			return -EOPNOTSUPP;
77		parse_state->eth_push = true;
78		memcpy(attr->eth.h_dest, act->vlan_push_eth.dst, ETH_ALEN);
79		memcpy(attr->eth.h_source, act->vlan_push_eth.src, ETH_ALEN);
80		break;
81	default:
82		NL_SET_ERR_MSG_MOD(extack, "Unexpected action id for VLAN");
83		return -EINVAL;
84	}
85
86	attr->total_vlan = vlan_idx + 1;
87
88	return 0;
89}
90
91int
92mlx5e_tc_act_vlan_add_push_action(struct mlx5e_priv *priv,
93				  struct mlx5_flow_attr *attr,
94				  struct net_device **out_dev,
95				  struct netlink_ext_ack *extack)
96{
97	struct net_device *vlan_dev = *out_dev;
98	struct flow_action_entry vlan_act = {
99		.id = FLOW_ACTION_VLAN_PUSH,
100		.vlan.vid = vlan_dev_vlan_id(vlan_dev),
101		.vlan.proto = vlan_dev_vlan_proto(vlan_dev),
102		.vlan.prio = 0,
103	};
104	int err;
105
106	err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action, extack, NULL);
107	if (err)
108		return err;
109
110	rcu_read_lock();
111	*out_dev = dev_get_by_index_rcu(dev_net(vlan_dev), dev_get_iflink(vlan_dev));
112	rcu_read_unlock();
113	if (!*out_dev)
114		return -ENODEV;
115
116	if (is_vlan_dev(*out_dev))
117		err = mlx5e_tc_act_vlan_add_push_action(priv, attr, out_dev, extack);
118
119	return err;
120}
121
122int
123mlx5e_tc_act_vlan_add_pop_action(struct mlx5e_priv *priv,
124				 struct mlx5_flow_attr *attr,
125				 struct netlink_ext_ack *extack)
126{
127	struct flow_action_entry vlan_act = {
128		.id = FLOW_ACTION_VLAN_POP,
129	};
130	int nest_level, err = 0;
131
132	nest_level = attr->parse_attr->filter_dev->lower_level -
133						priv->netdev->lower_level;
134	while (nest_level--) {
135		err = parse_tc_vlan_action(priv, &vlan_act, attr->esw_attr, &attr->action,
136					   extack, NULL);
137		if (err)
138			return err;
139	}
140
141	return err;
142}
143
144static int
145tc_act_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
146		  const struct flow_action_entry *act,
147		  struct mlx5e_priv *priv,
148		  struct mlx5_flow_attr *attr)
149{
150	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
151	int err;
152
153	if (act->id == FLOW_ACTION_VLAN_PUSH &&
154	    (attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP)) {
155		/* Replace vlan pop+push with vlan modify */
156		attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
157		err = mlx5e_tc_act_vlan_add_rewrite_action(priv, MLX5_FLOW_NAMESPACE_FDB, act,
158							   attr->parse_attr, &attr->action,
159							   parse_state->extack);
160	} else {
161		err = parse_tc_vlan_action(priv, act, esw_attr, &attr->action,
162					   parse_state->extack, parse_state);
163	}
164
165	if (err)
166		return err;
167
168	esw_attr->split_count = esw_attr->out_count;
169	parse_state->if_count = 0;
170
171	return 0;
172}
173
174static int
175tc_act_post_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state,
176		       struct mlx5e_priv *priv,
177		       struct mlx5_flow_attr *attr)
178{
179	struct mlx5e_tc_flow_parse_attr *parse_attr = attr->parse_attr;
180	struct netlink_ext_ack *extack = parse_state->extack;
181	struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
182	int err;
183
184	if (MLX5_CAP_GEN(esw->dev, prio_tag_required) &&
185	    attr->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_POP) {
186		/* For prio tag mode, replace vlan pop with rewrite vlan prio
187		 * tag rewrite.
188		 */
189		attr->action &= ~MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
190		err = add_vlan_prio_tag_rewrite_action(priv, parse_attr,
191						       &attr->action, extack);
192		if (err)
193			return err;
194	}
195
196	return 0;
197}
198
199struct mlx5e_tc_act mlx5e_tc_act_vlan = {
200	.parse_action = tc_act_parse_vlan,
201	.post_parse = tc_act_post_parse_vlan,
202};
203