xref: /openbmc/linux/drivers/net/ethernet/sfc/mae.c (revision ac73d4bf2cdaf2cb8a43df8ee4a5c066d2c5d7b4)
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 static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps)
116 {
117 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN);
118 	size_t outlen;
119 	int rc;
120 
121 	BUILD_BUG_ON(MC_CMD_MAE_GET_CAPS_IN_LEN);
122 
123 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_GET_CAPS, NULL, 0, outbuf,
124 			  sizeof(outbuf), &outlen);
125 	if (rc)
126 		return rc;
127 	if (outlen < sizeof(outbuf))
128 		return -EIO;
129 	caps->match_field_count = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT);
130 	caps->action_prios = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ACTION_PRIOS);
131 	return 0;
132 }
133 
134 static int efx_mae_get_rule_fields(struct efx_nic *efx, u32 cmd,
135 				   u8 *field_support)
136 {
137 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(MAE_NUM_FIELDS));
138 	MCDI_DECLARE_STRUCT_PTR(caps);
139 	unsigned int count;
140 	size_t outlen;
141 	int rc, i;
142 
143 	BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_IN_LEN);
144 
145 	rc = efx_mcdi_rpc(efx, cmd, NULL, 0, outbuf, sizeof(outbuf), &outlen);
146 	if (rc)
147 		return rc;
148 	count = MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_COUNT);
149 	memset(field_support, MAE_FIELD_UNSUPPORTED, MAE_NUM_FIELDS);
150 	caps = _MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_FIELD_FLAGS);
151 	/* We're only interested in the support status enum, not any other
152 	 * flags, so just extract that from each entry.
153 	 */
154 	for (i = 0; i < count; i++)
155 		if (i * sizeof(*outbuf) + MC_CMD_MAE_GET_AR_CAPS_OUT_FIELD_FLAGS_OFST < outlen)
156 			field_support[i] = EFX_DWORD_FIELD(caps[i], MAE_FIELD_FLAGS_SUPPORT_STATUS);
157 	return 0;
158 }
159 
160 int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps)
161 {
162 	int rc;
163 
164 	rc = efx_mae_get_basic_caps(efx, caps);
165 	if (rc)
166 		return rc;
167 	return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS,
168 				       caps->action_rule_fields);
169 }
170 
171 /* Bit twiddling:
172  * Prefix: 1...110...0
173  *      ~: 0...001...1
174  *    + 1: 0...010...0 is power of two
175  * so (~x) & ((~x) + 1) == 0.  Converse holds also.
176  */
177 #define is_prefix_byte(_x)	!(((_x) ^ 0xff) & (((_x) ^ 0xff) + 1))
178 
179 enum mask_type { MASK_ONES, MASK_ZEROES, MASK_PREFIX, MASK_OTHER };
180 
181 static const char *mask_type_name(enum mask_type typ)
182 {
183 	switch (typ) {
184 	case MASK_ONES:
185 		return "all-1s";
186 	case MASK_ZEROES:
187 		return "all-0s";
188 	case MASK_PREFIX:
189 		return "prefix";
190 	case MASK_OTHER:
191 		return "arbitrary";
192 	default: /* can't happen */
193 		return "unknown";
194 	}
195 }
196 
197 /* Checks a (big-endian) bytestring is a bit prefix */
198 static enum mask_type classify_mask(const u8 *mask, size_t len)
199 {
200 	bool zeroes = true; /* All bits seen so far are zeroes */
201 	bool ones = true; /* All bits seen so far are ones */
202 	bool prefix = true; /* Valid prefix so far */
203 	size_t i;
204 
205 	for (i = 0; i < len; i++) {
206 		if (ones) {
207 			if (!is_prefix_byte(mask[i]))
208 				prefix = false;
209 		} else if (mask[i]) {
210 			prefix = false;
211 		}
212 		if (mask[i] != 0xff)
213 			ones = false;
214 		if (mask[i])
215 			zeroes = false;
216 	}
217 	if (ones)
218 		return MASK_ONES;
219 	if (zeroes)
220 		return MASK_ZEROES;
221 	if (prefix)
222 		return MASK_PREFIX;
223 	return MASK_OTHER;
224 }
225 
226 static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ)
227 {
228 	switch (support) {
229 	case MAE_FIELD_UNSUPPORTED:
230 	case MAE_FIELD_SUPPORTED_MATCH_NEVER:
231 		if (typ == MASK_ZEROES)
232 			return 0;
233 		return -EOPNOTSUPP;
234 	case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL:
235 		if (typ == MASK_ZEROES)
236 			return 0;
237 		fallthrough;
238 	case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
239 		if (typ == MASK_ONES)
240 			return 0;
241 		return -EINVAL;
242 	case MAE_FIELD_SUPPORTED_MATCH_PREFIX:
243 		if (typ == MASK_OTHER)
244 			return -EOPNOTSUPP;
245 		return 0;
246 	case MAE_FIELD_SUPPORTED_MATCH_MASK:
247 		return 0;
248 	default:
249 		return -EIO;
250 	}
251 }
252 
253 int efx_mae_match_check_caps(struct efx_nic *efx,
254 			     const struct efx_tc_match_fields *mask,
255 			     struct netlink_ext_ack *extack)
256 {
257 	const u8 *supported_fields = efx->tc->caps->action_rule_fields;
258 	__be32 ingress_port = cpu_to_be32(mask->ingress_port);
259 	enum mask_type ingress_port_mask_type;
260 	int rc;
261 
262 	/* Check for _PREFIX assumes big-endian, so we need to convert */
263 	ingress_port_mask_type = classify_mask((const u8 *)&ingress_port,
264 					       sizeof(ingress_port));
265 	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT],
266 					 ingress_port_mask_type);
267 	if (rc) {
268 		NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field ingress_port",
269 				       mask_type_name(ingress_port_mask_type));
270 		return rc;
271 	}
272 	return 0;
273 }
274 
275 static bool efx_mae_asl_id(u32 id)
276 {
277 	return !!(id & BIT(31));
278 }
279 
280 int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
281 {
282 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
283 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN);
284 	size_t outlen;
285 	int rc;
286 
287 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
288 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
289 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
290 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
291 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
292 		       MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL);
293 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID,
294 		       MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL);
295 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
296 		       MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
297 	if (act->deliver)
298 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER,
299 			       act->dest_mport);
300 	BUILD_BUG_ON(MAE_MPORT_SELECTOR_NULL);
301 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_ALLOC, inbuf, sizeof(inbuf),
302 			  outbuf, sizeof(outbuf), &outlen);
303 	if (rc)
304 		return rc;
305 	if (outlen < sizeof(outbuf))
306 		return -EIO;
307 	act->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
308 	/* We rely on the high bit of AS IDs always being clear.
309 	 * The firmware API guarantees this, but let's check it ourselves.
310 	 */
311 	if (WARN_ON_ONCE(efx_mae_asl_id(act->fw_id))) {
312 		efx_mae_free_action_set(efx, act->fw_id);
313 		return -EIO;
314 	}
315 	return 0;
316 }
317 
318 int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id)
319 {
320 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1));
321 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1));
322 	size_t outlen;
323 	int rc;
324 
325 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_FREE_IN_AS_ID, fw_id);
326 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_FREE, inbuf, sizeof(inbuf),
327 			  outbuf, sizeof(outbuf), &outlen);
328 	if (rc)
329 		return rc;
330 	if (outlen < sizeof(outbuf))
331 		return -EIO;
332 	/* FW freed a different ID than we asked for, should never happen.
333 	 * Warn because it means we've now got a different idea to the FW of
334 	 * what action-sets exist, which could cause mayhem later.
335 	 */
336 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) != fw_id))
337 		return -EIO;
338 	return 0;
339 }
340 
341 int efx_mae_alloc_action_set_list(struct efx_nic *efx,
342 				  struct efx_tc_action_set_list *acts)
343 {
344 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN);
345 	struct efx_tc_action_set *act;
346 	size_t inlen, outlen, i = 0;
347 	efx_dword_t *inbuf;
348 	int rc;
349 
350 	list_for_each_entry(act, &acts->list, list)
351 		i++;
352 	if (i == 0)
353 		return -EINVAL;
354 	if (i == 1) {
355 		/* Don't wrap an ASL around a single AS, just use the AS_ID
356 		 * directly.  ASLs are a more limited resource.
357 		 */
358 		act = list_first_entry(&acts->list, struct efx_tc_action_set, list);
359 		acts->fw_id = act->fw_id;
360 		return 0;
361 	}
362 	if (i > MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2)
363 		return -EOPNOTSUPP; /* Too many actions */
364 	inlen = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(i);
365 	inbuf = kzalloc(inlen, GFP_KERNEL);
366 	if (!inbuf)
367 		return -ENOMEM;
368 	i = 0;
369 	list_for_each_entry(act, &acts->list, list) {
370 		MCDI_SET_ARRAY_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS,
371 				     i, act->fw_id);
372 		i++;
373 	}
374 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, i);
375 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_ALLOC, inbuf, inlen,
376 			  outbuf, sizeof(outbuf), &outlen);
377 	if (rc)
378 		goto out_free;
379 	if (outlen < sizeof(outbuf)) {
380 		rc = -EIO;
381 		goto out_free;
382 	}
383 	acts->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID);
384 	/* We rely on the high bit of ASL IDs always being set.
385 	 * The firmware API guarantees this, but let's check it ourselves.
386 	 */
387 	if (WARN_ON_ONCE(!efx_mae_asl_id(acts->fw_id))) {
388 		efx_mae_free_action_set_list(efx, acts);
389 		rc = -EIO;
390 	}
391 out_free:
392 	kfree(inbuf);
393 	return rc;
394 }
395 
396 int efx_mae_free_action_set_list(struct efx_nic *efx,
397 				 struct efx_tc_action_set_list *acts)
398 {
399 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1));
400 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1));
401 	size_t outlen;
402 	int rc;
403 
404 	/* If this is just an AS_ID with no ASL wrapper, then there is
405 	 * nothing for us to free.  (The AS will be freed later.)
406 	 */
407 	if (efx_mae_asl_id(acts->fw_id)) {
408 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_FREE_IN_ASL_ID,
409 			       acts->fw_id);
410 		rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_FREE, inbuf,
411 				  sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
412 		if (rc)
413 			return rc;
414 		if (outlen < sizeof(outbuf))
415 			return -EIO;
416 		/* FW freed a different ID than we asked for, should never happen.
417 		 * Warn because it means we've now got a different idea to the FW of
418 		 * what action-set-lists exist, which could cause mayhem later.
419 		 */
420 		if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) != acts->fw_id))
421 			return -EIO;
422 	}
423 	/* We're probably about to free @acts, but let's just make sure its
424 	 * fw_id is blatted so that it won't look valid if it leaks out.
425 	 */
426 	acts->fw_id = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL;
427 	return 0;
428 }
429 
430 static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
431 					   const struct efx_tc_match *match)
432 {
433 	if (match->mask.ingress_port) {
434 		if (~match->mask.ingress_port)
435 			return -EOPNOTSUPP;
436 		MCDI_STRUCT_SET_DWORD(match_crit,
437 				      MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR,
438 				      match->value.ingress_port);
439 	}
440 	MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,
441 			      match->mask.ingress_port);
442 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID,
443 			     match->value.recirc_id);
444 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK,
445 			     match->mask.recirc_id);
446 	return 0;
447 }
448 
449 int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
450 			u32 prio, u32 acts_id, u32 *id)
451 {
452 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_INSERT_IN_LEN(MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN));
453 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN);
454 	MCDI_DECLARE_STRUCT_PTR(match_crit);
455 	MCDI_DECLARE_STRUCT_PTR(response);
456 	size_t outlen;
457 	int rc;
458 
459 	if (!id)
460 		return -EINVAL;
461 
462 	match_crit = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA);
463 	response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_RESPONSE);
464 	if (efx_mae_asl_id(acts_id)) {
465 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id);
466 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID,
467 				      MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL);
468 	} else {
469 		/* We only had one AS, so we didn't wrap it in an ASL */
470 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID,
471 				      MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
472 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id);
473 	}
474 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_PRIO, prio);
475 	rc = efx_mae_populate_match_criteria(match_crit, match);
476 	if (rc)
477 		return rc;
478 
479 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_INSERT, inbuf, sizeof(inbuf),
480 			  outbuf, sizeof(outbuf), &outlen);
481 	if (rc)
482 		return rc;
483 	if (outlen < sizeof(outbuf))
484 		return -EIO;
485 	*id = MCDI_DWORD(outbuf, MAE_ACTION_RULE_INSERT_OUT_AR_ID);
486 	return 0;
487 }
488 
489 int efx_mae_delete_rule(struct efx_nic *efx, u32 id)
490 {
491 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
492 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1));
493 	size_t outlen;
494 	int rc;
495 
496 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_DELETE_IN_AR_ID, id);
497 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_DELETE, inbuf, sizeof(inbuf),
498 			  outbuf, sizeof(outbuf), &outlen);
499 	if (rc)
500 		return rc;
501 	if (outlen < sizeof(outbuf))
502 		return -EIO;
503 	/* FW freed a different ID than we asked for, should also never happen.
504 	 * Warn because it means we've now got a different idea to the FW of
505 	 * what rules exist, which could cause mayhem later.
506 	 */
507 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) != id))
508 		return -EIO;
509 	return 0;
510 }
511