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