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