X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fsfc%2Fbase%2Fefx_filter.c;h=412298acf357d337defde34ea06b85dd77d81582;hb=d0dcfe9824bb3386608834a1595f9154487ef24f;hp=560562d0d6f57211ff1918dccb86b6fb57c5d0a6;hpb=f9565517ff4f6e74bf40348138b5da936e936cb7;p=dpdk.git diff --git a/drivers/net/sfc/base/efx_filter.c b/drivers/net/sfc/base/efx_filter.c index 560562d0d6..412298acf3 100644 --- a/drivers/net/sfc/base/efx_filter.c +++ b/drivers/net/sfc/base/efx_filter.c @@ -1,31 +1,7 @@ -/* - * 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. +/* SPDX-License-Identifier: BSD-3-Clause * - * 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. + * Copyright (c) 2007-2018 Solarflare Communications Inc. + * All rights reserved. */ #include "efx.h" @@ -34,18 +10,97 @@ #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 || EFSYS_OPT_MEDFORD2 +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 || EFSYS_OPT_MEDFORD2 */ + __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; + efx_nic_cfg_t *encp = &(enp->en_nic_cfg); + efx_rc_t rc; EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER); EFSYS_ASSERT3P(spec, !=, NULL); EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX); + if ((spec->efs_flags & EFX_FILTER_FLAG_ACTION_MARK) && + !encp->enc_filter_action_mark_supported) { + rc = ENOTSUP; + goto fail1; + } + + if ((spec->efs_flags & EFX_FILTER_FLAG_ACTION_FLAG) && + !encp->enc_filter_action_flag_supported) { + rc = ENOTSUP; + goto fail2; + } + return (efop->efo_add(enp, spec, B_FALSE)); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); } __checkReturn efx_rc_t @@ -88,14 +143,34 @@ efx_filter_init( const efx_filter_ops_t *efop; efx_rc_t rc; - /* Check that efx_filter_spec_t is 64 bytes. */ - EFX_STATIC_ASSERT(sizeof (efx_filter_spec_t) == 64); - 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 */ + +#if EFSYS_OPT_MEDFORD2 + case EFX_FAMILY_MEDFORD2: + efop = &__efx_filter_ef10_ops; + break; +#endif /* EFSYS_OPT_MEDFORD2 */ default: EFSYS_ASSERT(0); @@ -134,11 +209,22 @@ efx_filter_fini( 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 uint32_t *list, - __out size_t *length) + __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; @@ -147,11 +233,20 @@ efx_filter_supported_filters( EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER); EFSYS_ASSERT(enp->en_efop->efo_supported_filters != NULL); - if ((rc = enp->en_efop->efo_supported_filters(enp, list, length)) != 0) + 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); @@ -206,7 +301,7 @@ efx_filter_spec_init_rx( 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_rss_context = EFX_RSS_CONTEXT_DEFAULT; spec->efs_dmaq_id = (uint16_t)erp->er_index; } @@ -300,6 +395,17 @@ efx_filter_spec_set_eth_local( return (0); } + void +efx_filter_spec_set_ether_type( + __inout efx_filter_spec_t *spec, + __in uint16_t ether_type) +{ + EFSYS_ASSERT3P(spec, !=, NULL); + + spec->efs_ether_type = ether_type; + spec->efs_match_flags |= EFX_FILTER_MATCH_ETHER_TYPE; +} + /* * Specify matching otherwise-unmatched unicast in a filter specification */ @@ -309,7 +415,7 @@ efx_filter_spec_set_uc_def( { EFSYS_ASSERT3P(spec, !=, NULL); - spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG; + spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_UCAST_DST; return (0); } @@ -322,11 +428,1127 @@ efx_filter_spec_set_mc_def( { EFSYS_ASSERT3P(spec, !=, NULL); - spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC_IG; - spec->efs_loc_mac[0] = 1; + spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_MCAST_DST; + return (0); +} + + +__checkReturn efx_rc_t +efx_filter_spec_set_encap_type( + __inout efx_filter_spec_t *spec, + __in efx_tunnel_protocol_t encap_type, + __in efx_filter_inner_frame_match_t inner_frame_match) +{ + uint32_t match_flags = EFX_FILTER_MATCH_ENCAP_TYPE; + uint8_t ip_proto; + efx_rc_t rc; + + EFSYS_ASSERT3P(spec, !=, NULL); + + switch (encap_type) { + case EFX_TUNNEL_PROTOCOL_VXLAN: + case EFX_TUNNEL_PROTOCOL_GENEVE: + ip_proto = EFX_IPPROTO_UDP; + break; + case EFX_TUNNEL_PROTOCOL_NVGRE: + ip_proto = EFX_IPPROTO_GRE; + break; + default: + EFSYS_ASSERT(0); + rc = EINVAL; + goto fail1; + } + + switch (inner_frame_match) { + case EFX_FILTER_INNER_FRAME_MATCH_UNKNOWN_MCAST_DST: + match_flags |= EFX_FILTER_MATCH_IFRM_UNKNOWN_MCAST_DST; + break; + case EFX_FILTER_INNER_FRAME_MATCH_UNKNOWN_UCAST_DST: + match_flags |= EFX_FILTER_MATCH_IFRM_UNKNOWN_UCAST_DST; + break; + case EFX_FILTER_INNER_FRAME_MATCH_OTHER: + /* This is for when specific inner frames are to be matched. */ + break; + default: + EFSYS_ASSERT(0); + rc = EINVAL; + goto fail2; + } + + spec->efs_encap_type = encap_type; + spec->efs_ip_proto = ip_proto; + spec->efs_match_flags |= (match_flags | EFX_FILTER_MATCH_IP_PROTO); + + return (0); + +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + +/* + * Specify inner and outer Ethernet address and VXLAN ID in filter + * specification. + */ + __checkReturn efx_rc_t +efx_filter_spec_set_vxlan_full( + __inout efx_filter_spec_t *spec, + __in const uint8_t *vxlan_id, + __in const uint8_t *inner_addr, + __in const uint8_t *outer_addr) +{ + EFSYS_ASSERT3P(spec, !=, NULL); + EFSYS_ASSERT3P(vxlan_id, !=, NULL); + EFSYS_ASSERT3P(inner_addr, !=, NULL); + EFSYS_ASSERT3P(outer_addr, !=, NULL); + + if ((inner_addr == NULL) && (outer_addr == NULL)) + return (EINVAL); + + if (vxlan_id != NULL) { + spec->efs_match_flags |= EFX_FILTER_MATCH_VNI_OR_VSID; + memcpy(spec->efs_vni_or_vsid, vxlan_id, EFX_VNI_OR_VSID_LEN); + } + if (outer_addr != NULL) { + spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC; + memcpy(spec->efs_loc_mac, outer_addr, EFX_MAC_ADDR_LEN); + } + if (inner_addr != NULL) { + spec->efs_match_flags |= EFX_FILTER_MATCH_IFRM_LOC_MAC; + memcpy(spec->efs_ifrm_loc_mac, inner_addr, EFX_MAC_ADDR_LEN); + } + spec->efs_match_flags |= EFX_FILTER_MATCH_ENCAP_TYPE; + spec->efs_encap_type = EFX_TUNNEL_PROTOCOL_VXLAN; + return (0); } +#if EFSYS_OPT_RX_SCALE + __checkReturn efx_rc_t +efx_filter_spec_set_rss_context( + __inout efx_filter_spec_t *spec, + __in uint32_t rss_context) +{ + efx_rc_t rc; + + EFSYS_ASSERT3P(spec, !=, NULL); + + /* The filter must have been created with EFX_FILTER_FLAG_RX_RSS. */ + if ((spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) == 0) { + rc = EINVAL; + goto fail1; + } + + spec->efs_rss_context = rss_context; + + return (0); + +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} +#endif + +#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); + + /* Siena only has one RSS context */ + if ((gen_spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) && + gen_spec->efs_rss_context != EFX_RSS_CONTEXT_DEFAULT) { + 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); + EFX_ZERO_OWORD(*filter); + 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 */