efx_filter.c revision 294017
1/*- 2 * Copyright (c) 2007-2015 Solarflare Communications Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * The views and conclusions contained in the software and documentation are 27 * those of the authors and should not be interpreted as representing official 28 * policies, either expressed or implied, of the FreeBSD Project. 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: stable/10/sys/dev/sfxge/common/efx_filter.c 294017 2016-01-14 16:28:21Z arybchik $"); 33 34#include "efx.h" 35#include "efx_impl.h" 36 37 38#if EFSYS_OPT_FILTER 39 40#if EFSYS_OPT_FALCON || EFSYS_OPT_SIENA 41 42static __checkReturn efx_rc_t 43falconsiena_filter_init( 44 __in efx_nic_t *enp); 45 46static void 47falconsiena_filter_fini( 48 __in efx_nic_t *enp); 49 50static __checkReturn efx_rc_t 51falconsiena_filter_restore( 52 __in efx_nic_t *enp); 53 54static __checkReturn efx_rc_t 55falconsiena_filter_add( 56 __in efx_nic_t *enp, 57 __inout efx_filter_spec_t *spec, 58 __in boolean_t may_replace); 59 60static __checkReturn efx_rc_t 61falconsiena_filter_delete( 62 __in efx_nic_t *enp, 63 __inout efx_filter_spec_t *spec); 64 65static __checkReturn efx_rc_t 66falconsiena_filter_supported_filters( 67 __in efx_nic_t *enp, 68 __out uint32_t *list, 69 __out size_t *length); 70 71#endif /* EFSYS_OPT_FALCON || EFSYS_OPT_SIENA */ 72 73#if EFSYS_OPT_FALCON 74static efx_filter_ops_t __efx_filter_falcon_ops = { 75 falconsiena_filter_init, /* efo_init */ 76 falconsiena_filter_fini, /* efo_fini */ 77 falconsiena_filter_restore, /* efo_restore */ 78 falconsiena_filter_add, /* efo_add */ 79 falconsiena_filter_delete, /* efo_delete */ 80 falconsiena_filter_supported_filters, /* efo_supported_filters */ 81 NULL, /* efo_reconfigure */ 82}; 83#endif /* EFSYS_OPT_FALCON */ 84 85#if EFSYS_OPT_SIENA 86static efx_filter_ops_t __efx_filter_siena_ops = { 87 falconsiena_filter_init, /* efo_init */ 88 falconsiena_filter_fini, /* efo_fini */ 89 falconsiena_filter_restore, /* efo_restore */ 90 falconsiena_filter_add, /* efo_add */ 91 falconsiena_filter_delete, /* efo_delete */ 92 falconsiena_filter_supported_filters, /* efo_supported_filters */ 93 NULL, /* efo_reconfigure */ 94}; 95#endif /* EFSYS_OPT_SIENA */ 96 97#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 98static efx_filter_ops_t __efx_filter_ef10_ops = { 99 ef10_filter_init, /* efo_init */ 100 ef10_filter_fini, /* efo_fini */ 101 ef10_filter_restore, /* efo_restore */ 102 ef10_filter_add, /* efo_add */ 103 ef10_filter_delete, /* efo_delete */ 104 ef10_filter_supported_filters, /* efo_supported_filters */ 105 ef10_filter_reconfigure, /* efo_reconfigure */ 106}; 107#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */ 108 109 __checkReturn efx_rc_t 110efx_filter_insert( 111 __in efx_nic_t *enp, 112 __inout efx_filter_spec_t *spec) 113{ 114 efx_filter_ops_t *efop = enp->en_efop; 115 116 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER); 117 EFSYS_ASSERT3P(spec, !=, NULL); 118 EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX); 119 120 return (efop->efo_add(enp, spec, B_FALSE)); 121} 122 123 __checkReturn efx_rc_t 124efx_filter_remove( 125 __in efx_nic_t *enp, 126 __inout efx_filter_spec_t *spec) 127{ 128 efx_filter_ops_t *efop = enp->en_efop; 129 130 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER); 131 EFSYS_ASSERT3P(spec, !=, NULL); 132 EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX); 133 134#if EFSYS_OPT_RX_SCALE 135 spec->efs_rss_context = enp->en_rss_context; 136#endif 137 138 return (efop->efo_delete(enp, spec)); 139} 140 141 __checkReturn efx_rc_t 142efx_filter_restore( 143 __in efx_nic_t *enp) 144{ 145 efx_rc_t rc; 146 147 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER); 148 149 if ((rc = enp->en_efop->efo_restore(enp)) != 0) 150 goto fail1; 151 152 return (0); 153 154fail1: 155 EFSYS_PROBE1(fail1, efx_rc_t, rc); 156 157 return (rc); 158} 159 160 __checkReturn efx_rc_t 161efx_filter_init( 162 __in efx_nic_t *enp) 163{ 164 efx_filter_ops_t *efop; 165 efx_rc_t rc; 166 167 /* Check that efx_filter_spec_t is 64 bytes. */ 168 EFX_STATIC_ASSERT(sizeof (efx_filter_spec_t) == 64); 169 170 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 171 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 172 EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_FILTER)); 173 174 switch (enp->en_family) { 175#if EFSYS_OPT_FALCON 176 case EFX_FAMILY_FALCON: 177 efop = (efx_filter_ops_t *)&__efx_filter_falcon_ops; 178 break; 179#endif /* EFSYS_OPT_FALCON */ 180 181#if EFSYS_OPT_SIENA 182 case EFX_FAMILY_SIENA: 183 efop = (efx_filter_ops_t *)&__efx_filter_siena_ops; 184 break; 185#endif /* EFSYS_OPT_SIENA */ 186 187#if EFSYS_OPT_HUNTINGTON 188 case EFX_FAMILY_HUNTINGTON: 189 efop = (efx_filter_ops_t *)&__efx_filter_ef10_ops; 190 break; 191#endif /* EFSYS_OPT_HUNTINGTON */ 192 193#if EFSYS_OPT_MEDFORD 194 case EFX_FAMILY_MEDFORD: 195 efop = (efx_filter_ops_t *)&__efx_filter_ef10_ops; 196 break; 197#endif /* EFSYS_OPT_MEDFORD */ 198 199 default: 200 EFSYS_ASSERT(0); 201 rc = ENOTSUP; 202 goto fail1; 203 } 204 205 if ((rc = efop->efo_init(enp)) != 0) 206 goto fail2; 207 208 enp->en_efop = efop; 209 enp->en_mod_flags |= EFX_MOD_FILTER; 210 return (0); 211 212fail2: 213 EFSYS_PROBE(fail2); 214fail1: 215 EFSYS_PROBE1(fail1, efx_rc_t, rc); 216 217 enp->en_efop = NULL; 218 enp->en_mod_flags &= ~EFX_MOD_FILTER; 219 return (rc); 220} 221 222 void 223efx_filter_fini( 224 __in efx_nic_t *enp) 225{ 226 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 227 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 228 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER); 229 230 enp->en_efop->efo_fini(enp); 231 232 enp->en_efop = NULL; 233 enp->en_mod_flags &= ~EFX_MOD_FILTER; 234} 235 236 __checkReturn efx_rc_t 237efx_filter_supported_filters( 238 __in efx_nic_t *enp, 239 __out uint32_t *list, 240 __out size_t *length) 241{ 242 efx_rc_t rc; 243 244 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 245 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 246 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER); 247 EFSYS_ASSERT(enp->en_efop->efo_supported_filters != NULL); 248 249 if ((rc = enp->en_efop->efo_supported_filters(enp, list, length)) != 0) 250 goto fail1; 251 252 return (0); 253 254fail1: 255 EFSYS_PROBE1(fail1, efx_rc_t, rc); 256 257 return (rc); 258} 259 260 __checkReturn efx_rc_t 261efx_filter_reconfigure( 262 __in efx_nic_t *enp, 263 __in_ecount(6) uint8_t const *mac_addr, 264 __in boolean_t all_unicst, 265 __in boolean_t mulcst, 266 __in boolean_t all_mulcst, 267 __in boolean_t brdcst, 268 __in_ecount(6*count) uint8_t const *addrs, 269 __in int count) 270{ 271 efx_rc_t rc; 272 273 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 274 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 275 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER); 276 277 if (enp->en_efop->efo_reconfigure != NULL) { 278 if ((rc = enp->en_efop->efo_reconfigure(enp, mac_addr, 279 all_unicst, mulcst, 280 all_mulcst, brdcst, 281 addrs, count)) != 0) 282 goto fail1; 283 } 284 285 return (0); 286 287fail1: 288 EFSYS_PROBE1(fail1, efx_rc_t, rc); 289 290 return (rc); 291} 292 293 void 294efx_filter_spec_init_rx( 295 __out efx_filter_spec_t *spec, 296 __in efx_filter_priority_t priority, 297 __in efx_filter_flag_t flags, 298 __in efx_rxq_t *erp) 299{ 300 EFSYS_ASSERT3P(spec, !=, NULL); 301 EFSYS_ASSERT3P(erp, !=, NULL); 302 EFSYS_ASSERT((flags & ~(EFX_FILTER_FLAG_RX_RSS | 303 EFX_FILTER_FLAG_RX_SCATTER)) == 0); 304 305 memset(spec, 0, sizeof (*spec)); 306 spec->efs_priority = priority; 307 spec->efs_flags = EFX_FILTER_FLAG_RX | flags; 308 spec->efs_rss_context = EFX_FILTER_SPEC_RSS_CONTEXT_DEFAULT; 309 spec->efs_dmaq_id = (uint16_t)erp->er_index; 310} 311 312 void 313efx_filter_spec_init_tx( 314 __out efx_filter_spec_t *spec, 315 __in efx_txq_t *etp) 316{ 317 EFSYS_ASSERT3P(spec, !=, NULL); 318 EFSYS_ASSERT3P(etp, !=, NULL); 319 320 memset(spec, 0, sizeof (*spec)); 321 spec->efs_priority = EFX_FILTER_PRI_REQUIRED; 322 spec->efs_flags = EFX_FILTER_FLAG_TX; 323 spec->efs_dmaq_id = (uint16_t)etp->et_index; 324} 325 326 327/* 328 * Specify IPv4 host, transport protocol and port in a filter specification 329 */ 330__checkReturn efx_rc_t 331efx_filter_spec_set_ipv4_local( 332 __inout efx_filter_spec_t *spec, 333 __in uint8_t proto, 334 __in uint32_t host, 335 __in uint16_t port) 336{ 337 EFSYS_ASSERT3P(spec, !=, NULL); 338 339 spec->efs_match_flags |= 340 EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO | 341 EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT; 342 spec->efs_ether_type = EFX_ETHER_TYPE_IPV4; 343 spec->efs_ip_proto = proto; 344 spec->efs_loc_host.eo_u32[0] = host; 345 spec->efs_loc_port = port; 346 return (0); 347} 348 349/* 350 * Specify IPv4 hosts, transport protocol and ports in a filter specification 351 */ 352__checkReturn efx_rc_t 353efx_filter_spec_set_ipv4_full( 354 __inout efx_filter_spec_t *spec, 355 __in uint8_t proto, 356 __in uint32_t lhost, 357 __in uint16_t lport, 358 __in uint32_t rhost, 359 __in uint16_t rport) 360{ 361 EFSYS_ASSERT3P(spec, !=, NULL); 362 363 spec->efs_match_flags |= 364 EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO | 365 EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT | 366 EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT; 367 spec->efs_ether_type = EFX_ETHER_TYPE_IPV4; 368 spec->efs_ip_proto = proto; 369 spec->efs_loc_host.eo_u32[0] = lhost; 370 spec->efs_loc_port = lport; 371 spec->efs_rem_host.eo_u32[0] = rhost; 372 spec->efs_rem_port = rport; 373 return (0); 374} 375 376/* 377 * Specify local Ethernet address and/or VID in filter specification 378 */ 379__checkReturn efx_rc_t 380efx_filter_spec_set_eth_local( 381 __inout efx_filter_spec_t *spec, 382 __in uint16_t vid, 383 __in const uint8_t *addr) 384{ 385 EFSYS_ASSERT3P(spec, !=, NULL); 386 EFSYS_ASSERT3P(addr, !=, NULL); 387 388 if (vid == EFX_FILTER_SPEC_VID_UNSPEC && addr == NULL) 389 return (EINVAL); 390 391 if (vid != EFX_FILTER_SPEC_VID_UNSPEC) { 392 spec->efs_match_flags |= EFX_FILTER_MATCH_OUTER_VID; 393 spec->efs_outer_vid = vid; 394 } 395 if (addr != NULL) { 396 spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC; 397 memcpy(spec->efs_loc_mac, addr, EFX_MAC_ADDR_LEN); 398 } 399 return (0); 400} 401 402/* 403 * Specify matching otherwise-unmatched unicast in a filter specification 404 */ 405__checkReturn efx_rc_t 406efx_filter_spec_set_uc_def( 407 __inout efx_filter_spec_t *spec) 408{ 409 EFSYS_ASSERT3P(spec, !=, NULL); 410 411 spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG; 412 return (0); 413} 414 415/* 416 * Specify matching otherwise-unmatched multicast in a filter specification 417 */ 418__checkReturn efx_rc_t 419efx_filter_spec_set_mc_def( 420 __inout efx_filter_spec_t *spec) 421{ 422 EFSYS_ASSERT3P(spec, !=, NULL); 423 424 spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG; 425 spec->efs_loc_mac[0] = 1; 426 return (0); 427} 428 429 430 431#if EFSYS_OPT_FALCON || EFSYS_OPT_SIENA 432 433/* 434 * "Fudge factors" - difference between programmed value and actual depth. 435 * Due to pipelined implementation we need to program H/W with a value that 436 * is larger than the hop limit we want. 437 */ 438#define FILTER_CTL_SRCH_FUDGE_WILD 3 439#define FILTER_CTL_SRCH_FUDGE_FULL 1 440 441/* 442 * Hard maximum hop limit. Hardware will time-out beyond 200-something. 443 * We also need to avoid infinite loops in efx_filter_search() when the 444 * table is full. 445 */ 446#define FILTER_CTL_SRCH_MAX 200 447 448static __checkReturn efx_rc_t 449falconsiena_filter_spec_from_gen_spec( 450 __out falconsiena_filter_spec_t *fs_spec, 451 __in efx_filter_spec_t *gen_spec) 452{ 453 efx_rc_t rc; 454 boolean_t is_full = B_FALSE; 455 456 if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) 457 EFSYS_ASSERT3U(gen_spec->efs_flags, ==, EFX_FILTER_FLAG_TX); 458 else 459 EFSYS_ASSERT3U(gen_spec->efs_flags, &, EFX_FILTER_FLAG_RX); 460 461 /* Falconsiena only has one RSS context */ 462 if ((gen_spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) && 463 gen_spec->efs_rss_context != 0) { 464 rc = EINVAL; 465 goto fail1; 466 } 467 468 fs_spec->fsfs_flags = gen_spec->efs_flags; 469 fs_spec->fsfs_dmaq_id = gen_spec->efs_dmaq_id; 470 471 switch (gen_spec->efs_match_flags) { 472 case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO | 473 EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT | 474 EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT: 475 is_full = B_TRUE; 476 /* Fall through */ 477 case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO | 478 EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT: { 479 uint32_t rhost, host1, host2; 480 uint16_t rport, port1, port2; 481 482 if (gen_spec->efs_ether_type != EFX_ETHER_TYPE_IPV4) { 483 rc = ENOTSUP; 484 goto fail2; 485 } 486 if (gen_spec->efs_loc_port == 0 || 487 (is_full && gen_spec->efs_rem_port == 0)) { 488 rc = EINVAL; 489 goto fail3; 490 } 491 switch (gen_spec->efs_ip_proto) { 492 case EFX_IPPROTO_TCP: 493 if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) { 494 fs_spec->fsfs_type = (is_full ? 495 EFX_FS_FILTER_TX_TCP_FULL : 496 EFX_FS_FILTER_TX_TCP_WILD); 497 } else { 498 fs_spec->fsfs_type = (is_full ? 499 EFX_FS_FILTER_RX_TCP_FULL : 500 EFX_FS_FILTER_RX_TCP_WILD); 501 } 502 break; 503 case EFX_IPPROTO_UDP: 504 if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) { 505 fs_spec->fsfs_type = (is_full ? 506 EFX_FS_FILTER_TX_UDP_FULL : 507 EFX_FS_FILTER_TX_UDP_WILD); 508 } else { 509 fs_spec->fsfs_type = (is_full ? 510 EFX_FS_FILTER_RX_UDP_FULL : 511 EFX_FS_FILTER_RX_UDP_WILD); 512 } 513 break; 514 default: 515 rc = ENOTSUP; 516 goto fail4; 517 } 518 /* 519 * The filter is constructed in terms of source and destination, 520 * with the odd wrinkle that the ports are swapped in a UDP 521 * wildcard filter. We need to convert from local and remote 522 * addresses (zero for a wildcard). 523 */ 524 rhost = is_full ? gen_spec->efs_rem_host.eo_u32[0] : 0; 525 rport = is_full ? gen_spec->efs_rem_port : 0; 526 if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) { 527 host1 = gen_spec->efs_loc_host.eo_u32[0]; 528 host2 = rhost; 529 } else { 530 host1 = rhost; 531 host2 = gen_spec->efs_loc_host.eo_u32[0]; 532 } 533 if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) { 534 if (fs_spec->fsfs_type == EFX_FS_FILTER_TX_UDP_WILD) { 535 port1 = rport; 536 port2 = gen_spec->efs_loc_port; 537 } else { 538 port1 = gen_spec->efs_loc_port; 539 port2 = rport; 540 } 541 } else { 542 if (fs_spec->fsfs_type == EFX_FS_FILTER_RX_UDP_WILD) { 543 port1 = gen_spec->efs_loc_port; 544 port2 = rport; 545 } else { 546 port1 = rport; 547 port2 = gen_spec->efs_loc_port; 548 } 549 } 550 fs_spec->fsfs_dword[0] = (host1 << 16) | port1; 551 fs_spec->fsfs_dword[1] = (port2 << 16) | (host1 >> 16); 552 fs_spec->fsfs_dword[2] = host2; 553 break; 554 } 555 556 case EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_OUTER_VID: 557 is_full = B_TRUE; 558 /* Fall through */ 559 case EFX_FILTER_MATCH_LOC_MAC: 560 if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) { 561 fs_spec->fsfs_type = (is_full ? 562 EFX_FS_FILTER_TX_MAC_FULL : 563 EFX_FS_FILTER_TX_MAC_WILD); 564 } else { 565 fs_spec->fsfs_type = (is_full ? 566 EFX_FS_FILTER_RX_MAC_FULL : 567 EFX_FS_FILTER_RX_MAC_WILD); 568 } 569 fs_spec->fsfs_dword[0] = is_full ? gen_spec->efs_outer_vid : 0; 570 fs_spec->fsfs_dword[1] = 571 gen_spec->efs_loc_mac[2] << 24 | 572 gen_spec->efs_loc_mac[3] << 16 | 573 gen_spec->efs_loc_mac[4] << 8 | 574 gen_spec->efs_loc_mac[5]; 575 fs_spec->fsfs_dword[2] = 576 gen_spec->efs_loc_mac[0] << 8 | 577 gen_spec->efs_loc_mac[1]; 578 break; 579 580 default: 581 EFSYS_ASSERT(B_FALSE); 582 rc = ENOTSUP; 583 goto fail5; 584 } 585 586 return (0); 587 588fail5: 589 EFSYS_PROBE(fail5); 590fail4: 591 EFSYS_PROBE(fail4); 592fail3: 593 EFSYS_PROBE(fail3); 594fail2: 595 EFSYS_PROBE(fail2); 596fail1: 597 EFSYS_PROBE1(fail1, efx_rc_t, rc); 598 599 return (rc); 600} 601 602/* 603 * The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit 604 * key derived from the n-tuple. 605 */ 606static uint16_t 607falconsiena_filter_tbl_hash( 608 __in uint32_t key) 609{ 610 uint16_t tmp; 611 612 /* First 16 rounds */ 613 tmp = 0x1fff ^ (uint16_t)(key >> 16); 614 tmp = tmp ^ tmp >> 3 ^ tmp >> 6; 615 tmp = tmp ^ tmp >> 9; 616 617 /* Last 16 rounds */ 618 tmp = tmp ^ tmp << 13 ^ (uint16_t)(key & 0xffff); 619 tmp = tmp ^ tmp >> 3 ^ tmp >> 6; 620 tmp = tmp ^ tmp >> 9; 621 622 return (tmp); 623} 624 625/* 626 * To allow for hash collisions, filter search continues at these 627 * increments from the first possible entry selected by the hash. 628 */ 629static uint16_t 630falconsiena_filter_tbl_increment( 631 __in uint32_t key) 632{ 633 return ((uint16_t)(key * 2 - 1)); 634} 635 636static __checkReturn boolean_t 637falconsiena_filter_test_used( 638 __in falconsiena_filter_tbl_t *fsftp, 639 __in unsigned int index) 640{ 641 EFSYS_ASSERT3P(fsftp->fsft_bitmap, !=, NULL); 642 return ((fsftp->fsft_bitmap[index / 32] & (1 << (index % 32))) != 0); 643} 644 645static void 646falconsiena_filter_set_used( 647 __in falconsiena_filter_tbl_t *fsftp, 648 __in unsigned int index) 649{ 650 EFSYS_ASSERT3P(fsftp->fsft_bitmap, !=, NULL); 651 fsftp->fsft_bitmap[index / 32] |= (1 << (index % 32)); 652 ++fsftp->fsft_used; 653} 654 655static void 656falconsiena_filter_clear_used( 657 __in falconsiena_filter_tbl_t *fsftp, 658 __in unsigned int index) 659{ 660 EFSYS_ASSERT3P(fsftp->fsft_bitmap, !=, NULL); 661 fsftp->fsft_bitmap[index / 32] &= ~(1 << (index % 32)); 662 663 --fsftp->fsft_used; 664 EFSYS_ASSERT3U(fsftp->fsft_used, >=, 0); 665} 666 667 668static falconsiena_filter_tbl_id_t 669falconsiena_filter_tbl_id( 670 __in falconsiena_filter_type_t type) 671{ 672 falconsiena_filter_tbl_id_t tbl_id; 673 674 switch (type) { 675 case EFX_FS_FILTER_RX_TCP_FULL: 676 case EFX_FS_FILTER_RX_TCP_WILD: 677 case EFX_FS_FILTER_RX_UDP_FULL: 678 case EFX_FS_FILTER_RX_UDP_WILD: 679 tbl_id = EFX_FS_FILTER_TBL_RX_IP; 680 break; 681 682#if EFSYS_OPT_SIENA 683 case EFX_FS_FILTER_RX_MAC_FULL: 684 case EFX_FS_FILTER_RX_MAC_WILD: 685 tbl_id = EFX_FS_FILTER_TBL_RX_MAC; 686 break; 687 688 case EFX_FS_FILTER_TX_TCP_FULL: 689 case EFX_FS_FILTER_TX_TCP_WILD: 690 case EFX_FS_FILTER_TX_UDP_FULL: 691 case EFX_FS_FILTER_TX_UDP_WILD: 692 tbl_id = EFX_FS_FILTER_TBL_TX_IP; 693 break; 694 695 case EFX_FS_FILTER_TX_MAC_FULL: 696 case EFX_FS_FILTER_TX_MAC_WILD: 697 tbl_id = EFX_FS_FILTER_TBL_TX_MAC; 698 break; 699#endif /* EFSYS_OPT_SIENA */ 700 701 default: 702 EFSYS_ASSERT(B_FALSE); 703 tbl_id = EFX_FS_FILTER_NTBLS; 704 break; 705 } 706 return (tbl_id); 707} 708 709static void 710falconsiena_filter_reset_search_depth( 711 __inout falconsiena_filter_t *fsfp, 712 __in falconsiena_filter_tbl_id_t tbl_id) 713{ 714 switch (tbl_id) { 715 case EFX_FS_FILTER_TBL_RX_IP: 716 fsfp->fsf_depth[EFX_FS_FILTER_RX_TCP_FULL] = 0; 717 fsfp->fsf_depth[EFX_FS_FILTER_RX_TCP_WILD] = 0; 718 fsfp->fsf_depth[EFX_FS_FILTER_RX_UDP_FULL] = 0; 719 fsfp->fsf_depth[EFX_FS_FILTER_RX_UDP_WILD] = 0; 720 break; 721 722#if EFSYS_OPT_SIENA 723 case EFX_FS_FILTER_TBL_RX_MAC: 724 fsfp->fsf_depth[EFX_FS_FILTER_RX_MAC_FULL] = 0; 725 fsfp->fsf_depth[EFX_FS_FILTER_RX_MAC_WILD] = 0; 726 break; 727 728 case EFX_FS_FILTER_TBL_TX_IP: 729 fsfp->fsf_depth[EFX_FS_FILTER_TX_TCP_FULL] = 0; 730 fsfp->fsf_depth[EFX_FS_FILTER_TX_TCP_WILD] = 0; 731 fsfp->fsf_depth[EFX_FS_FILTER_TX_UDP_FULL] = 0; 732 fsfp->fsf_depth[EFX_FS_FILTER_TX_UDP_WILD] = 0; 733 break; 734 735 case EFX_FS_FILTER_TBL_TX_MAC: 736 fsfp->fsf_depth[EFX_FS_FILTER_TX_MAC_FULL] = 0; 737 fsfp->fsf_depth[EFX_FS_FILTER_TX_MAC_WILD] = 0; 738 break; 739#endif /* EFSYS_OPT_SIENA */ 740 741 default: 742 EFSYS_ASSERT(B_FALSE); 743 break; 744 } 745} 746 747static void 748falconsiena_filter_push_rx_limits( 749 __in efx_nic_t *enp) 750{ 751 falconsiena_filter_t *fsfp = enp->en_filter.ef_falconsiena_filter; 752 efx_oword_t oword; 753 754 EFX_BAR_READO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword); 755 756 EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_FULL_SRCH_LIMIT, 757 fsfp->fsf_depth[EFX_FS_FILTER_RX_TCP_FULL] + 758 FILTER_CTL_SRCH_FUDGE_FULL); 759 EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_WILD_SRCH_LIMIT, 760 fsfp->fsf_depth[EFX_FS_FILTER_RX_TCP_WILD] + 761 FILTER_CTL_SRCH_FUDGE_WILD); 762 EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_FULL_SRCH_LIMIT, 763 fsfp->fsf_depth[EFX_FS_FILTER_RX_UDP_FULL] + 764 FILTER_CTL_SRCH_FUDGE_FULL); 765 EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_WILD_SRCH_LIMIT, 766 fsfp->fsf_depth[EFX_FS_FILTER_RX_UDP_WILD] + 767 FILTER_CTL_SRCH_FUDGE_WILD); 768 769#if EFSYS_OPT_SIENA 770 if (fsfp->fsf_tbl[EFX_FS_FILTER_TBL_RX_MAC].fsft_size) { 771 EFX_SET_OWORD_FIELD(oword, 772 FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT, 773 fsfp->fsf_depth[EFX_FS_FILTER_RX_MAC_FULL] + 774 FILTER_CTL_SRCH_FUDGE_FULL); 775 EFX_SET_OWORD_FIELD(oword, 776 FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT, 777 fsfp->fsf_depth[EFX_FS_FILTER_RX_MAC_WILD] + 778 FILTER_CTL_SRCH_FUDGE_WILD); 779 } 780#endif /* EFSYS_OPT_SIENA */ 781 782 EFX_BAR_WRITEO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword); 783} 784 785static void 786falconsiena_filter_push_tx_limits( 787 __in efx_nic_t *enp) 788{ 789 falconsiena_filter_t *fsfp = enp->en_filter.ef_falconsiena_filter; 790 efx_oword_t oword; 791 792 EFX_BAR_READO(enp, FR_AZ_TX_CFG_REG, &oword); 793 794 if (fsfp->fsf_tbl[EFX_FS_FILTER_TBL_TX_IP].fsft_size != 0) { 795 EFX_SET_OWORD_FIELD(oword, 796 FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE, 797 fsfp->fsf_depth[EFX_FS_FILTER_TX_TCP_FULL] + 798 FILTER_CTL_SRCH_FUDGE_FULL); 799 EFX_SET_OWORD_FIELD(oword, 800 FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE, 801 fsfp->fsf_depth[EFX_FS_FILTER_TX_TCP_WILD] + 802 FILTER_CTL_SRCH_FUDGE_WILD); 803 EFX_SET_OWORD_FIELD(oword, 804 FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE, 805 fsfp->fsf_depth[EFX_FS_FILTER_TX_UDP_FULL] + 806 FILTER_CTL_SRCH_FUDGE_FULL); 807 EFX_SET_OWORD_FIELD(oword, 808 FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE, 809 fsfp->fsf_depth[EFX_FS_FILTER_TX_UDP_WILD] + 810 FILTER_CTL_SRCH_FUDGE_WILD); 811 } 812 813 if (fsfp->fsf_tbl[EFX_FS_FILTER_TBL_TX_MAC].fsft_size != 0) { 814 EFX_SET_OWORD_FIELD( 815 oword, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE, 816 fsfp->fsf_depth[EFX_FS_FILTER_TX_MAC_FULL] + 817 FILTER_CTL_SRCH_FUDGE_FULL); 818 EFX_SET_OWORD_FIELD( 819 oword, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE, 820 fsfp->fsf_depth[EFX_FS_FILTER_TX_MAC_WILD] + 821 FILTER_CTL_SRCH_FUDGE_WILD); 822 } 823 824 EFX_BAR_WRITEO(enp, FR_AZ_TX_CFG_REG, &oword); 825} 826 827/* Build a filter entry and return its n-tuple key. */ 828static __checkReturn uint32_t 829falconsiena_filter_build( 830 __out efx_oword_t *filter, 831 __in falconsiena_filter_spec_t *spec) 832{ 833 uint32_t dword3; 834 uint32_t key; 835 uint8_t type = spec->fsfs_type; 836 uint32_t flags = spec->fsfs_flags; 837 838 switch (falconsiena_filter_tbl_id(type)) { 839 case EFX_FS_FILTER_TBL_RX_IP: { 840 boolean_t is_udp = (type == EFX_FS_FILTER_RX_UDP_FULL || 841 type == EFX_FS_FILTER_RX_UDP_WILD); 842 EFX_POPULATE_OWORD_7(*filter, 843 FRF_BZ_RSS_EN, 844 (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0, 845 FRF_BZ_SCATTER_EN, 846 (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0, 847 FRF_AZ_TCP_UDP, is_udp, 848 FRF_AZ_RXQ_ID, spec->fsfs_dmaq_id, 849 EFX_DWORD_2, spec->fsfs_dword[2], 850 EFX_DWORD_1, spec->fsfs_dword[1], 851 EFX_DWORD_0, spec->fsfs_dword[0]); 852 dword3 = is_udp; 853 break; 854 } 855 856#if EFSYS_OPT_SIENA 857 case EFX_FS_FILTER_TBL_RX_MAC: { 858 boolean_t is_wild = (type == EFX_FS_FILTER_RX_MAC_WILD); 859 EFX_POPULATE_OWORD_7(*filter, 860 FRF_CZ_RMFT_RSS_EN, 861 (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0, 862 FRF_CZ_RMFT_SCATTER_EN, 863 (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0, 864 FRF_CZ_RMFT_RXQ_ID, spec->fsfs_dmaq_id, 865 FRF_CZ_RMFT_WILDCARD_MATCH, is_wild, 866 FRF_CZ_RMFT_DEST_MAC_DW1, spec->fsfs_dword[2], 867 FRF_CZ_RMFT_DEST_MAC_DW0, spec->fsfs_dword[1], 868 FRF_CZ_RMFT_VLAN_ID, spec->fsfs_dword[0]); 869 dword3 = is_wild; 870 break; 871 } 872#endif /* EFSYS_OPT_SIENA */ 873 874 case EFX_FS_FILTER_TBL_TX_IP: { 875 boolean_t is_udp = (type == EFX_FS_FILTER_TX_UDP_FULL || 876 type == EFX_FS_FILTER_TX_UDP_WILD); 877 EFX_POPULATE_OWORD_5(*filter, 878 FRF_CZ_TIFT_TCP_UDP, is_udp, 879 FRF_CZ_TIFT_TXQ_ID, spec->fsfs_dmaq_id, 880 EFX_DWORD_2, spec->fsfs_dword[2], 881 EFX_DWORD_1, spec->fsfs_dword[1], 882 EFX_DWORD_0, spec->fsfs_dword[0]); 883 dword3 = is_udp | spec->fsfs_dmaq_id << 1; 884 break; 885 } 886 887#if EFSYS_OPT_SIENA 888 case EFX_FS_FILTER_TBL_TX_MAC: { 889 boolean_t is_wild = (type == EFX_FS_FILTER_TX_MAC_WILD); 890 EFX_POPULATE_OWORD_5(*filter, 891 FRF_CZ_TMFT_TXQ_ID, spec->fsfs_dmaq_id, 892 FRF_CZ_TMFT_WILDCARD_MATCH, is_wild, 893 FRF_CZ_TMFT_SRC_MAC_DW1, spec->fsfs_dword[2], 894 FRF_CZ_TMFT_SRC_MAC_DW0, spec->fsfs_dword[1], 895 FRF_CZ_TMFT_VLAN_ID, spec->fsfs_dword[0]); 896 dword3 = is_wild | spec->fsfs_dmaq_id << 1; 897 break; 898 } 899#endif /* EFSYS_OPT_SIENA */ 900 901 default: 902 EFSYS_ASSERT(B_FALSE); 903 return (0); 904 } 905 906 key = 907 spec->fsfs_dword[0] ^ 908 spec->fsfs_dword[1] ^ 909 spec->fsfs_dword[2] ^ 910 dword3; 911 912 return (key); 913} 914 915static __checkReturn efx_rc_t 916falconsiena_filter_push_entry( 917 __inout efx_nic_t *enp, 918 __in falconsiena_filter_type_t type, 919 __in int index, 920 __in efx_oword_t *eop) 921{ 922 efx_rc_t rc; 923 924 switch (type) { 925 case EFX_FS_FILTER_RX_TCP_FULL: 926 case EFX_FS_FILTER_RX_TCP_WILD: 927 case EFX_FS_FILTER_RX_UDP_FULL: 928 case EFX_FS_FILTER_RX_UDP_WILD: 929 EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_FILTER_TBL0, index, 930 eop, B_TRUE); 931 break; 932 933#if EFSYS_OPT_SIENA 934 case EFX_FS_FILTER_RX_MAC_FULL: 935 case EFX_FS_FILTER_RX_MAC_WILD: 936 EFX_BAR_TBL_WRITEO(enp, FR_CZ_RX_MAC_FILTER_TBL0, index, 937 eop, B_TRUE); 938 break; 939 940 case EFX_FS_FILTER_TX_TCP_FULL: 941 case EFX_FS_FILTER_TX_TCP_WILD: 942 case EFX_FS_FILTER_TX_UDP_FULL: 943 case EFX_FS_FILTER_TX_UDP_WILD: 944 EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_FILTER_TBL0, index, 945 eop, B_TRUE); 946 break; 947 948 case EFX_FS_FILTER_TX_MAC_FULL: 949 case EFX_FS_FILTER_TX_MAC_WILD: 950 EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_MAC_FILTER_TBL0, index, 951 eop, B_TRUE); 952 break; 953#endif /* EFSYS_OPT_SIENA */ 954 955 default: 956 EFSYS_ASSERT(B_FALSE); 957 rc = ENOTSUP; 958 goto fail1; 959 } 960 return (0); 961 962fail1: 963 return (rc); 964} 965 966 967static __checkReturn boolean_t 968falconsiena_filter_equal( 969 __in const falconsiena_filter_spec_t *left, 970 __in const falconsiena_filter_spec_t *right) 971{ 972 falconsiena_filter_tbl_id_t tbl_id; 973 974 tbl_id = falconsiena_filter_tbl_id(left->fsfs_type); 975 976 977 if (left->fsfs_type != right->fsfs_type) 978 return (B_FALSE); 979 980 if (memcmp(left->fsfs_dword, right->fsfs_dword, 981 sizeof (left->fsfs_dword))) 982 return (B_FALSE); 983 984 if ((tbl_id == EFX_FS_FILTER_TBL_TX_IP || 985 tbl_id == EFX_FS_FILTER_TBL_TX_MAC) && 986 left->fsfs_dmaq_id != right->fsfs_dmaq_id) 987 return (B_FALSE); 988 989 return (B_TRUE); 990} 991 992static __checkReturn efx_rc_t 993falconsiena_filter_search( 994 __in falconsiena_filter_tbl_t *fsftp, 995 __in falconsiena_filter_spec_t *spec, 996 __in uint32_t key, 997 __in boolean_t for_insert, 998 __out int *filter_index, 999 __out unsigned int *depth_required) 1000{ 1001 unsigned hash, incr, filter_idx, depth; 1002 1003 hash = falconsiena_filter_tbl_hash(key); 1004 incr = falconsiena_filter_tbl_increment(key); 1005 1006 filter_idx = hash & (fsftp->fsft_size - 1); 1007 depth = 1; 1008 1009 for (;;) { 1010 /* 1011 * Return success if entry is used and matches this spec 1012 * or entry is unused and we are trying to insert. 1013 */ 1014 if (falconsiena_filter_test_used(fsftp, filter_idx) ? 1015 falconsiena_filter_equal(spec, 1016 &fsftp->fsft_spec[filter_idx]) : 1017 for_insert) { 1018 *filter_index = filter_idx; 1019 *depth_required = depth; 1020 return (0); 1021 } 1022 1023 /* Return failure if we reached the maximum search depth */ 1024 if (depth == FILTER_CTL_SRCH_MAX) 1025 return (for_insert ? EBUSY : ENOENT); 1026 1027 filter_idx = (filter_idx + incr) & (fsftp->fsft_size - 1); 1028 ++depth; 1029 } 1030} 1031 1032static void 1033falconsiena_filter_clear_entry( 1034 __in efx_nic_t *enp, 1035 __in falconsiena_filter_tbl_t *fsftp, 1036 __in int index) 1037{ 1038 efx_oword_t filter; 1039 1040 if (falconsiena_filter_test_used(fsftp, index)) { 1041 falconsiena_filter_clear_used(fsftp, index); 1042 1043 EFX_ZERO_OWORD(filter); 1044 falconsiena_filter_push_entry(enp, 1045 fsftp->fsft_spec[index].fsfs_type, 1046 index, &filter); 1047 1048 memset(&fsftp->fsft_spec[index], 1049 0, sizeof (fsftp->fsft_spec[0])); 1050 } 1051} 1052 1053 void 1054falconsiena_filter_tbl_clear( 1055 __in efx_nic_t *enp, 1056 __in falconsiena_filter_tbl_id_t tbl_id) 1057{ 1058 falconsiena_filter_t *fsfp = enp->en_filter.ef_falconsiena_filter; 1059 falconsiena_filter_tbl_t *fsftp = &fsfp->fsf_tbl[tbl_id]; 1060 int index; 1061 int state; 1062 1063 EFSYS_LOCK(enp->en_eslp, state); 1064 1065 for (index = 0; index < fsftp->fsft_size; ++index) { 1066 falconsiena_filter_clear_entry(enp, fsftp, index); 1067 } 1068 1069 if (fsftp->fsft_used == 0) 1070 falconsiena_filter_reset_search_depth(fsfp, tbl_id); 1071 1072 EFSYS_UNLOCK(enp->en_eslp, state); 1073} 1074 1075static __checkReturn efx_rc_t 1076falconsiena_filter_init( 1077 __in efx_nic_t *enp) 1078{ 1079 falconsiena_filter_t *fsfp; 1080 falconsiena_filter_tbl_t *fsftp; 1081 int tbl_id; 1082 efx_rc_t rc; 1083 1084 EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (falconsiena_filter_t), fsfp); 1085 1086 if (!fsfp) { 1087 rc = ENOMEM; 1088 goto fail1; 1089 } 1090 1091 enp->en_filter.ef_falconsiena_filter = fsfp; 1092 1093 switch (enp->en_family) { 1094#if EFSYS_OPT_FALCON 1095 case EFX_FAMILY_FALCON: 1096 fsftp = &fsfp->fsf_tbl[EFX_FS_FILTER_TBL_RX_IP]; 1097 fsftp->fsft_size = FR_AZ_RX_FILTER_TBL0_ROWS; 1098 break; 1099#endif /* EFSYS_OPT_FALCON */ 1100 1101#if EFSYS_OPT_SIENA 1102 case EFX_FAMILY_SIENA: 1103 fsftp = &fsfp->fsf_tbl[EFX_FS_FILTER_TBL_RX_IP]; 1104 fsftp->fsft_size = FR_AZ_RX_FILTER_TBL0_ROWS; 1105 1106 fsftp = &fsfp->fsf_tbl[EFX_FS_FILTER_TBL_RX_MAC]; 1107 fsftp->fsft_size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS; 1108 1109 fsftp = &fsfp->fsf_tbl[EFX_FS_FILTER_TBL_TX_IP]; 1110 fsftp->fsft_size = FR_CZ_TX_FILTER_TBL0_ROWS; 1111 1112 fsftp = &fsfp->fsf_tbl[EFX_FS_FILTER_TBL_TX_MAC]; 1113 fsftp->fsft_size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS; 1114 break; 1115#endif /* EFSYS_OPT_SIENA */ 1116 1117 default: 1118 rc = ENOTSUP; 1119 goto fail2; 1120 } 1121 1122 for (tbl_id = 0; tbl_id < EFX_FS_FILTER_NTBLS; tbl_id++) { 1123 unsigned int bitmap_size; 1124 1125 fsftp = &fsfp->fsf_tbl[tbl_id]; 1126 if (fsftp->fsft_size == 0) 1127 continue; 1128 1129 EFX_STATIC_ASSERT(sizeof (fsftp->fsft_bitmap[0]) == 1130 sizeof (uint32_t)); 1131 bitmap_size = 1132 (fsftp->fsft_size + (sizeof (uint32_t) * 8) - 1) / 8; 1133 1134 EFSYS_KMEM_ALLOC(enp->en_esip, bitmap_size, fsftp->fsft_bitmap); 1135 if (!fsftp->fsft_bitmap) { 1136 rc = ENOMEM; 1137 goto fail3; 1138 } 1139 1140 EFSYS_KMEM_ALLOC(enp->en_esip, 1141 fsftp->fsft_size * sizeof (*fsftp->fsft_spec), 1142 fsftp->fsft_spec); 1143 if (!fsftp->fsft_spec) { 1144 rc = ENOMEM; 1145 goto fail4; 1146 } 1147 memset(fsftp->fsft_spec, 0, 1148 fsftp->fsft_size * sizeof (*fsftp->fsft_spec)); 1149 } 1150 1151 return (0); 1152 1153fail4: 1154 EFSYS_PROBE(fail4); 1155 1156fail3: 1157 EFSYS_PROBE(fail3); 1158 1159fail2: 1160 EFSYS_PROBE(fail2); 1161 falconsiena_filter_fini(enp); 1162 1163fail1: 1164 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1165 return (rc); 1166} 1167 1168static void 1169falconsiena_filter_fini( 1170 __in efx_nic_t *enp) 1171{ 1172 falconsiena_filter_t *fsfp = enp->en_filter.ef_falconsiena_filter; 1173 falconsiena_filter_tbl_id_t tbl_id; 1174 1175 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 1176 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 1177 1178 if (fsfp == NULL) 1179 return; 1180 1181 for (tbl_id = 0; tbl_id < EFX_FS_FILTER_NTBLS; tbl_id++) { 1182 falconsiena_filter_tbl_t *fsftp = &fsfp->fsf_tbl[tbl_id]; 1183 unsigned int bitmap_size; 1184 1185 EFX_STATIC_ASSERT(sizeof (fsftp->fsft_bitmap[0]) == 1186 sizeof (uint32_t)); 1187 bitmap_size = 1188 (fsftp->fsft_size + (sizeof (uint32_t) * 8) - 1) / 8; 1189 1190 if (fsftp->fsft_bitmap != NULL) { 1191 EFSYS_KMEM_FREE(enp->en_esip, bitmap_size, 1192 fsftp->fsft_bitmap); 1193 fsftp->fsft_bitmap = NULL; 1194 } 1195 1196 if (fsftp->fsft_spec != NULL) { 1197 EFSYS_KMEM_FREE(enp->en_esip, fsftp->fsft_size * 1198 sizeof (*fsftp->fsft_spec), fsftp->fsft_spec); 1199 fsftp->fsft_spec = NULL; 1200 } 1201 } 1202 1203 EFSYS_KMEM_FREE(enp->en_esip, sizeof (falconsiena_filter_t), 1204 enp->en_filter.ef_falconsiena_filter); 1205} 1206 1207/* Restore filter state after a reset */ 1208static __checkReturn efx_rc_t 1209falconsiena_filter_restore( 1210 __in efx_nic_t *enp) 1211{ 1212 falconsiena_filter_t *fsfp = enp->en_filter.ef_falconsiena_filter; 1213 falconsiena_filter_tbl_id_t tbl_id; 1214 falconsiena_filter_tbl_t *fsftp; 1215 falconsiena_filter_spec_t *spec; 1216 efx_oword_t filter; 1217 int filter_idx; 1218 int state; 1219 efx_rc_t rc; 1220 1221 EFSYS_LOCK(enp->en_eslp, state); 1222 1223 for (tbl_id = 0; tbl_id < EFX_FS_FILTER_NTBLS; tbl_id++) { 1224 fsftp = &fsfp->fsf_tbl[tbl_id]; 1225 for (filter_idx = 0; 1226 filter_idx < fsftp->fsft_size; 1227 filter_idx++) { 1228 if (!falconsiena_filter_test_used(fsftp, filter_idx)) 1229 continue; 1230 1231 spec = &fsftp->fsft_spec[filter_idx]; 1232 if ((rc = falconsiena_filter_build(&filter, spec)) != 0) 1233 goto fail1; 1234 if ((rc = falconsiena_filter_push_entry(enp, 1235 spec->fsfs_type, filter_idx, &filter)) != 0) 1236 goto fail2; 1237 } 1238 } 1239 1240 falconsiena_filter_push_rx_limits(enp); 1241 falconsiena_filter_push_tx_limits(enp); 1242 1243 EFSYS_UNLOCK(enp->en_eslp, state); 1244 1245 return (0); 1246 1247fail2: 1248 EFSYS_PROBE(fail2); 1249 1250fail1: 1251 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1252 1253 EFSYS_UNLOCK(enp->en_eslp, state); 1254 1255 return (rc); 1256} 1257 1258static __checkReturn efx_rc_t 1259falconsiena_filter_add( 1260 __in efx_nic_t *enp, 1261 __inout efx_filter_spec_t *spec, 1262 __in boolean_t may_replace) 1263{ 1264 efx_rc_t rc; 1265 falconsiena_filter_spec_t fs_spec; 1266 falconsiena_filter_t *fsfp = enp->en_filter.ef_falconsiena_filter; 1267 falconsiena_filter_tbl_id_t tbl_id; 1268 falconsiena_filter_tbl_t *fsftp; 1269 falconsiena_filter_spec_t *saved_fs_spec; 1270 efx_oword_t filter; 1271 int filter_idx; 1272 unsigned int depth; 1273 int state; 1274 uint32_t key; 1275 1276 1277 EFSYS_ASSERT3P(spec, !=, NULL); 1278 1279 if ((rc = falconsiena_filter_spec_from_gen_spec(&fs_spec, spec)) != 0) 1280 goto fail1; 1281 1282 tbl_id = falconsiena_filter_tbl_id(fs_spec.fsfs_type); 1283 fsftp = &fsfp->fsf_tbl[tbl_id]; 1284 1285 if (fsftp->fsft_size == 0) { 1286 rc = EINVAL; 1287 goto fail2; 1288 } 1289 1290 key = falconsiena_filter_build(&filter, &fs_spec); 1291 1292 EFSYS_LOCK(enp->en_eslp, state); 1293 1294 rc = falconsiena_filter_search(fsftp, &fs_spec, key, B_TRUE, 1295 &filter_idx, &depth); 1296 if (rc != 0) 1297 goto fail3; 1298 1299 EFSYS_ASSERT3U(filter_idx, <, fsftp->fsft_size); 1300 saved_fs_spec = &fsftp->fsft_spec[filter_idx]; 1301 1302 if (falconsiena_filter_test_used(fsftp, filter_idx)) { 1303 if (may_replace == B_FALSE) { 1304 rc = EEXIST; 1305 goto fail4; 1306 } 1307 } 1308 falconsiena_filter_set_used(fsftp, filter_idx); 1309 *saved_fs_spec = fs_spec; 1310 1311 if (fsfp->fsf_depth[fs_spec.fsfs_type] < depth) { 1312 fsfp->fsf_depth[fs_spec.fsfs_type] = depth; 1313 if (tbl_id == EFX_FS_FILTER_TBL_TX_IP || 1314 tbl_id == EFX_FS_FILTER_TBL_TX_MAC) 1315 falconsiena_filter_push_tx_limits(enp); 1316 else 1317 falconsiena_filter_push_rx_limits(enp); 1318 } 1319 1320 falconsiena_filter_push_entry(enp, fs_spec.fsfs_type, 1321 filter_idx, &filter); 1322 1323 EFSYS_UNLOCK(enp->en_eslp, state); 1324 return (0); 1325 1326fail4: 1327 EFSYS_PROBE(fail4); 1328 1329fail3: 1330 EFSYS_UNLOCK(enp->en_eslp, state); 1331 EFSYS_PROBE(fail3); 1332 1333fail2: 1334 EFSYS_PROBE(fail2); 1335 1336fail1: 1337 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1338 return (rc); 1339} 1340 1341static __checkReturn efx_rc_t 1342falconsiena_filter_delete( 1343 __in efx_nic_t *enp, 1344 __inout efx_filter_spec_t *spec) 1345{ 1346 efx_rc_t rc; 1347 falconsiena_filter_spec_t fs_spec; 1348 falconsiena_filter_t *fsfp = enp->en_filter.ef_falconsiena_filter; 1349 falconsiena_filter_tbl_id_t tbl_id; 1350 falconsiena_filter_tbl_t *fsftp; 1351 efx_oword_t filter; 1352 int filter_idx; 1353 unsigned int depth; 1354 int state; 1355 uint32_t key; 1356 1357 EFSYS_ASSERT3P(spec, !=, NULL); 1358 1359 if ((rc = falconsiena_filter_spec_from_gen_spec(&fs_spec, spec)) != 0) 1360 goto fail1; 1361 1362 tbl_id = falconsiena_filter_tbl_id(fs_spec.fsfs_type); 1363 fsftp = &fsfp->fsf_tbl[tbl_id]; 1364 1365 key = falconsiena_filter_build(&filter, &fs_spec); 1366 1367 EFSYS_LOCK(enp->en_eslp, state); 1368 1369 rc = falconsiena_filter_search(fsftp, &fs_spec, key, B_FALSE, 1370 &filter_idx, &depth); 1371 if (rc != 0) 1372 goto fail2; 1373 1374 falconsiena_filter_clear_entry(enp, fsftp, filter_idx); 1375 if (fsftp->fsft_used == 0) 1376 falconsiena_filter_reset_search_depth(fsfp, tbl_id); 1377 1378 EFSYS_UNLOCK(enp->en_eslp, state); 1379 return (0); 1380 1381fail2: 1382 EFSYS_UNLOCK(enp->en_eslp, state); 1383 EFSYS_PROBE(fail2); 1384 1385fail1: 1386 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1387 return (rc); 1388} 1389 1390#define MAX_SUPPORTED 4 1391 1392static __checkReturn efx_rc_t 1393falconsiena_filter_supported_filters( 1394 __in efx_nic_t *enp, 1395 __out uint32_t *list, 1396 __out size_t *length) 1397{ 1398 int index = 0; 1399 uint32_t rx_matches[MAX_SUPPORTED]; 1400 efx_rc_t rc; 1401 1402 if (list == NULL) { 1403 rc = EINVAL; 1404 goto fail1; 1405 } 1406 1407 rx_matches[index++] = 1408 EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO | 1409 EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT | 1410 EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT; 1411 1412 rx_matches[index++] = 1413 EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO | 1414 EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT; 1415 1416 if (enp->en_features & EFX_FEATURE_MAC_HEADER_FILTERS) { 1417 rx_matches[index++] = 1418 EFX_FILTER_MATCH_OUTER_VID | EFX_FILTER_MATCH_LOC_MAC; 1419 1420 rx_matches[index++] = EFX_FILTER_MATCH_LOC_MAC; 1421 } 1422 1423 EFSYS_ASSERT3U(index, <=, MAX_SUPPORTED); 1424 1425 *length = index; 1426 memcpy(list, rx_matches, *length); 1427 1428 return (0); 1429 1430fail1: 1431 1432 return (rc); 1433} 1434 1435#undef MAX_SUPPORTED 1436 1437#endif /* EFSYS_OPT_FALCON || EFSYS_OPT_SIENA */ 1438 1439#endif /* EFSYS_OPT_FILTER */ 1440