xref: /linux/drivers/net/ethernet/sfc/mae.c (revision cbdb1f163af2bb90d01be1f0263df1d8d5c9d9d3)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /****************************************************************************
3  * Driver for Solarflare network controllers and boards
4  * Copyright 2019 Solarflare Communications Inc.
5  * Copyright 2020-2022 Xilinx Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published
9  * by the Free Software Foundation, incorporated herein by reference.
10  */
11 
12 #include "mae.h"
13 #include "mcdi.h"
14 #include "mcdi_pcol_mae.h"
15 
16 int efx_mae_allocate_mport(struct efx_nic *efx, u32 *id, u32 *label)
17 {
18 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MPORT_ALLOC_ALIAS_OUT_LEN);
19 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_ALLOC_ALIAS_IN_LEN);
20 	size_t outlen;
21 	int rc;
22 
23 	if (WARN_ON_ONCE(!id))
24 		return -EINVAL;
25 	if (WARN_ON_ONCE(!label))
26 		return -EINVAL;
27 
28 	MCDI_SET_DWORD(inbuf, MAE_MPORT_ALLOC_ALIAS_IN_TYPE,
29 		       MC_CMD_MAE_MPORT_ALLOC_ALIAS_IN_MPORT_TYPE_ALIAS);
30 	MCDI_SET_DWORD(inbuf, MAE_MPORT_ALLOC_ALIAS_IN_DELIVER_MPORT,
31 		       MAE_MPORT_SELECTOR_ASSIGNED);
32 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_ALLOC, inbuf, sizeof(inbuf),
33 			  outbuf, sizeof(outbuf), &outlen);
34 	if (rc)
35 		return rc;
36 	if (outlen < sizeof(outbuf))
37 		return -EIO;
38 	*id = MCDI_DWORD(outbuf, MAE_MPORT_ALLOC_ALIAS_OUT_MPORT_ID);
39 	*label = MCDI_DWORD(outbuf, MAE_MPORT_ALLOC_ALIAS_OUT_LABEL);
40 	return 0;
41 }
42 
43 int efx_mae_free_mport(struct efx_nic *efx, u32 id)
44 {
45 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_FREE_IN_LEN);
46 
47 	BUILD_BUG_ON(MC_CMD_MAE_MPORT_FREE_OUT_LEN);
48 	MCDI_SET_DWORD(inbuf, MAE_MPORT_FREE_IN_MPORT_ID, id);
49 	return efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_FREE, inbuf, sizeof(inbuf),
50 			    NULL, 0, NULL);
51 }
52 
53 void efx_mae_mport_wire(struct efx_nic *efx, u32 *out)
54 {
55 	efx_dword_t mport;
56 
57 	EFX_POPULATE_DWORD_2(mport,
58 			     MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_PPORT,
59 			     MAE_MPORT_SELECTOR_PPORT_ID, efx->port_num);
60 	*out = EFX_DWORD_VAL(mport);
61 }
62 
63 void efx_mae_mport_uplink(struct efx_nic *efx __always_unused, u32 *out)
64 {
65 	efx_dword_t mport;
66 
67 	EFX_POPULATE_DWORD_3(mport,
68 			     MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_FUNC,
69 			     MAE_MPORT_SELECTOR_FUNC_PF_ID, MAE_MPORT_SELECTOR_FUNC_PF_ID_CALLER,
70 			     MAE_MPORT_SELECTOR_FUNC_VF_ID, MAE_MPORT_SELECTOR_FUNC_VF_ID_NULL);
71 	*out = EFX_DWORD_VAL(mport);
72 }
73 
74 void efx_mae_mport_vf(struct efx_nic *efx __always_unused, u32 vf_id, u32 *out)
75 {
76 	efx_dword_t mport;
77 
78 	EFX_POPULATE_DWORD_3(mport,
79 			     MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_FUNC,
80 			     MAE_MPORT_SELECTOR_FUNC_PF_ID, MAE_MPORT_SELECTOR_FUNC_PF_ID_CALLER,
81 			     MAE_MPORT_SELECTOR_FUNC_VF_ID, vf_id);
82 	*out = EFX_DWORD_VAL(mport);
83 }
84 
85 /* Constructs an mport selector from an mport ID, because they're not the same */
86 void efx_mae_mport_mport(struct efx_nic *efx __always_unused, u32 mport_id, u32 *out)
87 {
88 	efx_dword_t mport;
89 
90 	EFX_POPULATE_DWORD_2(mport,
91 			     MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_MPORT_ID,
92 			     MAE_MPORT_SELECTOR_MPORT_ID, mport_id);
93 	*out = EFX_DWORD_VAL(mport);
94 }
95 
96 /* id is really only 24 bits wide */
97 int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id)
98 {
99 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MPORT_LOOKUP_OUT_LEN);
100 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_LOOKUP_IN_LEN);
101 	size_t outlen;
102 	int rc;
103 
104 	MCDI_SET_DWORD(inbuf, MAE_MPORT_LOOKUP_IN_MPORT_SELECTOR, selector);
105 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_LOOKUP, inbuf, sizeof(inbuf),
106 			  outbuf, sizeof(outbuf), &outlen);
107 	if (rc)
108 		return rc;
109 	if (outlen < sizeof(outbuf))
110 		return -EIO;
111 	*id = MCDI_DWORD(outbuf, MAE_MPORT_LOOKUP_OUT_MPORT_ID);
112 	return 0;
113 }
114 
115 int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue)
116 {
117 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_START_V2_IN_LEN);
118 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_START_OUT_LEN);
119 	u32 out_flags;
120 	size_t outlen;
121 	int rc;
122 
123 	MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_QID,
124 		      efx_rx_queue_index(rx_queue));
125 	MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_PACKET_SIZE,
126 		      efx->net_dev->mtu);
127 	MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_COUNTER_TYPES_MASK,
128 		       BIT(MAE_COUNTER_TYPE_AR) | BIT(MAE_COUNTER_TYPE_CT) |
129 		       BIT(MAE_COUNTER_TYPE_OR));
130 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_START,
131 			  inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
132 	if (rc)
133 		return rc;
134 	if (outlen < sizeof(outbuf))
135 		return -EIO;
136 	out_flags = MCDI_DWORD(outbuf, MAE_COUNTERS_STREAM_START_OUT_FLAGS);
137 	if (out_flags & BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST)) {
138 		netif_dbg(efx, drv, efx->net_dev,
139 			  "MAE counter stream uses credits\n");
140 		rx_queue->grant_credits = true;
141 		out_flags &= ~BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST);
142 	}
143 	if (out_flags) {
144 		netif_err(efx, drv, efx->net_dev,
145 			  "MAE counter stream start: unrecognised flags %x\n",
146 			  out_flags);
147 		goto out_stop;
148 	}
149 	return 0;
150 out_stop:
151 	efx_mae_stop_counters(efx, rx_queue);
152 	return -EOPNOTSUPP;
153 }
154 
155 static bool efx_mae_counters_flushed(u32 *flush_gen, u32 *seen_gen)
156 {
157 	int i;
158 
159 	for (i = 0; i < EFX_TC_COUNTER_TYPE_MAX; i++)
160 		if ((s32)(flush_gen[i] - seen_gen[i]) > 0)
161 			return false;
162 	return true;
163 }
164 
165 int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue)
166 {
167 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_V2_OUT_LENMAX);
168 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_IN_LEN);
169 	size_t outlen;
170 	int rc, i;
171 
172 	MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_STOP_IN_QID,
173 		      efx_rx_queue_index(rx_queue));
174 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_STOP,
175 			  inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
176 
177 	if (rc)
178 		return rc;
179 
180 	netif_dbg(efx, drv, efx->net_dev, "Draining counters:\n");
181 	/* Only process received generation counts */
182 	for (i = 0; (i < (outlen / 4)) && (i < EFX_TC_COUNTER_TYPE_MAX); i++) {
183 		efx->tc->flush_gen[i] = MCDI_ARRAY_DWORD(outbuf,
184 							 MAE_COUNTERS_STREAM_STOP_V2_OUT_GENERATION_COUNT,
185 							 i);
186 		netif_dbg(efx, drv, efx->net_dev,
187 			  "\ttype %u, awaiting gen %u\n", i,
188 			  efx->tc->flush_gen[i]);
189 	}
190 
191 	efx->tc->flush_counters = true;
192 
193 	/* Drain can take up to 2 seconds owing to FWRIVERHD-2884; whatever
194 	 * timeout we use, that delay is added to unload on nonresponsive
195 	 * hardware, so 2500ms seems like a reasonable compromise.
196 	 */
197 	if (!wait_event_timeout(efx->tc->flush_wq,
198 				efx_mae_counters_flushed(efx->tc->flush_gen,
199 							 efx->tc->seen_gen),
200 				msecs_to_jiffies(2500)))
201 		netif_warn(efx, drv, efx->net_dev,
202 			   "Failed to drain counters RXQ, FW may be unhappy\n");
203 
204 	efx->tc->flush_counters = false;
205 
206 	return rc;
207 }
208 
209 void efx_mae_counters_grant_credits(struct work_struct *work)
210 {
211 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_LEN);
212 	struct efx_rx_queue *rx_queue = container_of(work, struct efx_rx_queue,
213 						     grant_work);
214 	struct efx_nic *efx = rx_queue->efx;
215 	unsigned int credits;
216 
217 	BUILD_BUG_ON(MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_OUT_LEN);
218 	credits = READ_ONCE(rx_queue->notified_count) - rx_queue->granted_count;
219 	MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_NUM_CREDITS,
220 		       credits);
221 	if (!efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS,
222 			  inbuf, sizeof(inbuf), NULL, 0, NULL))
223 		rx_queue->granted_count += credits;
224 }
225 
226 static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps)
227 {
228 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN);
229 	size_t outlen;
230 	int rc;
231 
232 	BUILD_BUG_ON(MC_CMD_MAE_GET_CAPS_IN_LEN);
233 
234 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_GET_CAPS, NULL, 0, outbuf,
235 			  sizeof(outbuf), &outlen);
236 	if (rc)
237 		return rc;
238 	if (outlen < sizeof(outbuf))
239 		return -EIO;
240 	caps->match_field_count = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT);
241 	caps->action_prios = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ACTION_PRIOS);
242 	return 0;
243 }
244 
245 static int efx_mae_get_rule_fields(struct efx_nic *efx, u32 cmd,
246 				   u8 *field_support)
247 {
248 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(MAE_NUM_FIELDS));
249 	MCDI_DECLARE_STRUCT_PTR(caps);
250 	unsigned int count;
251 	size_t outlen;
252 	int rc, i;
253 
254 	BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_IN_LEN);
255 
256 	rc = efx_mcdi_rpc(efx, cmd, NULL, 0, outbuf, sizeof(outbuf), &outlen);
257 	if (rc)
258 		return rc;
259 	count = MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_COUNT);
260 	memset(field_support, MAE_FIELD_UNSUPPORTED, MAE_NUM_FIELDS);
261 	caps = _MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_FIELD_FLAGS);
262 	/* We're only interested in the support status enum, not any other
263 	 * flags, so just extract that from each entry.
264 	 */
265 	for (i = 0; i < count; i++)
266 		if (i * sizeof(*outbuf) + MC_CMD_MAE_GET_AR_CAPS_OUT_FIELD_FLAGS_OFST < outlen)
267 			field_support[i] = EFX_DWORD_FIELD(caps[i], MAE_FIELD_FLAGS_SUPPORT_STATUS);
268 	return 0;
269 }
270 
271 int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps)
272 {
273 	int rc;
274 
275 	rc = efx_mae_get_basic_caps(efx, caps);
276 	if (rc)
277 		return rc;
278 	return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS,
279 				       caps->action_rule_fields);
280 }
281 
282 /* Bit twiddling:
283  * Prefix: 1...110...0
284  *      ~: 0...001...1
285  *    + 1: 0...010...0 is power of two
286  * so (~x) & ((~x) + 1) == 0.  Converse holds also.
287  */
288 #define is_prefix_byte(_x)	!(((_x) ^ 0xff) & (((_x) ^ 0xff) + 1))
289 
290 enum mask_type { MASK_ONES, MASK_ZEROES, MASK_PREFIX, MASK_OTHER };
291 
292 static const char *mask_type_name(enum mask_type typ)
293 {
294 	switch (typ) {
295 	case MASK_ONES:
296 		return "all-1s";
297 	case MASK_ZEROES:
298 		return "all-0s";
299 	case MASK_PREFIX:
300 		return "prefix";
301 	case MASK_OTHER:
302 		return "arbitrary";
303 	default: /* can't happen */
304 		return "unknown";
305 	}
306 }
307 
308 /* Checks a (big-endian) bytestring is a bit prefix */
309 static enum mask_type classify_mask(const u8 *mask, size_t len)
310 {
311 	bool zeroes = true; /* All bits seen so far are zeroes */
312 	bool ones = true; /* All bits seen so far are ones */
313 	bool prefix = true; /* Valid prefix so far */
314 	size_t i;
315 
316 	for (i = 0; i < len; i++) {
317 		if (ones) {
318 			if (!is_prefix_byte(mask[i]))
319 				prefix = false;
320 		} else if (mask[i]) {
321 			prefix = false;
322 		}
323 		if (mask[i] != 0xff)
324 			ones = false;
325 		if (mask[i])
326 			zeroes = false;
327 	}
328 	if (ones)
329 		return MASK_ONES;
330 	if (zeroes)
331 		return MASK_ZEROES;
332 	if (prefix)
333 		return MASK_PREFIX;
334 	return MASK_OTHER;
335 }
336 
337 static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ)
338 {
339 	switch (support) {
340 	case MAE_FIELD_UNSUPPORTED:
341 	case MAE_FIELD_SUPPORTED_MATCH_NEVER:
342 		if (typ == MASK_ZEROES)
343 			return 0;
344 		return -EOPNOTSUPP;
345 	case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL:
346 		if (typ == MASK_ZEROES)
347 			return 0;
348 		fallthrough;
349 	case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
350 		if (typ == MASK_ONES)
351 			return 0;
352 		return -EINVAL;
353 	case MAE_FIELD_SUPPORTED_MATCH_PREFIX:
354 		if (typ == MASK_OTHER)
355 			return -EOPNOTSUPP;
356 		return 0;
357 	case MAE_FIELD_SUPPORTED_MATCH_MASK:
358 		return 0;
359 	default:
360 		return -EIO;
361 	}
362 }
363 
364 /* Validate field mask against hardware capabilities.  Captures caller's 'rc' */
365 #define CHECK(_mcdi, _field)	({					       \
366 	enum mask_type typ = classify_mask((const u8 *)&mask->_field,	       \
367 					   sizeof(mask->_field));	       \
368 									       \
369 	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\
370 					 typ);				       \
371 	if (rc)								       \
372 		NL_SET_ERR_MSG_FMT_MOD(extack,				       \
373 				       "No support for %s mask in field %s",   \
374 				       mask_type_name(typ), #_field);	       \
375 	rc;								       \
376 })
377 /* Booleans need special handling */
378 #define CHECK_BIT(_mcdi, _field)	({				       \
379 	enum mask_type typ = mask->_field ? MASK_ONES : MASK_ZEROES;	       \
380 									       \
381 	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\
382 					 typ);				       \
383 	if (rc)								       \
384 		NL_SET_ERR_MSG_FMT_MOD(extack,				       \
385 				       "No support for %s mask in field %s",   \
386 				       mask_type_name(typ), #_field);	       \
387 	rc;								       \
388 })
389 
390 int efx_mae_match_check_caps(struct efx_nic *efx,
391 			     const struct efx_tc_match_fields *mask,
392 			     struct netlink_ext_ack *extack)
393 {
394 	const u8 *supported_fields = efx->tc->caps->action_rule_fields;
395 	__be32 ingress_port = cpu_to_be32(mask->ingress_port);
396 	enum mask_type ingress_port_mask_type;
397 	int rc;
398 
399 	/* Check for _PREFIX assumes big-endian, so we need to convert */
400 	ingress_port_mask_type = classify_mask((const u8 *)&ingress_port,
401 					       sizeof(ingress_port));
402 	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT],
403 					 ingress_port_mask_type);
404 	if (rc) {
405 		NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field ingress_port",
406 				       mask_type_name(ingress_port_mask_type));
407 		return rc;
408 	}
409 	if (CHECK(ETHER_TYPE, eth_proto) ||
410 	    CHECK(VLAN0_TCI, vlan_tci[0]) ||
411 	    CHECK(VLAN0_PROTO, vlan_proto[0]) ||
412 	    CHECK(VLAN1_TCI, vlan_tci[1]) ||
413 	    CHECK(VLAN1_PROTO, vlan_proto[1]) ||
414 	    CHECK(ETH_SADDR, eth_saddr) ||
415 	    CHECK(ETH_DADDR, eth_daddr) ||
416 	    CHECK(IP_PROTO, ip_proto) ||
417 	    CHECK(IP_TOS, ip_tos) ||
418 	    CHECK(IP_TTL, ip_ttl) ||
419 	    CHECK(SRC_IP4, src_ip) ||
420 	    CHECK(DST_IP4, dst_ip) ||
421 #ifdef CONFIG_IPV6
422 	    CHECK(SRC_IP6, src_ip6) ||
423 	    CHECK(DST_IP6, dst_ip6) ||
424 #endif
425 	    CHECK(L4_SPORT, l4_sport) ||
426 	    CHECK(L4_DPORT, l4_dport) ||
427 	    CHECK(TCP_FLAGS, tcp_flags) ||
428 	    CHECK_BIT(IS_IP_FRAG, ip_frag) ||
429 	    CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) ||
430 	    CHECK(RECIRC_ID, recirc_id))
431 		return rc;
432 	return 0;
433 }
434 #undef CHECK_BIT
435 #undef CHECK
436 
437 int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt)
438 {
439 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTER_ALLOC_OUT_LEN(1));
440 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTER_ALLOC_V2_IN_LEN);
441 	size_t outlen;
442 	int rc;
443 
444 	if (!cnt)
445 		return -EINVAL;
446 
447 	MCDI_SET_DWORD(inbuf, MAE_COUNTER_ALLOC_V2_IN_REQUESTED_COUNT, 1);
448 	MCDI_SET_DWORD(inbuf, MAE_COUNTER_ALLOC_V2_IN_COUNTER_TYPE, cnt->type);
449 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTER_ALLOC, inbuf, sizeof(inbuf),
450 			  outbuf, sizeof(outbuf), &outlen);
451 	if (rc)
452 		return rc;
453 	/* pcol says this can't happen, since count is 1 */
454 	if (outlen < sizeof(outbuf))
455 		return -EIO;
456 	cnt->fw_id = MCDI_DWORD(outbuf, MAE_COUNTER_ALLOC_OUT_COUNTER_ID);
457 	cnt->gen = MCDI_DWORD(outbuf, MAE_COUNTER_ALLOC_OUT_GENERATION_COUNT);
458 	return 0;
459 }
460 
461 int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt)
462 {
463 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTER_FREE_OUT_LEN(1));
464 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTER_FREE_V2_IN_LEN);
465 	size_t outlen;
466 	int rc;
467 
468 	MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_COUNTER_ID_COUNT, 1);
469 	MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_FREE_COUNTER_ID, cnt->fw_id);
470 	MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_COUNTER_TYPE, cnt->type);
471 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTER_FREE, inbuf, sizeof(inbuf),
472 			  outbuf, sizeof(outbuf), &outlen);
473 	if (rc)
474 		return rc;
475 	/* pcol says this can't happen, since count is 1 */
476 	if (outlen < sizeof(outbuf))
477 		return -EIO;
478 	/* FW freed a different ID than we asked for, should also never happen.
479 	 * Warn because it means we've now got a different idea to the FW of
480 	 * what counters exist, which could cause mayhem later.
481 	 */
482 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_COUNTER_FREE_OUT_FREED_COUNTER_ID) !=
483 		    cnt->fw_id))
484 		return -EIO;
485 	return 0;
486 }
487 
488 static bool efx_mae_asl_id(u32 id)
489 {
490 	return !!(id & BIT(31));
491 }
492 
493 int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
494 {
495 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
496 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN);
497 	size_t outlen;
498 	int rc;
499 
500 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
501 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
502 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
503 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
504 	if (act->count && !WARN_ON(!act->count->cnt))
505 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
506 			       act->count->cnt->fw_id);
507 	else
508 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
509 			       MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL);
510 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID,
511 		       MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL);
512 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
513 		       MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
514 	if (act->deliver)
515 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER,
516 			       act->dest_mport);
517 	BUILD_BUG_ON(MAE_MPORT_SELECTOR_NULL);
518 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_ALLOC, inbuf, sizeof(inbuf),
519 			  outbuf, sizeof(outbuf), &outlen);
520 	if (rc)
521 		return rc;
522 	if (outlen < sizeof(outbuf))
523 		return -EIO;
524 	act->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
525 	/* We rely on the high bit of AS IDs always being clear.
526 	 * The firmware API guarantees this, but let's check it ourselves.
527 	 */
528 	if (WARN_ON_ONCE(efx_mae_asl_id(act->fw_id))) {
529 		efx_mae_free_action_set(efx, act->fw_id);
530 		return -EIO;
531 	}
532 	return 0;
533 }
534 
535 int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id)
536 {
537 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1));
538 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1));
539 	size_t outlen;
540 	int rc;
541 
542 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_FREE_IN_AS_ID, fw_id);
543 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_FREE, inbuf, sizeof(inbuf),
544 			  outbuf, sizeof(outbuf), &outlen);
545 	if (rc)
546 		return rc;
547 	if (outlen < sizeof(outbuf))
548 		return -EIO;
549 	/* FW freed a different ID than we asked for, should never happen.
550 	 * Warn because it means we've now got a different idea to the FW of
551 	 * what action-sets exist, which could cause mayhem later.
552 	 */
553 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) != fw_id))
554 		return -EIO;
555 	return 0;
556 }
557 
558 int efx_mae_alloc_action_set_list(struct efx_nic *efx,
559 				  struct efx_tc_action_set_list *acts)
560 {
561 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN);
562 	struct efx_tc_action_set *act;
563 	size_t inlen, outlen, i = 0;
564 	efx_dword_t *inbuf;
565 	int rc;
566 
567 	list_for_each_entry(act, &acts->list, list)
568 		i++;
569 	if (i == 0)
570 		return -EINVAL;
571 	if (i == 1) {
572 		/* Don't wrap an ASL around a single AS, just use the AS_ID
573 		 * directly.  ASLs are a more limited resource.
574 		 */
575 		act = list_first_entry(&acts->list, struct efx_tc_action_set, list);
576 		acts->fw_id = act->fw_id;
577 		return 0;
578 	}
579 	if (i > MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2)
580 		return -EOPNOTSUPP; /* Too many actions */
581 	inlen = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(i);
582 	inbuf = kzalloc(inlen, GFP_KERNEL);
583 	if (!inbuf)
584 		return -ENOMEM;
585 	i = 0;
586 	list_for_each_entry(act, &acts->list, list) {
587 		MCDI_SET_ARRAY_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS,
588 				     i, act->fw_id);
589 		i++;
590 	}
591 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, i);
592 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_ALLOC, inbuf, inlen,
593 			  outbuf, sizeof(outbuf), &outlen);
594 	if (rc)
595 		goto out_free;
596 	if (outlen < sizeof(outbuf)) {
597 		rc = -EIO;
598 		goto out_free;
599 	}
600 	acts->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID);
601 	/* We rely on the high bit of ASL IDs always being set.
602 	 * The firmware API guarantees this, but let's check it ourselves.
603 	 */
604 	if (WARN_ON_ONCE(!efx_mae_asl_id(acts->fw_id))) {
605 		efx_mae_free_action_set_list(efx, acts);
606 		rc = -EIO;
607 	}
608 out_free:
609 	kfree(inbuf);
610 	return rc;
611 }
612 
613 int efx_mae_free_action_set_list(struct efx_nic *efx,
614 				 struct efx_tc_action_set_list *acts)
615 {
616 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1));
617 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1));
618 	size_t outlen;
619 	int rc;
620 
621 	/* If this is just an AS_ID with no ASL wrapper, then there is
622 	 * nothing for us to free.  (The AS will be freed later.)
623 	 */
624 	if (efx_mae_asl_id(acts->fw_id)) {
625 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_FREE_IN_ASL_ID,
626 			       acts->fw_id);
627 		rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_FREE, inbuf,
628 				  sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
629 		if (rc)
630 			return rc;
631 		if (outlen < sizeof(outbuf))
632 			return -EIO;
633 		/* FW freed a different ID than we asked for, should never happen.
634 		 * Warn because it means we've now got a different idea to the FW of
635 		 * what action-set-lists exist, which could cause mayhem later.
636 		 */
637 		if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) != acts->fw_id))
638 			return -EIO;
639 	}
640 	/* We're probably about to free @acts, but let's just make sure its
641 	 * fw_id is blatted so that it won't look valid if it leaks out.
642 	 */
643 	acts->fw_id = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL;
644 	return 0;
645 }
646 
647 static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
648 					   const struct efx_tc_match *match)
649 {
650 	if (match->mask.ingress_port) {
651 		if (~match->mask.ingress_port)
652 			return -EOPNOTSUPP;
653 		MCDI_STRUCT_SET_DWORD(match_crit,
654 				      MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR,
655 				      match->value.ingress_port);
656 	}
657 	MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,
658 			      match->mask.ingress_port);
659 	EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS),
660 			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG,
661 			     match->value.ip_frag,
662 			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG,
663 			     match->value.ip_firstfrag);
664 	EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_MASK),
665 			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG,
666 			     match->mask.ip_frag,
667 			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG,
668 			     match->mask.ip_firstfrag);
669 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID,
670 			     match->value.recirc_id);
671 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK,
672 			     match->mask.recirc_id);
673 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE,
674 				match->value.eth_proto);
675 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE_MASK,
676 				match->mask.eth_proto);
677 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE,
678 				match->value.vlan_tci[0]);
679 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE_MASK,
680 				match->mask.vlan_tci[0]);
681 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE,
682 				match->value.vlan_proto[0]);
683 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE_MASK,
684 				match->mask.vlan_proto[0]);
685 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE,
686 				match->value.vlan_tci[1]);
687 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE_MASK,
688 				match->mask.vlan_tci[1]);
689 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE,
690 				match->value.vlan_proto[1]);
691 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE_MASK,
692 				match->mask.vlan_proto[1]);
693 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE),
694 	       match->value.eth_saddr, ETH_ALEN);
695 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE_MASK),
696 	       match->mask.eth_saddr, ETH_ALEN);
697 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE),
698 	       match->value.eth_daddr, ETH_ALEN);
699 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE_MASK),
700 	       match->mask.eth_daddr, ETH_ALEN);
701 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO,
702 			     match->value.ip_proto);
703 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO_MASK,
704 			     match->mask.ip_proto);
705 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS,
706 			     match->value.ip_tos);
707 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS_MASK,
708 			     match->mask.ip_tos);
709 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL,
710 			     match->value.ip_ttl);
711 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL_MASK,
712 			     match->mask.ip_ttl);
713 	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE,
714 				 match->value.src_ip);
715 	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE_MASK,
716 				 match->mask.src_ip);
717 	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE,
718 				 match->value.dst_ip);
719 	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE_MASK,
720 				 match->mask.dst_ip);
721 #ifdef CONFIG_IPV6
722 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE),
723 	       &match->value.src_ip6, sizeof(struct in6_addr));
724 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE_MASK),
725 	       &match->mask.src_ip6, sizeof(struct in6_addr));
726 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE),
727 	       &match->value.dst_ip6, sizeof(struct in6_addr));
728 	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE_MASK),
729 	       &match->mask.dst_ip6, sizeof(struct in6_addr));
730 #endif
731 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE,
732 				match->value.l4_sport);
733 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE_MASK,
734 				match->mask.l4_sport);
735 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE,
736 				match->value.l4_dport);
737 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE_MASK,
738 				match->mask.l4_dport);
739 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE,
740 				match->value.tcp_flags);
741 	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK,
742 				match->mask.tcp_flags);
743 	return 0;
744 }
745 
746 int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
747 			u32 prio, u32 acts_id, u32 *id)
748 {
749 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_INSERT_IN_LEN(MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN));
750 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN);
751 	MCDI_DECLARE_STRUCT_PTR(match_crit);
752 	MCDI_DECLARE_STRUCT_PTR(response);
753 	size_t outlen;
754 	int rc;
755 
756 	if (!id)
757 		return -EINVAL;
758 
759 	match_crit = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA);
760 	response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_RESPONSE);
761 	if (efx_mae_asl_id(acts_id)) {
762 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id);
763 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID,
764 				      MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL);
765 	} else {
766 		/* We only had one AS, so we didn't wrap it in an ASL */
767 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID,
768 				      MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
769 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id);
770 	}
771 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_PRIO, prio);
772 	rc = efx_mae_populate_match_criteria(match_crit, match);
773 	if (rc)
774 		return rc;
775 
776 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_INSERT, inbuf, sizeof(inbuf),
777 			  outbuf, sizeof(outbuf), &outlen);
778 	if (rc)
779 		return rc;
780 	if (outlen < sizeof(outbuf))
781 		return -EIO;
782 	*id = MCDI_DWORD(outbuf, MAE_ACTION_RULE_INSERT_OUT_AR_ID);
783 	return 0;
784 }
785 
786 int efx_mae_delete_rule(struct efx_nic *efx, u32 id)
787 {
788 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
789 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1));
790 	size_t outlen;
791 	int rc;
792 
793 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_DELETE_IN_AR_ID, id);
794 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_DELETE, inbuf, sizeof(inbuf),
795 			  outbuf, sizeof(outbuf), &outlen);
796 	if (rc)
797 		return rc;
798 	if (outlen < sizeof(outbuf))
799 		return -EIO;
800 	/* FW freed a different ID than we asked for, should also never happen.
801 	 * Warn because it means we've now got a different idea to the FW of
802 	 * what rules exist, which could cause mayhem later.
803 	 */
804 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) != id))
805 		return -EIO;
806 	return 0;
807 }
808