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 
7 struct lan966x_tc_flower_parse_usage {
8 	struct flow_cls_offload *f;
9 	struct flow_rule *frule;
10 	struct vcap_rule *vrule;
11 	unsigned int used_keys;
12 	u16 l3_proto;
13 };
14 
15 static int lan966x_tc_flower_handler_ethaddr_usage(struct lan966x_tc_flower_parse_usage *st)
16 {
17 	enum vcap_key_field smac_key = VCAP_KF_L2_SMAC;
18 	enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC;
19 	struct flow_match_eth_addrs match;
20 	struct vcap_u48_key smac, dmac;
21 	int err = 0;
22 
23 	flow_rule_match_eth_addrs(st->frule, &match);
24 
25 	if (!is_zero_ether_addr(match.mask->src)) {
26 		vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN);
27 		vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN);
28 		err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac);
29 		if (err)
30 			goto out;
31 	}
32 
33 	if (!is_zero_ether_addr(match.mask->dst)) {
34 		vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN);
35 		vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN);
36 		err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac);
37 		if (err)
38 			goto out;
39 	}
40 
41 	st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS);
42 
43 	return err;
44 
45 out:
46 	NL_SET_ERR_MSG_MOD(st->f->common.extack, "eth_addr parse error");
47 	return err;
48 }
49 
50 static int
51 (*lan966x_tc_flower_handlers_usage[])(struct lan966x_tc_flower_parse_usage *st) = {
52 	[FLOW_DISSECTOR_KEY_ETH_ADDRS] = lan966x_tc_flower_handler_ethaddr_usage,
53 };
54 
55 static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f,
56 					    struct vcap_admin *admin,
57 					    struct vcap_rule *vrule,
58 					    u16 *l3_proto)
59 {
60 	struct lan966x_tc_flower_parse_usage state = {
61 		.f = f,
62 		.vrule = vrule,
63 		.l3_proto = ETH_P_ALL,
64 	};
65 	int err = 0;
66 
67 	state.frule = flow_cls_offload_flow_rule(f);
68 	for (int i = 0; i < ARRAY_SIZE(lan966x_tc_flower_handlers_usage); ++i) {
69 		if (!flow_rule_match_key(state.frule, i) ||
70 		    !lan966x_tc_flower_handlers_usage[i])
71 			continue;
72 
73 		err = lan966x_tc_flower_handlers_usage[i](&state);
74 		if (err)
75 			return err;
76 	}
77 
78 	if (l3_proto)
79 		*l3_proto = state.l3_proto;
80 
81 	return err;
82 }
83 
84 static int lan966x_tc_flower_action_check(struct vcap_control *vctrl,
85 					  struct flow_cls_offload *fco,
86 					  struct vcap_admin *admin)
87 {
88 	struct flow_rule *rule = flow_cls_offload_flow_rule(fco);
89 	struct flow_action_entry *actent, *last_actent = NULL;
90 	struct flow_action *act = &rule->action;
91 	u64 action_mask = 0;
92 	int idx;
93 
94 	if (!flow_action_has_entries(act)) {
95 		NL_SET_ERR_MSG_MOD(fco->common.extack, "No actions");
96 		return -EINVAL;
97 	}
98 
99 	if (!flow_action_basic_hw_stats_check(act, fco->common.extack))
100 		return -EOPNOTSUPP;
101 
102 	flow_action_for_each(idx, actent, act) {
103 		if (action_mask & BIT(actent->id)) {
104 			NL_SET_ERR_MSG_MOD(fco->common.extack,
105 					   "More actions of the same type");
106 			return -EINVAL;
107 		}
108 		action_mask |= BIT(actent->id);
109 		last_actent = actent; /* Save last action for later check */
110 	}
111 
112 	/* Check that last action is a goto */
113 	if (last_actent->id != FLOW_ACTION_GOTO) {
114 		NL_SET_ERR_MSG_MOD(fco->common.extack,
115 				   "Last action must be 'goto'");
116 		return -EINVAL;
117 	}
118 
119 	/* Check if the goto chain is in the next lookup */
120 	if (!vcap_is_next_lookup(vctrl, fco->common.chain_index,
121 				 last_actent->chain_index)) {
122 		NL_SET_ERR_MSG_MOD(fco->common.extack,
123 				   "Invalid goto chain");
124 		return -EINVAL;
125 	}
126 
127 	/* Catch unsupported combinations of actions */
128 	if (action_mask & BIT(FLOW_ACTION_TRAP) &&
129 	    action_mask & BIT(FLOW_ACTION_ACCEPT)) {
130 		NL_SET_ERR_MSG_MOD(fco->common.extack,
131 				   "Cannot combine pass and trap action");
132 		return -EOPNOTSUPP;
133 	}
134 
135 	return 0;
136 }
137 
138 static int lan966x_tc_flower_add(struct lan966x_port *port,
139 				 struct flow_cls_offload *f,
140 				 struct vcap_admin *admin)
141 {
142 	struct flow_action_entry *act;
143 	u16 l3_proto = ETH_P_ALL;
144 	struct flow_rule *frule;
145 	struct vcap_rule *vrule;
146 	int err, idx;
147 
148 	err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl, f,
149 					     admin);
150 	if (err)
151 		return err;
152 
153 	vrule = vcap_alloc_rule(port->lan966x->vcap_ctrl, port->dev,
154 				f->common.chain_index, VCAP_USER_TC,
155 				f->common.prio, 0);
156 	if (IS_ERR(vrule))
157 		return PTR_ERR(vrule);
158 
159 	vrule->cookie = f->cookie;
160 	err = lan966x_tc_flower_use_dissectors(f, admin, vrule, &l3_proto);
161 	if (err)
162 		goto out;
163 
164 	frule = flow_cls_offload_flow_rule(f);
165 
166 	flow_action_for_each(idx, act, &frule->action) {
167 		switch (act->id) {
168 		case FLOW_ACTION_TRAP:
169 			err = vcap_rule_add_action_bit(vrule,
170 						       VCAP_AF_CPU_COPY_ENA,
171 						       VCAP_BIT_1);
172 			err |= vcap_rule_add_action_u32(vrule,
173 							VCAP_AF_CPU_QUEUE_NUM,
174 							0);
175 			err |= vcap_rule_add_action_u32(vrule, VCAP_AF_MASK_MODE,
176 							LAN966X_PMM_REPLACE);
177 			err |= vcap_set_rule_set_actionset(vrule,
178 							   VCAP_AFS_BASE_TYPE);
179 			if (err)
180 				goto out;
181 
182 			break;
183 		case FLOW_ACTION_GOTO:
184 			break;
185 		default:
186 			NL_SET_ERR_MSG_MOD(f->common.extack,
187 					   "Unsupported TC action");
188 			err = -EOPNOTSUPP;
189 			goto out;
190 		}
191 	}
192 
193 	err = vcap_val_rule(vrule, l3_proto);
194 	if (err) {
195 		vcap_set_tc_exterr(f, vrule);
196 		goto out;
197 	}
198 
199 	err = vcap_add_rule(vrule);
200 	if (err)
201 		NL_SET_ERR_MSG_MOD(f->common.extack,
202 				   "Could not add the filter");
203 out:
204 	vcap_free_rule(vrule);
205 	return err;
206 }
207 
208 static int lan966x_tc_flower_del(struct lan966x_port *port,
209 				 struct flow_cls_offload *f,
210 				 struct vcap_admin *admin)
211 {
212 	struct vcap_control *vctrl;
213 	int err = -ENOENT, rule_id;
214 
215 	vctrl = port->lan966x->vcap_ctrl;
216 	while (true) {
217 		rule_id = vcap_lookup_rule_by_cookie(vctrl, f->cookie);
218 		if (rule_id <= 0)
219 			break;
220 
221 		err = vcap_del_rule(vctrl, port->dev, rule_id);
222 		if (err) {
223 			NL_SET_ERR_MSG_MOD(f->common.extack,
224 					   "Cannot delete rule");
225 			break;
226 		}
227 	}
228 
229 	return err;
230 }
231 
232 int lan966x_tc_flower(struct lan966x_port *port,
233 		      struct flow_cls_offload *f)
234 {
235 	struct vcap_admin *admin;
236 
237 	admin = vcap_find_admin(port->lan966x->vcap_ctrl,
238 				f->common.chain_index);
239 	if (!admin) {
240 		NL_SET_ERR_MSG_MOD(f->common.extack, "Invalid chain");
241 		return -EINVAL;
242 	}
243 
244 	switch (f->command) {
245 	case FLOW_CLS_REPLACE:
246 		return lan966x_tc_flower_add(port, f, admin);
247 	case FLOW_CLS_DESTROY:
248 		return lan966x_tc_flower_del(port, f, admin);
249 	default:
250 		return -EOPNOTSUPP;
251 	}
252 
253 	return 0;
254 }
255