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 int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue) 116 { 117 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_START_V2_IN_LEN); 118 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_START_OUT_LEN); 119 u32 out_flags; 120 size_t outlen; 121 int rc; 122 123 MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_QID, 124 efx_rx_queue_index(rx_queue)); 125 MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_PACKET_SIZE, 126 efx->net_dev->mtu); 127 MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_COUNTER_TYPES_MASK, 128 BIT(MAE_COUNTER_TYPE_AR) | BIT(MAE_COUNTER_TYPE_CT) | 129 BIT(MAE_COUNTER_TYPE_OR)); 130 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_START, 131 inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); 132 if (rc) 133 return rc; 134 if (outlen < sizeof(outbuf)) 135 return -EIO; 136 out_flags = MCDI_DWORD(outbuf, MAE_COUNTERS_STREAM_START_OUT_FLAGS); 137 if (out_flags & BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST)) { 138 netif_dbg(efx, drv, efx->net_dev, 139 "MAE counter stream uses credits\n"); 140 rx_queue->grant_credits = true; 141 out_flags &= ~BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST); 142 } 143 if (out_flags) { 144 netif_err(efx, drv, efx->net_dev, 145 "MAE counter stream start: unrecognised flags %x\n", 146 out_flags); 147 goto out_stop; 148 } 149 return 0; 150 out_stop: 151 efx_mae_stop_counters(efx, rx_queue); 152 return -EOPNOTSUPP; 153 } 154 155 static bool efx_mae_counters_flushed(u32 *flush_gen, u32 *seen_gen) 156 { 157 int i; 158 159 for (i = 0; i < EFX_TC_COUNTER_TYPE_MAX; i++) 160 if ((s32)(flush_gen[i] - seen_gen[i]) > 0) 161 return false; 162 return true; 163 } 164 165 int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue) 166 { 167 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_V2_OUT_LENMAX); 168 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_IN_LEN); 169 size_t outlen; 170 int rc, i; 171 172 MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_STOP_IN_QID, 173 efx_rx_queue_index(rx_queue)); 174 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_STOP, 175 inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); 176 177 if (rc) 178 return rc; 179 180 netif_dbg(efx, drv, efx->net_dev, "Draining counters:\n"); 181 /* Only process received generation counts */ 182 for (i = 0; (i < (outlen / 4)) && (i < EFX_TC_COUNTER_TYPE_MAX); i++) { 183 efx->tc->flush_gen[i] = MCDI_ARRAY_DWORD(outbuf, 184 MAE_COUNTERS_STREAM_STOP_V2_OUT_GENERATION_COUNT, 185 i); 186 netif_dbg(efx, drv, efx->net_dev, 187 "\ttype %u, awaiting gen %u\n", i, 188 efx->tc->flush_gen[i]); 189 } 190 191 efx->tc->flush_counters = true; 192 193 /* Drain can take up to 2 seconds owing to FWRIVERHD-2884; whatever 194 * timeout we use, that delay is added to unload on nonresponsive 195 * hardware, so 2500ms seems like a reasonable compromise. 196 */ 197 if (!wait_event_timeout(efx->tc->flush_wq, 198 efx_mae_counters_flushed(efx->tc->flush_gen, 199 efx->tc->seen_gen), 200 msecs_to_jiffies(2500))) 201 netif_warn(efx, drv, efx->net_dev, 202 "Failed to drain counters RXQ, FW may be unhappy\n"); 203 204 efx->tc->flush_counters = false; 205 206 return rc; 207 } 208 209 void efx_mae_counters_grant_credits(struct work_struct *work) 210 { 211 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_LEN); 212 struct efx_rx_queue *rx_queue = container_of(work, struct efx_rx_queue, 213 grant_work); 214 struct efx_nic *efx = rx_queue->efx; 215 unsigned int credits; 216 217 BUILD_BUG_ON(MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_OUT_LEN); 218 credits = READ_ONCE(rx_queue->notified_count) - rx_queue->granted_count; 219 MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_NUM_CREDITS, 220 credits); 221 if (!efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS, 222 inbuf, sizeof(inbuf), NULL, 0, NULL)) 223 rx_queue->granted_count += credits; 224 } 225 226 static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps) 227 { 228 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN); 229 size_t outlen; 230 int rc; 231 232 BUILD_BUG_ON(MC_CMD_MAE_GET_CAPS_IN_LEN); 233 234 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_GET_CAPS, NULL, 0, outbuf, 235 sizeof(outbuf), &outlen); 236 if (rc) 237 return rc; 238 if (outlen < sizeof(outbuf)) 239 return -EIO; 240 caps->match_field_count = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT); 241 caps->action_prios = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ACTION_PRIOS); 242 return 0; 243 } 244 245 static int efx_mae_get_rule_fields(struct efx_nic *efx, u32 cmd, 246 u8 *field_support) 247 { 248 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(MAE_NUM_FIELDS)); 249 MCDI_DECLARE_STRUCT_PTR(caps); 250 unsigned int count; 251 size_t outlen; 252 int rc, i; 253 254 BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_IN_LEN); 255 256 rc = efx_mcdi_rpc(efx, cmd, NULL, 0, outbuf, sizeof(outbuf), &outlen); 257 if (rc) 258 return rc; 259 count = MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_COUNT); 260 memset(field_support, MAE_FIELD_UNSUPPORTED, MAE_NUM_FIELDS); 261 caps = _MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_FIELD_FLAGS); 262 /* We're only interested in the support status enum, not any other 263 * flags, so just extract that from each entry. 264 */ 265 for (i = 0; i < count; i++) 266 if (i * sizeof(*outbuf) + MC_CMD_MAE_GET_AR_CAPS_OUT_FIELD_FLAGS_OFST < outlen) 267 field_support[i] = EFX_DWORD_FIELD(caps[i], MAE_FIELD_FLAGS_SUPPORT_STATUS); 268 return 0; 269 } 270 271 int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps) 272 { 273 int rc; 274 275 rc = efx_mae_get_basic_caps(efx, caps); 276 if (rc) 277 return rc; 278 return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS, 279 caps->action_rule_fields); 280 } 281 282 /* Bit twiddling: 283 * Prefix: 1...110...0 284 * ~: 0...001...1 285 * + 1: 0...010...0 is power of two 286 * so (~x) & ((~x) + 1) == 0. Converse holds also. 287 */ 288 #define is_prefix_byte(_x) !(((_x) ^ 0xff) & (((_x) ^ 0xff) + 1)) 289 290 enum mask_type { MASK_ONES, MASK_ZEROES, MASK_PREFIX, MASK_OTHER }; 291 292 static const char *mask_type_name(enum mask_type typ) 293 { 294 switch (typ) { 295 case MASK_ONES: 296 return "all-1s"; 297 case MASK_ZEROES: 298 return "all-0s"; 299 case MASK_PREFIX: 300 return "prefix"; 301 case MASK_OTHER: 302 return "arbitrary"; 303 default: /* can't happen */ 304 return "unknown"; 305 } 306 } 307 308 /* Checks a (big-endian) bytestring is a bit prefix */ 309 static enum mask_type classify_mask(const u8 *mask, size_t len) 310 { 311 bool zeroes = true; /* All bits seen so far are zeroes */ 312 bool ones = true; /* All bits seen so far are ones */ 313 bool prefix = true; /* Valid prefix so far */ 314 size_t i; 315 316 for (i = 0; i < len; i++) { 317 if (ones) { 318 if (!is_prefix_byte(mask[i])) 319 prefix = false; 320 } else if (mask[i]) { 321 prefix = false; 322 } 323 if (mask[i] != 0xff) 324 ones = false; 325 if (mask[i]) 326 zeroes = false; 327 } 328 if (ones) 329 return MASK_ONES; 330 if (zeroes) 331 return MASK_ZEROES; 332 if (prefix) 333 return MASK_PREFIX; 334 return MASK_OTHER; 335 } 336 337 static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ) 338 { 339 switch (support) { 340 case MAE_FIELD_UNSUPPORTED: 341 case MAE_FIELD_SUPPORTED_MATCH_NEVER: 342 if (typ == MASK_ZEROES) 343 return 0; 344 return -EOPNOTSUPP; 345 case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL: 346 if (typ == MASK_ZEROES) 347 return 0; 348 fallthrough; 349 case MAE_FIELD_SUPPORTED_MATCH_ALWAYS: 350 if (typ == MASK_ONES) 351 return 0; 352 return -EINVAL; 353 case MAE_FIELD_SUPPORTED_MATCH_PREFIX: 354 if (typ == MASK_OTHER) 355 return -EOPNOTSUPP; 356 return 0; 357 case MAE_FIELD_SUPPORTED_MATCH_MASK: 358 return 0; 359 default: 360 return -EIO; 361 } 362 } 363 364 /* Validate field mask against hardware capabilities. Captures caller's 'rc' */ 365 #define CHECK(_mcdi, _field) ({ \ 366 enum mask_type typ = classify_mask((const u8 *)&mask->_field, \ 367 sizeof(mask->_field)); \ 368 \ 369 rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\ 370 typ); \ 371 if (rc) \ 372 NL_SET_ERR_MSG_FMT_MOD(extack, \ 373 "No support for %s mask in field %s", \ 374 mask_type_name(typ), #_field); \ 375 rc; \ 376 }) 377 /* Booleans need special handling */ 378 #define CHECK_BIT(_mcdi, _field) ({ \ 379 enum mask_type typ = mask->_field ? MASK_ONES : MASK_ZEROES; \ 380 \ 381 rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\ 382 typ); \ 383 if (rc) \ 384 NL_SET_ERR_MSG_FMT_MOD(extack, \ 385 "No support for %s mask in field %s", \ 386 mask_type_name(typ), #_field); \ 387 rc; \ 388 }) 389 390 int efx_mae_match_check_caps(struct efx_nic *efx, 391 const struct efx_tc_match_fields *mask, 392 struct netlink_ext_ack *extack) 393 { 394 const u8 *supported_fields = efx->tc->caps->action_rule_fields; 395 __be32 ingress_port = cpu_to_be32(mask->ingress_port); 396 enum mask_type ingress_port_mask_type; 397 int rc; 398 399 /* Check for _PREFIX assumes big-endian, so we need to convert */ 400 ingress_port_mask_type = classify_mask((const u8 *)&ingress_port, 401 sizeof(ingress_port)); 402 rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT], 403 ingress_port_mask_type); 404 if (rc) { 405 NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field ingress_port", 406 mask_type_name(ingress_port_mask_type)); 407 return rc; 408 } 409 if (CHECK(ETHER_TYPE, eth_proto) || 410 CHECK(VLAN0_TCI, vlan_tci[0]) || 411 CHECK(VLAN0_PROTO, vlan_proto[0]) || 412 CHECK(VLAN1_TCI, vlan_tci[1]) || 413 CHECK(VLAN1_PROTO, vlan_proto[1]) || 414 CHECK(ETH_SADDR, eth_saddr) || 415 CHECK(ETH_DADDR, eth_daddr) || 416 CHECK(IP_PROTO, ip_proto) || 417 CHECK(IP_TOS, ip_tos) || 418 CHECK(IP_TTL, ip_ttl) || 419 CHECK(SRC_IP4, src_ip) || 420 CHECK(DST_IP4, dst_ip) || 421 #ifdef CONFIG_IPV6 422 CHECK(SRC_IP6, src_ip6) || 423 CHECK(DST_IP6, dst_ip6) || 424 #endif 425 CHECK(L4_SPORT, l4_sport) || 426 CHECK(L4_DPORT, l4_dport) || 427 CHECK(TCP_FLAGS, tcp_flags) || 428 CHECK_BIT(IS_IP_FRAG, ip_frag) || 429 CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) || 430 CHECK(RECIRC_ID, recirc_id)) 431 return rc; 432 return 0; 433 } 434 #undef CHECK_BIT 435 #undef CHECK 436 437 int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt) 438 { 439 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTER_ALLOC_OUT_LEN(1)); 440 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTER_ALLOC_V2_IN_LEN); 441 size_t outlen; 442 int rc; 443 444 if (!cnt) 445 return -EINVAL; 446 447 MCDI_SET_DWORD(inbuf, MAE_COUNTER_ALLOC_V2_IN_REQUESTED_COUNT, 1); 448 MCDI_SET_DWORD(inbuf, MAE_COUNTER_ALLOC_V2_IN_COUNTER_TYPE, cnt->type); 449 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTER_ALLOC, inbuf, sizeof(inbuf), 450 outbuf, sizeof(outbuf), &outlen); 451 if (rc) 452 return rc; 453 /* pcol says this can't happen, since count is 1 */ 454 if (outlen < sizeof(outbuf)) 455 return -EIO; 456 cnt->fw_id = MCDI_DWORD(outbuf, MAE_COUNTER_ALLOC_OUT_COUNTER_ID); 457 cnt->gen = MCDI_DWORD(outbuf, MAE_COUNTER_ALLOC_OUT_GENERATION_COUNT); 458 return 0; 459 } 460 461 int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt) 462 { 463 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTER_FREE_OUT_LEN(1)); 464 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTER_FREE_V2_IN_LEN); 465 size_t outlen; 466 int rc; 467 468 MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_COUNTER_ID_COUNT, 1); 469 MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_FREE_COUNTER_ID, cnt->fw_id); 470 MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_COUNTER_TYPE, cnt->type); 471 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTER_FREE, inbuf, sizeof(inbuf), 472 outbuf, sizeof(outbuf), &outlen); 473 if (rc) 474 return rc; 475 /* pcol says this can't happen, since count is 1 */ 476 if (outlen < sizeof(outbuf)) 477 return -EIO; 478 /* FW freed a different ID than we asked for, should also never happen. 479 * Warn because it means we've now got a different idea to the FW of 480 * what counters exist, which could cause mayhem later. 481 */ 482 if (WARN_ON(MCDI_DWORD(outbuf, MAE_COUNTER_FREE_OUT_FREED_COUNTER_ID) != 483 cnt->fw_id)) 484 return -EIO; 485 return 0; 486 } 487 488 static bool efx_mae_asl_id(u32 id) 489 { 490 return !!(id & BIT(31)); 491 } 492 493 int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act) 494 { 495 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN); 496 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN); 497 size_t outlen; 498 int rc; 499 500 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID, 501 MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); 502 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID, 503 MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL); 504 if (act->count && !WARN_ON(!act->count->cnt)) 505 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID, 506 act->count->cnt->fw_id); 507 else 508 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID, 509 MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL); 510 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID, 511 MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL); 512 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID, 513 MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL); 514 if (act->deliver) 515 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER, 516 act->dest_mport); 517 BUILD_BUG_ON(MAE_MPORT_SELECTOR_NULL); 518 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_ALLOC, inbuf, sizeof(inbuf), 519 outbuf, sizeof(outbuf), &outlen); 520 if (rc) 521 return rc; 522 if (outlen < sizeof(outbuf)) 523 return -EIO; 524 act->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_ALLOC_OUT_AS_ID); 525 /* We rely on the high bit of AS IDs always being clear. 526 * The firmware API guarantees this, but let's check it ourselves. 527 */ 528 if (WARN_ON_ONCE(efx_mae_asl_id(act->fw_id))) { 529 efx_mae_free_action_set(efx, act->fw_id); 530 return -EIO; 531 } 532 return 0; 533 } 534 535 int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id) 536 { 537 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1)); 538 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1)); 539 size_t outlen; 540 int rc; 541 542 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_FREE_IN_AS_ID, fw_id); 543 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_FREE, inbuf, sizeof(inbuf), 544 outbuf, sizeof(outbuf), &outlen); 545 if (rc) 546 return rc; 547 if (outlen < sizeof(outbuf)) 548 return -EIO; 549 /* FW freed a different ID than we asked for, should never happen. 550 * Warn because it means we've now got a different idea to the FW of 551 * what action-sets exist, which could cause mayhem later. 552 */ 553 if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) != fw_id)) 554 return -EIO; 555 return 0; 556 } 557 558 int efx_mae_alloc_action_set_list(struct efx_nic *efx, 559 struct efx_tc_action_set_list *acts) 560 { 561 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN); 562 struct efx_tc_action_set *act; 563 size_t inlen, outlen, i = 0; 564 efx_dword_t *inbuf; 565 int rc; 566 567 list_for_each_entry(act, &acts->list, list) 568 i++; 569 if (i == 0) 570 return -EINVAL; 571 if (i == 1) { 572 /* Don't wrap an ASL around a single AS, just use the AS_ID 573 * directly. ASLs are a more limited resource. 574 */ 575 act = list_first_entry(&acts->list, struct efx_tc_action_set, list); 576 acts->fw_id = act->fw_id; 577 return 0; 578 } 579 if (i > MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2) 580 return -EOPNOTSUPP; /* Too many actions */ 581 inlen = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(i); 582 inbuf = kzalloc(inlen, GFP_KERNEL); 583 if (!inbuf) 584 return -ENOMEM; 585 i = 0; 586 list_for_each_entry(act, &acts->list, list) { 587 MCDI_SET_ARRAY_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS, 588 i, act->fw_id); 589 i++; 590 } 591 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, i); 592 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_ALLOC, inbuf, inlen, 593 outbuf, sizeof(outbuf), &outlen); 594 if (rc) 595 goto out_free; 596 if (outlen < sizeof(outbuf)) { 597 rc = -EIO; 598 goto out_free; 599 } 600 acts->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID); 601 /* We rely on the high bit of ASL IDs always being set. 602 * The firmware API guarantees this, but let's check it ourselves. 603 */ 604 if (WARN_ON_ONCE(!efx_mae_asl_id(acts->fw_id))) { 605 efx_mae_free_action_set_list(efx, acts); 606 rc = -EIO; 607 } 608 out_free: 609 kfree(inbuf); 610 return rc; 611 } 612 613 int efx_mae_free_action_set_list(struct efx_nic *efx, 614 struct efx_tc_action_set_list *acts) 615 { 616 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1)); 617 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1)); 618 size_t outlen; 619 int rc; 620 621 /* If this is just an AS_ID with no ASL wrapper, then there is 622 * nothing for us to free. (The AS will be freed later.) 623 */ 624 if (efx_mae_asl_id(acts->fw_id)) { 625 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_FREE_IN_ASL_ID, 626 acts->fw_id); 627 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_FREE, inbuf, 628 sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); 629 if (rc) 630 return rc; 631 if (outlen < sizeof(outbuf)) 632 return -EIO; 633 /* FW freed a different ID than we asked for, should never happen. 634 * Warn because it means we've now got a different idea to the FW of 635 * what action-set-lists exist, which could cause mayhem later. 636 */ 637 if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) != acts->fw_id)) 638 return -EIO; 639 } 640 /* We're probably about to free @acts, but let's just make sure its 641 * fw_id is blatted so that it won't look valid if it leaks out. 642 */ 643 acts->fw_id = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL; 644 return 0; 645 } 646 647 static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit), 648 const struct efx_tc_match *match) 649 { 650 if (match->mask.ingress_port) { 651 if (~match->mask.ingress_port) 652 return -EOPNOTSUPP; 653 MCDI_STRUCT_SET_DWORD(match_crit, 654 MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR, 655 match->value.ingress_port); 656 } 657 MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK, 658 match->mask.ingress_port); 659 EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS), 660 MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG, 661 match->value.ip_frag, 662 MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG, 663 match->value.ip_firstfrag); 664 EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_MASK), 665 MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG, 666 match->mask.ip_frag, 667 MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG, 668 match->mask.ip_firstfrag); 669 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID, 670 match->value.recirc_id); 671 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK, 672 match->mask.recirc_id); 673 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE, 674 match->value.eth_proto); 675 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE_MASK, 676 match->mask.eth_proto); 677 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE, 678 match->value.vlan_tci[0]); 679 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE_MASK, 680 match->mask.vlan_tci[0]); 681 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE, 682 match->value.vlan_proto[0]); 683 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE_MASK, 684 match->mask.vlan_proto[0]); 685 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE, 686 match->value.vlan_tci[1]); 687 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE_MASK, 688 match->mask.vlan_tci[1]); 689 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE, 690 match->value.vlan_proto[1]); 691 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE_MASK, 692 match->mask.vlan_proto[1]); 693 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE), 694 match->value.eth_saddr, ETH_ALEN); 695 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE_MASK), 696 match->mask.eth_saddr, ETH_ALEN); 697 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE), 698 match->value.eth_daddr, ETH_ALEN); 699 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE_MASK), 700 match->mask.eth_daddr, ETH_ALEN); 701 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO, 702 match->value.ip_proto); 703 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO_MASK, 704 match->mask.ip_proto); 705 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS, 706 match->value.ip_tos); 707 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS_MASK, 708 match->mask.ip_tos); 709 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL, 710 match->value.ip_ttl); 711 MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL_MASK, 712 match->mask.ip_ttl); 713 MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE, 714 match->value.src_ip); 715 MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE_MASK, 716 match->mask.src_ip); 717 MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE, 718 match->value.dst_ip); 719 MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE_MASK, 720 match->mask.dst_ip); 721 #ifdef CONFIG_IPV6 722 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE), 723 &match->value.src_ip6, sizeof(struct in6_addr)); 724 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE_MASK), 725 &match->mask.src_ip6, sizeof(struct in6_addr)); 726 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE), 727 &match->value.dst_ip6, sizeof(struct in6_addr)); 728 memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE_MASK), 729 &match->mask.dst_ip6, sizeof(struct in6_addr)); 730 #endif 731 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE, 732 match->value.l4_sport); 733 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE_MASK, 734 match->mask.l4_sport); 735 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE, 736 match->value.l4_dport); 737 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE_MASK, 738 match->mask.l4_dport); 739 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE, 740 match->value.tcp_flags); 741 MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK, 742 match->mask.tcp_flags); 743 return 0; 744 } 745 746 int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match, 747 u32 prio, u32 acts_id, u32 *id) 748 { 749 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_INSERT_IN_LEN(MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN)); 750 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN); 751 MCDI_DECLARE_STRUCT_PTR(match_crit); 752 MCDI_DECLARE_STRUCT_PTR(response); 753 size_t outlen; 754 int rc; 755 756 if (!id) 757 return -EINVAL; 758 759 match_crit = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA); 760 response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_RESPONSE); 761 if (efx_mae_asl_id(acts_id)) { 762 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id); 763 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, 764 MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL); 765 } else { 766 /* We only had one AS, so we didn't wrap it in an ASL */ 767 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, 768 MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL); 769 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id); 770 } 771 MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_PRIO, prio); 772 rc = efx_mae_populate_match_criteria(match_crit, match); 773 if (rc) 774 return rc; 775 776 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_INSERT, inbuf, sizeof(inbuf), 777 outbuf, sizeof(outbuf), &outlen); 778 if (rc) 779 return rc; 780 if (outlen < sizeof(outbuf)) 781 return -EIO; 782 *id = MCDI_DWORD(outbuf, MAE_ACTION_RULE_INSERT_OUT_AR_ID); 783 return 0; 784 } 785 786 int efx_mae_delete_rule(struct efx_nic *efx, u32 id) 787 { 788 MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1)); 789 MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1)); 790 size_t outlen; 791 int rc; 792 793 MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_DELETE_IN_AR_ID, id); 794 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_DELETE, inbuf, sizeof(inbuf), 795 outbuf, sizeof(outbuf), &outlen); 796 if (rc) 797 return rc; 798 if (outlen < sizeof(outbuf)) 799 return -EIO; 800 /* FW freed a different ID than we asked for, should also never happen. 801 * Warn because it means we've now got a different idea to the FW of 802 * what rules exist, which could cause mayhem later. 803 */ 804 if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) != id)) 805 return -EIO; 806 return 0; 807 } 808