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
efx_mae_allocate_mport(struct efx_nic * efx,u32 * id,u32 * label)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
efx_mae_free_mport(struct efx_nic * efx,u32 id)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
efx_mae_mport_wire(struct efx_nic * efx,u32 * out)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
efx_mae_mport_uplink(struct efx_nic * efx __always_unused,u32 * out)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
efx_mae_mport_vf(struct efx_nic * efx __always_unused,u32 vf_id,u32 * out)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 */
efx_mae_mport_mport(struct efx_nic * efx __always_unused,u32 mport_id,u32 * out)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 */
efx_mae_lookup_mport(struct efx_nic * efx,u32 selector,u32 * id)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
efx_mae_get_basic_caps(struct efx_nic * efx,struct mae_caps * caps)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
efx_mae_get_rule_fields(struct efx_nic * efx,u32 cmd,u8 * field_support)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
efx_mae_get_caps(struct efx_nic * efx,struct mae_caps * caps)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
mask_type_name(enum mask_type typ)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 */
classify_mask(const u8 * mask,size_t len)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
efx_mae_match_check_cap_typ(u8 support,enum mask_type typ)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
efx_mae_match_check_caps(struct efx_nic * efx,const struct efx_tc_match_fields * mask,struct netlink_ext_ack * extack)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
efx_mae_asl_id(u32 id)276 static bool efx_mae_asl_id(u32 id)
277 {
278 return !!(id & BIT(31));
279 }
280
efx_mae_alloc_action_set(struct efx_nic * efx,struct efx_tc_action_set * act)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
efx_mae_free_action_set(struct efx_nic * efx,u32 fw_id)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
efx_mae_alloc_action_set_list(struct efx_nic * efx,struct efx_tc_action_set_list * acts)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
efx_mae_free_action_set_list(struct efx_nic * efx,struct efx_tc_action_set_list * acts)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
efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR (match_crit),const struct efx_tc_match * match)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
efx_mae_insert_rule(struct efx_nic * efx,const struct efx_tc_match * match,u32 prio,u32 acts_id,u32 * id)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
efx_mae_delete_rule(struct efx_nic * efx,u32 id)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