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