Imported Upstream version 17.05
[deb_dpdk.git] / drivers / net / sfc / base / efx_filter.c
diff --git a/drivers/net/sfc/base/efx_filter.c b/drivers/net/sfc/base/efx_filter.c
new file mode 100644 (file)
index 0000000..ba31026
--- /dev/null
@@ -0,0 +1,1424 @@
+/*
+ * Copyright (c) 2007-2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#include "efx.h"
+#include "efx_impl.h"
+
+
+#if EFSYS_OPT_FILTER
+
+#if EFSYS_OPT_SIENA
+
+static __checkReturn   efx_rc_t
+siena_filter_init(
+       __in            efx_nic_t *enp);
+
+static                 void
+siena_filter_fini(
+       __in            efx_nic_t *enp);
+
+static __checkReturn   efx_rc_t
+siena_filter_restore(
+       __in            efx_nic_t *enp);
+
+static __checkReturn   efx_rc_t
+siena_filter_add(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec,
+       __in            boolean_t may_replace);
+
+static __checkReturn   efx_rc_t
+siena_filter_delete(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec);
+
+static __checkReturn   efx_rc_t
+siena_filter_supported_filters(
+       __in                            efx_nic_t *enp,
+       __out_ecount(buffer_length)     uint32_t *buffer,
+       __in                            size_t buffer_length,
+       __out                           size_t *list_lengthp);
+
+#endif /* EFSYS_OPT_SIENA */
+
+#if EFSYS_OPT_SIENA
+static const efx_filter_ops_t  __efx_filter_siena_ops = {
+       siena_filter_init,              /* efo_init */
+       siena_filter_fini,              /* efo_fini */
+       siena_filter_restore,           /* efo_restore */
+       siena_filter_add,               /* efo_add */
+       siena_filter_delete,            /* efo_delete */
+       siena_filter_supported_filters, /* efo_supported_filters */
+       NULL,                           /* efo_reconfigure */
+};
+#endif /* EFSYS_OPT_SIENA */
+
+#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
+static const efx_filter_ops_t  __efx_filter_ef10_ops = {
+       ef10_filter_init,               /* efo_init */
+       ef10_filter_fini,               /* efo_fini */
+       ef10_filter_restore,            /* efo_restore */
+       ef10_filter_add,                /* efo_add */
+       ef10_filter_delete,             /* efo_delete */
+       ef10_filter_supported_filters,  /* efo_supported_filters */
+       ef10_filter_reconfigure,        /* efo_reconfigure */
+};
+#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
+
+       __checkReturn   efx_rc_t
+efx_filter_insert(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec)
+{
+       const efx_filter_ops_t *efop = enp->en_efop;
+
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
+       EFSYS_ASSERT3P(spec, !=, NULL);
+       EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX);
+
+       return (efop->efo_add(enp, spec, B_FALSE));
+}
+
+       __checkReturn   efx_rc_t
+efx_filter_remove(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec)
+{
+       const efx_filter_ops_t *efop = enp->en_efop;
+
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
+       EFSYS_ASSERT3P(spec, !=, NULL);
+       EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX);
+
+#if EFSYS_OPT_RX_SCALE
+       spec->efs_rss_context = enp->en_rss_context;
+#endif
+
+       return (efop->efo_delete(enp, spec));
+}
+
+       __checkReturn   efx_rc_t
+efx_filter_restore(
+       __in            efx_nic_t *enp)
+{
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
+
+       if ((rc = enp->en_efop->efo_restore(enp)) != 0)
+               goto fail1;
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+efx_filter_init(
+       __in            efx_nic_t *enp)
+{
+       const efx_filter_ops_t *efop;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
+       EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_FILTER));
+
+       switch (enp->en_family) {
+#if EFSYS_OPT_SIENA
+       case EFX_FAMILY_SIENA:
+               efop = &__efx_filter_siena_ops;
+               break;
+#endif /* EFSYS_OPT_SIENA */
+
+#if EFSYS_OPT_HUNTINGTON
+       case EFX_FAMILY_HUNTINGTON:
+               efop = &__efx_filter_ef10_ops;
+               break;
+#endif /* EFSYS_OPT_HUNTINGTON */
+
+#if EFSYS_OPT_MEDFORD
+       case EFX_FAMILY_MEDFORD:
+               efop = &__efx_filter_ef10_ops;
+               break;
+#endif /* EFSYS_OPT_MEDFORD */
+
+       default:
+               EFSYS_ASSERT(0);
+               rc = ENOTSUP;
+               goto fail1;
+       }
+
+       if ((rc = efop->efo_init(enp)) != 0)
+               goto fail2;
+
+       enp->en_efop = efop;
+       enp->en_mod_flags |= EFX_MOD_FILTER;
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       enp->en_efop = NULL;
+       enp->en_mod_flags &= ~EFX_MOD_FILTER;
+       return (rc);
+}
+
+                       void
+efx_filter_fini(
+       __in            efx_nic_t *enp)
+{
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
+
+       enp->en_efop->efo_fini(enp);
+
+       enp->en_efop = NULL;
+       enp->en_mod_flags &= ~EFX_MOD_FILTER;
+}
+
+/*
+ * Query the possible combinations of match flags which can be filtered on.
+ * These are returned as a list, of which each 32 bit element is a bitmask
+ * formed of EFX_FILTER_MATCH flags.
+ *
+ * The combinations are ordered in priority from highest to lowest.
+ *
+ * If the provided buffer is too short to hold the list, the call with fail with
+ * ENOSPC and *list_lengthp will be set to the buffer length required.
+ */
+       __checkReturn   efx_rc_t
+efx_filter_supported_filters(
+       __in                            efx_nic_t *enp,
+       __out_ecount(buffer_length)     uint32_t *buffer,
+       __in                            size_t buffer_length,
+       __out                           size_t *list_lengthp)
+{
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
+       EFSYS_ASSERT(enp->en_efop->efo_supported_filters != NULL);
+
+       if (buffer == NULL) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       rc = enp->en_efop->efo_supported_filters(enp, buffer, buffer_length,
+                                                   list_lengthp);
+       if (rc != 0)
+               goto fail2;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+efx_filter_reconfigure(
+       __in                            efx_nic_t *enp,
+       __in_ecount(6)                  uint8_t const *mac_addr,
+       __in                            boolean_t all_unicst,
+       __in                            boolean_t mulcst,
+       __in                            boolean_t all_mulcst,
+       __in                            boolean_t brdcst,
+       __in_ecount(6*count)            uint8_t const *addrs,
+       __in                            uint32_t count)
+{
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
+
+       if (enp->en_efop->efo_reconfigure != NULL) {
+               if ((rc = enp->en_efop->efo_reconfigure(enp, mac_addr,
+                                                       all_unicst, mulcst,
+                                                       all_mulcst, brdcst,
+                                                       addrs, count)) != 0)
+                       goto fail1;
+       }
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+               void
+efx_filter_spec_init_rx(
+       __out           efx_filter_spec_t *spec,
+       __in            efx_filter_priority_t priority,
+       __in            efx_filter_flags_t flags,
+       __in            efx_rxq_t *erp)
+{
+       EFSYS_ASSERT3P(spec, !=, NULL);
+       EFSYS_ASSERT3P(erp, !=, NULL);
+       EFSYS_ASSERT((flags & ~(EFX_FILTER_FLAG_RX_RSS |
+                               EFX_FILTER_FLAG_RX_SCATTER)) == 0);
+
+       memset(spec, 0, sizeof (*spec));
+       spec->efs_priority = priority;
+       spec->efs_flags = EFX_FILTER_FLAG_RX | flags;
+       spec->efs_rss_context = EFX_FILTER_SPEC_RSS_CONTEXT_DEFAULT;
+       spec->efs_dmaq_id = (uint16_t)erp->er_index;
+}
+
+               void
+efx_filter_spec_init_tx(
+       __out           efx_filter_spec_t *spec,
+       __in            efx_txq_t *etp)
+{
+       EFSYS_ASSERT3P(spec, !=, NULL);
+       EFSYS_ASSERT3P(etp, !=, NULL);
+
+       memset(spec, 0, sizeof (*spec));
+       spec->efs_priority = EFX_FILTER_PRI_REQUIRED;
+       spec->efs_flags = EFX_FILTER_FLAG_TX;
+       spec->efs_dmaq_id = (uint16_t)etp->et_index;
+}
+
+
+/*
+ *  Specify IPv4 host, transport protocol and port in a filter specification
+ */
+__checkReturn          efx_rc_t
+efx_filter_spec_set_ipv4_local(
+       __inout         efx_filter_spec_t *spec,
+       __in            uint8_t proto,
+       __in            uint32_t host,
+       __in            uint16_t port)
+{
+       EFSYS_ASSERT3P(spec, !=, NULL);
+
+       spec->efs_match_flags |=
+               EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+               EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
+       spec->efs_ether_type = EFX_ETHER_TYPE_IPV4;
+       spec->efs_ip_proto = proto;
+       spec->efs_loc_host.eo_u32[0] = host;
+       spec->efs_loc_port = port;
+       return (0);
+}
+
+/*
+ * Specify IPv4 hosts, transport protocol and ports in a filter specification
+ */
+__checkReturn          efx_rc_t
+efx_filter_spec_set_ipv4_full(
+       __inout         efx_filter_spec_t *spec,
+       __in            uint8_t proto,
+       __in            uint32_t lhost,
+       __in            uint16_t lport,
+       __in            uint32_t rhost,
+       __in            uint16_t rport)
+{
+       EFSYS_ASSERT3P(spec, !=, NULL);
+
+       spec->efs_match_flags |=
+               EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+               EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
+               EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
+       spec->efs_ether_type = EFX_ETHER_TYPE_IPV4;
+       spec->efs_ip_proto = proto;
+       spec->efs_loc_host.eo_u32[0] = lhost;
+       spec->efs_loc_port = lport;
+       spec->efs_rem_host.eo_u32[0] = rhost;
+       spec->efs_rem_port = rport;
+       return (0);
+}
+
+/*
+ * Specify local Ethernet address and/or VID in filter specification
+ */
+__checkReturn          efx_rc_t
+efx_filter_spec_set_eth_local(
+       __inout         efx_filter_spec_t *spec,
+       __in            uint16_t vid,
+       __in            const uint8_t *addr)
+{
+       EFSYS_ASSERT3P(spec, !=, NULL);
+       EFSYS_ASSERT3P(addr, !=, NULL);
+
+       if (vid == EFX_FILTER_SPEC_VID_UNSPEC && addr == NULL)
+               return (EINVAL);
+
+       if (vid != EFX_FILTER_SPEC_VID_UNSPEC) {
+               spec->efs_match_flags |= EFX_FILTER_MATCH_OUTER_VID;
+               spec->efs_outer_vid = vid;
+       }
+       if (addr != NULL) {
+               spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC;
+               memcpy(spec->efs_loc_mac, addr, EFX_MAC_ADDR_LEN);
+       }
+       return (0);
+}
+
+/*
+ * Specify matching otherwise-unmatched unicast in a filter specification
+ */
+__checkReturn          efx_rc_t
+efx_filter_spec_set_uc_def(
+       __inout         efx_filter_spec_t *spec)
+{
+       EFSYS_ASSERT3P(spec, !=, NULL);
+
+       spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_UCAST_DST;
+       return (0);
+}
+
+/*
+ * Specify matching otherwise-unmatched multicast in a filter specification
+ */
+__checkReturn          efx_rc_t
+efx_filter_spec_set_mc_def(
+       __inout         efx_filter_spec_t *spec)
+{
+       EFSYS_ASSERT3P(spec, !=, NULL);
+
+       spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_MCAST_DST;
+       return (0);
+}
+
+
+
+#if EFSYS_OPT_SIENA
+
+/*
+ * "Fudge factors" - difference between programmed value and actual depth.
+ * Due to pipelined implementation we need to program H/W with a value that
+ * is larger than the hop limit we want.
+ */
+#define        FILTER_CTL_SRCH_FUDGE_WILD 3
+#define        FILTER_CTL_SRCH_FUDGE_FULL 1
+
+/*
+ * Hard maximum hop limit.  Hardware will time-out beyond 200-something.
+ * We also need to avoid infinite loops in efx_filter_search() when the
+ * table is full.
+ */
+#define        FILTER_CTL_SRCH_MAX 200
+
+static __checkReturn   efx_rc_t
+siena_filter_spec_from_gen_spec(
+       __out           siena_filter_spec_t *sf_spec,
+       __in            efx_filter_spec_t *gen_spec)
+{
+       efx_rc_t rc;
+       boolean_t is_full = B_FALSE;
+
+       if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX)
+               EFSYS_ASSERT3U(gen_spec->efs_flags, ==, EFX_FILTER_FLAG_TX);
+       else
+               EFSYS_ASSERT3U(gen_spec->efs_flags, &, EFX_FILTER_FLAG_RX);
+
+       /* Falconsiena only has one RSS context */
+       if ((gen_spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) &&
+           gen_spec->efs_rss_context != 0) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       sf_spec->sfs_flags = gen_spec->efs_flags;
+       sf_spec->sfs_dmaq_id = gen_spec->efs_dmaq_id;
+
+       switch (gen_spec->efs_match_flags) {
+       case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+           EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
+           EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT:
+               is_full = B_TRUE;
+               /* Fall through */
+       case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+           EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT: {
+               uint32_t rhost, host1, host2;
+               uint16_t rport, port1, port2;
+
+               if (gen_spec->efs_ether_type != EFX_ETHER_TYPE_IPV4) {
+                       rc = ENOTSUP;
+                       goto fail2;
+               }
+               if (gen_spec->efs_loc_port == 0 ||
+                   (is_full && gen_spec->efs_rem_port == 0)) {
+                       rc = EINVAL;
+                       goto fail3;
+               }
+               switch (gen_spec->efs_ip_proto) {
+               case EFX_IPPROTO_TCP:
+                       if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                               sf_spec->sfs_type = (is_full ?
+                                   EFX_SIENA_FILTER_TX_TCP_FULL :
+                                   EFX_SIENA_FILTER_TX_TCP_WILD);
+                       } else {
+                               sf_spec->sfs_type = (is_full ?
+                                   EFX_SIENA_FILTER_RX_TCP_FULL :
+                                   EFX_SIENA_FILTER_RX_TCP_WILD);
+                       }
+                       break;
+               case EFX_IPPROTO_UDP:
+                       if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                               sf_spec->sfs_type = (is_full ?
+                                   EFX_SIENA_FILTER_TX_UDP_FULL :
+                                   EFX_SIENA_FILTER_TX_UDP_WILD);
+                       } else {
+                               sf_spec->sfs_type = (is_full ?
+                                   EFX_SIENA_FILTER_RX_UDP_FULL :
+                                   EFX_SIENA_FILTER_RX_UDP_WILD);
+                       }
+                       break;
+               default:
+                       rc = ENOTSUP;
+                       goto fail4;
+               }
+               /*
+                * The filter is constructed in terms of source and destination,
+                * with the odd wrinkle that the ports are swapped in a UDP
+                * wildcard filter. We need to convert from local and remote
+                * addresses (zero for a wildcard).
+                */
+               rhost = is_full ? gen_spec->efs_rem_host.eo_u32[0] : 0;
+               rport = is_full ? gen_spec->efs_rem_port : 0;
+               if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                       host1 = gen_spec->efs_loc_host.eo_u32[0];
+                       host2 = rhost;
+               } else {
+                       host1 = rhost;
+                       host2 = gen_spec->efs_loc_host.eo_u32[0];
+               }
+               if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                       if (sf_spec->sfs_type ==
+                           EFX_SIENA_FILTER_TX_UDP_WILD) {
+                               port1 = rport;
+                               port2 = gen_spec->efs_loc_port;
+                       } else {
+                               port1 = gen_spec->efs_loc_port;
+                               port2 = rport;
+                       }
+               } else {
+                       if (sf_spec->sfs_type ==
+                           EFX_SIENA_FILTER_RX_UDP_WILD) {
+                               port1 = gen_spec->efs_loc_port;
+                               port2 = rport;
+                       } else {
+                               port1 = rport;
+                               port2 = gen_spec->efs_loc_port;
+                       }
+               }
+               sf_spec->sfs_dword[0] = (host1 << 16) | port1;
+               sf_spec->sfs_dword[1] = (port2 << 16) | (host1 >> 16);
+               sf_spec->sfs_dword[2] = host2;
+               break;
+       }
+
+       case EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_OUTER_VID:
+               is_full = B_TRUE;
+               /* Fall through */
+       case EFX_FILTER_MATCH_LOC_MAC:
+               if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
+                       sf_spec->sfs_type = (is_full ?
+                           EFX_SIENA_FILTER_TX_MAC_FULL :
+                           EFX_SIENA_FILTER_TX_MAC_WILD);
+               } else {
+                       sf_spec->sfs_type = (is_full ?
+                           EFX_SIENA_FILTER_RX_MAC_FULL :
+                           EFX_SIENA_FILTER_RX_MAC_WILD);
+               }
+               sf_spec->sfs_dword[0] = is_full ? gen_spec->efs_outer_vid : 0;
+               sf_spec->sfs_dword[1] =
+                   gen_spec->efs_loc_mac[2] << 24 |
+                   gen_spec->efs_loc_mac[3] << 16 |
+                   gen_spec->efs_loc_mac[4] <<  8 |
+                   gen_spec->efs_loc_mac[5];
+               sf_spec->sfs_dword[2] =
+                   gen_spec->efs_loc_mac[0] << 8 |
+                   gen_spec->efs_loc_mac[1];
+               break;
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               rc = ENOTSUP;
+               goto fail5;
+       }
+
+       return (0);
+
+fail5:
+       EFSYS_PROBE(fail5);
+fail4:
+       EFSYS_PROBE(fail4);
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+/*
+ * The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit
+ * key derived from the n-tuple.
+ */
+static                 uint16_t
+siena_filter_tbl_hash(
+       __in            uint32_t key)
+{
+       uint16_t tmp;
+
+       /* First 16 rounds */
+       tmp = 0x1fff ^ (uint16_t)(key >> 16);
+       tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
+       tmp = tmp ^ tmp >> 9;
+
+       /* Last 16 rounds */
+       tmp = tmp ^ tmp << 13 ^ (uint16_t)(key & 0xffff);
+       tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
+       tmp = tmp ^ tmp >> 9;
+
+       return (tmp);
+}
+
+/*
+ * To allow for hash collisions, filter search continues at these
+ * increments from the first possible entry selected by the hash.
+ */
+static                 uint16_t
+siena_filter_tbl_increment(
+       __in            uint32_t key)
+{
+       return ((uint16_t)(key * 2 - 1));
+}
+
+static __checkReturn   boolean_t
+siena_filter_test_used(
+       __in            siena_filter_tbl_t *sftp,
+       __in            unsigned int index)
+{
+       EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
+       return ((sftp->sft_bitmap[index / 32] & (1 << (index % 32))) != 0);
+}
+
+static                 void
+siena_filter_set_used(
+       __in            siena_filter_tbl_t *sftp,
+       __in            unsigned int index)
+{
+       EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
+       sftp->sft_bitmap[index / 32] |= (1 << (index % 32));
+       ++sftp->sft_used;
+}
+
+static                 void
+siena_filter_clear_used(
+       __in            siena_filter_tbl_t *sftp,
+       __in            unsigned int index)
+{
+       EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
+       sftp->sft_bitmap[index / 32] &= ~(1 << (index % 32));
+
+       --sftp->sft_used;
+       EFSYS_ASSERT3U(sftp->sft_used, >=, 0);
+}
+
+
+static                 siena_filter_tbl_id_t
+siena_filter_tbl_id(
+       __in            siena_filter_type_t type)
+{
+       siena_filter_tbl_id_t tbl_id;
+
+       switch (type) {
+       case EFX_SIENA_FILTER_RX_TCP_FULL:
+       case EFX_SIENA_FILTER_RX_TCP_WILD:
+       case EFX_SIENA_FILTER_RX_UDP_FULL:
+       case EFX_SIENA_FILTER_RX_UDP_WILD:
+               tbl_id = EFX_SIENA_FILTER_TBL_RX_IP;
+               break;
+
+       case EFX_SIENA_FILTER_RX_MAC_FULL:
+       case EFX_SIENA_FILTER_RX_MAC_WILD:
+               tbl_id = EFX_SIENA_FILTER_TBL_RX_MAC;
+               break;
+
+       case EFX_SIENA_FILTER_TX_TCP_FULL:
+       case EFX_SIENA_FILTER_TX_TCP_WILD:
+       case EFX_SIENA_FILTER_TX_UDP_FULL:
+       case EFX_SIENA_FILTER_TX_UDP_WILD:
+               tbl_id = EFX_SIENA_FILTER_TBL_TX_IP;
+               break;
+
+       case EFX_SIENA_FILTER_TX_MAC_FULL:
+       case EFX_SIENA_FILTER_TX_MAC_WILD:
+               tbl_id = EFX_SIENA_FILTER_TBL_TX_MAC;
+               break;
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               tbl_id = EFX_SIENA_FILTER_NTBLS;
+               break;
+       }
+       return (tbl_id);
+}
+
+static                 void
+siena_filter_reset_search_depth(
+       __inout         siena_filter_t *sfp,
+       __in            siena_filter_tbl_id_t tbl_id)
+{
+       switch (tbl_id) {
+       case EFX_SIENA_FILTER_TBL_RX_IP:
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] = 0;
+               break;
+
+       case EFX_SIENA_FILTER_TBL_RX_MAC:
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] = 0;
+               break;
+
+       case EFX_SIENA_FILTER_TBL_TX_IP:
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] = 0;
+               break;
+
+       case EFX_SIENA_FILTER_TBL_TX_MAC:
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] = 0;
+               sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] = 0;
+               break;
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               break;
+       }
+}
+
+static                 void
+siena_filter_push_rx_limits(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       efx_oword_t oword;
+
+       EFX_BAR_READO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
+
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_FULL_SRCH_LIMIT,
+           sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] +
+           FILTER_CTL_SRCH_FUDGE_FULL);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_WILD_SRCH_LIMIT,
+           sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] +
+           FILTER_CTL_SRCH_FUDGE_WILD);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_FULL_SRCH_LIMIT,
+           sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] +
+           FILTER_CTL_SRCH_FUDGE_FULL);
+       EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_WILD_SRCH_LIMIT,
+           sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] +
+           FILTER_CTL_SRCH_FUDGE_WILD);
+
+       if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC].sft_size) {
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT,
+                   sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] +
+                   FILTER_CTL_SRCH_FUDGE_FULL);
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT,
+                   sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] +
+                   FILTER_CTL_SRCH_FUDGE_WILD);
+       }
+
+       EFX_BAR_WRITEO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
+}
+
+static                 void
+siena_filter_push_tx_limits(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       efx_oword_t oword;
+
+       EFX_BAR_READO(enp, FR_AZ_TX_CFG_REG, &oword);
+
+       if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP].sft_size != 0) {
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE,
+                   sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] +
+                   FILTER_CTL_SRCH_FUDGE_FULL);
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE,
+                   sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] +
+                   FILTER_CTL_SRCH_FUDGE_WILD);
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE,
+                   sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] +
+                   FILTER_CTL_SRCH_FUDGE_FULL);
+               EFX_SET_OWORD_FIELD(oword,
+                   FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE,
+                   sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] +
+                   FILTER_CTL_SRCH_FUDGE_WILD);
+       }
+
+       if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC].sft_size != 0) {
+               EFX_SET_OWORD_FIELD(
+                       oword, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE,
+                       sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] +
+                       FILTER_CTL_SRCH_FUDGE_FULL);
+               EFX_SET_OWORD_FIELD(
+                       oword, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE,
+                       sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] +
+                       FILTER_CTL_SRCH_FUDGE_WILD);
+       }
+
+       EFX_BAR_WRITEO(enp, FR_AZ_TX_CFG_REG, &oword);
+}
+
+/* Build a filter entry and return its n-tuple key. */
+static __checkReturn   uint32_t
+siena_filter_build(
+       __out           efx_oword_t *filter,
+       __in            siena_filter_spec_t *spec)
+{
+       uint32_t dword3;
+       uint32_t key;
+       uint8_t  type  = spec->sfs_type;
+       uint32_t flags = spec->sfs_flags;
+
+       switch (siena_filter_tbl_id(type)) {
+       case EFX_SIENA_FILTER_TBL_RX_IP: {
+               boolean_t is_udp = (type == EFX_SIENA_FILTER_RX_UDP_FULL ||
+                   type == EFX_SIENA_FILTER_RX_UDP_WILD);
+               EFX_POPULATE_OWORD_7(*filter,
+                   FRF_BZ_RSS_EN,
+                   (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
+                   FRF_BZ_SCATTER_EN,
+                   (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
+                   FRF_AZ_TCP_UDP, is_udp,
+                   FRF_AZ_RXQ_ID, spec->sfs_dmaq_id,
+                   EFX_DWORD_2, spec->sfs_dword[2],
+                   EFX_DWORD_1, spec->sfs_dword[1],
+                   EFX_DWORD_0, spec->sfs_dword[0]);
+               dword3 = is_udp;
+               break;
+       }
+
+       case EFX_SIENA_FILTER_TBL_RX_MAC: {
+               boolean_t is_wild = (type == EFX_SIENA_FILTER_RX_MAC_WILD);
+               EFX_POPULATE_OWORD_7(*filter,
+                   FRF_CZ_RMFT_RSS_EN,
+                   (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
+                   FRF_CZ_RMFT_SCATTER_EN,
+                   (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
+                   FRF_CZ_RMFT_RXQ_ID, spec->sfs_dmaq_id,
+                   FRF_CZ_RMFT_WILDCARD_MATCH, is_wild,
+                   FRF_CZ_RMFT_DEST_MAC_DW1, spec->sfs_dword[2],
+                   FRF_CZ_RMFT_DEST_MAC_DW0, spec->sfs_dword[1],
+                   FRF_CZ_RMFT_VLAN_ID, spec->sfs_dword[0]);
+               dword3 = is_wild;
+               break;
+       }
+
+       case EFX_SIENA_FILTER_TBL_TX_IP: {
+               boolean_t is_udp = (type == EFX_SIENA_FILTER_TX_UDP_FULL ||
+                   type == EFX_SIENA_FILTER_TX_UDP_WILD);
+               EFX_POPULATE_OWORD_5(*filter,
+                   FRF_CZ_TIFT_TCP_UDP, is_udp,
+                   FRF_CZ_TIFT_TXQ_ID, spec->sfs_dmaq_id,
+                   EFX_DWORD_2, spec->sfs_dword[2],
+                   EFX_DWORD_1, spec->sfs_dword[1],
+                   EFX_DWORD_0, spec->sfs_dword[0]);
+               dword3 = is_udp | spec->sfs_dmaq_id << 1;
+               break;
+       }
+
+       case EFX_SIENA_FILTER_TBL_TX_MAC: {
+               boolean_t is_wild = (type == EFX_SIENA_FILTER_TX_MAC_WILD);
+               EFX_POPULATE_OWORD_5(*filter,
+                   FRF_CZ_TMFT_TXQ_ID, spec->sfs_dmaq_id,
+                   FRF_CZ_TMFT_WILDCARD_MATCH, is_wild,
+                   FRF_CZ_TMFT_SRC_MAC_DW1, spec->sfs_dword[2],
+                   FRF_CZ_TMFT_SRC_MAC_DW0, spec->sfs_dword[1],
+                   FRF_CZ_TMFT_VLAN_ID, spec->sfs_dword[0]);
+               dword3 = is_wild | spec->sfs_dmaq_id << 1;
+               break;
+       }
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               return (0);
+       }
+
+       key =
+           spec->sfs_dword[0] ^
+           spec->sfs_dword[1] ^
+           spec->sfs_dword[2] ^
+           dword3;
+
+       return (key);
+}
+
+static __checkReturn           efx_rc_t
+siena_filter_push_entry(
+       __inout                 efx_nic_t *enp,
+       __in                    siena_filter_type_t type,
+       __in                    int index,
+       __in                    efx_oword_t *eop)
+{
+       efx_rc_t rc;
+
+       switch (type) {
+       case EFX_SIENA_FILTER_RX_TCP_FULL:
+       case EFX_SIENA_FILTER_RX_TCP_WILD:
+       case EFX_SIENA_FILTER_RX_UDP_FULL:
+       case EFX_SIENA_FILTER_RX_UDP_WILD:
+               EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_FILTER_TBL0, index,
+                   eop, B_TRUE);
+               break;
+
+       case EFX_SIENA_FILTER_RX_MAC_FULL:
+       case EFX_SIENA_FILTER_RX_MAC_WILD:
+               EFX_BAR_TBL_WRITEO(enp, FR_CZ_RX_MAC_FILTER_TBL0, index,
+                   eop, B_TRUE);
+               break;
+
+       case EFX_SIENA_FILTER_TX_TCP_FULL:
+       case EFX_SIENA_FILTER_TX_TCP_WILD:
+       case EFX_SIENA_FILTER_TX_UDP_FULL:
+       case EFX_SIENA_FILTER_TX_UDP_WILD:
+               EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_FILTER_TBL0, index,
+                   eop, B_TRUE);
+               break;
+
+       case EFX_SIENA_FILTER_TX_MAC_FULL:
+       case EFX_SIENA_FILTER_TX_MAC_WILD:
+               EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_MAC_FILTER_TBL0, index,
+                   eop, B_TRUE);
+               break;
+
+       default:
+               EFSYS_ASSERT(B_FALSE);
+               rc = ENOTSUP;
+               goto fail1;
+       }
+       return (0);
+
+fail1:
+       return (rc);
+}
+
+
+static __checkReturn   boolean_t
+siena_filter_equal(
+       __in            const siena_filter_spec_t *left,
+       __in            const siena_filter_spec_t *right)
+{
+       siena_filter_tbl_id_t tbl_id;
+
+       tbl_id = siena_filter_tbl_id(left->sfs_type);
+
+
+       if (left->sfs_type != right->sfs_type)
+               return (B_FALSE);
+
+       if (memcmp(left->sfs_dword, right->sfs_dword,
+               sizeof (left->sfs_dword)))
+               return (B_FALSE);
+
+       if ((tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
+               tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC) &&
+           left->sfs_dmaq_id != right->sfs_dmaq_id)
+               return (B_FALSE);
+
+       return (B_TRUE);
+}
+
+static __checkReturn   efx_rc_t
+siena_filter_search(
+       __in            siena_filter_tbl_t *sftp,
+       __in            siena_filter_spec_t *spec,
+       __in            uint32_t key,
+       __in            boolean_t for_insert,
+       __out           int *filter_index,
+       __out           unsigned int *depth_required)
+{
+       unsigned int hash, incr, filter_idx, depth;
+
+       hash = siena_filter_tbl_hash(key);
+       incr = siena_filter_tbl_increment(key);
+
+       filter_idx = hash & (sftp->sft_size - 1);
+       depth = 1;
+
+       for (;;) {
+               /*
+                * Return success if entry is used and matches this spec
+                * or entry is unused and we are trying to insert.
+                */
+               if (siena_filter_test_used(sftp, filter_idx) ?
+                   siena_filter_equal(spec,
+                   &sftp->sft_spec[filter_idx]) :
+                   for_insert) {
+                       *filter_index = filter_idx;
+                       *depth_required = depth;
+                       return (0);
+               }
+
+               /* Return failure if we reached the maximum search depth */
+               if (depth == FILTER_CTL_SRCH_MAX)
+                       return (for_insert ? EBUSY : ENOENT);
+
+               filter_idx = (filter_idx + incr) & (sftp->sft_size - 1);
+               ++depth;
+       }
+}
+
+static                 void
+siena_filter_clear_entry(
+       __in            efx_nic_t *enp,
+       __in            siena_filter_tbl_t *sftp,
+       __in            int index)
+{
+       efx_oword_t filter;
+
+       if (siena_filter_test_used(sftp, index)) {
+               siena_filter_clear_used(sftp, index);
+
+               EFX_ZERO_OWORD(filter);
+               siena_filter_push_entry(enp,
+                   sftp->sft_spec[index].sfs_type,
+                   index, &filter);
+
+               memset(&sftp->sft_spec[index],
+                   0, sizeof (sftp->sft_spec[0]));
+       }
+}
+
+                       void
+siena_filter_tbl_clear(
+       __in            efx_nic_t *enp,
+       __in            siena_filter_tbl_id_t tbl_id)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
+       int index;
+       efsys_lock_state_t state;
+
+       EFSYS_LOCK(enp->en_eslp, state);
+
+       for (index = 0; index < sftp->sft_size; ++index) {
+               siena_filter_clear_entry(enp, sftp, index);
+       }
+
+       if (sftp->sft_used == 0)
+               siena_filter_reset_search_depth(sfp, tbl_id);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+}
+
+static __checkReturn   efx_rc_t
+siena_filter_init(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp;
+       siena_filter_tbl_t *sftp;
+       int tbl_id;
+       efx_rc_t rc;
+
+       EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (siena_filter_t), sfp);
+
+       if (!sfp) {
+               rc = ENOMEM;
+               goto fail1;
+       }
+
+       enp->en_filter.ef_siena_filter = sfp;
+
+       switch (enp->en_family) {
+       case EFX_FAMILY_SIENA:
+               sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_IP];
+               sftp->sft_size = FR_AZ_RX_FILTER_TBL0_ROWS;
+
+               sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC];
+               sftp->sft_size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS;
+
+               sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP];
+               sftp->sft_size = FR_CZ_TX_FILTER_TBL0_ROWS;
+
+               sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC];
+               sftp->sft_size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS;
+               break;
+
+       default:
+               rc = ENOTSUP;
+               goto fail2;
+       }
+
+       for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
+               unsigned int bitmap_size;
+
+               sftp = &sfp->sf_tbl[tbl_id];
+               if (sftp->sft_size == 0)
+                       continue;
+
+               EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
+                   sizeof (uint32_t));
+               bitmap_size =
+                   (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
+
+               EFSYS_KMEM_ALLOC(enp->en_esip, bitmap_size, sftp->sft_bitmap);
+               if (!sftp->sft_bitmap) {
+                       rc = ENOMEM;
+                       goto fail3;
+               }
+
+               EFSYS_KMEM_ALLOC(enp->en_esip,
+                   sftp->sft_size * sizeof (*sftp->sft_spec),
+                   sftp->sft_spec);
+               if (!sftp->sft_spec) {
+                       rc = ENOMEM;
+                       goto fail4;
+               }
+               memset(sftp->sft_spec, 0,
+                   sftp->sft_size * sizeof (*sftp->sft_spec));
+       }
+
+       return (0);
+
+fail4:
+       EFSYS_PROBE(fail4);
+
+fail3:
+       EFSYS_PROBE(fail3);
+
+fail2:
+       EFSYS_PROBE(fail2);
+       siena_filter_fini(enp);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+       return (rc);
+}
+
+static                 void
+siena_filter_fini(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_id_t tbl_id;
+
+       EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+       EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
+
+       if (sfp == NULL)
+               return;
+
+       for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
+               siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
+               unsigned int bitmap_size;
+
+               EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
+                   sizeof (uint32_t));
+               bitmap_size =
+                   (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
+
+               if (sftp->sft_bitmap != NULL) {
+                       EFSYS_KMEM_FREE(enp->en_esip, bitmap_size,
+                           sftp->sft_bitmap);
+                       sftp->sft_bitmap = NULL;
+               }
+
+               if (sftp->sft_spec != NULL) {
+                       EFSYS_KMEM_FREE(enp->en_esip, sftp->sft_size *
+                           sizeof (*sftp->sft_spec), sftp->sft_spec);
+                       sftp->sft_spec = NULL;
+               }
+       }
+
+       EFSYS_KMEM_FREE(enp->en_esip, sizeof (siena_filter_t),
+           enp->en_filter.ef_siena_filter);
+}
+
+/* Restore filter state after a reset */
+static __checkReturn   efx_rc_t
+siena_filter_restore(
+       __in            efx_nic_t *enp)
+{
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_id_t tbl_id;
+       siena_filter_tbl_t *sftp;
+       siena_filter_spec_t *spec;
+       efx_oword_t filter;
+       int filter_idx;
+       efsys_lock_state_t state;
+       uint32_t key;
+       efx_rc_t rc;
+
+       EFSYS_LOCK(enp->en_eslp, state);
+
+       for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
+               sftp = &sfp->sf_tbl[tbl_id];
+               for (filter_idx = 0;
+                       filter_idx < sftp->sft_size;
+                       filter_idx++) {
+                       if (!siena_filter_test_used(sftp, filter_idx))
+                               continue;
+
+                       spec = &sftp->sft_spec[filter_idx];
+                       if ((key = siena_filter_build(&filter, spec)) == 0) {
+                               rc = EINVAL;
+                               goto fail1;
+                       }
+                       if ((rc = siena_filter_push_entry(enp,
+                                   spec->sfs_type, filter_idx, &filter)) != 0)
+                               goto fail2;
+               }
+       }
+
+       siena_filter_push_rx_limits(enp);
+       siena_filter_push_tx_limits(enp);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+
+       return (rc);
+}
+
+static  __checkReturn  efx_rc_t
+siena_filter_add(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec,
+       __in            boolean_t may_replace)
+{
+       efx_rc_t rc;
+       siena_filter_spec_t sf_spec;
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_id_t tbl_id;
+       siena_filter_tbl_t *sftp;
+       siena_filter_spec_t *saved_sf_spec;
+       efx_oword_t filter;
+       int filter_idx;
+       unsigned int depth;
+       efsys_lock_state_t state;
+       uint32_t key;
+
+
+       EFSYS_ASSERT3P(spec, !=, NULL);
+
+       if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
+               goto fail1;
+
+       tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
+       sftp = &sfp->sf_tbl[tbl_id];
+
+       if (sftp->sft_size == 0) {
+               rc = EINVAL;
+               goto fail2;
+       }
+
+       key = siena_filter_build(&filter, &sf_spec);
+
+       EFSYS_LOCK(enp->en_eslp, state);
+
+       rc = siena_filter_search(sftp, &sf_spec, key, B_TRUE,
+           &filter_idx, &depth);
+       if (rc != 0)
+               goto fail3;
+
+       EFSYS_ASSERT3U(filter_idx, <, sftp->sft_size);
+       saved_sf_spec = &sftp->sft_spec[filter_idx];
+
+       if (siena_filter_test_used(sftp, filter_idx)) {
+               if (may_replace == B_FALSE) {
+                       rc = EEXIST;
+                       goto fail4;
+               }
+       }
+       siena_filter_set_used(sftp, filter_idx);
+       *saved_sf_spec = sf_spec;
+
+       if (sfp->sf_depth[sf_spec.sfs_type] < depth) {
+               sfp->sf_depth[sf_spec.sfs_type] = depth;
+               if (tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
+                   tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC)
+                       siena_filter_push_tx_limits(enp);
+               else
+                       siena_filter_push_rx_limits(enp);
+       }
+
+       siena_filter_push_entry(enp, sf_spec.sfs_type,
+           filter_idx, &filter);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+       return (0);
+
+fail4:
+       EFSYS_PROBE(fail4);
+
+fail3:
+       EFSYS_UNLOCK(enp->en_eslp, state);
+       EFSYS_PROBE(fail3);
+
+fail2:
+       EFSYS_PROBE(fail2);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+       return (rc);
+}
+
+static  __checkReturn  efx_rc_t
+siena_filter_delete(
+       __in            efx_nic_t *enp,
+       __inout         efx_filter_spec_t *spec)
+{
+       efx_rc_t rc;
+       siena_filter_spec_t sf_spec;
+       siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
+       siena_filter_tbl_id_t tbl_id;
+       siena_filter_tbl_t *sftp;
+       efx_oword_t filter;
+       int filter_idx;
+       unsigned int depth;
+       efsys_lock_state_t state;
+       uint32_t key;
+
+       EFSYS_ASSERT3P(spec, !=, NULL);
+
+       if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
+               goto fail1;
+
+       tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
+       sftp = &sfp->sf_tbl[tbl_id];
+
+       key = siena_filter_build(&filter, &sf_spec);
+
+       EFSYS_LOCK(enp->en_eslp, state);
+
+       rc = siena_filter_search(sftp, &sf_spec, key, B_FALSE,
+           &filter_idx, &depth);
+       if (rc != 0)
+               goto fail2;
+
+       siena_filter_clear_entry(enp, sftp, filter_idx);
+       if (sftp->sft_used == 0)
+               siena_filter_reset_search_depth(sfp, tbl_id);
+
+       EFSYS_UNLOCK(enp->en_eslp, state);
+       return (0);
+
+fail2:
+       EFSYS_UNLOCK(enp->en_eslp, state);
+       EFSYS_PROBE(fail2);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+       return (rc);
+}
+
+#define        SIENA_MAX_SUPPORTED_MATCHES 4
+
+static __checkReturn   efx_rc_t
+siena_filter_supported_filters(
+       __in                            efx_nic_t *enp,
+       __out_ecount(buffer_length)     uint32_t *buffer,
+       __in                            size_t buffer_length,
+       __out                           size_t *list_lengthp)
+{
+       uint32_t index = 0;
+       uint32_t rx_matches[SIENA_MAX_SUPPORTED_MATCHES];
+       size_t list_length;
+       efx_rc_t rc;
+
+       rx_matches[index++] =
+           EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+           EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
+           EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
+
+       rx_matches[index++] =
+           EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
+           EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
+
+       if (enp->en_features & EFX_FEATURE_MAC_HEADER_FILTERS) {
+               rx_matches[index++] =
+                   EFX_FILTER_MATCH_OUTER_VID | EFX_FILTER_MATCH_LOC_MAC;
+
+               rx_matches[index++] = EFX_FILTER_MATCH_LOC_MAC;
+       }
+
+       EFSYS_ASSERT3U(index, <=, SIENA_MAX_SUPPORTED_MATCHES);
+       list_length = index;
+
+       *list_lengthp = list_length;
+
+       if (buffer_length < list_length) {
+               rc = ENOSPC;
+               goto fail1;
+       }
+
+       memcpy(buffer, rx_matches, list_length * sizeof (rx_matches[0]));
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+#undef MAX_SUPPORTED
+
+#endif /* EFSYS_OPT_SIENA */
+
+#endif /* EFSYS_OPT_FILTER */