ef10_filter.c revision 342414
1283514Sarybchik/*- 2300607Sarybchik * Copyright (c) 2007-2016 Solarflare Communications Inc. 3283514Sarybchik * All rights reserved. 4283514Sarybchik * 5283514Sarybchik * Redistribution and use in source and binary forms, with or without 6283514Sarybchik * modification, are permitted provided that the following conditions are met: 7283514Sarybchik * 8283514Sarybchik * 1. Redistributions of source code must retain the above copyright notice, 9283514Sarybchik * this list of conditions and the following disclaimer. 10283514Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice, 11283514Sarybchik * this list of conditions and the following disclaimer in the documentation 12283514Sarybchik * and/or other materials provided with the distribution. 13283514Sarybchik * 14283514Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15283514Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16283514Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17283514Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18283514Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19283514Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20283514Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21283514Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22283514Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23283514Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24283514Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25283514Sarybchik * 26283514Sarybchik * The views and conclusions contained in the software and documentation are 27283514Sarybchik * those of the authors and should not be interpreted as representing official 28283514Sarybchik * policies, either expressed or implied, of the FreeBSD Project. 29283514Sarybchik */ 30283514Sarybchik 31283514Sarybchik#include <sys/cdefs.h> 32283514Sarybchik__FBSDID("$FreeBSD: stable/11/sys/dev/sfxge/common/ef10_filter.c 342414 2018-12-25 06:51:58Z arybchik $"); 33283514Sarybchik 34283514Sarybchik#include "efx.h" 35283514Sarybchik#include "efx_impl.h" 36283514Sarybchik 37299597Sarybchik#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD 38283514Sarybchik 39283514Sarybchik#if EFSYS_OPT_FILTER 40283514Sarybchik 41293764Sarybchik#define EFE_SPEC(eftp, index) ((eftp)->eft_entry[(index)].efe_spec) 42283514Sarybchik 43283514Sarybchikstatic efx_filter_spec_t * 44293764Sarybchikef10_filter_entry_spec( 45293764Sarybchik __in const ef10_filter_table_t *eftp, 46283514Sarybchik __in unsigned int index) 47283514Sarybchik{ 48293764Sarybchik return ((efx_filter_spec_t *)(EFE_SPEC(eftp, index) & 49293764Sarybchik ~(uintptr_t)EFX_EF10_FILTER_FLAGS)); 50283514Sarybchik} 51283514Sarybchik 52283514Sarybchikstatic boolean_t 53293764Sarybchikef10_filter_entry_is_busy( 54293764Sarybchik __in const ef10_filter_table_t *eftp, 55283514Sarybchik __in unsigned int index) 56283514Sarybchik{ 57293764Sarybchik if (EFE_SPEC(eftp, index) & EFX_EF10_FILTER_FLAG_BUSY) 58283514Sarybchik return (B_TRUE); 59283514Sarybchik else 60283514Sarybchik return (B_FALSE); 61283514Sarybchik} 62283514Sarybchik 63283514Sarybchikstatic boolean_t 64293764Sarybchikef10_filter_entry_is_auto_old( 65293764Sarybchik __in const ef10_filter_table_t *eftp, 66283514Sarybchik __in unsigned int index) 67283514Sarybchik{ 68293764Sarybchik if (EFE_SPEC(eftp, index) & EFX_EF10_FILTER_FLAG_AUTO_OLD) 69283514Sarybchik return (B_TRUE); 70283514Sarybchik else 71283514Sarybchik return (B_FALSE); 72283514Sarybchik} 73283514Sarybchik 74283514Sarybchikstatic void 75293764Sarybchikef10_filter_set_entry( 76293764Sarybchik __inout ef10_filter_table_t *eftp, 77283514Sarybchik __in unsigned int index, 78283514Sarybchik __in_opt const efx_filter_spec_t *efsp) 79283514Sarybchik{ 80293764Sarybchik EFE_SPEC(eftp, index) = (uintptr_t)efsp; 81283514Sarybchik} 82283514Sarybchik 83283514Sarybchikstatic void 84293764Sarybchikef10_filter_set_entry_busy( 85293764Sarybchik __inout ef10_filter_table_t *eftp, 86283514Sarybchik __in unsigned int index) 87283514Sarybchik{ 88293764Sarybchik EFE_SPEC(eftp, index) |= (uintptr_t)EFX_EF10_FILTER_FLAG_BUSY; 89283514Sarybchik} 90283514Sarybchik 91283514Sarybchikstatic void 92293764Sarybchikef10_filter_set_entry_not_busy( 93293764Sarybchik __inout ef10_filter_table_t *eftp, 94283514Sarybchik __in unsigned int index) 95283514Sarybchik{ 96293764Sarybchik EFE_SPEC(eftp, index) &= ~(uintptr_t)EFX_EF10_FILTER_FLAG_BUSY; 97283514Sarybchik} 98283514Sarybchik 99283514Sarybchikstatic void 100293764Sarybchikef10_filter_set_entry_auto_old( 101293764Sarybchik __inout ef10_filter_table_t *eftp, 102283514Sarybchik __in unsigned int index) 103283514Sarybchik{ 104293764Sarybchik EFSYS_ASSERT(ef10_filter_entry_spec(eftp, index) != NULL); 105293764Sarybchik EFE_SPEC(eftp, index) |= (uintptr_t)EFX_EF10_FILTER_FLAG_AUTO_OLD; 106283514Sarybchik} 107283514Sarybchik 108283514Sarybchikstatic void 109293764Sarybchikef10_filter_set_entry_not_auto_old( 110293764Sarybchik __inout ef10_filter_table_t *eftp, 111283514Sarybchik __in unsigned int index) 112283514Sarybchik{ 113293764Sarybchik EFE_SPEC(eftp, index) &= ~(uintptr_t)EFX_EF10_FILTER_FLAG_AUTO_OLD; 114293764Sarybchik EFSYS_ASSERT(ef10_filter_entry_spec(eftp, index) != NULL); 115283514Sarybchik} 116283514Sarybchik 117291436Sarybchik __checkReturn efx_rc_t 118293764Sarybchikef10_filter_init( 119283514Sarybchik __in efx_nic_t *enp) 120283514Sarybchik{ 121291436Sarybchik efx_rc_t rc; 122293764Sarybchik ef10_filter_table_t *eftp; 123283514Sarybchik 124293764Sarybchik EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 125293764Sarybchik enp->en_family == EFX_FAMILY_MEDFORD); 126283514Sarybchik 127283514Sarybchik#define MATCH_MASK(match) (EFX_MASK32(match) << EFX_LOW_BIT(match)) 128283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_REM_HOST == 129342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_IP)); 130283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_LOC_HOST == 131342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_IP)); 132283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_REM_MAC == 133342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_MAC)); 134283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_REM_PORT == 135342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_SRC_PORT)); 136283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_LOC_MAC == 137342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_MAC)); 138283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_LOC_PORT == 139342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_DST_PORT)); 140283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_ETHER_TYPE == 141342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_ETHER_TYPE)); 142283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_INNER_VID == 143342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_INNER_VLAN)); 144283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_OUTER_VID == 145342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_OUTER_VLAN)); 146283514Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_IP_PROTO == 147342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_IP_PROTO)); 148311484Sarybchik EFX_STATIC_ASSERT(EFX_FILTER_MATCH_UNKNOWN_MCAST_DST == 149342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_MCAST_DST)); 150311484Sarybchik EFX_STATIC_ASSERT((uint32_t)EFX_FILTER_MATCH_UNKNOWN_UCAST_DST == 151342414Sarybchik MATCH_MASK(MC_CMD_FILTER_OP_EXT_IN_MATCH_UNKNOWN_UCAST_DST)); 152283514Sarybchik#undef MATCH_MASK 153283514Sarybchik 154293764Sarybchik EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (ef10_filter_table_t), eftp); 155283514Sarybchik 156293764Sarybchik if (!eftp) { 157283514Sarybchik rc = ENOMEM; 158283514Sarybchik goto fail1; 159283514Sarybchik } 160283514Sarybchik 161293764Sarybchik enp->en_filter.ef_ef10_filter_table = eftp; 162283514Sarybchik 163283514Sarybchik return (0); 164283514Sarybchik 165283514Sarybchikfail1: 166291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 167283514Sarybchik 168283514Sarybchik return (rc); 169283514Sarybchik} 170283514Sarybchik 171283514Sarybchik void 172293764Sarybchikef10_filter_fini( 173283514Sarybchik __in efx_nic_t *enp) 174283514Sarybchik{ 175293764Sarybchik EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 176293764Sarybchik enp->en_family == EFX_FAMILY_MEDFORD); 177283514Sarybchik 178293764Sarybchik if (enp->en_filter.ef_ef10_filter_table != NULL) { 179293764Sarybchik EFSYS_KMEM_FREE(enp->en_esip, sizeof (ef10_filter_table_t), 180293764Sarybchik enp->en_filter.ef_ef10_filter_table); 181283514Sarybchik } 182283514Sarybchik} 183283514Sarybchik 184291436Sarybchikstatic __checkReturn efx_rc_t 185283514Sarybchikefx_mcdi_filter_op_add( 186283514Sarybchik __in efx_nic_t *enp, 187283514Sarybchik __in efx_filter_spec_t *spec, 188283514Sarybchik __in unsigned int filter_op, 189293764Sarybchik __inout ef10_filter_handle_t *handle) 190283514Sarybchik{ 191283514Sarybchik efx_mcdi_req_t req; 192342414Sarybchik uint8_t payload[MAX(MC_CMD_FILTER_OP_EXT_IN_LEN, 193342414Sarybchik MC_CMD_FILTER_OP_EXT_OUT_LEN)]; 194291436Sarybchik efx_rc_t rc; 195283514Sarybchik 196283514Sarybchik memset(payload, 0, sizeof (payload)); 197283514Sarybchik req.emr_cmd = MC_CMD_FILTER_OP; 198283514Sarybchik req.emr_in_buf = payload; 199342414Sarybchik req.emr_in_length = MC_CMD_FILTER_OP_EXT_IN_LEN; 200283514Sarybchik req.emr_out_buf = payload; 201342414Sarybchik req.emr_out_length = MC_CMD_FILTER_OP_EXT_OUT_LEN; 202283514Sarybchik 203283514Sarybchik switch (filter_op) { 204283514Sarybchik case MC_CMD_FILTER_OP_IN_OP_REPLACE: 205342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_HANDLE_LO, 206293764Sarybchik handle->efh_lo); 207342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_HANDLE_HI, 208293764Sarybchik handle->efh_hi); 209283514Sarybchik /* Fall through */ 210283514Sarybchik case MC_CMD_FILTER_OP_IN_OP_INSERT: 211283514Sarybchik case MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE: 212342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_OP, filter_op); 213283514Sarybchik break; 214283514Sarybchik default: 215283514Sarybchik EFSYS_ASSERT(0); 216283514Sarybchik rc = EINVAL; 217283514Sarybchik goto fail1; 218283514Sarybchik } 219283514Sarybchik 220342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_PORT_ID, 221283514Sarybchik EVB_PORT_ID_ASSIGNED); 222342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_MATCH_FIELDS, 223311484Sarybchik spec->efs_match_flags); 224342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_RX_DEST, 225342414Sarybchik MC_CMD_FILTER_OP_EXT_IN_RX_DEST_HOST); 226342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_RX_QUEUE, 227283514Sarybchik spec->efs_dmaq_id); 228283514Sarybchik if (spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) { 229342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_RX_CONTEXT, 230283514Sarybchik spec->efs_rss_context); 231283514Sarybchik } 232342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_RX_MODE, 233283514Sarybchik spec->efs_flags & EFX_FILTER_FLAG_RX_RSS ? 234342414Sarybchik MC_CMD_FILTER_OP_EXT_IN_RX_MODE_RSS : 235342414Sarybchik MC_CMD_FILTER_OP_EXT_IN_RX_MODE_SIMPLE); 236342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_TX_DEST, 237342414Sarybchik MC_CMD_FILTER_OP_EXT_IN_TX_DEST_DEFAULT); 238283514Sarybchik 239283514Sarybchik if (filter_op != MC_CMD_FILTER_OP_IN_OP_REPLACE) { 240283514Sarybchik /* 241283514Sarybchik * NOTE: Unlike most MCDI requests, the filter fields 242283514Sarybchik * are presented in network (big endian) byte order. 243283514Sarybchik */ 244342414Sarybchik memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_EXT_IN_SRC_MAC), 245283514Sarybchik spec->efs_rem_mac, EFX_MAC_ADDR_LEN); 246342414Sarybchik memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_EXT_IN_DST_MAC), 247283514Sarybchik spec->efs_loc_mac, EFX_MAC_ADDR_LEN); 248283514Sarybchik 249342414Sarybchik MCDI_IN_SET_WORD(req, FILTER_OP_EXT_IN_SRC_PORT, 250283514Sarybchik __CPU_TO_BE_16(spec->efs_rem_port)); 251342414Sarybchik MCDI_IN_SET_WORD(req, FILTER_OP_EXT_IN_DST_PORT, 252283514Sarybchik __CPU_TO_BE_16(spec->efs_loc_port)); 253283514Sarybchik 254342414Sarybchik MCDI_IN_SET_WORD(req, FILTER_OP_EXT_IN_ETHER_TYPE, 255283514Sarybchik __CPU_TO_BE_16(spec->efs_ether_type)); 256283514Sarybchik 257342414Sarybchik MCDI_IN_SET_WORD(req, FILTER_OP_EXT_IN_INNER_VLAN, 258283514Sarybchik __CPU_TO_BE_16(spec->efs_inner_vid)); 259342414Sarybchik MCDI_IN_SET_WORD(req, FILTER_OP_EXT_IN_OUTER_VLAN, 260283514Sarybchik __CPU_TO_BE_16(spec->efs_outer_vid)); 261283514Sarybchik 262283514Sarybchik /* IP protocol (in low byte, high byte is zero) */ 263342414Sarybchik MCDI_IN_SET_BYTE(req, FILTER_OP_EXT_IN_IP_PROTO, 264283514Sarybchik spec->efs_ip_proto); 265283514Sarybchik 266283514Sarybchik EFX_STATIC_ASSERT(sizeof (spec->efs_rem_host) == 267342414Sarybchik MC_CMD_FILTER_OP_EXT_IN_SRC_IP_LEN); 268283514Sarybchik EFX_STATIC_ASSERT(sizeof (spec->efs_loc_host) == 269342414Sarybchik MC_CMD_FILTER_OP_EXT_IN_DST_IP_LEN); 270283514Sarybchik 271342414Sarybchik memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_EXT_IN_SRC_IP), 272283514Sarybchik &spec->efs_rem_host.eo_byte[0], 273342414Sarybchik MC_CMD_FILTER_OP_EXT_IN_SRC_IP_LEN); 274342414Sarybchik memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_EXT_IN_DST_IP), 275283514Sarybchik &spec->efs_loc_host.eo_byte[0], 276342414Sarybchik MC_CMD_FILTER_OP_EXT_IN_DST_IP_LEN); 277283514Sarybchik } 278283514Sarybchik 279283514Sarybchik efx_mcdi_execute(enp, &req); 280283514Sarybchik 281283514Sarybchik if (req.emr_rc != 0) { 282283514Sarybchik rc = req.emr_rc; 283283514Sarybchik goto fail2; 284283514Sarybchik } 285283514Sarybchik 286342414Sarybchik if (req.emr_out_length_used < MC_CMD_FILTER_OP_EXT_OUT_LEN) { 287283514Sarybchik rc = EMSGSIZE; 288283514Sarybchik goto fail3; 289283514Sarybchik } 290283514Sarybchik 291342414Sarybchik handle->efh_lo = MCDI_OUT_DWORD(req, FILTER_OP_EXT_OUT_HANDLE_LO); 292342414Sarybchik handle->efh_hi = MCDI_OUT_DWORD(req, FILTER_OP_EXT_OUT_HANDLE_HI); 293283514Sarybchik 294283514Sarybchik return (0); 295283514Sarybchik 296283514Sarybchikfail3: 297283514Sarybchik EFSYS_PROBE(fail3); 298283514Sarybchikfail2: 299283514Sarybchik EFSYS_PROBE(fail2); 300283514Sarybchikfail1: 301291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 302283514Sarybchik 303283514Sarybchik return (rc); 304283514Sarybchik 305283514Sarybchik} 306283514Sarybchik 307291436Sarybchikstatic __checkReturn efx_rc_t 308283514Sarybchikefx_mcdi_filter_op_delete( 309283514Sarybchik __in efx_nic_t *enp, 310283514Sarybchik __in unsigned int filter_op, 311293764Sarybchik __inout ef10_filter_handle_t *handle) 312283514Sarybchik{ 313283514Sarybchik efx_mcdi_req_t req; 314342414Sarybchik uint8_t payload[MAX(MC_CMD_FILTER_OP_EXT_IN_LEN, 315342414Sarybchik MC_CMD_FILTER_OP_EXT_OUT_LEN)]; 316291436Sarybchik efx_rc_t rc; 317283514Sarybchik 318283514Sarybchik memset(payload, 0, sizeof (payload)); 319283514Sarybchik req.emr_cmd = MC_CMD_FILTER_OP; 320283514Sarybchik req.emr_in_buf = payload; 321342414Sarybchik req.emr_in_length = MC_CMD_FILTER_OP_EXT_IN_LEN; 322283514Sarybchik req.emr_out_buf = payload; 323342414Sarybchik req.emr_out_length = MC_CMD_FILTER_OP_EXT_OUT_LEN; 324283514Sarybchik 325283514Sarybchik switch (filter_op) { 326283514Sarybchik case MC_CMD_FILTER_OP_IN_OP_REMOVE: 327342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_OP, 328283514Sarybchik MC_CMD_FILTER_OP_IN_OP_REMOVE); 329283514Sarybchik break; 330283514Sarybchik case MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE: 331342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_OP, 332283514Sarybchik MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE); 333283514Sarybchik break; 334283514Sarybchik default: 335283514Sarybchik EFSYS_ASSERT(0); 336283514Sarybchik rc = EINVAL; 337283514Sarybchik goto fail1; 338283514Sarybchik } 339283514Sarybchik 340342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_HANDLE_LO, handle->efh_lo); 341342414Sarybchik MCDI_IN_SET_DWORD(req, FILTER_OP_EXT_IN_HANDLE_HI, handle->efh_hi); 342283514Sarybchik 343299925Sarybchik efx_mcdi_execute_quiet(enp, &req); 344283514Sarybchik 345283514Sarybchik if (req.emr_rc != 0) { 346283514Sarybchik rc = req.emr_rc; 347283514Sarybchik goto fail2; 348283514Sarybchik } 349283514Sarybchik 350342414Sarybchik if (req.emr_out_length_used < MC_CMD_FILTER_OP_EXT_OUT_LEN) { 351283514Sarybchik rc = EMSGSIZE; 352283514Sarybchik goto fail3; 353283514Sarybchik } 354283514Sarybchik 355283514Sarybchik return (0); 356283514Sarybchik 357283514Sarybchikfail3: 358283514Sarybchik EFSYS_PROBE(fail3); 359283514Sarybchik 360283514Sarybchikfail2: 361283514Sarybchik EFSYS_PROBE(fail2); 362283514Sarybchikfail1: 363291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 364283514Sarybchik 365283514Sarybchik return (rc); 366283514Sarybchik} 367283514Sarybchik 368283514Sarybchikstatic __checkReturn boolean_t 369293764Sarybchikef10_filter_equal( 370283514Sarybchik __in const efx_filter_spec_t *left, 371283514Sarybchik __in const efx_filter_spec_t *right) 372283514Sarybchik{ 373283514Sarybchik /* FIXME: Consider rx vs tx filters (look at efs_flags) */ 374283514Sarybchik if (left->efs_match_flags != right->efs_match_flags) 375283514Sarybchik return (B_FALSE); 376283514Sarybchik if (!EFX_OWORD_IS_EQUAL(left->efs_rem_host, right->efs_rem_host)) 377283514Sarybchik return (B_FALSE); 378283514Sarybchik if (!EFX_OWORD_IS_EQUAL(left->efs_loc_host, right->efs_loc_host)) 379283514Sarybchik return (B_FALSE); 380283514Sarybchik if (memcmp(left->efs_rem_mac, right->efs_rem_mac, EFX_MAC_ADDR_LEN)) 381283514Sarybchik return (B_FALSE); 382283514Sarybchik if (memcmp(left->efs_loc_mac, right->efs_loc_mac, EFX_MAC_ADDR_LEN)) 383283514Sarybchik return (B_FALSE); 384283514Sarybchik if (left->efs_rem_port != right->efs_rem_port) 385283514Sarybchik return (B_FALSE); 386283514Sarybchik if (left->efs_loc_port != right->efs_loc_port) 387283514Sarybchik return (B_FALSE); 388283514Sarybchik if (left->efs_inner_vid != right->efs_inner_vid) 389283514Sarybchik return (B_FALSE); 390283514Sarybchik if (left->efs_outer_vid != right->efs_outer_vid) 391283514Sarybchik return (B_FALSE); 392283514Sarybchik if (left->efs_ether_type != right->efs_ether_type) 393283514Sarybchik return (B_FALSE); 394283514Sarybchik if (left->efs_ip_proto != right->efs_ip_proto) 395283514Sarybchik return (B_FALSE); 396283514Sarybchik 397283514Sarybchik return (B_TRUE); 398283514Sarybchik 399283514Sarybchik} 400283514Sarybchik 401283514Sarybchikstatic __checkReturn boolean_t 402293764Sarybchikef10_filter_same_dest( 403283514Sarybchik __in const efx_filter_spec_t *left, 404283514Sarybchik __in const efx_filter_spec_t *right) 405283514Sarybchik{ 406283514Sarybchik if ((left->efs_flags & EFX_FILTER_FLAG_RX_RSS) && 407283514Sarybchik (right->efs_flags & EFX_FILTER_FLAG_RX_RSS)) { 408283514Sarybchik if (left->efs_rss_context == right->efs_rss_context) 409283514Sarybchik return (B_TRUE); 410283514Sarybchik } else if ((~(left->efs_flags) & EFX_FILTER_FLAG_RX_RSS) && 411283514Sarybchik (~(right->efs_flags) & EFX_FILTER_FLAG_RX_RSS)) { 412283514Sarybchik if (left->efs_dmaq_id == right->efs_dmaq_id) 413283514Sarybchik return (B_TRUE); 414283514Sarybchik } 415283514Sarybchik return (B_FALSE); 416283514Sarybchik} 417283514Sarybchik 418283514Sarybchikstatic __checkReturn uint32_t 419293764Sarybchikef10_filter_hash( 420283514Sarybchik __in efx_filter_spec_t *spec) 421283514Sarybchik{ 422283514Sarybchik EFX_STATIC_ASSERT((sizeof (efx_filter_spec_t) % sizeof (uint32_t)) 423283514Sarybchik == 0); 424283514Sarybchik EFX_STATIC_ASSERT((EFX_FIELD_OFFSET(efx_filter_spec_t, efs_outer_vid) % 425283514Sarybchik sizeof (uint32_t)) == 0); 426283514Sarybchik 427283514Sarybchik /* 428283514Sarybchik * As the area of the efx_filter_spec_t we need to hash is DWORD 429283514Sarybchik * aligned and an exact number of DWORDs in size we can use the 430283514Sarybchik * optimised efx_hash_dwords() rather than efx_hash_bytes() 431283514Sarybchik */ 432283514Sarybchik return (efx_hash_dwords((const uint32_t *)&spec->efs_outer_vid, 433283514Sarybchik (sizeof (efx_filter_spec_t) - 434283514Sarybchik EFX_FIELD_OFFSET(efx_filter_spec_t, efs_outer_vid)) / 435283514Sarybchik sizeof (uint32_t), 0)); 436283514Sarybchik} 437283514Sarybchik 438283514Sarybchik/* 439283514Sarybchik * Decide whether a filter should be exclusive or else should allow 440283514Sarybchik * delivery to additional recipients. Currently we decide that 441283514Sarybchik * filters for specific local unicast MAC and IP addresses are 442283514Sarybchik * exclusive. 443283514Sarybchik */ 444283514Sarybchikstatic __checkReturn boolean_t 445293764Sarybchikef10_filter_is_exclusive( 446283514Sarybchik __in efx_filter_spec_t *spec) 447283514Sarybchik{ 448283514Sarybchik if ((spec->efs_match_flags & EFX_FILTER_MATCH_LOC_MAC) && 449283514Sarybchik !EFX_MAC_ADDR_IS_MULTICAST(spec->efs_loc_mac)) 450283514Sarybchik return (B_TRUE); 451283514Sarybchik 452283514Sarybchik if ((spec->efs_match_flags & 453283514Sarybchik (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_LOC_HOST)) == 454283514Sarybchik (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_LOC_HOST)) { 455283514Sarybchik if ((spec->efs_ether_type == EFX_ETHER_TYPE_IPV4) && 456283514Sarybchik ((spec->efs_loc_host.eo_u8[0] & 0xf) != 0xe)) 457283514Sarybchik return (B_TRUE); 458283514Sarybchik if ((spec->efs_ether_type == EFX_ETHER_TYPE_IPV6) && 459283514Sarybchik (spec->efs_loc_host.eo_u8[0] != 0xff)) 460283514Sarybchik return (B_TRUE); 461283514Sarybchik } 462283514Sarybchik 463283514Sarybchik return (B_FALSE); 464283514Sarybchik} 465283514Sarybchik 466291436Sarybchik __checkReturn efx_rc_t 467293764Sarybchikef10_filter_restore( 468283514Sarybchik __in efx_nic_t *enp) 469283514Sarybchik{ 470283514Sarybchik int tbl_id; 471283514Sarybchik efx_filter_spec_t *spec; 472293764Sarybchik ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table; 473283514Sarybchik boolean_t restoring; 474311032Sarybchik efsys_lock_state_t state; 475291436Sarybchik efx_rc_t rc; 476283514Sarybchik 477293764Sarybchik EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 478293764Sarybchik enp->en_family == EFX_FAMILY_MEDFORD); 479283514Sarybchik 480293764Sarybchik for (tbl_id = 0; tbl_id < EFX_EF10_FILTER_TBL_ROWS; tbl_id++) { 481283514Sarybchik 482283514Sarybchik EFSYS_LOCK(enp->en_eslp, state); 483283514Sarybchik 484293764Sarybchik spec = ef10_filter_entry_spec(eftp, tbl_id); 485283514Sarybchik if (spec == NULL) { 486283514Sarybchik restoring = B_FALSE; 487293764Sarybchik } else if (ef10_filter_entry_is_busy(eftp, tbl_id)) { 488283514Sarybchik /* Ignore busy entries. */ 489283514Sarybchik restoring = B_FALSE; 490283514Sarybchik } else { 491293764Sarybchik ef10_filter_set_entry_busy(eftp, tbl_id); 492283514Sarybchik restoring = B_TRUE; 493283514Sarybchik } 494283514Sarybchik 495283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 496283514Sarybchik 497283514Sarybchik if (restoring == B_FALSE) 498283514Sarybchik continue; 499283514Sarybchik 500293764Sarybchik if (ef10_filter_is_exclusive(spec)) { 501283514Sarybchik rc = efx_mcdi_filter_op_add(enp, spec, 502283514Sarybchik MC_CMD_FILTER_OP_IN_OP_INSERT, 503293764Sarybchik &eftp->eft_entry[tbl_id].efe_handle); 504283514Sarybchik } else { 505283514Sarybchik rc = efx_mcdi_filter_op_add(enp, spec, 506283514Sarybchik MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE, 507293764Sarybchik &eftp->eft_entry[tbl_id].efe_handle); 508283514Sarybchik } 509283514Sarybchik 510283514Sarybchik if (rc != 0) 511283514Sarybchik goto fail1; 512283514Sarybchik 513283514Sarybchik EFSYS_LOCK(enp->en_eslp, state); 514283514Sarybchik 515293764Sarybchik ef10_filter_set_entry_not_busy(eftp, tbl_id); 516283514Sarybchik 517283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 518283514Sarybchik } 519283514Sarybchik 520283514Sarybchik return (0); 521283514Sarybchik 522283514Sarybchikfail1: 523291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 524283514Sarybchik 525283514Sarybchik return (rc); 526283514Sarybchik} 527283514Sarybchik 528283514Sarybchik/* 529283514Sarybchik * An arbitrary search limit for the software hash table. As per the linux net 530283514Sarybchik * driver. 531283514Sarybchik */ 532293764Sarybchik#define EF10_FILTER_SEARCH_LIMIT 200 533283514Sarybchik 534291436Sarybchikstatic __checkReturn efx_rc_t 535293764Sarybchikef10_filter_add_internal( 536283514Sarybchik __in efx_nic_t *enp, 537283514Sarybchik __inout efx_filter_spec_t *spec, 538283514Sarybchik __in boolean_t may_replace, 539283514Sarybchik __out_opt uint32_t *filter_id) 540283514Sarybchik{ 541291436Sarybchik efx_rc_t rc; 542293764Sarybchik ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table; 543283514Sarybchik efx_filter_spec_t *saved_spec; 544283514Sarybchik uint32_t hash; 545283514Sarybchik unsigned int depth; 546283514Sarybchik int ins_index; 547283514Sarybchik boolean_t replacing = B_FALSE; 548283514Sarybchik unsigned int i; 549311032Sarybchik efsys_lock_state_t state; 550283514Sarybchik boolean_t locked = B_FALSE; 551283514Sarybchik 552293764Sarybchik EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 553293764Sarybchik enp->en_family == EFX_FAMILY_MEDFORD); 554283514Sarybchik 555283514Sarybchik#if EFSYS_OPT_RX_SCALE 556283514Sarybchik spec->efs_rss_context = enp->en_rss_context; 557283514Sarybchik#endif 558283514Sarybchik 559293764Sarybchik hash = ef10_filter_hash(spec); 560283514Sarybchik 561283514Sarybchik /* 562283514Sarybchik * FIXME: Add support for inserting filters of different priorities 563283514Sarybchik * and removing lower priority multicast filters (bug 42378) 564283514Sarybchik */ 565283514Sarybchik 566283514Sarybchik /* 567283514Sarybchik * Find any existing filters with the same match tuple or 568283514Sarybchik * else a free slot to insert at. If any of them are busy, 569283514Sarybchik * we have to wait and retry. 570283514Sarybchik */ 571283514Sarybchik for (;;) { 572283514Sarybchik ins_index = -1; 573283514Sarybchik depth = 1; 574283514Sarybchik EFSYS_LOCK(enp->en_eslp, state); 575283514Sarybchik locked = B_TRUE; 576283514Sarybchik 577283514Sarybchik for (;;) { 578293764Sarybchik i = (hash + depth) & (EFX_EF10_FILTER_TBL_ROWS - 1); 579293764Sarybchik saved_spec = ef10_filter_entry_spec(eftp, i); 580283514Sarybchik 581283514Sarybchik if (!saved_spec) { 582283514Sarybchik if (ins_index < 0) { 583283514Sarybchik ins_index = i; 584283514Sarybchik } 585293764Sarybchik } else if (ef10_filter_equal(spec, saved_spec)) { 586293764Sarybchik if (ef10_filter_entry_is_busy(eftp, i)) 587283514Sarybchik break; 588283514Sarybchik if (saved_spec->efs_priority 589283514Sarybchik == EFX_FILTER_PRI_AUTO) { 590283514Sarybchik ins_index = i; 591283514Sarybchik goto found; 592293764Sarybchik } else if (ef10_filter_is_exclusive(spec)) { 593283514Sarybchik if (may_replace) { 594283514Sarybchik ins_index = i; 595283514Sarybchik goto found; 596283514Sarybchik } else { 597283514Sarybchik rc = EEXIST; 598283514Sarybchik goto fail1; 599283514Sarybchik } 600283514Sarybchik } 601283514Sarybchik 602283514Sarybchik /* Leave existing */ 603283514Sarybchik } 604283514Sarybchik 605283514Sarybchik /* 606283514Sarybchik * Once we reach the maximum search depth, use 607283514Sarybchik * the first suitable slot or return EBUSY if 608283514Sarybchik * there was none. 609283514Sarybchik */ 610293764Sarybchik if (depth == EF10_FILTER_SEARCH_LIMIT) { 611283514Sarybchik if (ins_index < 0) { 612283514Sarybchik rc = EBUSY; 613283514Sarybchik goto fail2; 614283514Sarybchik } 615283514Sarybchik goto found; 616283514Sarybchik } 617283514Sarybchik depth++; 618283514Sarybchik } 619283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 620283514Sarybchik locked = B_FALSE; 621283514Sarybchik } 622283514Sarybchik 623283514Sarybchikfound: 624283514Sarybchik /* 625283514Sarybchik * Create a software table entry if necessary, and mark it 626283514Sarybchik * busy. We might yet fail to insert, but any attempt to 627283514Sarybchik * insert a conflicting filter while we're waiting for the 628283514Sarybchik * firmware must find the busy entry. 629283514Sarybchik */ 630293764Sarybchik saved_spec = ef10_filter_entry_spec(eftp, ins_index); 631283514Sarybchik if (saved_spec) { 632283514Sarybchik if (saved_spec->efs_priority == EFX_FILTER_PRI_AUTO) { 633283514Sarybchik /* This is a filter we are refreshing */ 634293764Sarybchik ef10_filter_set_entry_not_auto_old(eftp, ins_index); 635283514Sarybchik goto out_unlock; 636283514Sarybchik 637283514Sarybchik } 638283514Sarybchik replacing = B_TRUE; 639283514Sarybchik } else { 640283514Sarybchik EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec), saved_spec); 641283514Sarybchik if (!saved_spec) { 642283514Sarybchik rc = ENOMEM; 643283514Sarybchik goto fail3; 644283514Sarybchik } 645283514Sarybchik *saved_spec = *spec; 646293764Sarybchik ef10_filter_set_entry(eftp, ins_index, saved_spec); 647283514Sarybchik } 648293764Sarybchik ef10_filter_set_entry_busy(eftp, ins_index); 649283514Sarybchik 650283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 651283514Sarybchik locked = B_FALSE; 652283514Sarybchik 653283514Sarybchik /* 654283514Sarybchik * On replacing the filter handle may change after after a successful 655283514Sarybchik * replace operation. 656283514Sarybchik */ 657283514Sarybchik if (replacing) { 658283514Sarybchik rc = efx_mcdi_filter_op_add(enp, spec, 659283514Sarybchik MC_CMD_FILTER_OP_IN_OP_REPLACE, 660293764Sarybchik &eftp->eft_entry[ins_index].efe_handle); 661293764Sarybchik } else if (ef10_filter_is_exclusive(spec)) { 662283514Sarybchik rc = efx_mcdi_filter_op_add(enp, spec, 663283514Sarybchik MC_CMD_FILTER_OP_IN_OP_INSERT, 664293764Sarybchik &eftp->eft_entry[ins_index].efe_handle); 665283514Sarybchik } else { 666283514Sarybchik rc = efx_mcdi_filter_op_add(enp, spec, 667283514Sarybchik MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE, 668293764Sarybchik &eftp->eft_entry[ins_index].efe_handle); 669283514Sarybchik } 670283514Sarybchik 671283514Sarybchik if (rc != 0) 672283514Sarybchik goto fail4; 673283514Sarybchik 674283514Sarybchik EFSYS_LOCK(enp->en_eslp, state); 675283514Sarybchik locked = B_TRUE; 676283514Sarybchik 677283514Sarybchik if (replacing) { 678283514Sarybchik /* Update the fields that may differ */ 679283514Sarybchik saved_spec->efs_priority = spec->efs_priority; 680283514Sarybchik saved_spec->efs_flags = spec->efs_flags; 681283514Sarybchik saved_spec->efs_rss_context = spec->efs_rss_context; 682283514Sarybchik saved_spec->efs_dmaq_id = spec->efs_dmaq_id; 683283514Sarybchik } 684283514Sarybchik 685293764Sarybchik ef10_filter_set_entry_not_busy(eftp, ins_index); 686283514Sarybchik 687283514Sarybchikout_unlock: 688283514Sarybchik 689283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 690283514Sarybchik locked = B_FALSE; 691283514Sarybchik 692283514Sarybchik if (filter_id) 693283514Sarybchik *filter_id = ins_index; 694283514Sarybchik 695283514Sarybchik return (0); 696283514Sarybchik 697283514Sarybchikfail4: 698283514Sarybchik EFSYS_PROBE(fail4); 699283514Sarybchik 700283514Sarybchik if (!replacing) { 701283514Sarybchik EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), saved_spec); 702283514Sarybchik saved_spec = NULL; 703283514Sarybchik } 704293764Sarybchik ef10_filter_set_entry_not_busy(eftp, ins_index); 705293764Sarybchik ef10_filter_set_entry(eftp, ins_index, NULL); 706283514Sarybchik 707283514Sarybchikfail3: 708283514Sarybchik EFSYS_PROBE(fail3); 709283514Sarybchik 710283514Sarybchikfail2: 711283514Sarybchik EFSYS_PROBE(fail2); 712283514Sarybchik 713283514Sarybchikfail1: 714291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 715283514Sarybchik 716283514Sarybchik if (locked) 717283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 718283514Sarybchik 719283514Sarybchik return (rc); 720283514Sarybchik} 721283514Sarybchik 722291436Sarybchik __checkReturn efx_rc_t 723293764Sarybchikef10_filter_add( 724283514Sarybchik __in efx_nic_t *enp, 725283514Sarybchik __inout efx_filter_spec_t *spec, 726283514Sarybchik __in boolean_t may_replace) 727283514Sarybchik{ 728291436Sarybchik efx_rc_t rc; 729283514Sarybchik 730293764Sarybchik rc = ef10_filter_add_internal(enp, spec, may_replace, NULL); 731283514Sarybchik if (rc != 0) 732283514Sarybchik goto fail1; 733283514Sarybchik 734283514Sarybchik return (0); 735283514Sarybchik 736283514Sarybchikfail1: 737291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 738283514Sarybchik 739283514Sarybchik return (rc); 740283514Sarybchik} 741283514Sarybchik 742283514Sarybchik 743291436Sarybchikstatic __checkReturn efx_rc_t 744293764Sarybchikef10_filter_delete_internal( 745283514Sarybchik __in efx_nic_t *enp, 746283514Sarybchik __in uint32_t filter_id) 747283514Sarybchik{ 748291436Sarybchik efx_rc_t rc; 749293764Sarybchik ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table; 750283514Sarybchik efx_filter_spec_t *spec; 751311032Sarybchik efsys_lock_state_t state; 752293764Sarybchik uint32_t filter_idx = filter_id % EFX_EF10_FILTER_TBL_ROWS; 753283514Sarybchik 754283514Sarybchik /* 755283514Sarybchik * Find the software table entry and mark it busy. Don't 756283514Sarybchik * remove it yet; any attempt to update while we're waiting 757283514Sarybchik * for the firmware must find the busy entry. 758283514Sarybchik * 759283514Sarybchik * FIXME: What if the busy flag is never cleared? 760283514Sarybchik */ 761283514Sarybchik EFSYS_LOCK(enp->en_eslp, state); 762293764Sarybchik while (ef10_filter_entry_is_busy(table, filter_idx)) { 763283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 764283514Sarybchik EFSYS_SPIN(1); 765283514Sarybchik EFSYS_LOCK(enp->en_eslp, state); 766283514Sarybchik } 767293764Sarybchik if ((spec = ef10_filter_entry_spec(table, filter_idx)) != NULL) { 768293764Sarybchik ef10_filter_set_entry_busy(table, filter_idx); 769283514Sarybchik } 770283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 771283514Sarybchik 772283514Sarybchik if (spec == NULL) { 773283514Sarybchik rc = ENOENT; 774283514Sarybchik goto fail1; 775283514Sarybchik } 776283514Sarybchik 777283514Sarybchik /* 778283514Sarybchik * Try to remove the hardware filter. This may fail if the MC has 779283514Sarybchik * rebooted (which frees all hardware filter resources). 780283514Sarybchik */ 781293764Sarybchik if (ef10_filter_is_exclusive(spec)) { 782283514Sarybchik rc = efx_mcdi_filter_op_delete(enp, 783283514Sarybchik MC_CMD_FILTER_OP_IN_OP_REMOVE, 784293764Sarybchik &table->eft_entry[filter_idx].efe_handle); 785283514Sarybchik } else { 786283514Sarybchik rc = efx_mcdi_filter_op_delete(enp, 787283514Sarybchik MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE, 788293764Sarybchik &table->eft_entry[filter_idx].efe_handle); 789283514Sarybchik } 790283514Sarybchik 791283514Sarybchik /* Free the software table entry */ 792283514Sarybchik EFSYS_LOCK(enp->en_eslp, state); 793293764Sarybchik ef10_filter_set_entry_not_busy(table, filter_idx); 794293764Sarybchik ef10_filter_set_entry(table, filter_idx, NULL); 795283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 796283514Sarybchik 797283514Sarybchik EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec); 798283514Sarybchik 799283514Sarybchik /* Check result of hardware filter removal */ 800283514Sarybchik if (rc != 0) 801283514Sarybchik goto fail2; 802283514Sarybchik 803283514Sarybchik return (0); 804283514Sarybchik 805283514Sarybchikfail2: 806283514Sarybchik EFSYS_PROBE(fail2); 807283514Sarybchik 808283514Sarybchikfail1: 809291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 810283514Sarybchik 811283514Sarybchik return (rc); 812283514Sarybchik} 813283514Sarybchik 814291436Sarybchik __checkReturn efx_rc_t 815293764Sarybchikef10_filter_delete( 816283514Sarybchik __in efx_nic_t *enp, 817283514Sarybchik __inout efx_filter_spec_t *spec) 818283514Sarybchik{ 819291436Sarybchik efx_rc_t rc; 820293764Sarybchik ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table; 821283514Sarybchik efx_filter_spec_t *saved_spec; 822283514Sarybchik unsigned int hash; 823283514Sarybchik unsigned int depth; 824283514Sarybchik unsigned int i; 825311032Sarybchik efsys_lock_state_t state; 826283514Sarybchik boolean_t locked = B_FALSE; 827283514Sarybchik 828293764Sarybchik EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON || 829293764Sarybchik enp->en_family == EFX_FAMILY_MEDFORD); 830283514Sarybchik 831293764Sarybchik hash = ef10_filter_hash(spec); 832283514Sarybchik 833283514Sarybchik EFSYS_LOCK(enp->en_eslp, state); 834283514Sarybchik locked = B_TRUE; 835283514Sarybchik 836283514Sarybchik depth = 1; 837283514Sarybchik for (;;) { 838293764Sarybchik i = (hash + depth) & (EFX_EF10_FILTER_TBL_ROWS - 1); 839293764Sarybchik saved_spec = ef10_filter_entry_spec(table, i); 840293764Sarybchik if (saved_spec && ef10_filter_equal(spec, saved_spec) && 841293764Sarybchik ef10_filter_same_dest(spec, saved_spec)) { 842283514Sarybchik break; 843283514Sarybchik } 844293764Sarybchik if (depth == EF10_FILTER_SEARCH_LIMIT) { 845283514Sarybchik rc = ENOENT; 846283514Sarybchik goto fail1; 847283514Sarybchik } 848283514Sarybchik depth++; 849283514Sarybchik } 850283514Sarybchik 851283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 852283514Sarybchik locked = B_FALSE; 853283514Sarybchik 854293764Sarybchik rc = ef10_filter_delete_internal(enp, i); 855283514Sarybchik if (rc != 0) 856283514Sarybchik goto fail2; 857283514Sarybchik 858283514Sarybchik return (0); 859283514Sarybchik 860283514Sarybchikfail2: 861283514Sarybchik EFSYS_PROBE(fail2); 862283514Sarybchik 863283514Sarybchikfail1: 864291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 865283514Sarybchik 866283514Sarybchik if (locked) 867283514Sarybchik EFSYS_UNLOCK(enp->en_eslp, state); 868283514Sarybchik 869283514Sarybchik return (rc); 870283514Sarybchik} 871283514Sarybchik 872291436Sarybchikstatic __checkReturn efx_rc_t 873283514Sarybchikefx_mcdi_get_parser_disp_info( 874311485Sarybchik __in efx_nic_t *enp, 875311485Sarybchik __out_ecount(buffer_length) uint32_t *buffer, 876311485Sarybchik __in size_t buffer_length, 877311485Sarybchik __out size_t *list_lengthp) 878283514Sarybchik{ 879283514Sarybchik efx_mcdi_req_t req; 880283514Sarybchik uint8_t payload[MAX(MC_CMD_GET_PARSER_DISP_INFO_IN_LEN, 881283514Sarybchik MC_CMD_GET_PARSER_DISP_INFO_OUT_LENMAX)]; 882311485Sarybchik size_t matches_count; 883311485Sarybchik size_t list_size; 884291436Sarybchik efx_rc_t rc; 885283514Sarybchik 886283514Sarybchik (void) memset(payload, 0, sizeof (payload)); 887283514Sarybchik req.emr_cmd = MC_CMD_GET_PARSER_DISP_INFO; 888283514Sarybchik req.emr_in_buf = payload; 889283514Sarybchik req.emr_in_length = MC_CMD_GET_PARSER_DISP_INFO_IN_LEN; 890283514Sarybchik req.emr_out_buf = payload; 891283514Sarybchik req.emr_out_length = MC_CMD_GET_PARSER_DISP_INFO_OUT_LENMAX; 892283514Sarybchik 893283514Sarybchik MCDI_IN_SET_DWORD(req, GET_PARSER_DISP_INFO_OUT_OP, 894283514Sarybchik MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_SUPPORTED_RX_MATCHES); 895283514Sarybchik 896283514Sarybchik efx_mcdi_execute(enp, &req); 897283514Sarybchik 898283514Sarybchik if (req.emr_rc != 0) { 899283514Sarybchik rc = req.emr_rc; 900283514Sarybchik goto fail1; 901283514Sarybchik } 902283514Sarybchik 903311485Sarybchik matches_count = MCDI_OUT_DWORD(req, 904283514Sarybchik GET_PARSER_DISP_INFO_OUT_NUM_SUPPORTED_MATCHES); 905283514Sarybchik 906283514Sarybchik if (req.emr_out_length_used < 907311485Sarybchik MC_CMD_GET_PARSER_DISP_INFO_OUT_LEN(matches_count)) { 908283514Sarybchik rc = EMSGSIZE; 909283514Sarybchik goto fail2; 910283514Sarybchik } 911283514Sarybchik 912311485Sarybchik *list_lengthp = matches_count; 913311485Sarybchik 914311485Sarybchik if (buffer_length < matches_count) { 915311485Sarybchik rc = ENOSPC; 916311485Sarybchik goto fail3; 917311485Sarybchik } 918311485Sarybchik 919311485Sarybchik /* 920311485Sarybchik * Check that the elements in the list in the MCDI response are the size 921311485Sarybchik * we expect, so we can just copy them directly. Any conversion of the 922311485Sarybchik * flags is handled by the caller. 923311485Sarybchik */ 924283514Sarybchik EFX_STATIC_ASSERT(sizeof (uint32_t) == 925283514Sarybchik MC_CMD_GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES_LEN); 926283514Sarybchik 927311485Sarybchik list_size = matches_count * 928311485Sarybchik MC_CMD_GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES_LEN; 929311485Sarybchik memcpy(buffer, 930311485Sarybchik MCDI_OUT2(req, uint32_t, 931311485Sarybchik GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES), 932311485Sarybchik list_size); 933311485Sarybchik 934283514Sarybchik return (0); 935283514Sarybchik 936311485Sarybchikfail3: 937311485Sarybchik EFSYS_PROBE(fail3); 938283514Sarybchikfail2: 939283514Sarybchik EFSYS_PROBE(fail2); 940283514Sarybchikfail1: 941291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 942283514Sarybchik 943283514Sarybchik return (rc); 944283514Sarybchik} 945283514Sarybchik 946291436Sarybchik __checkReturn efx_rc_t 947293764Sarybchikef10_filter_supported_filters( 948311485Sarybchik __in efx_nic_t *enp, 949311485Sarybchik __out_ecount(buffer_length) uint32_t *buffer, 950311485Sarybchik __in size_t buffer_length, 951311485Sarybchik __out size_t *list_lengthp) 952283514Sarybchik{ 953311485Sarybchik 954311485Sarybchik size_t mcdi_list_length; 955311485Sarybchik size_t list_length; 956311485Sarybchik uint32_t i; 957291436Sarybchik efx_rc_t rc; 958311485Sarybchik uint32_t all_filter_flags = 959311485Sarybchik (EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_LOC_HOST | 960311485Sarybchik EFX_FILTER_MATCH_REM_MAC | EFX_FILTER_MATCH_REM_PORT | 961311485Sarybchik EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_LOC_PORT | 962311485Sarybchik EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_INNER_VID | 963311485Sarybchik EFX_FILTER_MATCH_OUTER_VID | EFX_FILTER_MATCH_IP_PROTO | 964311485Sarybchik EFX_FILTER_MATCH_UNKNOWN_MCAST_DST | 965311485Sarybchik EFX_FILTER_MATCH_UNKNOWN_UCAST_DST); 966283514Sarybchik 967311485Sarybchik rc = efx_mcdi_get_parser_disp_info(enp, buffer, buffer_length, 968311485Sarybchik &mcdi_list_length); 969311485Sarybchik if (rc != 0) { 970311485Sarybchik if (rc == ENOSPC) { 971311485Sarybchik /* Pass through mcdi_list_length for the list length */ 972311485Sarybchik *list_lengthp = mcdi_list_length; 973311485Sarybchik } 974283514Sarybchik goto fail1; 975311485Sarybchik } 976283514Sarybchik 977311485Sarybchik /* 978311485Sarybchik * The static assertions in ef10_filter_init() ensure that the values of 979311485Sarybchik * the EFX_FILTER_MATCH flags match those used by MCDI, so they don't 980311485Sarybchik * need to be converted. 981311485Sarybchik * 982311485Sarybchik * In case support is added to MCDI for additional flags, remove any 983311485Sarybchik * matches from the list which include flags we don't support. The order 984311485Sarybchik * of the matches is preserved as they are ordered from highest to 985311485Sarybchik * lowest priority. 986311485Sarybchik */ 987311485Sarybchik EFSYS_ASSERT(mcdi_list_length <= buffer_length); 988311485Sarybchik list_length = 0; 989311485Sarybchik for (i = 0; i < mcdi_list_length; i++) { 990311485Sarybchik if ((buffer[i] & ~all_filter_flags) == 0) { 991311485Sarybchik buffer[list_length] = buffer[i]; 992311485Sarybchik list_length++; 993311485Sarybchik } 994311485Sarybchik } 995311485Sarybchik 996311485Sarybchik *list_lengthp = list_length; 997311485Sarybchik 998283514Sarybchik return (0); 999283514Sarybchik 1000283514Sarybchikfail1: 1001291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 1002283514Sarybchik 1003283514Sarybchik return (rc); 1004283514Sarybchik} 1005283514Sarybchik 1006291436Sarybchikstatic __checkReturn efx_rc_t 1007299411Sarybchikef10_filter_insert_unicast( 1008283514Sarybchik __in efx_nic_t *enp, 1009283514Sarybchik __in_ecount(6) uint8_t const *addr, 1010311022Sarybchik __in efx_filter_flags_t filter_flags) 1011283514Sarybchik{ 1012293764Sarybchik ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table; 1013283514Sarybchik efx_filter_spec_t spec; 1014291436Sarybchik efx_rc_t rc; 1015283514Sarybchik 1016283514Sarybchik /* Insert the filter for the local station address */ 1017283514Sarybchik efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO, 1018283514Sarybchik filter_flags, 1019293764Sarybchik eftp->eft_default_rxq); 1020283514Sarybchik efx_filter_spec_set_eth_local(&spec, EFX_FILTER_SPEC_VID_UNSPEC, addr); 1021283514Sarybchik 1022293764Sarybchik rc = ef10_filter_add_internal(enp, &spec, B_TRUE, 1023299411Sarybchik &eftp->eft_unicst_filter_indexes[eftp->eft_unicst_filter_count]); 1024299411Sarybchik if (rc != 0) 1025299411Sarybchik goto fail1; 1026283514Sarybchik 1027299411Sarybchik eftp->eft_unicst_filter_count++; 1028299411Sarybchik EFSYS_ASSERT(eftp->eft_unicst_filter_count <= 1029299411Sarybchik EFX_EF10_FILTER_UNICAST_FILTERS_MAX); 1030299411Sarybchik 1031283514Sarybchik return (0); 1032283514Sarybchik 1033299411Sarybchikfail1: 1034299411Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 1035299411Sarybchik return (rc); 1036299411Sarybchik} 1037299411Sarybchik 1038299411Sarybchikstatic __checkReturn efx_rc_t 1039299411Sarybchikef10_filter_insert_all_unicast( 1040299411Sarybchik __in efx_nic_t *enp, 1041311022Sarybchik __in efx_filter_flags_t filter_flags) 1042299411Sarybchik{ 1043299411Sarybchik ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table; 1044299411Sarybchik efx_filter_spec_t spec; 1045299411Sarybchik efx_rc_t rc; 1046299411Sarybchik 1047283514Sarybchik /* Insert the unknown unicast filter */ 1048283514Sarybchik efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO, 1049283514Sarybchik filter_flags, 1050293764Sarybchik eftp->eft_default_rxq); 1051283514Sarybchik efx_filter_spec_set_uc_def(&spec); 1052293764Sarybchik rc = ef10_filter_add_internal(enp, &spec, B_TRUE, 1053299411Sarybchik &eftp->eft_unicst_filter_indexes[eftp->eft_unicst_filter_count]); 1054283514Sarybchik if (rc != 0) 1055283514Sarybchik goto fail1; 1056283514Sarybchik 1057299411Sarybchik eftp->eft_unicst_filter_count++; 1058299411Sarybchik EFSYS_ASSERT(eftp->eft_unicst_filter_count <= 1059299411Sarybchik EFX_EF10_FILTER_UNICAST_FILTERS_MAX); 1060283514Sarybchik 1061283514Sarybchik return (0); 1062283514Sarybchik 1063283514Sarybchikfail1: 1064291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 1065283514Sarybchik return (rc); 1066283514Sarybchik} 1067283514Sarybchik 1068291436Sarybchikstatic __checkReturn efx_rc_t 1069299518Sarybchikef10_filter_insert_multicast_list( 1070283514Sarybchik __in efx_nic_t *enp, 1071283514Sarybchik __in boolean_t mulcst, 1072283514Sarybchik __in boolean_t brdcst, 1073283514Sarybchik __in_ecount(6*count) uint8_t const *addrs, 1074299410Sarybchik __in uint32_t count, 1075311022Sarybchik __in efx_filter_flags_t filter_flags, 1076299518Sarybchik __in boolean_t rollback) 1077283514Sarybchik{ 1078293764Sarybchik ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table; 1079283514Sarybchik efx_filter_spec_t spec; 1080283514Sarybchik uint8_t addr[6]; 1081299518Sarybchik uint32_t i; 1082299518Sarybchik uint32_t filter_index; 1083299518Sarybchik uint32_t filter_count; 1084291436Sarybchik efx_rc_t rc; 1085283514Sarybchik 1086283514Sarybchik if (mulcst == B_FALSE) 1087283514Sarybchik count = 0; 1088283514Sarybchik 1089283514Sarybchik if (count + (brdcst ? 1 : 0) > 1090293764Sarybchik EFX_ARRAY_SIZE(eftp->eft_mulcst_filter_indexes)) { 1091299518Sarybchik /* Too many MAC addresses */ 1092299518Sarybchik rc = EINVAL; 1093299518Sarybchik goto fail1; 1094283514Sarybchik } 1095283514Sarybchik 1096283514Sarybchik /* Insert/renew multicast address list filters */ 1097299518Sarybchik filter_count = 0; 1098299518Sarybchik for (i = 0; i < count; i++) { 1099283514Sarybchik efx_filter_spec_init_rx(&spec, 1100283514Sarybchik EFX_FILTER_PRI_AUTO, 1101283514Sarybchik filter_flags, 1102293764Sarybchik eftp->eft_default_rxq); 1103283514Sarybchik 1104283514Sarybchik efx_filter_spec_set_eth_local(&spec, 1105283514Sarybchik EFX_FILTER_SPEC_VID_UNSPEC, 1106283514Sarybchik &addrs[i * EFX_MAC_ADDR_LEN]); 1107283514Sarybchik 1108293764Sarybchik rc = ef10_filter_add_internal(enp, &spec, B_TRUE, 1109299518Sarybchik &filter_index); 1110299518Sarybchik 1111299518Sarybchik if (rc == 0) { 1112299518Sarybchik eftp->eft_mulcst_filter_indexes[filter_count] = 1113299518Sarybchik filter_index; 1114299518Sarybchik filter_count++; 1115299518Sarybchik } else if (rollback == B_TRUE) { 1116299518Sarybchik /* Only stop upon failure if told to rollback */ 1117283514Sarybchik goto rollback; 1118283514Sarybchik } 1119299518Sarybchik 1120283514Sarybchik } 1121283514Sarybchik 1122283514Sarybchik if (brdcst == B_TRUE) { 1123283514Sarybchik /* Insert/renew broadcast address filter */ 1124283514Sarybchik efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO, 1125283514Sarybchik filter_flags, 1126293764Sarybchik eftp->eft_default_rxq); 1127283514Sarybchik 1128283514Sarybchik EFX_MAC_BROADCAST_ADDR_SET(addr); 1129283514Sarybchik efx_filter_spec_set_eth_local(&spec, EFX_FILTER_SPEC_VID_UNSPEC, 1130283514Sarybchik addr); 1131283514Sarybchik 1132293764Sarybchik rc = ef10_filter_add_internal(enp, &spec, B_TRUE, 1133299518Sarybchik &filter_index); 1134299518Sarybchik 1135299518Sarybchik if (rc == 0) { 1136299518Sarybchik eftp->eft_mulcst_filter_indexes[filter_count] = 1137299518Sarybchik filter_index; 1138299518Sarybchik filter_count++; 1139299518Sarybchik } else if (rollback == B_TRUE) { 1140299518Sarybchik /* Only stop upon failure if told to rollback */ 1141283514Sarybchik goto rollback; 1142283514Sarybchik } 1143283514Sarybchik } 1144283514Sarybchik 1145299518Sarybchik eftp->eft_mulcst_filter_count = filter_count; 1146299594Sarybchik eftp->eft_using_all_mulcst = B_FALSE; 1147299518Sarybchik 1148283514Sarybchik return (0); 1149283514Sarybchik 1150283514Sarybchikrollback: 1151299518Sarybchik /* Remove any filters we have inserted */ 1152299518Sarybchik i = filter_count; 1153283514Sarybchik while (i--) { 1154293764Sarybchik (void) ef10_filter_delete_internal(enp, 1155293764Sarybchik eftp->eft_mulcst_filter_indexes[i]); 1156283514Sarybchik } 1157293764Sarybchik eftp->eft_mulcst_filter_count = 0; 1158283514Sarybchik 1159299518Sarybchikfail1: 1160299518Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 1161299518Sarybchik 1162299518Sarybchik return (rc); 1163299518Sarybchik} 1164299518Sarybchik 1165299518Sarybchikstatic __checkReturn efx_rc_t 1166299518Sarybchikef10_filter_insert_all_multicast( 1167299518Sarybchik __in efx_nic_t *enp, 1168311022Sarybchik __in efx_filter_flags_t filter_flags) 1169299518Sarybchik{ 1170299518Sarybchik ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table; 1171299518Sarybchik efx_filter_spec_t spec; 1172299518Sarybchik efx_rc_t rc; 1173299518Sarybchik 1174283514Sarybchik /* Insert the unknown multicast filter */ 1175283514Sarybchik efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO, 1176283514Sarybchik filter_flags, 1177293764Sarybchik eftp->eft_default_rxq); 1178283514Sarybchik efx_filter_spec_set_mc_def(&spec); 1179283514Sarybchik 1180293764Sarybchik rc = ef10_filter_add_internal(enp, &spec, B_TRUE, 1181293764Sarybchik &eftp->eft_mulcst_filter_indexes[0]); 1182283514Sarybchik if (rc != 0) 1183283514Sarybchik goto fail1; 1184283514Sarybchik 1185293764Sarybchik eftp->eft_mulcst_filter_count = 1; 1186299594Sarybchik eftp->eft_using_all_mulcst = B_TRUE; 1187283514Sarybchik 1188283514Sarybchik /* 1189283514Sarybchik * FIXME: If brdcst == B_FALSE, add a filter to drop broadcast traffic. 1190283514Sarybchik */ 1191283514Sarybchik 1192283514Sarybchik return (0); 1193283514Sarybchik 1194283514Sarybchikfail1: 1195291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 1196283514Sarybchik 1197283514Sarybchik return (rc); 1198283514Sarybchik} 1199283514Sarybchik 1200299594Sarybchikstatic void 1201299594Sarybchikef10_filter_remove_old( 1202299594Sarybchik __in efx_nic_t *enp) 1203299594Sarybchik{ 1204299594Sarybchik ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table; 1205299594Sarybchik uint32_t i; 1206283514Sarybchik 1207299594Sarybchik for (i = 0; i < EFX_ARRAY_SIZE(table->eft_entry); i++) { 1208299594Sarybchik if (ef10_filter_entry_is_auto_old(table, i)) { 1209299594Sarybchik (void) ef10_filter_delete_internal(enp, i); 1210299594Sarybchik } 1211299594Sarybchik } 1212299594Sarybchik} 1213299594Sarybchik 1214299594Sarybchik 1215291436Sarybchikstatic __checkReturn efx_rc_t 1216299595Sarybchikef10_filter_get_workarounds( 1217283514Sarybchik __in efx_nic_t *enp) 1218283514Sarybchik{ 1219283514Sarybchik efx_nic_cfg_t *encp = &enp->en_nic_cfg; 1220283514Sarybchik uint32_t implemented = 0; 1221283514Sarybchik uint32_t enabled = 0; 1222291436Sarybchik efx_rc_t rc; 1223283514Sarybchik 1224283514Sarybchik rc = efx_mcdi_get_workarounds(enp, &implemented, &enabled); 1225283514Sarybchik if (rc == 0) { 1226283514Sarybchik /* Check if chained multicast filter support is enabled */ 1227283514Sarybchik if (implemented & enabled & MC_CMD_GET_WORKAROUNDS_OUT_BUG26807) 1228283514Sarybchik encp->enc_bug26807_workaround = B_TRUE; 1229283514Sarybchik else 1230283514Sarybchik encp->enc_bug26807_workaround = B_FALSE; 1231283514Sarybchik } else if (rc == ENOTSUP) { 1232283514Sarybchik /* 1233283514Sarybchik * Firmware is too old to support GET_WORKAROUNDS, and support 1234283514Sarybchik * for this workaround was implemented later. 1235283514Sarybchik */ 1236283514Sarybchik encp->enc_bug26807_workaround = B_FALSE; 1237283514Sarybchik } else { 1238283514Sarybchik goto fail1; 1239283514Sarybchik } 1240283514Sarybchik 1241283514Sarybchik return (0); 1242283514Sarybchik 1243283514Sarybchikfail1: 1244291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 1245283514Sarybchik 1246283514Sarybchik return (rc); 1247283514Sarybchik 1248283514Sarybchik} 1249283514Sarybchik 1250283514Sarybchik 1251283514Sarybchik/* 1252283514Sarybchik * Reconfigure all filters. 1253283514Sarybchik * If all_unicst and/or all mulcst filters cannot be applied then 1254283514Sarybchik * return ENOTSUP (Note the filters for the specified addresses are 1255283514Sarybchik * still applied in this case). 1256283514Sarybchik */ 1257291436Sarybchik __checkReturn efx_rc_t 1258293764Sarybchikef10_filter_reconfigure( 1259283514Sarybchik __in efx_nic_t *enp, 1260283514Sarybchik __in_ecount(6) uint8_t const *mac_addr, 1261283514Sarybchik __in boolean_t all_unicst, 1262283514Sarybchik __in boolean_t mulcst, 1263283514Sarybchik __in boolean_t all_mulcst, 1264283514Sarybchik __in boolean_t brdcst, 1265283514Sarybchik __in_ecount(6*count) uint8_t const *addrs, 1266299410Sarybchik __in uint32_t count) 1267283514Sarybchik{ 1268299594Sarybchik efx_nic_cfg_t *encp = &enp->en_nic_cfg; 1269293764Sarybchik ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table; 1270311022Sarybchik efx_filter_flags_t filter_flags; 1271310930Sarybchik unsigned int i; 1272299518Sarybchik efx_rc_t all_unicst_rc = 0; 1273299518Sarybchik efx_rc_t all_mulcst_rc = 0; 1274291436Sarybchik efx_rc_t rc; 1275283514Sarybchik 1276293764Sarybchik if (table->eft_default_rxq == NULL) { 1277283514Sarybchik /* 1278283514Sarybchik * Filters direct traffic to the default RXQ, and so cannot be 1279283514Sarybchik * inserted until it is available. Any currently configured 1280283514Sarybchik * filters must be removed (ignore errors in case the MC 1281283514Sarybchik * has rebooted, which removes hardware filters). 1282283514Sarybchik */ 1283299411Sarybchik for (i = 0; i < table->eft_unicst_filter_count; i++) { 1284293764Sarybchik (void) ef10_filter_delete_internal(enp, 1285299411Sarybchik table->eft_unicst_filter_indexes[i]); 1286283514Sarybchik } 1287299411Sarybchik table->eft_unicst_filter_count = 0; 1288299411Sarybchik 1289293764Sarybchik for (i = 0; i < table->eft_mulcst_filter_count; i++) { 1290293764Sarybchik (void) ef10_filter_delete_internal(enp, 1291293764Sarybchik table->eft_mulcst_filter_indexes[i]); 1292283514Sarybchik } 1293293764Sarybchik table->eft_mulcst_filter_count = 0; 1294283514Sarybchik 1295283514Sarybchik return (0); 1296283514Sarybchik } 1297283514Sarybchik 1298293764Sarybchik if (table->eft_using_rss) 1299283514Sarybchik filter_flags = EFX_FILTER_FLAG_RX_RSS; 1300283514Sarybchik else 1301283514Sarybchik filter_flags = 0; 1302283514Sarybchik 1303283514Sarybchik /* Mark old filters which may need to be removed */ 1304299411Sarybchik for (i = 0; i < table->eft_unicst_filter_count; i++) { 1305293764Sarybchik ef10_filter_set_entry_auto_old(table, 1306299411Sarybchik table->eft_unicst_filter_indexes[i]); 1307283514Sarybchik } 1308293764Sarybchik for (i = 0; i < table->eft_mulcst_filter_count; i++) { 1309293764Sarybchik ef10_filter_set_entry_auto_old(table, 1310293764Sarybchik table->eft_mulcst_filter_indexes[i]); 1311283514Sarybchik } 1312283514Sarybchik 1313299411Sarybchik /* 1314299411Sarybchik * Insert or renew unicast filters. 1315299411Sarybchik * 1316299411Sarybchik * Frimware does not perform chaining on unicast filters. As traffic is 1317299411Sarybchik * therefore only delivered to the first matching filter, we should 1318299411Sarybchik * always insert the specific filter for our MAC address, to try and 1319299411Sarybchik * ensure we get that traffic. 1320299411Sarybchik * 1321299411Sarybchik * (If the filter for our MAC address has already been inserted by 1322299411Sarybchik * another function, we won't receive traffic sent to us, even if we 1323299411Sarybchik * insert a unicast mismatch filter. To prevent traffic stealing, this 1324299411Sarybchik * therefore relies on the privilege model only allowing functions to 1325299411Sarybchik * insert filters for their own MAC address unless explicitly given 1326299411Sarybchik * additional privileges by the user. This also means that, even on a 1327299411Sarybchik * priviliged function, inserting a unicast mismatch filter may not 1328299411Sarybchik * catch all traffic in multi PCI function scenarios.) 1329299411Sarybchik */ 1330299411Sarybchik table->eft_unicst_filter_count = 0; 1331299411Sarybchik rc = ef10_filter_insert_unicast(enp, mac_addr, filter_flags); 1332299411Sarybchik if (all_unicst || (rc != 0)) { 1333299411Sarybchik all_unicst_rc = ef10_filter_insert_all_unicast(enp, 1334299411Sarybchik filter_flags); 1335299411Sarybchik if ((rc != 0) && (all_unicst_rc != 0)) 1336283514Sarybchik goto fail1; 1337283514Sarybchik } 1338283514Sarybchik 1339283514Sarybchik /* 1340283514Sarybchik * WORKAROUND_BUG26807 controls firmware support for chained multicast 1341283514Sarybchik * filters, and can only be enabled or disabled when the hardware filter 1342283514Sarybchik * table is empty. 1343283514Sarybchik * 1344299595Sarybchik * Chained multicast filters require support from the datapath firmware, 1345299595Sarybchik * and may not be available (e.g. low-latency variants or old Huntington 1346299595Sarybchik * firmware). 1347299595Sarybchik * 1348283514Sarybchik * Firmware will reset (FLR) functions which have inserted filters in 1349283514Sarybchik * the hardware filter table when the workaround is enabled/disabled. 1350283514Sarybchik * Functions without any hardware filters are not reset. 1351283514Sarybchik * 1352283514Sarybchik * Re-check if the workaround is enabled after adding unicast hardware 1353299595Sarybchik * filters. This ensures that encp->enc_bug26807_workaround matches the 1354283514Sarybchik * firmware state, and that later changes to enable/disable the 1355283514Sarybchik * workaround will result in this function seeing a reset (FLR). 1356293764Sarybchik * 1357299595Sarybchik * In common-code drivers, we only support multiple PCI function 1358299595Sarybchik * scenarios with firmware that supports multicast chaining, so we can 1359299595Sarybchik * assume it is enabled for such cases and hence simplify the filter 1360299595Sarybchik * insertion logic. Firmware that does not support multicast chaining 1361299595Sarybchik * does not support multiple PCI function configurations either, so 1362299595Sarybchik * filter insertion is much simpler and the same strategies can still be 1363299595Sarybchik * used. 1364283514Sarybchik */ 1365299595Sarybchik if ((rc = ef10_filter_get_workarounds(enp)) != 0) 1366299411Sarybchik goto fail2; 1367283514Sarybchik 1368299594Sarybchik if ((table->eft_using_all_mulcst != all_mulcst) && 1369299594Sarybchik (encp->enc_bug26807_workaround == B_TRUE)) { 1370299594Sarybchik /* 1371299594Sarybchik * Multicast filter chaining is enabled, so traffic that matches 1372299594Sarybchik * more than one multicast filter will be replicated and 1373299594Sarybchik * delivered to multiple recipients. To avoid this duplicate 1374299594Sarybchik * delivery, remove old multicast filters before inserting new 1375299594Sarybchik * multicast filters. 1376299594Sarybchik */ 1377299594Sarybchik ef10_filter_remove_old(enp); 1378299594Sarybchik } 1379299594Sarybchik 1380283514Sarybchik /* Insert or renew multicast filters */ 1381299518Sarybchik if (all_mulcst == B_TRUE) { 1382299518Sarybchik /* 1383299518Sarybchik * Insert the all multicast filter. If that fails, try to insert 1384299518Sarybchik * all of our multicast filters (but without rollback on 1385299518Sarybchik * failure). 1386299518Sarybchik */ 1387299518Sarybchik all_mulcst_rc = ef10_filter_insert_all_multicast(enp, 1388299518Sarybchik filter_flags); 1389299518Sarybchik if (all_mulcst_rc != 0) { 1390299518Sarybchik rc = ef10_filter_insert_multicast_list(enp, B_TRUE, 1391299518Sarybchik brdcst, addrs, count, filter_flags, B_FALSE); 1392299518Sarybchik if (rc != 0) 1393299518Sarybchik goto fail3; 1394283514Sarybchik } 1395299518Sarybchik } else { 1396299518Sarybchik /* 1397299518Sarybchik * Insert filters for multicast addresses. 1398299518Sarybchik * If any insertion fails, then rollback and try to insert the 1399299518Sarybchik * all multicast filter instead. 1400299518Sarybchik * If that also fails, try to insert all of the multicast 1401299518Sarybchik * filters (but without rollback on failure). 1402299518Sarybchik */ 1403299518Sarybchik rc = ef10_filter_insert_multicast_list(enp, mulcst, brdcst, 1404299518Sarybchik addrs, count, filter_flags, B_TRUE); 1405299518Sarybchik if (rc != 0) { 1406299594Sarybchik if ((table->eft_using_all_mulcst == B_FALSE) && 1407299594Sarybchik (encp->enc_bug26807_workaround == B_TRUE)) { 1408299594Sarybchik /* 1409299594Sarybchik * Multicast filter chaining is on, so remove 1410299594Sarybchik * old filters before inserting the multicast 1411299594Sarybchik * all filter to avoid duplicate delivery caused 1412299594Sarybchik * by packets matching multiple filters. 1413299594Sarybchik */ 1414299594Sarybchik ef10_filter_remove_old(enp); 1415299594Sarybchik } 1416299594Sarybchik 1417299518Sarybchik rc = ef10_filter_insert_all_multicast(enp, 1418299518Sarybchik filter_flags); 1419299518Sarybchik if (rc != 0) { 1420299518Sarybchik rc = ef10_filter_insert_multicast_list(enp, 1421299518Sarybchik mulcst, brdcst, 1422299518Sarybchik addrs, count, filter_flags, B_FALSE); 1423299518Sarybchik if (rc != 0) 1424299518Sarybchik goto fail4; 1425299518Sarybchik } 1426299518Sarybchik } 1427283514Sarybchik } 1428283514Sarybchik 1429283514Sarybchik /* Remove old filters which were not renewed */ 1430299594Sarybchik ef10_filter_remove_old(enp); 1431283514Sarybchik 1432283514Sarybchik /* report if any optional flags were rejected */ 1433283514Sarybchik if (((all_unicst != B_FALSE) && (all_unicst_rc != 0)) || 1434283514Sarybchik ((all_mulcst != B_FALSE) && (all_mulcst_rc != 0))) { 1435283514Sarybchik rc = ENOTSUP; 1436283514Sarybchik } 1437283514Sarybchik 1438283514Sarybchik return (rc); 1439283514Sarybchik 1440283514Sarybchikfail4: 1441283514Sarybchik EFSYS_PROBE(fail4); 1442283514Sarybchikfail3: 1443283514Sarybchik EFSYS_PROBE(fail3); 1444283514Sarybchikfail2: 1445283514Sarybchik EFSYS_PROBE(fail2); 1446283514Sarybchikfail1: 1447291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 1448283514Sarybchik 1449283514Sarybchik /* Clear auto old flags */ 1450293764Sarybchik for (i = 0; i < EFX_ARRAY_SIZE(table->eft_entry); i++) { 1451293764Sarybchik if (ef10_filter_entry_is_auto_old(table, i)) { 1452293764Sarybchik ef10_filter_set_entry_not_auto_old(table, i); 1453283514Sarybchik } 1454283514Sarybchik } 1455283514Sarybchik 1456283514Sarybchik return (rc); 1457283514Sarybchik} 1458283514Sarybchik 1459283514Sarybchik void 1460293764Sarybchikef10_filter_get_default_rxq( 1461283514Sarybchik __in efx_nic_t *enp, 1462283514Sarybchik __out efx_rxq_t **erpp, 1463283514Sarybchik __out boolean_t *using_rss) 1464283514Sarybchik{ 1465293764Sarybchik ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table; 1466283514Sarybchik 1467293764Sarybchik *erpp = table->eft_default_rxq; 1468293764Sarybchik *using_rss = table->eft_using_rss; 1469283514Sarybchik} 1470283514Sarybchik 1471283514Sarybchik 1472283514Sarybchik void 1473293764Sarybchikef10_filter_default_rxq_set( 1474283514Sarybchik __in efx_nic_t *enp, 1475283514Sarybchik __in efx_rxq_t *erp, 1476283514Sarybchik __in boolean_t using_rss) 1477283514Sarybchik{ 1478293764Sarybchik ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table; 1479283514Sarybchik 1480283514Sarybchik#if EFSYS_OPT_RX_SCALE 1481283514Sarybchik EFSYS_ASSERT((using_rss == B_FALSE) || 1482293754Sarybchik (enp->en_rss_context != EF10_RSS_CONTEXT_INVALID)); 1483293764Sarybchik table->eft_using_rss = using_rss; 1484283514Sarybchik#else 1485283514Sarybchik EFSYS_ASSERT(using_rss == B_FALSE); 1486293764Sarybchik table->eft_using_rss = B_FALSE; 1487283514Sarybchik#endif 1488293764Sarybchik table->eft_default_rxq = erp; 1489283514Sarybchik} 1490283514Sarybchik 1491283514Sarybchik void 1492293764Sarybchikef10_filter_default_rxq_clear( 1493283514Sarybchik __in efx_nic_t *enp) 1494283514Sarybchik{ 1495293764Sarybchik ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table; 1496283514Sarybchik 1497293764Sarybchik table->eft_default_rxq = NULL; 1498293764Sarybchik table->eft_using_rss = B_FALSE; 1499283514Sarybchik} 1500283514Sarybchik 1501283514Sarybchik 1502283514Sarybchik#endif /* EFSYS_OPT_FILTER */ 1503283514Sarybchik 1504299597Sarybchik#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */ 1505