1 // SPDX-License-Identifier: GPL-2.0+
2 
3 #include "lan966x_main.h"
4 #include "vcap_api.h"
5 #include "vcap_api_client.h"
6 #include "vcap_tc.h"
7 
8 static bool lan966x_tc_is_known_etype(u16 etype)
9 {
10 	switch (etype) {
11 	case ETH_P_ALL:
12 	case ETH_P_ARP:
13 	case ETH_P_IP:
14 	case ETH_P_IPV6:
15 		return true;
16 	}
17 
18 	return false;
19 }
20 
21 static int
22 lan966x_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st)
23 {
24 	struct flow_match_control match;
25 	int err = 0;
26 
27 	flow_rule_match_control(st->frule, &match);
28 	if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
29 		if (match.key->flags & FLOW_DIS_IS_FRAGMENT)
30 			err = vcap_rule_add_key_bit(st->vrule,
31 						    VCAP_KF_L3_FRAGMENT,
32 						    VCAP_BIT_1);
33 		else
34 			err = vcap_rule_add_key_bit(st->vrule,
35 						    VCAP_KF_L3_FRAGMENT,
36 						    VCAP_BIT_0);
37 		if (err)
38 			goto out;
39 	}
40 
41 	if (match.mask->flags & FLOW_DIS_FIRST_FRAG) {
42 		if (match.key->flags & FLOW_DIS_FIRST_FRAG)
43 			err = vcap_rule_add_key_bit(st->vrule,
44 						    VCAP_KF_L3_FRAG_OFS_GT0,
45 						    VCAP_BIT_0);
46 		else
47 			err = vcap_rule_add_key_bit(st->vrule,
48 						    VCAP_KF_L3_FRAG_OFS_GT0,
49 						    VCAP_BIT_1);
50 		if (err)
51 			goto out;
52 	}
53 
54 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
55 
56 	return err;
57 
58 out:
59 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error");
60 	return err;
61 }
62 
63 static int
64 lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st)
65 {
66 	struct flow_match_basic match;
67 	int err = 0;
68 
69 	flow_rule_match_basic(st->frule, &match);
70 	if (match.mask->n_proto) {
71 		st->l3_proto = be16_to_cpu(match.key->n_proto);
72 		if (!lan966x_tc_is_known_etype(st->l3_proto)) {
73 			err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE,
74 						    st->l3_proto, ~0);
75 			if (err)
76 				goto out;
77 		} else if (st->l3_proto == ETH_P_IP) {
78 			err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS,
79 						    VCAP_BIT_1);
80 			if (err)
81 				goto out;
82 		}
83 	}
84 	if (match.mask->ip_proto) {
85 		st->l4_proto = match.key->ip_proto;
86 
87 		if (st->l4_proto == IPPROTO_TCP) {
88 			err = vcap_rule_add_key_bit(st->vrule,
89 						    VCAP_KF_TCP_IS,
90 						    VCAP_BIT_1);
91 			if (err)
92 				goto out;
93 		} else if (st->l4_proto == IPPROTO_UDP) {
94 			err = vcap_rule_add_key_bit(st->vrule,
95 						    VCAP_KF_TCP_IS,
96 						    VCAP_BIT_0);
97 			if (err)
98 				goto out;
99 		} else {
100 			err = vcap_rule_add_key_u32(st->vrule,
101 						    VCAP_KF_L3_IP_PROTO,
102 						    st->l4_proto, ~0);
103 			if (err)
104 				goto out;
105 		}
106 	}
107 
108 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC);
109 	return err;
110 out:
111 	NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error");
112 	return err;
113 }
114 
115 static int
116 lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st)
117 {
118 	return vcap_tc_flower_handler_vlan_usage(st,
119 						 VCAP_KF_8021Q_VID_CLS,
120 						 VCAP_KF_8021Q_PCP_CLS);
121 }
122 
123 static int
124 (*lan966x_tc_flower_handlers_usage[])(struct vcap_tc_flower_parse_usage *st) = {
125 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage,
126 	[FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage,
127 	[FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage,
128 	[FLOW_DISSECTOR_KEY_CONTROL] = lan966x_tc_flower_handler_control_usage,
129 	[FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage,
130 	[FLOW_DISSECTOR_KEY_BASIC] = lan966x_tc_flower_handler_basic_usage,
131 	[FLOW_DISSECTOR_KEY_VLAN] = lan966x_tc_flower_handler_vlan_usage,
132 	[FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage,
133 	[FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage,
134 	[FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage,
135 };
136 
137 static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f,
138 					    struct vcap_admin *admin,
139 					    struct vcap_rule *vrule,
140 					    u16 *l3_proto)
141 {
142 	struct vcap_tc_flower_parse_usage state = {
143 		.fco = f,
144 		.vrule = vrule,
145 		.l3_proto = ETH_P_ALL,
146 	};
147 	int err = 0;
148 
149 	state.frule = flow_cls_offload_flow_rule(f);
150 	for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) {
151 		if (!flow_rule_match_key(state.frule, i) ||
152 		    !lan966x_tc_flower_handlers_usage[i])
153 			continue;
154 
155 		err = lan966x_tc_flower_handlers_usage[i](&state);
156 		if (err)
157 			return err;
158 	}
159 
160 	if (l3_proto)
161 		*l3_proto = state.l3_proto;
162 
163 	return err;
164 }
165 
166 static int lan966x_tc_flower_action_check(struct vcap_control *vctrl,
167 					  struct net_device *dev,
168 					  struct flow_cls_offload *fco,
169 					  bool ingress)
170 {
171 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
172 	struct flow_action_entry *actent, *last_actent = NULL;
173 	struct flow_action *act = &rule->action;
174 	u64 action_mask = 0;
175 	int idx;
176 
177 	if (!flow_action_has_entries(act)) {
178 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
179 		return -EINVAL;
180 	}
181 
182 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
183 		return -EOPNOTSUPP;
184 
185 	flow_action_for_each(idx, actent, act) {
186 		if (action_mask & BIT(actent->id)) {
187 			NL_SET_ERR_MSG_MOD(fco->common.extack,
188 					   "More actions of the same type");
189 			return -EINVAL;
190 		}
191 		action_mask |= BIT(actent->id);
192 		last_actent = actent; /* Save last action for later check */
193 	}
194 
195 	/* Check that last action is a goto
196 	 * The last chain/lookup does not need to have goto action
197 	 */
198 	if (last_actent->id == FLOW_ACTION_GOTO) {
199 		/* Check if the destination chain is in one of the VCAPs */
200 		if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
201 					 last_actent->chain_index)) {
202 			NL_SET_ERR_MSG_MOD(fco->common.extack,
203 					   "Invalid goto chain");
204 			return -EINVAL;
205 		}
206 	} else if (!vcap_is_last_chain(vctrl, fco->common.chain_index,
207 				       ingress)) {
208 		NL_SET_ERR_MSG_MOD(fco->common.extack,
209 				   "Last action must be 'goto'");
210 		return -EINVAL;
211 	}
212 
213 	/* Catch unsupported combinations of actions */
214 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
215 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
216 		NL_SET_ERR_MSG_MOD(fco->common.extack,
217 				   "Cannot combine pass and trap action");
218 		return -EOPNOTSUPP;
219 	}
220 
221 	return 0;
222 }
223 
224 static int lan966x_tc_flower_add(struct lan966x_port *port,
225 				 struct flow_cls_offload *f,
226 				 struct vcap_admin *admin,
227 				 bool ingress)
228 {
229 	struct flow_action_entry *act;
230 	u16 l3_proto = ETH_P_ALL;
231 	struct flow_rule *frule;
232 	struct vcap_rule *vrule;
233 	int err, idx;
234 
235 	err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl,
236 					     port->dev, f, ingress);
237 	if (err)
238 		return err;
239 
240 	vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev,
241 				f->common.chain_index, VCAP_USER_TC,
242 				f->common.prio, 0);
243 	if (IS_ERR(vrule))
244 		return PTR_ERR(vrule);
245 
246 	vrule->cookie = f->cookie;
247 	err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto);
248 	if (err)
249 		goto out;
250 
251 	frule = flow_cls_offload_flow_rule(f);
252 
253 	flow_action_for_each(idx, act, &frule->action) {
254 		switch (act->id) {
255 		case FLOW_ACTION_TRAP:
256 			err = vcap_rule_add_action_bit(vrule,
257 						       VCAP_AF_CPU_COPY_ENA,
258 						       VCAP_BIT_1);
259 			err |= vcap_rule_add_action_u32(vrule,
260 							VCAP_AF_CPU_QUEUE_NUM,
261 							0);
262 			err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
263 							LAN966X_PMM_REPLACE);
264 			if (err)
265 				goto out;
266 
267 			break;
268 		case FLOW_ACTION_GOTO:
269 			break;
270 		default:
271 			NL_SET_ERR_MSG_MOD(f->common.extack,
272 					   "Unsupported TC action");
273 			err = -EOPNOTSUPP;
274 			goto out;
275 		}
276 	}
277 
278 	err = vcap_val_rule(vrule, l3_proto);
279 	if (err) {
280 		vcap_set_tc_exterr(f, vrule);
281 		goto out;
282 	}
283 
284 	err = vcap_add_rule(vrule);
285 	if (err)
286 		NL_SET_ERR_MSG_MOD(f->common.extack,
287 				   "Could not add the filter");
288 out:
289 	vcap_free_rule(vrule);
290 	return err;
291 }
292 
293 static int lan966x_tc_flower_del(struct lan966x_port *port,
294 				 struct flow_cls_offload *f,
295 				 struct vcap_admin *admin)
296 {
297 	struct vcap_control *vctrl;
298 	int err = -ENOENT, rule_id;
299 
300 	vctrl = port->lan966x->vcap_ctrl;
301 	while (true) {
302 		rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie);
303 		if (rule_id <= 0)
304 			break;
305 
306 		err = vcap_del_rule(vctrl, port->dev, rule_id);
307 		if (err) {
308 			NL_SET_ERR_MSG_MOD(f->common.extack,
309 					   "Cannot delete rule");
310 			break;
311 		}
312 	}
313 
314 	return err;
315 }
316 
317 static int lan966x_tc_flower_stats(struct lan966x_port *port,
318 				   struct flow_cls_offload *f,
319 				   struct vcap_admin *admin)
320 {
321 	struct vcap_counter count = {};
322 	int err;
323 
324 	err = vcap_get_rule_count_by_cookie(port->lan966x->vcap_ctrl,
325 					    &count, f->cookie);
326 	if (err)
327 		return err;
328 
329 	flow_stats_update(&f->stats, 0x0, count.value, 0, 0,
330 			  FLOW_ACTION_HW_STATS_IMMEDIATE);
331 
332 	return err;
333 }
334 
335 int lan966x_tc_flower(struct lan966x_port *port,
336 		      struct flow_cls_offload *f,
337 		      bool ingress)
338 {
339 	struct vcap_admin *admin;
340 
341 	admin = vcap_find_admin(port->lan966x->vcap_ctrl,
342 				f->common.chain_index);
343 	if (!admin) {
344 		NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain");
345 		return -EINVAL;
346 	}
347 
348 	switch (f->command) {
349 	case FLOW_CLS_REPLACE:
350 		return lan966x_tc_flower_add(port, f, admin, ingress);
351 	case FLOW_CLS_DESTROY:
352 		return lan966x_tc_flower_del(port, f, admin);
353 	case FLOW_CLS_STATS:
354 		return lan966x_tc_flower_stats(port, f, admin);
355 	default:
356 		return -EOPNOTSUPP;
357 	}
358 
359 	return 0;
360 }
361