xref: /openbmc/linux/drivers/net/ethernet/sfc/mae.c (revision ae108c48)
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 		efx_tc_err(efx, "No support for %s mask in field ingress_port\n",
269 			   mask_type_name(ingress_port_mask_type));
270 		NL_SET_ERR_MSG_MOD(extack, "Unsupported mask type for ingress_port");
271 		return rc;
272 	}
273 	return 0;
274 }
275 
276 static bool efx_mae_asl_id(u32 id)
277 {
278 	return !!(id & BIT(31));
279 }
280 
281 int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
282 {
283 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
284 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN);
285 	size_t outlen;
286 	int rc;
287 
288 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
289 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
290 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
291 		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
292 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
293 		       MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL);
294 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID,
295 		       MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL);
296 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
297 		       MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
298 	if (act->deliver)
299 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER,
300 			       act->dest_mport);
301 	BUILD_BUG_ON(MAE_MPORT_SELECTOR_NULL);
302 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_ALLOC, inbuf, sizeof(inbuf),
303 			  outbuf, sizeof(outbuf), &outlen);
304 	if (rc)
305 		return rc;
306 	if (outlen < sizeof(outbuf))
307 		return -EIO;
308 	act->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
309 	/* We rely on the high bit of AS IDs always being clear.
310 	 * The firmware API guarantees this, but let's check it ourselves.
311 	 */
312 	if (WARN_ON_ONCE(efx_mae_asl_id(act->fw_id))) {
313 		efx_mae_free_action_set(efx, act->fw_id);
314 		return -EIO;
315 	}
316 	return 0;
317 }
318 
319 int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id)
320 {
321 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1));
322 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1));
323 	size_t outlen;
324 	int rc;
325 
326 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_FREE_IN_AS_ID, fw_id);
327 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_FREE, inbuf, sizeof(inbuf),
328 			  outbuf, sizeof(outbuf), &outlen);
329 	if (rc)
330 		return rc;
331 	if (outlen < sizeof(outbuf))
332 		return -EIO;
333 	/* FW freed a different ID than we asked for, should never happen.
334 	 * Warn because it means we've now got a different idea to the FW of
335 	 * what action-sets exist, which could cause mayhem later.
336 	 */
337 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) != fw_id))
338 		return -EIO;
339 	return 0;
340 }
341 
342 int efx_mae_alloc_action_set_list(struct efx_nic *efx,
343 				  struct efx_tc_action_set_list *acts)
344 {
345 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN);
346 	struct efx_tc_action_set *act;
347 	size_t inlen, outlen, i = 0;
348 	efx_dword_t *inbuf;
349 	int rc;
350 
351 	list_for_each_entry(act, &acts->list, list)
352 		i++;
353 	if (i == 0)
354 		return -EINVAL;
355 	if (i == 1) {
356 		/* Don't wrap an ASL around a single AS, just use the AS_ID
357 		 * directly.  ASLs are a more limited resource.
358 		 */
359 		act = list_first_entry(&acts->list, struct efx_tc_action_set, list);
360 		acts->fw_id = act->fw_id;
361 		return 0;
362 	}
363 	if (i > MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2)
364 		return -EOPNOTSUPP; /* Too many actions */
365 	inlen = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(i);
366 	inbuf = kzalloc(inlen, GFP_KERNEL);
367 	if (!inbuf)
368 		return -ENOMEM;
369 	i = 0;
370 	list_for_each_entry(act, &acts->list, list) {
371 		MCDI_SET_ARRAY_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS,
372 				     i, act->fw_id);
373 		i++;
374 	}
375 	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, i);
376 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_ALLOC, inbuf, inlen,
377 			  outbuf, sizeof(outbuf), &outlen);
378 	if (rc)
379 		goto out_free;
380 	if (outlen < sizeof(outbuf)) {
381 		rc = -EIO;
382 		goto out_free;
383 	}
384 	acts->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID);
385 	/* We rely on the high bit of ASL IDs always being set.
386 	 * The firmware API guarantees this, but let's check it ourselves.
387 	 */
388 	if (WARN_ON_ONCE(!efx_mae_asl_id(acts->fw_id))) {
389 		efx_mae_free_action_set_list(efx, acts);
390 		rc = -EIO;
391 	}
392 out_free:
393 	kfree(inbuf);
394 	return rc;
395 }
396 
397 int efx_mae_free_action_set_list(struct efx_nic *efx,
398 				 struct efx_tc_action_set_list *acts)
399 {
400 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1));
401 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1));
402 	size_t outlen;
403 	int rc;
404 
405 	/* If this is just an AS_ID with no ASL wrapper, then there is
406 	 * nothing for us to free.  (The AS will be freed later.)
407 	 */
408 	if (efx_mae_asl_id(acts->fw_id)) {
409 		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_FREE_IN_ASL_ID,
410 			       acts->fw_id);
411 		rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_FREE, inbuf,
412 				  sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
413 		if (rc)
414 			return rc;
415 		if (outlen < sizeof(outbuf))
416 			return -EIO;
417 		/* FW freed a different ID than we asked for, should never happen.
418 		 * Warn because it means we've now got a different idea to the FW of
419 		 * what action-set-lists exist, which could cause mayhem later.
420 		 */
421 		if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) != acts->fw_id))
422 			return -EIO;
423 	}
424 	/* We're probably about to free @acts, but let's just make sure its
425 	 * fw_id is blatted so that it won't look valid if it leaks out.
426 	 */
427 	acts->fw_id = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL;
428 	return 0;
429 }
430 
431 static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
432 					   const struct efx_tc_match *match)
433 {
434 	if (match->mask.ingress_port) {
435 		if (~match->mask.ingress_port)
436 			return -EOPNOTSUPP;
437 		MCDI_STRUCT_SET_DWORD(match_crit,
438 				      MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR,
439 				      match->value.ingress_port);
440 	}
441 	MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,
442 			      match->mask.ingress_port);
443 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID,
444 			     match->value.recirc_id);
445 	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK,
446 			     match->mask.recirc_id);
447 	return 0;
448 }
449 
450 int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
451 			u32 prio, u32 acts_id, u32 *id)
452 {
453 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_INSERT_IN_LEN(MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN));
454 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN);
455 	MCDI_DECLARE_STRUCT_PTR(match_crit);
456 	MCDI_DECLARE_STRUCT_PTR(response);
457 	size_t outlen;
458 	int rc;
459 
460 	if (!id)
461 		return -EINVAL;
462 
463 	match_crit = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA);
464 	response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_RESPONSE);
465 	if (efx_mae_asl_id(acts_id)) {
466 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id);
467 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID,
468 				      MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL);
469 	} else {
470 		/* We only had one AS, so we didn't wrap it in an ASL */
471 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID,
472 				      MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
473 		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id);
474 	}
475 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_PRIO, prio);
476 	rc = efx_mae_populate_match_criteria(match_crit, match);
477 	if (rc)
478 		return rc;
479 
480 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_INSERT, inbuf, sizeof(inbuf),
481 			  outbuf, sizeof(outbuf), &outlen);
482 	if (rc)
483 		return rc;
484 	if (outlen < sizeof(outbuf))
485 		return -EIO;
486 	*id = MCDI_DWORD(outbuf, MAE_ACTION_RULE_INSERT_OUT_AR_ID);
487 	return 0;
488 }
489 
490 int efx_mae_delete_rule(struct efx_nic *efx, u32 id)
491 {
492 	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
493 	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1));
494 	size_t outlen;
495 	int rc;
496 
497 	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_DELETE_IN_AR_ID, id);
498 	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_DELETE, inbuf, sizeof(inbuf),
499 			  outbuf, sizeof(outbuf), &outlen);
500 	if (rc)
501 		return rc;
502 	if (outlen < sizeof(outbuf))
503 		return -EIO;
504 	/* FW freed a different ID than we asked for, should also never happen.
505 	 * Warn because it means we've now got a different idea to the FW of
506 	 * what rules exist, which could cause mayhem later.
507 	 */
508 	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) != id))
509 		return -EIO;
510 	return 0;
511 }
512