ef10_filter.c revision 293754
1200581Srdivacky/*- 2200581Srdivacky * Copyright (c) 2007-2015 Solarflare Communications Inc. 3200581Srdivacky * All rights reserved. 4200581Srdivacky * 5200581Srdivacky * Redistribution and use in source and binary forms, with or without 6200581Srdivacky * modification, are permitted provided that the following conditions are met: 7200581Srdivacky * 8200581Srdivacky * 1. Redistributions of source code must retain the above copyright notice, 9200581Srdivacky * this list of conditions and the following disclaimer. 10200581Srdivacky * 2. Redistributions in binary form must reproduce the above copyright notice, 11200581Srdivacky * this list of conditions and the following disclaimer in the documentation 12200581Srdivacky * and/or other materials provided with the distribution. 13200581Srdivacky * 14200581Srdivacky * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15234353Sdim * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16221345Sdim * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17218893Sdim * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18200581Srdivacky * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19200581Srdivacky * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20201360Srdivacky * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21218893Sdim * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22200581Srdivacky * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23200581Srdivacky * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24200581Srdivacky * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25200581Srdivacky * 26200581Srdivacky * The views and conclusions contained in the software and documentation are 27200581Srdivacky * those of the authors and should not be interpreted as representing official 28200581Srdivacky * policies, either expressed or implied, of the FreeBSD Project. 29218893Sdim */ 30218893Sdim 31234353Sdim#include <sys/cdefs.h> 32218893Sdim__FBSDID("$FreeBSD: head/sys/dev/sfxge/common/hunt_filter.c 293754 2016-01-12 13:34:55Z arybchik $"); 33218893Sdim 34200581Srdivacky#include "efsys.h" 35200581Srdivacky#include "efx.h" 36200581Srdivacky#include "efx_types.h" 37218893Sdim#include "efx_regs_mcdi.h" 38200581Srdivacky#include "efx_impl.h" 39200581Srdivacky 40200581Srdivacky#if EFSYS_OPT_HUNTINGTON 41200581Srdivacky 42200581Srdivacky#if EFSYS_OPT_FILTER 43200581Srdivacky 44243830Sdim#define HFE_SPEC(hftp, index) ((hftp)->hft_entry[(index)].hfe_spec) 45200581Srdivacky 46200581Srdivackystatic efx_filter_spec_t * 47201360Srdivackyhunt_filter_entry_spec( 48200581Srdivacky __in const hunt_filter_table_t *hftp, 49200581Srdivacky __in unsigned int index) 50201360Srdivacky{ 51200581Srdivacky return ((efx_filter_spec_t *)(HFE_SPEC(hftp, index) & 52201360Srdivacky ~(uintptr_t)EFX_HUNT_FILTER_FLAGS)); 53200581Srdivacky} 54243830Sdim 55200581Srdivackystatic boolean_t 56200581Srdivackyhunt_filter_entry_is_busy( 57200581Srdivacky __in const hunt_filter_table_t *hftp, 58200581Srdivacky __in unsigned int index) 59200581Srdivacky{ 60200581Srdivacky if (HFE_SPEC(hftp, index) & EFX_HUNT_FILTER_FLAG_BUSY) 61200581Srdivacky return (B_TRUE); 62218893Sdim else 63200581Srdivacky return (B_FALSE); 64200581Srdivacky} 65200581Srdivacky 66200581Srdivackystatic boolean_t 67200581Srdivackyhunt_filter_entry_is_auto_old( 68200581Srdivacky __in const hunt_filter_table_t *hftp, 69200581Srdivacky __in unsigned int index) 70200581Srdivacky{ 71218893Sdim if (HFE_SPEC(hftp, index) & EFX_HUNT_FILTER_FLAG_AUTO_OLD) 72200581Srdivacky return (B_TRUE); 73200581Srdivacky else 74200581Srdivacky return (B_FALSE); 75218893Sdim} 76200581Srdivacky 77218893Sdimstatic void 78218893Sdimhunt_filter_set_entry( 79200581Srdivacky __inout hunt_filter_table_t *hftp, 80218893Sdim __in unsigned int index, 81200581Srdivacky __in_opt const efx_filter_spec_t *efsp) 82200581Srdivacky{ 83200581Srdivacky HFE_SPEC(hftp, index) = (uintptr_t)efsp; 84200581Srdivacky} 85200581Srdivacky 86200581Srdivackystatic void 87200581Srdivackyhunt_filter_set_entry_busy( 88200581Srdivacky __inout hunt_filter_table_t *hftp, 89200581Srdivacky __in unsigned int index) 90200581Srdivacky{ 91200581Srdivacky HFE_SPEC(hftp, index) |= (uintptr_t)EFX_HUNT_FILTER_FLAG_BUSY; 92200581Srdivacky} 93200581Srdivacky 94218893Sdimstatic void 95218893Sdimhunt_filter_set_entry_not_busy( 96218893Sdim __inout hunt_filter_table_t *hftp, 97200581Srdivacky __in unsigned int index) 98200581Srdivacky{ 99218893Sdim HFE_SPEC(hftp, index) &= ~(uintptr_t)EFX_HUNT_FILTER_FLAG_BUSY; 100200581Srdivacky} 101218893Sdim 102200581Srdivackystatic void 103200581Srdivackyhunt_filter_set_entry_auto_old( 104218893Sdim __inout hunt_filter_table_t *hftp, 105200581Srdivacky __in unsigned int index) 106218893Sdim{ 107200581Srdivacky EFSYS_ASSERT(hunt_filter_entry_spec(hftp, index) != NULL); 108200581Srdivacky HFE_SPEC(hftp, index) |= (uintptr_t)EFX_HUNT_FILTER_FLAG_AUTO_OLD; 109200581Srdivacky} 110200581Srdivacky 111200581Srdivackystatic void 112200581Srdivackyhunt_filter_set_entry_not_auto_old( 113200581Srdivacky __inout hunt_filter_table_t *hftp, 114200581Srdivacky __in unsigned int index) 115200581Srdivacky{ 116200581Srdivacky HFE_SPEC(hftp, index) &= ~(uintptr_t)EFX_HUNT_FILTER_FLAG_AUTO_OLD; 117200581Srdivacky EFSYS_ASSERT(hunt_filter_entry_spec(hftp, index) != NULL); 118200581Srdivacky} 119200581Srdivacky 120200581Srdivacky __checkReturn efx_rc_t 121200581Srdivackyhunt_filter_init( 122200581Srdivacky __in efx_nic_t *enp) 123218893Sdim{ 124200581Srdivacky efx_rc_t rc; 125200581Srdivacky hunt_filter_table_t *hftp; 126200581Srdivacky 127218893Sdim EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON); 128200581Srdivacky 129200581Srdivacky#define MATCH_MASK(match) (EFX_MASK32(match) << EFX_LOW_BIT(match)) 130200581Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_REM_HOST == 131200581Srdivacky MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_SRC_IP)); 132200581Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_LOC_HOST == 133200581Srdivacky MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_DST_IP)); 134200581Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_REM_MAC == 135218893Sdim MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_SRC_MAC)); 136200581Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_REM_PORT == 137218893Sdim MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_SRC_PORT)); 138200581Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_LOC_MAC == 139200581Srdivacky MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_DST_MAC)); 140200581Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_LOC_PORT == 141200581Srdivacky MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_DST_PORT)); 142200581Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_ETHER_TYPE == 143200581Srdivacky MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_ETHER_TYPE)); 144200581Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_INNER_VID == 145200581Srdivacky MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_INNER_VLAN)); 146204642Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_OUTER_VID == 147204642Srdivacky MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_OUTER_VLAN)); 148200581Srdivacky EFX_STATIC_ASSERT(EFX_FILTER_MATCH_IP_PROTO == 149200581Srdivacky MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_IP_PROTO)); 150200581Srdivacky#undef MATCH_MASK 151218893Sdim 152200581Srdivacky EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (hunt_filter_table_t), hftp); 153200581Srdivacky 154200581Srdivacky if (!hftp) { 155200581Srdivacky rc = ENOMEM; 156200581Srdivacky goto fail1; 157200581Srdivacky } 158200581Srdivacky 159200581Srdivacky enp->en_filter.ef_hunt_filter_table = hftp; 160200581Srdivacky 161200581Srdivacky return (0); 162200581Srdivacky 163200581Srdivackyfail1: 164200581Srdivacky EFSYS_PROBE1(fail1, efx_rc_t, rc); 165200581Srdivacky 166200581Srdivacky return (rc); 167200581Srdivacky} 168218893Sdim 169200581Srdivacky void 170200581Srdivackyhunt_filter_fini( 171200581Srdivacky __in efx_nic_t *enp) 172218893Sdim{ 173200581Srdivacky EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON); 174200581Srdivacky 175200581Srdivacky if (enp->en_filter.ef_hunt_filter_table != NULL) { 176200581Srdivacky EFSYS_KMEM_FREE(enp->en_esip, sizeof (hunt_filter_table_t), 177218893Sdim enp->en_filter.ef_hunt_filter_table); 178200581Srdivacky } 179200581Srdivacky} 180200581Srdivacky 181200581Srdivackystatic __checkReturn efx_rc_t 182200581Srdivackyefx_mcdi_filter_op_add( 183200581Srdivacky __in efx_nic_t *enp, 184200581Srdivacky __in efx_filter_spec_t *spec, 185200581Srdivacky __in unsigned int filter_op, 186200581Srdivacky __inout hunt_filter_handle_t *handle) 187200581Srdivacky{ 188218893Sdim efx_mcdi_req_t req; 189218893Sdim uint8_t payload[MAX(MC_CMD_FILTER_OP_IN_LEN, 190234353Sdim MC_CMD_FILTER_OP_OUT_LEN)]; 191218893Sdim uint32_t match_fields = 0; 192200581Srdivacky efx_rc_t rc; 193218893Sdim 194218893Sdim memset(payload, 0, sizeof (payload)); 195218893Sdim req.emr_cmd = MC_CMD_FILTER_OP; 196200581Srdivacky req.emr_in_buf = payload; 197218893Sdim req.emr_in_length = MC_CMD_FILTER_OP_IN_LEN; 198200581Srdivacky req.emr_out_buf = payload; 199200581Srdivacky req.emr_out_length = MC_CMD_FILTER_OP_OUT_LEN; 200218893Sdim 201218893Sdim switch (filter_op) { 202218893Sdim case MC_CMD_FILTER_OP_IN_OP_REPLACE: 203218893Sdim MCDI_IN_SET_DWORD(req, FILTER_OP_IN_HANDLE_LO, 204200581Srdivacky handle->hfh_lo); 205200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_HANDLE_HI, 206200581Srdivacky handle->hfh_hi); 207218893Sdim /* Fall through */ 208218893Sdim case MC_CMD_FILTER_OP_IN_OP_INSERT: 209218893Sdim case MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE: 210218893Sdim MCDI_IN_SET_DWORD(req, FILTER_OP_IN_OP, filter_op); 211218893Sdim break; 212200581Srdivacky default: 213200581Srdivacky EFSYS_ASSERT(0); 214200581Srdivacky rc = EINVAL; 215218893Sdim goto fail1; 216200581Srdivacky } 217200581Srdivacky 218200581Srdivacky if (spec->efs_match_flags & EFX_FILTER_MATCH_LOC_MAC_IG) { 219200581Srdivacky /* 220200581Srdivacky * The LOC_MAC_IG match flag can represent unknown unicast 221204642Srdivacky * or multicast filters - use the MAC address to distinguish 222200581Srdivacky * them. 223218893Sdim */ 224200581Srdivacky if (EFX_MAC_ADDR_IS_MULTICAST(spec->efs_loc_mac)) 225200581Srdivacky match_fields |= 1U << 226200581Srdivacky MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_LBN; 227218893Sdim else 228200581Srdivacky match_fields |= 1U << 229200581Srdivacky MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_LBN; 230218893Sdim } 231200581Srdivacky 232234353Sdim match_fields |= spec->efs_match_flags & (~EFX_FILTER_MATCH_LOC_MAC_IG); 233200581Srdivacky 234200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_PORT_ID, 235218893Sdim EVB_PORT_ID_ASSIGNED); 236200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_MATCH_FIELDS, 237200581Srdivacky match_fields); 238218893Sdim MCDI_IN_SET_DWORD(req, FILTER_OP_IN_RX_DEST, 239200581Srdivacky MC_CMD_FILTER_OP_IN_RX_DEST_HOST); 240200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_RX_QUEUE, 241200581Srdivacky spec->efs_dmaq_id); 242200581Srdivacky if (spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) { 243200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_RX_CONTEXT, 244200581Srdivacky spec->efs_rss_context); 245200581Srdivacky } 246204642Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_RX_MODE, 247204642Srdivacky spec->efs_flags & EFX_FILTER_FLAG_RX_RSS ? 248200581Srdivacky MC_CMD_FILTER_OP_IN_RX_MODE_RSS : 249200581Srdivacky MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE); 250200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_TX_DEST, 251200581Srdivacky MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT); 252200581Srdivacky 253200581Srdivacky if (filter_op != MC_CMD_FILTER_OP_IN_OP_REPLACE) { 254200581Srdivacky /* 255200581Srdivacky * NOTE: Unlike most MCDI requests, the filter fields 256200581Srdivacky * are presented in network (big endian) byte order. 257200581Srdivacky */ 258200581Srdivacky memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_IN_SRC_MAC), 259200581Srdivacky spec->efs_rem_mac, EFX_MAC_ADDR_LEN); 260218893Sdim memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_IN_DST_MAC), 261200581Srdivacky spec->efs_loc_mac, EFX_MAC_ADDR_LEN); 262200581Srdivacky 263200581Srdivacky MCDI_IN_SET_WORD(req, FILTER_OP_IN_SRC_PORT, 264200581Srdivacky __CPU_TO_BE_16(spec->efs_rem_port)); 265200581Srdivacky MCDI_IN_SET_WORD(req, FILTER_OP_IN_DST_PORT, 266200581Srdivacky __CPU_TO_BE_16(spec->efs_loc_port)); 267200581Srdivacky 268218893Sdim MCDI_IN_SET_WORD(req, FILTER_OP_IN_ETHER_TYPE, 269204642Srdivacky __CPU_TO_BE_16(spec->efs_ether_type)); 270200581Srdivacky 271218893Sdim MCDI_IN_SET_WORD(req, FILTER_OP_IN_INNER_VLAN, 272200581Srdivacky __CPU_TO_BE_16(spec->efs_inner_vid)); 273200581Srdivacky MCDI_IN_SET_WORD(req, FILTER_OP_IN_OUTER_VLAN, 274200581Srdivacky __CPU_TO_BE_16(spec->efs_outer_vid)); 275200581Srdivacky 276200581Srdivacky /* IP protocol (in low byte, high byte is zero) */ 277200581Srdivacky MCDI_IN_SET_BYTE(req, FILTER_OP_IN_IP_PROTO, 278200581Srdivacky spec->efs_ip_proto); 279218893Sdim 280200581Srdivacky EFX_STATIC_ASSERT(sizeof (spec->efs_rem_host) == 281200581Srdivacky MC_CMD_FILTER_OP_IN_SRC_IP_LEN); 282200581Srdivacky EFX_STATIC_ASSERT(sizeof (spec->efs_loc_host) == 283200581Srdivacky MC_CMD_FILTER_OP_IN_DST_IP_LEN); 284200581Srdivacky 285200581Srdivacky memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_IN_SRC_IP), 286218893Sdim &spec->efs_rem_host.eo_byte[0], 287200581Srdivacky MC_CMD_FILTER_OP_IN_SRC_IP_LEN); 288234353Sdim memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_IN_DST_IP), 289200581Srdivacky &spec->efs_loc_host.eo_byte[0], 290200581Srdivacky MC_CMD_FILTER_OP_IN_DST_IP_LEN); 291200581Srdivacky } 292200581Srdivacky 293200581Srdivacky efx_mcdi_execute(enp, &req); 294200581Srdivacky 295200581Srdivacky if (req.emr_rc != 0) { 296200581Srdivacky rc = req.emr_rc; 297200581Srdivacky goto fail2; 298218893Sdim } 299200581Srdivacky 300200581Srdivacky if (req.emr_out_length_used < MC_CMD_FILTER_OP_OUT_LEN) { 301200581Srdivacky rc = EMSGSIZE; 302200581Srdivacky goto fail3; 303200581Srdivacky } 304200581Srdivacky 305204642Srdivacky handle->hfh_lo = MCDI_OUT_DWORD(req, FILTER_OP_OUT_HANDLE_LO); 306204642Srdivacky handle->hfh_hi = MCDI_OUT_DWORD(req, FILTER_OP_OUT_HANDLE_HI); 307200581Srdivacky 308200581Srdivacky return (0); 309218893Sdim 310200581Srdivackyfail3: 311200581Srdivacky EFSYS_PROBE(fail3); 312218893Sdimfail2: 313200581Srdivacky EFSYS_PROBE(fail2); 314200581Srdivackyfail1: 315200581Srdivacky EFSYS_PROBE1(fail1, efx_rc_t, rc); 316200581Srdivacky 317200581Srdivacky return (rc); 318200581Srdivacky 319204642Srdivacky} 320204642Srdivacky 321204642Srdivackystatic __checkReturn efx_rc_t 322204642Srdivackyefx_mcdi_filter_op_delete( 323204642Srdivacky __in efx_nic_t *enp, 324200581Srdivacky __in unsigned int filter_op, 325204642Srdivacky __inout hunt_filter_handle_t *handle) 326200581Srdivacky{ 327204642Srdivacky efx_mcdi_req_t req; 328204642Srdivacky uint8_t payload[MAX(MC_CMD_FILTER_OP_IN_LEN, 329204642Srdivacky MC_CMD_FILTER_OP_OUT_LEN)]; 330204642Srdivacky efx_rc_t rc; 331204642Srdivacky 332204642Srdivacky memset(payload, 0, sizeof (payload)); 333204642Srdivacky req.emr_cmd = MC_CMD_FILTER_OP; 334204642Srdivacky req.emr_in_buf = payload; 335200581Srdivacky req.emr_in_length = MC_CMD_FILTER_OP_IN_LEN; 336200581Srdivacky req.emr_out_buf = payload; 337200581Srdivacky req.emr_out_length = MC_CMD_FILTER_OP_OUT_LEN; 338200581Srdivacky 339200581Srdivacky switch (filter_op) { 340200581Srdivacky case MC_CMD_FILTER_OP_IN_OP_REMOVE: 341200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_OP, 342200581Srdivacky MC_CMD_FILTER_OP_IN_OP_REMOVE); 343200581Srdivacky break; 344200581Srdivacky case MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE: 345200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_OP, 346200581Srdivacky MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE); 347200581Srdivacky break; 348200581Srdivacky default: 349200581Srdivacky EFSYS_ASSERT(0); 350218893Sdim rc = EINVAL; 351200581Srdivacky goto fail1; 352200581Srdivacky } 353218893Sdim 354200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_HANDLE_LO, handle->hfh_lo); 355200581Srdivacky MCDI_IN_SET_DWORD(req, FILTER_OP_IN_HANDLE_HI, handle->hfh_hi); 356218893Sdim 357200581Srdivacky efx_mcdi_execute(enp, &req); 358200581Srdivacky 359200581Srdivacky if (req.emr_rc != 0) { 360200581Srdivacky rc = req.emr_rc; 361200581Srdivacky goto fail2; 362200581Srdivacky } 363200581Srdivacky 364200581Srdivacky if (req.emr_out_length_used < MC_CMD_FILTER_OP_OUT_LEN) { 365200581Srdivacky rc = EMSGSIZE; 366200581Srdivacky goto fail3; 367200581Srdivacky } 368200581Srdivacky 369200581Srdivacky return (0); 370200581Srdivacky 371200581Srdivackyfail3: 372200581Srdivacky EFSYS_PROBE(fail3); 373200581Srdivacky 374200581Srdivackyfail2: 375204642Srdivacky EFSYS_PROBE(fail2); 376204642Srdivackyfail1: 377204642Srdivacky EFSYS_PROBE1(fail1, efx_rc_t, rc); 378200581Srdivacky 379200581Srdivacky return (rc); 380200581Srdivacky} 381200581Srdivacky 382218893Sdimstatic __checkReturn boolean_t 383218893Sdimhunt_filter_equal( 384218893Sdim __in const efx_filter_spec_t *left, 385234353Sdim __in const efx_filter_spec_t *right) 386218893Sdim{ 387200581Srdivacky /* FIXME: Consider rx vs tx filters (look at efs_flags) */ 388200581Srdivacky if (left->efs_match_flags != right->efs_match_flags) 389218893Sdim return (B_FALSE); 390218893Sdim if (!EFX_OWORD_IS_EQUAL(left->efs_rem_host, right->efs_rem_host)) 391218893Sdim return (B_FALSE); 392218893Sdim if (!EFX_OWORD_IS_EQUAL(left->efs_loc_host, right->efs_loc_host)) 393218893Sdim return (B_FALSE); 394218893Sdim if (memcmp(left->efs_rem_mac, right->efs_rem_mac, EFX_MAC_ADDR_LEN)) 395200581Srdivacky return (B_FALSE); 396200581Srdivacky if (memcmp(left->efs_loc_mac, right->efs_loc_mac, EFX_MAC_ADDR_LEN)) 397200581Srdivacky return (B_FALSE); 398218893Sdim if (left->efs_rem_port != right->efs_rem_port) 399200581Srdivacky return (B_FALSE); 400200581Srdivacky if (left->efs_loc_port != right->efs_loc_port) 401200581Srdivacky return (B_FALSE); 402200581Srdivacky if (left->efs_inner_vid != right->efs_inner_vid) 403200581Srdivacky return (B_FALSE); 404200581Srdivacky if (left->efs_outer_vid != right->efs_outer_vid) 405200581Srdivacky return (B_FALSE); 406200581Srdivacky if (left->efs_ether_type != right->efs_ether_type) 407200581Srdivacky return (B_FALSE); 408200581Srdivacky if (left->efs_ip_proto != right->efs_ip_proto) 409218893Sdim return (B_FALSE); 410218893Sdim 411226633Sdim return (B_TRUE); 412226633Sdim 413226633Sdim} 414200581Srdivacky 415200581Srdivackystatic __checkReturn boolean_t 416200581Srdivackyhunt_filter_same_dest( 417200581Srdivacky __in const efx_filter_spec_t *left, 418218893Sdim __in const efx_filter_spec_t *right) 419200581Srdivacky{ 420200581Srdivacky if ((left->efs_flags & EFX_FILTER_FLAG_RX_RSS) && 421200581Srdivacky (right->efs_flags & EFX_FILTER_FLAG_RX_RSS)) { 422200581Srdivacky if (left->efs_rss_context == right->efs_rss_context) 423218893Sdim return (B_TRUE); 424200581Srdivacky } else if ((~(left->efs_flags) & EFX_FILTER_FLAG_RX_RSS) && 425200581Srdivacky (~(right->efs_flags) & EFX_FILTER_FLAG_RX_RSS)) { 426200581Srdivacky if (left->efs_dmaq_id == right->efs_dmaq_id) 427200581Srdivacky return (B_TRUE); 428200581Srdivacky } 429200581Srdivacky return (B_FALSE); 430200581Srdivacky} 431218893Sdim 432200581Srdivackystatic __checkReturn uint32_t 433200581Srdivackyhunt_filter_hash( 434200581Srdivacky __in efx_filter_spec_t *spec) 435200581Srdivacky{ 436200581Srdivacky EFX_STATIC_ASSERT((sizeof (efx_filter_spec_t) % sizeof (uint32_t)) 437200581Srdivacky == 0); 438200581Srdivacky EFX_STATIC_ASSERT((EFX_FIELD_OFFSET(efx_filter_spec_t, efs_outer_vid) % 439200581Srdivacky sizeof (uint32_t)) == 0); 440200581Srdivacky 441218893Sdim /* 442200581Srdivacky * As the area of the efx_filter_spec_t we need to hash is DWORD 443200581Srdivacky * aligned and an exact number of DWORDs in size we can use the 444 * optimised efx_hash_dwords() rather than efx_hash_bytes() 445 */ 446 return (efx_hash_dwords((const uint32_t *)&spec->efs_outer_vid, 447 (sizeof (efx_filter_spec_t) - 448 EFX_FIELD_OFFSET(efx_filter_spec_t, efs_outer_vid)) / 449 sizeof (uint32_t), 0)); 450} 451 452/* 453 * Decide whether a filter should be exclusive or else should allow 454 * delivery to additional recipients. Currently we decide that 455 * filters for specific local unicast MAC and IP addresses are 456 * exclusive. 457 */ 458static __checkReturn boolean_t 459hunt_filter_is_exclusive( 460 __in efx_filter_spec_t *spec) 461{ 462 if ((spec->efs_match_flags & EFX_FILTER_MATCH_LOC_MAC) && 463 !EFX_MAC_ADDR_IS_MULTICAST(spec->efs_loc_mac)) 464 return (B_TRUE); 465 466 if ((spec->efs_match_flags & 467 (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_LOC_HOST)) == 468 (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_LOC_HOST)) { 469 if ((spec->efs_ether_type == EFX_ETHER_TYPE_IPV4) && 470 ((spec->efs_loc_host.eo_u8[0] & 0xf) != 0xe)) 471 return (B_TRUE); 472 if ((spec->efs_ether_type == EFX_ETHER_TYPE_IPV6) && 473 (spec->efs_loc_host.eo_u8[0] != 0xff)) 474 return (B_TRUE); 475 } 476 477 return (B_FALSE); 478} 479 480 __checkReturn efx_rc_t 481hunt_filter_restore( 482 __in efx_nic_t *enp) 483{ 484 int tbl_id; 485 efx_filter_spec_t *spec; 486 hunt_filter_table_t *hftp = enp->en_filter.ef_hunt_filter_table; 487 boolean_t restoring; 488 int state; 489 efx_rc_t rc; 490 491 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON); 492 493 for (tbl_id = 0; tbl_id < EFX_HUNT_FILTER_TBL_ROWS; tbl_id++) { 494 495 EFSYS_LOCK(enp->en_eslp, state); 496 497 spec = hunt_filter_entry_spec(hftp, tbl_id); 498 if (spec == NULL) { 499 restoring = B_FALSE; 500 } else if (hunt_filter_entry_is_busy(hftp, tbl_id)) { 501 /* Ignore busy entries. */ 502 restoring = B_FALSE; 503 } else { 504 hunt_filter_set_entry_busy(hftp, tbl_id); 505 restoring = B_TRUE; 506 } 507 508 EFSYS_UNLOCK(enp->en_eslp, state); 509 510 if (restoring == B_FALSE) 511 continue; 512 513 if (hunt_filter_is_exclusive(spec)) { 514 rc = efx_mcdi_filter_op_add(enp, spec, 515 MC_CMD_FILTER_OP_IN_OP_INSERT, 516 &hftp->hft_entry[tbl_id].hfe_handle); 517 } else { 518 rc = efx_mcdi_filter_op_add(enp, spec, 519 MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE, 520 &hftp->hft_entry[tbl_id].hfe_handle); 521 } 522 523 if (rc != 0) 524 goto fail1; 525 526 EFSYS_LOCK(enp->en_eslp, state); 527 528 hunt_filter_set_entry_not_busy(hftp, tbl_id); 529 530 EFSYS_UNLOCK(enp->en_eslp, state); 531 } 532 533 return (0); 534 535fail1: 536 EFSYS_PROBE1(fail1, efx_rc_t, rc); 537 538 return (rc); 539} 540 541/* 542 * An arbitrary search limit for the software hash table. As per the linux net 543 * driver. 544 */ 545#define EFX_HUNT_FILTER_SEARCH_LIMIT 200 546 547static __checkReturn efx_rc_t 548hunt_filter_add_internal( 549 __in efx_nic_t *enp, 550 __inout efx_filter_spec_t *spec, 551 __in boolean_t may_replace, 552 __out_opt uint32_t *filter_id) 553{ 554 efx_rc_t rc; 555 hunt_filter_table_t *hftp = enp->en_filter.ef_hunt_filter_table; 556 efx_filter_spec_t *saved_spec; 557 uint32_t hash; 558 unsigned int depth; 559 int ins_index; 560 boolean_t replacing = B_FALSE; 561 unsigned int i; 562 int state; 563 boolean_t locked = B_FALSE; 564 565 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON); 566 567#if EFSYS_OPT_RX_SCALE 568 spec->efs_rss_context = enp->en_rss_context; 569#endif 570 571 hash = hunt_filter_hash(spec); 572 573 /* 574 * FIXME: Add support for inserting filters of different priorities 575 * and removing lower priority multicast filters (bug 42378) 576 */ 577 578 /* 579 * Find any existing filters with the same match tuple or 580 * else a free slot to insert at. If any of them are busy, 581 * we have to wait and retry. 582 */ 583 for (;;) { 584 ins_index = -1; 585 depth = 1; 586 EFSYS_LOCK(enp->en_eslp, state); 587 locked = B_TRUE; 588 589 for (;;) { 590 i = (hash + depth) & (EFX_HUNT_FILTER_TBL_ROWS - 1); 591 saved_spec = hunt_filter_entry_spec(hftp, i); 592 593 if (!saved_spec) { 594 if (ins_index < 0) { 595 ins_index = i; 596 } 597 } else if (hunt_filter_equal(spec, saved_spec)) { 598 if (hunt_filter_entry_is_busy(hftp, i)) 599 break; 600 if (saved_spec->efs_priority 601 == EFX_FILTER_PRI_AUTO) { 602 ins_index = i; 603 goto found; 604 } else if (hunt_filter_is_exclusive(spec)) { 605 if (may_replace) { 606 ins_index = i; 607 goto found; 608 } else { 609 rc = EEXIST; 610 goto fail1; 611 } 612 } 613 614 /* Leave existing */ 615 } 616 617 /* 618 * Once we reach the maximum search depth, use 619 * the first suitable slot or return EBUSY if 620 * there was none. 621 */ 622 if (depth == EFX_HUNT_FILTER_SEARCH_LIMIT) { 623 if (ins_index < 0) { 624 rc = EBUSY; 625 goto fail2; 626 } 627 goto found; 628 } 629 depth++; 630 } 631 EFSYS_UNLOCK(enp->en_eslp, state); 632 locked = B_FALSE; 633 } 634 635found: 636 /* 637 * Create a software table entry if necessary, and mark it 638 * busy. We might yet fail to insert, but any attempt to 639 * insert a conflicting filter while we're waiting for the 640 * firmware must find the busy entry. 641 */ 642 saved_spec = hunt_filter_entry_spec(hftp, ins_index); 643 if (saved_spec) { 644 if (saved_spec->efs_priority == EFX_FILTER_PRI_AUTO) { 645 /* This is a filter we are refreshing */ 646 hunt_filter_set_entry_not_auto_old(hftp, ins_index); 647 goto out_unlock; 648 649 } 650 replacing = B_TRUE; 651 } else { 652 EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec), saved_spec); 653 if (!saved_spec) { 654 rc = ENOMEM; 655 goto fail3; 656 } 657 *saved_spec = *spec; 658 hunt_filter_set_entry(hftp, ins_index, saved_spec); 659 } 660 hunt_filter_set_entry_busy(hftp, ins_index); 661 662 EFSYS_UNLOCK(enp->en_eslp, state); 663 locked = B_FALSE; 664 665 /* 666 * On replacing the filter handle may change after after a successful 667 * replace operation. 668 */ 669 if (replacing) { 670 rc = efx_mcdi_filter_op_add(enp, spec, 671 MC_CMD_FILTER_OP_IN_OP_REPLACE, 672 &hftp->hft_entry[ins_index].hfe_handle); 673 } else if (hunt_filter_is_exclusive(spec)) { 674 rc = efx_mcdi_filter_op_add(enp, spec, 675 MC_CMD_FILTER_OP_IN_OP_INSERT, 676 &hftp->hft_entry[ins_index].hfe_handle); 677 } else { 678 rc = efx_mcdi_filter_op_add(enp, spec, 679 MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE, 680 &hftp->hft_entry[ins_index].hfe_handle); 681 } 682 683 if (rc != 0) 684 goto fail4; 685 686 EFSYS_LOCK(enp->en_eslp, state); 687 locked = B_TRUE; 688 689 if (replacing) { 690 /* Update the fields that may differ */ 691 saved_spec->efs_priority = spec->efs_priority; 692 saved_spec->efs_flags = spec->efs_flags; 693 saved_spec->efs_rss_context = spec->efs_rss_context; 694 saved_spec->efs_dmaq_id = spec->efs_dmaq_id; 695 } 696 697 hunt_filter_set_entry_not_busy(hftp, ins_index); 698 699out_unlock: 700 701 EFSYS_UNLOCK(enp->en_eslp, state); 702 locked = B_FALSE; 703 704 if (filter_id) 705 *filter_id = ins_index; 706 707 return (0); 708 709fail4: 710 EFSYS_PROBE(fail4); 711 712 if (!replacing) { 713 EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), saved_spec); 714 saved_spec = NULL; 715 } 716 hunt_filter_set_entry_not_busy(hftp, ins_index); 717 hunt_filter_set_entry(hftp, ins_index, NULL); 718 719fail3: 720 EFSYS_PROBE(fail3); 721 722fail2: 723 EFSYS_PROBE(fail2); 724 725fail1: 726 EFSYS_PROBE1(fail1, efx_rc_t, rc); 727 728 if (locked) 729 EFSYS_UNLOCK(enp->en_eslp, state); 730 731 return (rc); 732} 733 734 __checkReturn efx_rc_t 735hunt_filter_add( 736 __in efx_nic_t *enp, 737 __inout efx_filter_spec_t *spec, 738 __in boolean_t may_replace) 739{ 740 efx_rc_t rc; 741 742 rc = hunt_filter_add_internal(enp, spec, may_replace, NULL); 743 if (rc != 0) 744 goto fail1; 745 746 return (0); 747 748fail1: 749 EFSYS_PROBE1(fail1, efx_rc_t, rc); 750 751 return (rc); 752} 753 754 755static __checkReturn efx_rc_t 756hunt_filter_delete_internal( 757 __in efx_nic_t *enp, 758 __in uint32_t filter_id) 759{ 760 efx_rc_t rc; 761 hunt_filter_table_t *table = enp->en_filter.ef_hunt_filter_table; 762 efx_filter_spec_t *spec; 763 int state; 764 uint32_t filter_idx = filter_id % EFX_HUNT_FILTER_TBL_ROWS; 765 766 /* 767 * Find the software table entry and mark it busy. Don't 768 * remove it yet; any attempt to update while we're waiting 769 * for the firmware must find the busy entry. 770 * 771 * FIXME: What if the busy flag is never cleared? 772 */ 773 EFSYS_LOCK(enp->en_eslp, state); 774 while (hunt_filter_entry_is_busy(table, filter_idx)) { 775 EFSYS_UNLOCK(enp->en_eslp, state); 776 EFSYS_SPIN(1); 777 EFSYS_LOCK(enp->en_eslp, state); 778 } 779 if ((spec = hunt_filter_entry_spec(table, filter_idx)) != NULL) { 780 hunt_filter_set_entry_busy(table, filter_idx); 781 } 782 EFSYS_UNLOCK(enp->en_eslp, state); 783 784 if (spec == NULL) { 785 rc = ENOENT; 786 goto fail1; 787 } 788 789 /* 790 * Try to remove the hardware filter. This may fail if the MC has 791 * rebooted (which frees all hardware filter resources). 792 */ 793 if (hunt_filter_is_exclusive(spec)) { 794 rc = efx_mcdi_filter_op_delete(enp, 795 MC_CMD_FILTER_OP_IN_OP_REMOVE, 796 &table->hft_entry[filter_idx].hfe_handle); 797 } else { 798 rc = efx_mcdi_filter_op_delete(enp, 799 MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE, 800 &table->hft_entry[filter_idx].hfe_handle); 801 } 802 803 /* Free the software table entry */ 804 EFSYS_LOCK(enp->en_eslp, state); 805 hunt_filter_set_entry_not_busy(table, filter_idx); 806 hunt_filter_set_entry(table, filter_idx, NULL); 807 EFSYS_UNLOCK(enp->en_eslp, state); 808 809 EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec); 810 811 /* Check result of hardware filter removal */ 812 if (rc != 0) 813 goto fail2; 814 815 return (0); 816 817fail2: 818 EFSYS_PROBE(fail2); 819 820fail1: 821 EFSYS_PROBE1(fail1, efx_rc_t, rc); 822 823 return (rc); 824} 825 826 __checkReturn efx_rc_t 827hunt_filter_delete( 828 __in efx_nic_t *enp, 829 __inout efx_filter_spec_t *spec) 830{ 831 efx_rc_t rc; 832 hunt_filter_table_t *table = enp->en_filter.ef_hunt_filter_table; 833 efx_filter_spec_t *saved_spec; 834 unsigned int hash; 835 unsigned int depth; 836 unsigned int i; 837 int state; 838 boolean_t locked = B_FALSE; 839 840 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON); 841 842 hash = hunt_filter_hash(spec); 843 844 EFSYS_LOCK(enp->en_eslp, state); 845 locked = B_TRUE; 846 847 depth = 1; 848 for (;;) { 849 i = (hash + depth) & (EFX_HUNT_FILTER_TBL_ROWS - 1); 850 saved_spec = hunt_filter_entry_spec(table, i); 851 if (saved_spec && hunt_filter_equal(spec, saved_spec) && 852 hunt_filter_same_dest(spec, saved_spec)) { 853 break; 854 } 855 if (depth == EFX_HUNT_FILTER_SEARCH_LIMIT) { 856 rc = ENOENT; 857 goto fail1; 858 } 859 depth++; 860 } 861 862 EFSYS_UNLOCK(enp->en_eslp, state); 863 locked = B_FALSE; 864 865 rc = hunt_filter_delete_internal(enp, i); 866 if (rc != 0) 867 goto fail2; 868 869 return (0); 870 871fail2: 872 EFSYS_PROBE(fail2); 873 874fail1: 875 EFSYS_PROBE1(fail1, efx_rc_t, rc); 876 877 if (locked) 878 EFSYS_UNLOCK(enp->en_eslp, state); 879 880 return (rc); 881} 882 883static __checkReturn efx_rc_t 884efx_mcdi_get_parser_disp_info( 885 __in efx_nic_t *enp, 886 __out uint32_t *list, 887 __out size_t *length) 888{ 889 efx_mcdi_req_t req; 890 uint8_t payload[MAX(MC_CMD_GET_PARSER_DISP_INFO_IN_LEN, 891 MC_CMD_GET_PARSER_DISP_INFO_OUT_LENMAX)]; 892 efx_rc_t rc; 893 uint32_t i; 894 boolean_t support_unknown_ucast = B_FALSE; 895 boolean_t support_unknown_mcast = B_FALSE; 896 897 (void) memset(payload, 0, sizeof (payload)); 898 req.emr_cmd = MC_CMD_GET_PARSER_DISP_INFO; 899 req.emr_in_buf = payload; 900 req.emr_in_length = MC_CMD_GET_PARSER_DISP_INFO_IN_LEN; 901 req.emr_out_buf = payload; 902 req.emr_out_length = MC_CMD_GET_PARSER_DISP_INFO_OUT_LENMAX; 903 904 MCDI_IN_SET_DWORD(req, GET_PARSER_DISP_INFO_OUT_OP, 905 MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_SUPPORTED_RX_MATCHES); 906 907 efx_mcdi_execute(enp, &req); 908 909 if (req.emr_rc != 0) { 910 rc = req.emr_rc; 911 goto fail1; 912 } 913 914 *length = MCDI_OUT_DWORD(req, 915 GET_PARSER_DISP_INFO_OUT_NUM_SUPPORTED_MATCHES); 916 917 if (req.emr_out_length_used < 918 MC_CMD_GET_PARSER_DISP_INFO_OUT_LEN(*length)) { 919 rc = EMSGSIZE; 920 goto fail2; 921 } 922 923 memcpy(list, 924 MCDI_OUT2(req, 925 uint32_t, 926 GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES), 927 (*length) * sizeof (uint32_t)); 928 EFX_STATIC_ASSERT(sizeof (uint32_t) == 929 MC_CMD_GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES_LEN); 930 931 /* 932 * Remove UNKNOWN UCAST and MCAST flags, and if both are present, change 933 * the lower priority one to LOC_MAC_IG. 934 */ 935 for (i = 0; i < *length; i++) { 936 if (list[i] & MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_LBN) { 937 list[i] &= 938 (~MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_LBN); 939 support_unknown_ucast = B_TRUE; 940 } 941 if (list[i] & MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_LBN) { 942 list[i] &= 943 (~MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_LBN); 944 support_unknown_mcast = B_TRUE; 945 } 946 947 if (support_unknown_ucast && support_unknown_mcast) { 948 list[i] &= EFX_FILTER_MATCH_LOC_MAC_IG; 949 break; 950 } 951 } 952 953 return (0); 954 955fail2: 956 EFSYS_PROBE(fail2); 957fail1: 958 EFSYS_PROBE1(fail1, efx_rc_t, rc); 959 960 return (rc); 961} 962 963 __checkReturn efx_rc_t 964hunt_filter_supported_filters( 965 __in efx_nic_t *enp, 966 __out uint32_t *list, 967 __out size_t *length) 968{ 969 efx_rc_t rc; 970 971 if ((rc = efx_mcdi_get_parser_disp_info(enp, list, length) != 0)) 972 goto fail1; 973 974 return (0); 975 976fail1: 977 EFSYS_PROBE1(fail1, efx_rc_t, rc); 978 979 return (rc); 980} 981 982static __checkReturn efx_rc_t 983hunt_filter_unicast_refresh( 984 __in efx_nic_t *enp, 985 __in_ecount(6) uint8_t const *addr, 986 __in boolean_t all_unicst, 987 __in efx_filter_flag_t filter_flags) 988{ 989 hunt_filter_table_t *hftp = enp->en_filter.ef_hunt_filter_table; 990 efx_filter_spec_t spec; 991 efx_rc_t rc; 992 993 if (all_unicst == B_TRUE) 994 goto use_uc_def; 995 996 /* Insert the filter for the local station address */ 997 efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO, 998 filter_flags, 999 hftp->hft_default_rxq); 1000 efx_filter_spec_set_eth_local(&spec, EFX_FILTER_SPEC_VID_UNSPEC, addr); 1001 1002 rc = hunt_filter_add_internal(enp, &spec, B_TRUE, 1003 &hftp->hft_unicst_filter_index); 1004 if (rc != 0) { 1005 /* 1006 * Fall back to an unknown filter. We may be able to subscribe 1007 * to it even if we couldn't insert the unicast filter. 1008 */ 1009 goto use_uc_def; 1010 } 1011 hftp->hft_unicst_filter_set = B_TRUE; 1012 1013 return (0); 1014 1015use_uc_def: 1016 /* Insert the unknown unicast filter */ 1017 efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO, 1018 filter_flags, 1019 hftp->hft_default_rxq); 1020 efx_filter_spec_set_uc_def(&spec); 1021 rc = hunt_filter_add_internal(enp, &spec, B_TRUE, 1022 &hftp->hft_unicst_filter_index); 1023 if (rc != 0) 1024 goto fail1; 1025 1026 hftp->hft_unicst_filter_set = B_TRUE; 1027 1028 return (0); 1029 1030fail1: 1031 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1032 1033 if (hftp->hft_unicst_filter_set != B_FALSE) { 1034 (void) hunt_filter_delete_internal(enp, 1035 hftp->hft_unicst_filter_index); 1036 1037 hftp->hft_unicst_filter_set = B_FALSE; 1038 } 1039 1040 return (rc); 1041} 1042 1043static __checkReturn efx_rc_t 1044hunt_filter_multicast_refresh( 1045 __in efx_nic_t *enp, 1046 __in boolean_t mulcst, 1047 __in boolean_t all_mulcst, 1048 __in boolean_t brdcst, 1049 __in_ecount(6*count) uint8_t const *addrs, 1050 __in int count, 1051 __in efx_filter_flag_t filter_flags) 1052{ 1053 hunt_filter_table_t *hftp = enp->en_filter.ef_hunt_filter_table; 1054 efx_filter_spec_t spec; 1055 uint8_t addr[6]; 1056 unsigned i; 1057 efx_rc_t rc; 1058 1059 if (all_mulcst == B_TRUE) 1060 goto use_mc_def; 1061 1062 if (mulcst == B_FALSE) 1063 count = 0; 1064 1065 if (count + (brdcst ? 1 : 0) > 1066 EFX_ARRAY_SIZE(hftp->hft_mulcst_filter_indexes)) { 1067 /* Too many MAC addresses; use unknown multicast filter */ 1068 goto use_mc_def; 1069 } 1070 1071 /* Insert/renew multicast address list filters */ 1072 hftp->hft_mulcst_filter_count = count; 1073 for (i = 0; i < hftp->hft_mulcst_filter_count; i++) { 1074 efx_filter_spec_init_rx(&spec, 1075 EFX_FILTER_PRI_AUTO, 1076 filter_flags, 1077 hftp->hft_default_rxq); 1078 1079 efx_filter_spec_set_eth_local(&spec, 1080 EFX_FILTER_SPEC_VID_UNSPEC, 1081 &addrs[i * EFX_MAC_ADDR_LEN]); 1082 1083 rc = hunt_filter_add_internal(enp, &spec, B_TRUE, 1084 &hftp->hft_mulcst_filter_indexes[i]); 1085 if (rc != 0) { 1086 /* Rollback, then use unknown multicast filter */ 1087 goto rollback; 1088 } 1089 } 1090 1091 if (brdcst == B_TRUE) { 1092 /* Insert/renew broadcast address filter */ 1093 hftp->hft_mulcst_filter_count++; 1094 efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO, 1095 filter_flags, 1096 hftp->hft_default_rxq); 1097 1098 EFX_MAC_BROADCAST_ADDR_SET(addr); 1099 efx_filter_spec_set_eth_local(&spec, EFX_FILTER_SPEC_VID_UNSPEC, 1100 addr); 1101 1102 rc = hunt_filter_add_internal(enp, &spec, B_TRUE, 1103 &hftp->hft_mulcst_filter_indexes[ 1104 hftp->hft_mulcst_filter_count - 1]); 1105 if (rc != 0) { 1106 /* Rollback, then use unknown multicast filter */ 1107 goto rollback; 1108 } 1109 } 1110 1111 return (0); 1112 1113rollback: 1114 /* 1115 * Rollback by removing any filters we have inserted 1116 * before inserting the unknown multicast filter. 1117 */ 1118 while (i--) { 1119 (void) hunt_filter_delete_internal(enp, 1120 hftp->hft_mulcst_filter_indexes[i]); 1121 } 1122 hftp->hft_mulcst_filter_count = 0; 1123 1124use_mc_def: 1125 /* Insert the unknown multicast filter */ 1126 efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO, 1127 filter_flags, 1128 hftp->hft_default_rxq); 1129 efx_filter_spec_set_mc_def(&spec); 1130 1131 rc = hunt_filter_add_internal(enp, &spec, B_TRUE, 1132 &hftp->hft_mulcst_filter_indexes[0]); 1133 if (rc != 0) 1134 goto fail1; 1135 1136 hftp->hft_mulcst_filter_count = 1; 1137 1138 /* 1139 * FIXME: If brdcst == B_FALSE, add a filter to drop broadcast traffic. 1140 */ 1141 1142 return (0); 1143 1144fail1: 1145 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1146 1147 return (rc); 1148 1149} 1150 1151 1152static __checkReturn efx_rc_t 1153hunt_filter_get_workarounds( 1154 __in efx_nic_t *enp) 1155{ 1156 efx_nic_cfg_t *encp = &enp->en_nic_cfg; 1157 uint32_t implemented = 0; 1158 uint32_t enabled = 0; 1159 efx_rc_t rc; 1160 1161 rc = efx_mcdi_get_workarounds(enp, &implemented, &enabled); 1162 if (rc == 0) { 1163 /* Check if chained multicast filter support is enabled */ 1164 if (implemented & enabled & MC_CMD_GET_WORKAROUNDS_OUT_BUG26807) 1165 encp->enc_bug26807_workaround = B_TRUE; 1166 else 1167 encp->enc_bug26807_workaround = B_FALSE; 1168 } else if (rc == ENOTSUP) { 1169 /* 1170 * Firmware is too old to support GET_WORKAROUNDS, and support 1171 * for this workaround was implemented later. 1172 */ 1173 encp->enc_bug26807_workaround = B_FALSE; 1174 } else { 1175 goto fail1; 1176 } 1177 1178 return (0); 1179 1180fail1: 1181 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1182 1183 return (rc); 1184 1185} 1186 1187 1188/* 1189 * Reconfigure all filters. 1190 * If all_unicst and/or all mulcst filters cannot be applied then 1191 * return ENOTSUP (Note the filters for the specified addresses are 1192 * still applied in this case). 1193 */ 1194 __checkReturn efx_rc_t 1195hunt_filter_reconfigure( 1196 __in efx_nic_t *enp, 1197 __in_ecount(6) uint8_t const *mac_addr, 1198 __in boolean_t all_unicst, 1199 __in boolean_t mulcst, 1200 __in boolean_t all_mulcst, 1201 __in boolean_t brdcst, 1202 __in_ecount(6*count) uint8_t const *addrs, 1203 __in int count) 1204{ 1205 hunt_filter_table_t *table = enp->en_filter.ef_hunt_filter_table; 1206 efx_filter_flag_t filter_flags; 1207 unsigned i; 1208 int all_unicst_rc; 1209 int all_mulcst_rc; 1210 efx_rc_t rc; 1211 1212 if (table->hft_default_rxq == NULL) { 1213 /* 1214 * Filters direct traffic to the default RXQ, and so cannot be 1215 * inserted until it is available. Any currently configured 1216 * filters must be removed (ignore errors in case the MC 1217 * has rebooted, which removes hardware filters). 1218 */ 1219 if (table->hft_unicst_filter_set != B_FALSE) { 1220 (void) hunt_filter_delete_internal(enp, 1221 table->hft_unicst_filter_index); 1222 table->hft_unicst_filter_set = B_FALSE; 1223 } 1224 for (i = 0; i < table->hft_mulcst_filter_count; i++) { 1225 (void) hunt_filter_delete_internal(enp, 1226 table->hft_mulcst_filter_indexes[i]); 1227 } 1228 table->hft_mulcst_filter_count = 0; 1229 1230 return (0); 1231 } 1232 1233 if (table->hft_using_rss) 1234 filter_flags = EFX_FILTER_FLAG_RX_RSS; 1235 else 1236 filter_flags = 0; 1237 1238 /* Mark old filters which may need to be removed */ 1239 if (table->hft_unicst_filter_set != B_FALSE) { 1240 hunt_filter_set_entry_auto_old(table, 1241 table->hft_unicst_filter_index); 1242 } 1243 for (i = 0; i < table->hft_mulcst_filter_count; i++) { 1244 hunt_filter_set_entry_auto_old(table, 1245 table->hft_mulcst_filter_indexes[i]); 1246 } 1247 1248 /* Insert or renew unicast filters */ 1249 if ((all_unicst_rc = hunt_filter_unicast_refresh(enp, mac_addr, 1250 all_unicst, filter_flags)) != 0) { 1251 if (all_unicst == B_FALSE) { 1252 rc = all_unicst_rc; 1253 goto fail1; 1254 } 1255 /* Retry without all_unicast flag */ 1256 rc = hunt_filter_unicast_refresh(enp, mac_addr, 1257 B_FALSE, filter_flags); 1258 if (rc != 0) 1259 goto fail2; 1260 } 1261 1262 /* 1263 * WORKAROUND_BUG26807 controls firmware support for chained multicast 1264 * filters, and can only be enabled or disabled when the hardware filter 1265 * table is empty. 1266 * 1267 * Firmware will reset (FLR) functions which have inserted filters in 1268 * the hardware filter table when the workaround is enabled/disabled. 1269 * Functions without any hardware filters are not reset. 1270 * 1271 * Re-check if the workaround is enabled after adding unicast hardware 1272 * filters. This ensures that encp->enc_workaround_bug26807 matches the 1273 * firmware state, and that later changes to enable/disable the 1274 * workaround will result in this function seeing a reset (FLR). 1275 */ 1276 if ((rc = hunt_filter_get_workarounds(enp)) != 0) 1277 goto fail3; 1278 1279 /* Insert or renew multicast filters */ 1280 if ((all_mulcst_rc = hunt_filter_multicast_refresh(enp, mulcst, 1281 all_mulcst, brdcst, 1282 addrs, count, filter_flags)) != 0) { 1283 if (all_mulcst == B_FALSE) { 1284 rc = all_mulcst_rc; 1285 goto fail4; 1286 } 1287 /* Retry without all_mulcast flag */ 1288 rc = hunt_filter_multicast_refresh(enp, mulcst, 1289 B_FALSE, brdcst, 1290 addrs, count, filter_flags); 1291 if (rc != 0) 1292 goto fail5; 1293 } 1294 1295 /* Remove old filters which were not renewed */ 1296 for (i = 0; i < EFX_ARRAY_SIZE(table->hft_entry); i++) { 1297 if (hunt_filter_entry_is_auto_old(table, i)) { 1298 (void) hunt_filter_delete_internal(enp, i); 1299 } 1300 } 1301 1302 /* report if any optional flags were rejected */ 1303 if (((all_unicst != B_FALSE) && (all_unicst_rc != 0)) || 1304 ((all_mulcst != B_FALSE) && (all_mulcst_rc != 0))) { 1305 rc = ENOTSUP; 1306 } 1307 1308 return (rc); 1309 1310fail5: 1311 EFSYS_PROBE(fail5); 1312fail4: 1313 EFSYS_PROBE(fail4); 1314fail3: 1315 EFSYS_PROBE(fail3); 1316fail2: 1317 EFSYS_PROBE(fail2); 1318fail1: 1319 EFSYS_PROBE1(fail1, efx_rc_t, rc); 1320 1321 /* Clear auto old flags */ 1322 for (i = 0; i < EFX_ARRAY_SIZE(table->hft_entry); i++) { 1323 if (hunt_filter_entry_is_auto_old(table, i)) { 1324 hunt_filter_set_entry_not_auto_old(table, i); 1325 } 1326 } 1327 1328 return (rc); 1329} 1330 1331 void 1332hunt_filter_get_default_rxq( 1333 __in efx_nic_t *enp, 1334 __out efx_rxq_t **erpp, 1335 __out boolean_t *using_rss) 1336{ 1337 hunt_filter_table_t *table = enp->en_filter.ef_hunt_filter_table; 1338 1339 *erpp = table->hft_default_rxq; 1340 *using_rss = table->hft_using_rss; 1341} 1342 1343 1344 void 1345hunt_filter_default_rxq_set( 1346 __in efx_nic_t *enp, 1347 __in efx_rxq_t *erp, 1348 __in boolean_t using_rss) 1349{ 1350 hunt_filter_table_t *table = enp->en_filter.ef_hunt_filter_table; 1351 1352#if EFSYS_OPT_RX_SCALE 1353 EFSYS_ASSERT((using_rss == B_FALSE) || 1354 (enp->en_rss_context != EF10_RSS_CONTEXT_INVALID)); 1355 table->hft_using_rss = using_rss; 1356#else 1357 EFSYS_ASSERT(using_rss == B_FALSE); 1358 table->hft_using_rss = B_FALSE; 1359#endif 1360 table->hft_default_rxq = erp; 1361} 1362 1363 void 1364hunt_filter_default_rxq_clear( 1365 __in efx_nic_t *enp) 1366{ 1367 hunt_filter_table_t *table = enp->en_filter.ef_hunt_filter_table; 1368 1369 table->hft_default_rxq = NULL; 1370 table->hft_using_rss = B_FALSE; 1371} 1372 1373 1374#endif /* EFSYS_OPT_FILTER */ 1375 1376#endif /* EFSYS_OPT_HUNTINGTON */ 1377