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 /* Validate field mask against hardware capabilities. Captures caller's 'rc' */ 254 #define CHECK(_mcdi, _field) ({ \ 255 enum mask_type typ = classify_mask((const u8 *)&mask->_field, \ 256 sizeof(mask->_field)); \ 257 \ 258 rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\ 259 typ); \ 260 if (rc) \ 261 NL_SET_ERR_MSG_FMT_MOD(extack, \ 262 "No support for %s mask in field %s", \ 263 mask_type_name(typ), #_field); \ 264 rc; \ 265 }) 266 /* Booleans need special handling */ 267 #define CHECK_BIT(_mcdi, _field) ({ \ 268 enum mask_type typ = mask->_field ? MASK_ONES : MASK_ZEROES; \ 269 \ 270 rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\ 271 typ); \ 272 if (rc) \ 273 NL_SET_ERR_MSG_FMT_MOD(extack, \ 274 "No support for %s mask in field %s", \ 275 mask_type_name(typ), #_field); \ 276 rc; \ 277 }) 278 279 int efx_mae_match_check_caps(struct efx_nic *efx, 280 const struct efx_tc_match_fields *mask, 281 struct netlink_ext_ack *extack) 282 { 283 const u8 *supported_fields = efx->tc->caps->action_rule_fields; 284 __be32 ingress_port = cpu_to_be32(mask->ingress_port); 285 enum mask_type ingress_port_mask_type; 286 int rc; 287 288 /* Check for _PREFIX assumes big-endian, so we need to convert */ 289 ingress_port_mask_type = classify_mask((const u8 *)&ingress_port, 290 sizeof(ingress_port)); 291 rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT], 292 ingress_port_mask_type); 293 if (rc) { 294 NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field ingress_port", 295 mask_type_name(ingress_port_mask_type)); 296 return rc; 297 } 298 if (CHECK(ETHER_TYPE, eth_proto) || 299 CHECK(VLAN0_TCI, vlan_tci[0]) || 300 CHECK(VLAN0_PROTO, vlan_proto[0]) || 301 CHECK(VLAN1_TCI, vlan_tci[1]) || 302 CHECK(VLAN1_PROTO, vlan_proto[1]) || 303 CHECK(ETH_SADDR, eth_saddr) || 304 CHECK(ETH_DADDR, eth_daddr) || 305 CHECK(IP_PROTO, ip_proto) || 306 CHECK(IP_TOS, ip_tos) || 307 CHECK(IP_TTL, ip_ttl) || 308 CHECK(SRC_IP4, src_ip) || 309 CHECK(DST_IP4, dst_ip) || 310 #ifdef CONFIG_IPV6 311 CHECK(SRC_IP6, src_ip6) || 312 CHECK(DST_IP6, dst_ip6) || 313 #endif 314 CHECK(L4_SPORT, l4_sport) || 315 CHECK(L4_DPORT, l4_dport) || 316 CHECK(TCP_FLAGS, tcp_flags) || 317 CHECK_BIT(IS_IP_FRAG, ip_frag) || 318 CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) || 319 CHECK(RECIRC_ID, recirc_id)) 320 return rc; 321 return 0; 322 } 323 #undef CHECK_BIT 324 #undef CHECK 325 326 static bool efx_mae_asl_id(u32 id) 327 { 328 return !!(id & BIT(31)); 329 } 330 331 int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act) 332 { 333 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN); 334 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN); 335 size_t outlen; 336 int rc; 337 338 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID, 339 MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); 340 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID, 341 MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); 342 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID, 343 MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL); 344 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID, 345 MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL); 346 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID, 347 MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL); 348 if (act->deliver) 349 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER, 350 act->dest_mport); 351 BUILD_BUG_ON(MAE_MPORT_SELECTOR_NULL); 352 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_ALLOC, inbuf, sizeof(inbuf), 353 outbuf, sizeof(outbuf), &outlen); 354 if (rc) 355 return rc; 356 if (outlen < sizeof(outbuf)) 357 return -EIO; 358 act->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_ALLOC_OUT_AS_ID); 359 /* We rely on the high bit of AS IDs always being clear. 360 * The firmware API guarantees this, but let's check it ourselves. 361 */ 362 if (WARN_ON_ONCE(efx_mae_asl_id(act->fw_id))) { 363 efx_mae_free_action_set(efx, act->fw_id); 364 return -EIO; 365 } 366 return 0; 367 } 368 369 int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id) 370 { 371 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1)); 372 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1)); 373 size_t outlen; 374 int rc; 375 376 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_FREE_IN_AS_ID, fw_id); 377 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_FREE, inbuf, sizeof(inbuf), 378 outbuf, sizeof(outbuf), &outlen); 379 if (rc) 380 return rc; 381 if (outlen < sizeof(outbuf)) 382 return -EIO; 383 /* FW freed a different ID than we asked for, should never happen. 384 * Warn because it means we've now got a different idea to the FW of 385 * what action-sets exist, which could cause mayhem later. 386 */ 387 if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) != fw_id)) 388 return -EIO; 389 return 0; 390 } 391 392 int efx_mae_alloc_action_set_list(struct efx_nic *efx, 393 struct efx_tc_action_set_list *acts) 394 { 395 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN); 396 struct efx_tc_action_set *act; 397 size_t inlen, outlen, i = 0; 398 efx_dword_t *inbuf; 399 int rc; 400 401 list_for_each_entry(act, &acts->list, list) 402 i++; 403 if (i == 0) 404 return -EINVAL; 405 if (i == 1) { 406 /* Don't wrap an ASL around a single AS, just use the AS_ID 407 * directly. ASLs are a more limited resource. 408 */ 409 act = list_first_entry(&acts->list, struct efx_tc_action_set, list); 410 acts->fw_id = act->fw_id; 411 return 0; 412 } 413 if (i > MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2) 414 return -EOPNOTSUPP; /* Too many actions */ 415 inlen = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(i); 416 inbuf = kzalloc(inlen, GFP_KERNEL); 417 if (!inbuf) 418 return -ENOMEM; 419 i = 0; 420 list_for_each_entry(act, &acts->list, list) { 421 MCDI_SET_ARRAY_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS, 422 i, act->fw_id); 423 i++; 424 } 425 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, i); 426 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_ALLOC, inbuf, inlen, 427 outbuf, sizeof(outbuf), &outlen); 428 if (rc) 429 goto out_free; 430 if (outlen < sizeof(outbuf)) { 431 rc = -EIO; 432 goto out_free; 433 } 434 acts->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID); 435 /* We rely on the high bit of ASL IDs always being set. 436 * The firmware API guarantees this, but let's check it ourselves. 437 */ 438 if (WARN_ON_ONCE(!efx_mae_asl_id(acts->fw_id))) { 439 efx_mae_free_action_set_list(efx, acts); 440 rc = -EIO; 441 } 442 out_free: 443 kfree(inbuf); 444 return rc; 445 } 446 447 int efx_mae_free_action_set_list(struct efx_nic *efx, 448 struct efx_tc_action_set_list *acts) 449 { 450 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1)); 451 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1)); 452 size_t outlen; 453 int rc; 454 455 /* If this is just an AS_ID with no ASL wrapper, then there is 456 * nothing for us to free. (The AS will be freed later.) 457 */ 458 if (efx_mae_asl_id(acts->fw_id)) { 459 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_FREE_IN_ASL_ID, 460 acts->fw_id); 461 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_FREE, inbuf, 462 sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); 463 if (rc) 464 return rc; 465 if (outlen < sizeof(outbuf)) 466 return -EIO; 467 /* FW freed a different ID than we asked for, should never happen. 468 * Warn because it means we've now got a different idea to the FW of 469 * what action-set-lists exist, which could cause mayhem later. 470 */ 471 if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) != acts->fw_id)) 472 return -EIO; 473 } 474 /* We're probably about to free @acts, but let's just make sure its 475 * fw_id is blatted so that it won't look valid if it leaks out. 476 */ 477 acts->fw_id = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL; 478 return 0; 479 } 480 481 static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit), 482 const struct efx_tc_match *match) 483 { 484 if (match->mask.ingress_port) { 485 if (~match->mask.ingress_port) 486 return -EOPNOTSUPP; 487 MCDI_STRUCT_SET_DWORD(match_crit, 488 MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR, 489 match->value.ingress_port); 490 } 491 MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK, 492 match->mask.ingress_port); 493 EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS), 494 MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG, 495 match->value.ip_frag, 496 MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG, 497 match->value.ip_firstfrag); 498 EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_MASK), 499 MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG, 500 match->mask.ip_frag, 501 MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG, 502 match->mask.ip_firstfrag); 503 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID, 504 match->value.recirc_id); 505 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK, 506 match->mask.recirc_id); 507 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE, 508 match->value.eth_proto); 509 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE_MASK, 510 match->mask.eth_proto); 511 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE, 512 match->value.vlan_tci[0]); 513 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE_MASK, 514 match->mask.vlan_tci[0]); 515 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE, 516 match->value.vlan_proto[0]); 517 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE_MASK, 518 match->mask.vlan_proto[0]); 519 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE, 520 match->value.vlan_tci[1]); 521 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE_MASK, 522 match->mask.vlan_tci[1]); 523 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE, 524 match->value.vlan_proto[1]); 525 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE_MASK, 526 match->mask.vlan_proto[1]); 527 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE), 528 match->value.eth_saddr, ETH_ALEN); 529 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE_MASK), 530 match->mask.eth_saddr, ETH_ALEN); 531 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE), 532 match->value.eth_daddr, ETH_ALEN); 533 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE_MASK), 534 match->mask.eth_daddr, ETH_ALEN); 535 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO, 536 match->value.ip_proto); 537 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO_MASK, 538 match->mask.ip_proto); 539 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS, 540 match->value.ip_tos); 541 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS_MASK, 542 match->mask.ip_tos); 543 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL, 544 match->value.ip_ttl); 545 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL_MASK, 546 match->mask.ip_ttl); 547 MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE, 548 match->value.src_ip); 549 MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE_MASK, 550 match->mask.src_ip); 551 MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE, 552 match->value.dst_ip); 553 MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE_MASK, 554 match->mask.dst_ip); 555 #ifdef CONFIG_IPV6 556 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE), 557 &match->value.src_ip6, sizeof(struct in6_addr)); 558 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE_MASK), 559 &match->mask.src_ip6, sizeof(struct in6_addr)); 560 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE), 561 &match->value.dst_ip6, sizeof(struct in6_addr)); 562 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE_MASK), 563 &match->mask.dst_ip6, sizeof(struct in6_addr)); 564 #endif 565 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE, 566 match->value.l4_sport); 567 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE_MASK, 568 match->mask.l4_sport); 569 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE, 570 match->value.l4_dport); 571 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE_MASK, 572 match->mask.l4_dport); 573 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE, 574 match->value.tcp_flags); 575 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK, 576 match->mask.tcp_flags); 577 return 0; 578 } 579 580 int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match, 581 u32 prio, u32 acts_id, u32 *id) 582 { 583 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_INSERT_IN_LEN(MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN)); 584 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN); 585 MCDI_DECLARE_STRUCT_PTR(match_crit); 586 MCDI_DECLARE_STRUCT_PTR(response); 587 size_t outlen; 588 int rc; 589 590 if (!id) 591 return -EINVAL; 592 593 match_crit = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA); 594 response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_RESPONSE); 595 if (efx_mae_asl_id(acts_id)) { 596 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id); 597 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, 598 MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL); 599 } else { 600 /* We only had one AS, so we didn't wrap it in an ASL */ 601 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, 602 MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL); 603 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id); 604 } 605 MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_PRIO, prio); 606 rc = efx_mae_populate_match_criteria(match_crit, match); 607 if (rc) 608 return rc; 609 610 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_INSERT, inbuf, sizeof(inbuf), 611 outbuf, sizeof(outbuf), &outlen); 612 if (rc) 613 return rc; 614 if (outlen < sizeof(outbuf)) 615 return -EIO; 616 *id = MCDI_DWORD(outbuf, MAE_ACTION_RULE_INSERT_OUT_AR_ID); 617 return 0; 618 } 619 620 int efx_mae_delete_rule(struct efx_nic *efx, u32 id) 621 { 622 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1)); 623 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1)); 624 size_t outlen; 625 int rc; 626 627 MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_DELETE_IN_AR_ID, id); 628 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_DELETE, inbuf, sizeof(inbuf), 629 outbuf, sizeof(outbuf), &outlen); 630 if (rc) 631 return rc; 632 if (outlen < sizeof(outbuf)) 633 return -EIO; 634 /* FW freed a different ID than we asked for, should also never happen. 635 * Warn because it means we've now got a different idea to the FW of 636 * what rules exist, which could cause mayhem later. 637 */ 638 if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) != id)) 639 return -EIO; 640 return 0; 641 } 642