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
lan966x_tc_is_known_etype(u16 etype)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
lan966x_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage * st)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
lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage * st)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
lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage * st)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
lan966x_tc_flower_use_dissectors(struct flow_cls_offload * f,struct vcap_admin * admin,struct vcap_rule * vrule,u16 * l3_proto)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
lan966x_tc_flower_action_check(struct vcap_control * vctrl,struct net_device * dev,struct flow_cls_offload * fco,bool ingress)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
lan966x_tc_flower_add(struct lan966x_port * port,struct flow_cls_offload * f,struct vcap_admin * admin,bool ingress)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
lan966x_tc_flower_del(struct lan966x_port * port,struct flow_cls_offload * f,struct vcap_admin * admin)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
lan966x_tc_flower_stats(struct lan966x_port * port,struct flow_cls_offload * f,struct vcap_admin * admin)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
lan966x_tc_flower(struct lan966x_port * port,struct flow_cls_offload * f,bool ingress)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