ef10_filter.c revision 293764
1283514Sarybchik/*-
2283514Sarybchik * Copyright (c) 2007-2015 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: head/sys/dev/sfxge/common/hunt_filter.c 293764 2016-01-12 15:24:13Z arybchik $");
33283514Sarybchik
34283514Sarybchik#include "efsys.h"
35283514Sarybchik#include "efx.h"
36283514Sarybchik#include "efx_types.h"
37283514Sarybchik#include "efx_regs_mcdi.h"
38283514Sarybchik#include "efx_impl.h"
39283514Sarybchik
40283514Sarybchik#if EFSYS_OPT_HUNTINGTON
41283514Sarybchik
42283514Sarybchik#if EFSYS_OPT_FILTER
43283514Sarybchik
44293764Sarybchik#define	EFE_SPEC(eftp, index)	((eftp)->eft_entry[(index)].efe_spec)
45283514Sarybchik
46283514Sarybchikstatic			efx_filter_spec_t *
47293764Sarybchikef10_filter_entry_spec(
48293764Sarybchik	__in		const ef10_filter_table_t *eftp,
49283514Sarybchik	__in		unsigned int index)
50283514Sarybchik{
51293764Sarybchik	return ((efx_filter_spec_t *)(EFE_SPEC(eftp, index) &
52293764Sarybchik		~(uintptr_t)EFX_EF10_FILTER_FLAGS));
53283514Sarybchik}
54283514Sarybchik
55283514Sarybchikstatic			boolean_t
56293764Sarybchikef10_filter_entry_is_busy(
57293764Sarybchik	__in		const ef10_filter_table_t *eftp,
58283514Sarybchik	__in		unsigned int index)
59283514Sarybchik{
60293764Sarybchik	if (EFE_SPEC(eftp, index) & EFX_EF10_FILTER_FLAG_BUSY)
61283514Sarybchik		return (B_TRUE);
62283514Sarybchik	else
63283514Sarybchik		return (B_FALSE);
64283514Sarybchik}
65283514Sarybchik
66283514Sarybchikstatic			boolean_t
67293764Sarybchikef10_filter_entry_is_auto_old(
68293764Sarybchik	__in		const ef10_filter_table_t *eftp,
69283514Sarybchik	__in		unsigned int index)
70283514Sarybchik{
71293764Sarybchik	if (EFE_SPEC(eftp, index) & EFX_EF10_FILTER_FLAG_AUTO_OLD)
72283514Sarybchik		return (B_TRUE);
73283514Sarybchik	else
74283514Sarybchik		return (B_FALSE);
75283514Sarybchik}
76283514Sarybchik
77283514Sarybchikstatic			void
78293764Sarybchikef10_filter_set_entry(
79293764Sarybchik	__inout		ef10_filter_table_t *eftp,
80283514Sarybchik	__in		unsigned int index,
81283514Sarybchik	__in_opt	const efx_filter_spec_t *efsp)
82283514Sarybchik{
83293764Sarybchik	EFE_SPEC(eftp, index) = (uintptr_t)efsp;
84283514Sarybchik}
85283514Sarybchik
86283514Sarybchikstatic			void
87293764Sarybchikef10_filter_set_entry_busy(
88293764Sarybchik	__inout		ef10_filter_table_t *eftp,
89283514Sarybchik	__in		unsigned int index)
90283514Sarybchik{
91293764Sarybchik	EFE_SPEC(eftp, index) |= (uintptr_t)EFX_EF10_FILTER_FLAG_BUSY;
92283514Sarybchik}
93283514Sarybchik
94283514Sarybchikstatic			void
95293764Sarybchikef10_filter_set_entry_not_busy(
96293764Sarybchik	__inout		ef10_filter_table_t *eftp,
97283514Sarybchik	__in		unsigned int index)
98283514Sarybchik{
99293764Sarybchik	EFE_SPEC(eftp, index) &= ~(uintptr_t)EFX_EF10_FILTER_FLAG_BUSY;
100283514Sarybchik}
101283514Sarybchik
102283514Sarybchikstatic			void
103293764Sarybchikef10_filter_set_entry_auto_old(
104293764Sarybchik	__inout		ef10_filter_table_t *eftp,
105283514Sarybchik	__in		unsigned int index)
106283514Sarybchik{
107293764Sarybchik	EFSYS_ASSERT(ef10_filter_entry_spec(eftp, index) != NULL);
108293764Sarybchik	EFE_SPEC(eftp, index) |= (uintptr_t)EFX_EF10_FILTER_FLAG_AUTO_OLD;
109283514Sarybchik}
110283514Sarybchik
111283514Sarybchikstatic			void
112293764Sarybchikef10_filter_set_entry_not_auto_old(
113293764Sarybchik	__inout		ef10_filter_table_t *eftp,
114283514Sarybchik	__in		unsigned int index)
115283514Sarybchik{
116293764Sarybchik	EFE_SPEC(eftp, index) &= ~(uintptr_t)EFX_EF10_FILTER_FLAG_AUTO_OLD;
117293764Sarybchik	EFSYS_ASSERT(ef10_filter_entry_spec(eftp, index) != NULL);
118283514Sarybchik}
119283514Sarybchik
120291436Sarybchik	__checkReturn	efx_rc_t
121293764Sarybchikef10_filter_init(
122283514Sarybchik	__in		efx_nic_t *enp)
123283514Sarybchik{
124291436Sarybchik	efx_rc_t rc;
125293764Sarybchik	ef10_filter_table_t *eftp;
126283514Sarybchik
127293764Sarybchik	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
128293764Sarybchik		    enp->en_family == EFX_FAMILY_MEDFORD);
129283514Sarybchik
130283514Sarybchik#define	MATCH_MASK(match) (EFX_MASK32(match) << EFX_LOW_BIT(match))
131283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_REM_HOST ==
132283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_SRC_IP));
133283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_LOC_HOST ==
134283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_DST_IP));
135283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_REM_MAC ==
136283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_SRC_MAC));
137283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_REM_PORT ==
138283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_SRC_PORT));
139283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_LOC_MAC ==
140283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_DST_MAC));
141283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_LOC_PORT ==
142283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_DST_PORT));
143283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_ETHER_TYPE ==
144283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_ETHER_TYPE));
145283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_INNER_VID ==
146283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_INNER_VLAN));
147283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_OUTER_VID ==
148283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_OUTER_VLAN));
149283514Sarybchik	EFX_STATIC_ASSERT(EFX_FILTER_MATCH_IP_PROTO ==
150283514Sarybchik	    MATCH_MASK(MC_CMD_FILTER_OP_IN_MATCH_IP_PROTO));
151283514Sarybchik#undef MATCH_MASK
152283514Sarybchik
153293764Sarybchik	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (ef10_filter_table_t), eftp);
154283514Sarybchik
155293764Sarybchik	if (!eftp) {
156283514Sarybchik		rc = ENOMEM;
157283514Sarybchik		goto fail1;
158283514Sarybchik	}
159283514Sarybchik
160293764Sarybchik	enp->en_filter.ef_ef10_filter_table = eftp;
161283514Sarybchik
162283514Sarybchik	return (0);
163283514Sarybchik
164283514Sarybchikfail1:
165291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
166283514Sarybchik
167283514Sarybchik	return (rc);
168283514Sarybchik}
169283514Sarybchik
170283514Sarybchik			void
171293764Sarybchikef10_filter_fini(
172283514Sarybchik	__in		efx_nic_t *enp)
173283514Sarybchik{
174293764Sarybchik	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
175293764Sarybchik		    enp->en_family == EFX_FAMILY_MEDFORD);
176283514Sarybchik
177293764Sarybchik	if (enp->en_filter.ef_ef10_filter_table != NULL) {
178293764Sarybchik		EFSYS_KMEM_FREE(enp->en_esip, sizeof (ef10_filter_table_t),
179293764Sarybchik		    enp->en_filter.ef_ef10_filter_table);
180283514Sarybchik	}
181283514Sarybchik}
182283514Sarybchik
183291436Sarybchikstatic	__checkReturn	efx_rc_t
184283514Sarybchikefx_mcdi_filter_op_add(
185283514Sarybchik	__in		efx_nic_t *enp,
186283514Sarybchik	__in		efx_filter_spec_t *spec,
187283514Sarybchik	__in		unsigned int filter_op,
188293764Sarybchik	__inout		ef10_filter_handle_t *handle)
189283514Sarybchik{
190283514Sarybchik	efx_mcdi_req_t req;
191283514Sarybchik	uint8_t payload[MAX(MC_CMD_FILTER_OP_IN_LEN,
192283514Sarybchik			    MC_CMD_FILTER_OP_OUT_LEN)];
193283514Sarybchik	uint32_t match_fields = 0;
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;
199283514Sarybchik	req.emr_in_length = MC_CMD_FILTER_OP_IN_LEN;
200283514Sarybchik	req.emr_out_buf = payload;
201283514Sarybchik	req.emr_out_length = MC_CMD_FILTER_OP_OUT_LEN;
202283514Sarybchik
203283514Sarybchik	switch (filter_op) {
204283514Sarybchik	case MC_CMD_FILTER_OP_IN_OP_REPLACE:
205283514Sarybchik		MCDI_IN_SET_DWORD(req, FILTER_OP_IN_HANDLE_LO,
206293764Sarybchik		    handle->efh_lo);
207283514Sarybchik		MCDI_IN_SET_DWORD(req, FILTER_OP_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:
212283514Sarybchik		MCDI_IN_SET_DWORD(req, FILTER_OP_IN_OP, filter_op);
213283514Sarybchik		break;
214283514Sarybchik	default:
215283514Sarybchik		EFSYS_ASSERT(0);
216283514Sarybchik		rc = EINVAL;
217283514Sarybchik		goto fail1;
218283514Sarybchik	}
219283514Sarybchik
220283514Sarybchik	if (spec->efs_match_flags & EFX_FILTER_MATCH_LOC_MAC_IG) {
221283514Sarybchik		/*
222283514Sarybchik		 * The LOC_MAC_IG match flag can represent unknown unicast
223283514Sarybchik		 *  or multicast filters - use the MAC address to distinguish
224283514Sarybchik		 *  them.
225283514Sarybchik		 */
226283514Sarybchik		if (EFX_MAC_ADDR_IS_MULTICAST(spec->efs_loc_mac))
227283514Sarybchik			match_fields |= 1U <<
228283514Sarybchik				MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_LBN;
229283514Sarybchik		else
230283514Sarybchik			match_fields |= 1U <<
231283514Sarybchik				MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_LBN;
232283514Sarybchik	}
233283514Sarybchik
234283514Sarybchik	match_fields |= spec->efs_match_flags & (~EFX_FILTER_MATCH_LOC_MAC_IG);
235283514Sarybchik
236283514Sarybchik	MCDI_IN_SET_DWORD(req, FILTER_OP_IN_PORT_ID,
237283514Sarybchik	    EVB_PORT_ID_ASSIGNED);
238283514Sarybchik	MCDI_IN_SET_DWORD(req, FILTER_OP_IN_MATCH_FIELDS,
239283514Sarybchik	    match_fields);
240283514Sarybchik	MCDI_IN_SET_DWORD(req, FILTER_OP_IN_RX_DEST,
241283514Sarybchik	    MC_CMD_FILTER_OP_IN_RX_DEST_HOST);
242283514Sarybchik	MCDI_IN_SET_DWORD(req, FILTER_OP_IN_RX_QUEUE,
243283514Sarybchik	    spec->efs_dmaq_id);
244283514Sarybchik	if (spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) {
245283514Sarybchik		MCDI_IN_SET_DWORD(req, FILTER_OP_IN_RX_CONTEXT,
246283514Sarybchik		    spec->efs_rss_context);
247283514Sarybchik	}
248283514Sarybchik	MCDI_IN_SET_DWORD(req, FILTER_OP_IN_RX_MODE,
249283514Sarybchik	    spec->efs_flags & EFX_FILTER_FLAG_RX_RSS ?
250283514Sarybchik	    MC_CMD_FILTER_OP_IN_RX_MODE_RSS :
251283514Sarybchik	    MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE);
252283514Sarybchik	MCDI_IN_SET_DWORD(req, FILTER_OP_IN_TX_DEST,
253283514Sarybchik	    MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT);
254283514Sarybchik
255283514Sarybchik	if (filter_op != MC_CMD_FILTER_OP_IN_OP_REPLACE) {
256283514Sarybchik		/*
257283514Sarybchik		 * NOTE: Unlike most MCDI requests, the filter fields
258283514Sarybchik		 * are presented in network (big endian) byte order.
259283514Sarybchik		 */
260283514Sarybchik		memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_IN_SRC_MAC),
261283514Sarybchik		    spec->efs_rem_mac, EFX_MAC_ADDR_LEN);
262283514Sarybchik		memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_IN_DST_MAC),
263283514Sarybchik		    spec->efs_loc_mac, EFX_MAC_ADDR_LEN);
264283514Sarybchik
265283514Sarybchik		MCDI_IN_SET_WORD(req, FILTER_OP_IN_SRC_PORT,
266283514Sarybchik		    __CPU_TO_BE_16(spec->efs_rem_port));
267283514Sarybchik		MCDI_IN_SET_WORD(req, FILTER_OP_IN_DST_PORT,
268283514Sarybchik		    __CPU_TO_BE_16(spec->efs_loc_port));
269283514Sarybchik
270283514Sarybchik		MCDI_IN_SET_WORD(req, FILTER_OP_IN_ETHER_TYPE,
271283514Sarybchik		    __CPU_TO_BE_16(spec->efs_ether_type));
272283514Sarybchik
273283514Sarybchik		MCDI_IN_SET_WORD(req, FILTER_OP_IN_INNER_VLAN,
274283514Sarybchik		    __CPU_TO_BE_16(spec->efs_inner_vid));
275283514Sarybchik		MCDI_IN_SET_WORD(req, FILTER_OP_IN_OUTER_VLAN,
276283514Sarybchik		    __CPU_TO_BE_16(spec->efs_outer_vid));
277283514Sarybchik
278283514Sarybchik		/* IP protocol (in low byte, high byte is zero) */
279283514Sarybchik		MCDI_IN_SET_BYTE(req, FILTER_OP_IN_IP_PROTO,
280283514Sarybchik		    spec->efs_ip_proto);
281283514Sarybchik
282283514Sarybchik		EFX_STATIC_ASSERT(sizeof (spec->efs_rem_host) ==
283283514Sarybchik		    MC_CMD_FILTER_OP_IN_SRC_IP_LEN);
284283514Sarybchik		EFX_STATIC_ASSERT(sizeof (spec->efs_loc_host) ==
285283514Sarybchik		    MC_CMD_FILTER_OP_IN_DST_IP_LEN);
286283514Sarybchik
287283514Sarybchik		memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_IN_SRC_IP),
288283514Sarybchik		    &spec->efs_rem_host.eo_byte[0],
289283514Sarybchik		    MC_CMD_FILTER_OP_IN_SRC_IP_LEN);
290283514Sarybchik		memcpy(MCDI_IN2(req, uint8_t, FILTER_OP_IN_DST_IP),
291283514Sarybchik		    &spec->efs_loc_host.eo_byte[0],
292283514Sarybchik		    MC_CMD_FILTER_OP_IN_DST_IP_LEN);
293283514Sarybchik	}
294283514Sarybchik
295283514Sarybchik	efx_mcdi_execute(enp, &req);
296283514Sarybchik
297283514Sarybchik	if (req.emr_rc != 0) {
298283514Sarybchik		rc = req.emr_rc;
299283514Sarybchik		goto fail2;
300283514Sarybchik	}
301283514Sarybchik
302283514Sarybchik	if (req.emr_out_length_used < MC_CMD_FILTER_OP_OUT_LEN) {
303283514Sarybchik		rc = EMSGSIZE;
304283514Sarybchik		goto fail3;
305283514Sarybchik	}
306283514Sarybchik
307293764Sarybchik	handle->efh_lo = MCDI_OUT_DWORD(req, FILTER_OP_OUT_HANDLE_LO);
308293764Sarybchik	handle->efh_hi = MCDI_OUT_DWORD(req, FILTER_OP_OUT_HANDLE_HI);
309283514Sarybchik
310283514Sarybchik	return (0);
311283514Sarybchik
312283514Sarybchikfail3:
313283514Sarybchik	EFSYS_PROBE(fail3);
314283514Sarybchikfail2:
315283514Sarybchik	EFSYS_PROBE(fail2);
316283514Sarybchikfail1:
317291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
318283514Sarybchik
319283514Sarybchik	return (rc);
320283514Sarybchik
321283514Sarybchik}
322283514Sarybchik
323291436Sarybchikstatic	__checkReturn	efx_rc_t
324283514Sarybchikefx_mcdi_filter_op_delete(
325283514Sarybchik	__in		efx_nic_t *enp,
326283514Sarybchik	__in		unsigned int filter_op,
327293764Sarybchik	__inout		ef10_filter_handle_t *handle)
328283514Sarybchik{
329283514Sarybchik	efx_mcdi_req_t req;
330283514Sarybchik	uint8_t payload[MAX(MC_CMD_FILTER_OP_IN_LEN,
331283514Sarybchik			    MC_CMD_FILTER_OP_OUT_LEN)];
332291436Sarybchik	efx_rc_t rc;
333283514Sarybchik
334283514Sarybchik	memset(payload, 0, sizeof (payload));
335283514Sarybchik	req.emr_cmd = MC_CMD_FILTER_OP;
336283514Sarybchik	req.emr_in_buf = payload;
337283514Sarybchik	req.emr_in_length = MC_CMD_FILTER_OP_IN_LEN;
338283514Sarybchik	req.emr_out_buf = payload;
339283514Sarybchik	req.emr_out_length = MC_CMD_FILTER_OP_OUT_LEN;
340283514Sarybchik
341283514Sarybchik	switch (filter_op) {
342283514Sarybchik	case MC_CMD_FILTER_OP_IN_OP_REMOVE:
343283514Sarybchik		MCDI_IN_SET_DWORD(req, FILTER_OP_IN_OP,
344283514Sarybchik		    MC_CMD_FILTER_OP_IN_OP_REMOVE);
345283514Sarybchik		break;
346283514Sarybchik	case MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE:
347283514Sarybchik		MCDI_IN_SET_DWORD(req, FILTER_OP_IN_OP,
348283514Sarybchik		    MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE);
349283514Sarybchik		break;
350283514Sarybchik	default:
351283514Sarybchik		EFSYS_ASSERT(0);
352283514Sarybchik		rc = EINVAL;
353283514Sarybchik		goto fail1;
354283514Sarybchik	}
355283514Sarybchik
356293764Sarybchik	MCDI_IN_SET_DWORD(req, FILTER_OP_IN_HANDLE_LO, handle->efh_lo);
357293764Sarybchik	MCDI_IN_SET_DWORD(req, FILTER_OP_IN_HANDLE_HI, handle->efh_hi);
358283514Sarybchik
359283514Sarybchik	efx_mcdi_execute(enp, &req);
360283514Sarybchik
361283514Sarybchik	if (req.emr_rc != 0) {
362283514Sarybchik		rc = req.emr_rc;
363283514Sarybchik		goto fail2;
364283514Sarybchik	}
365283514Sarybchik
366283514Sarybchik	if (req.emr_out_length_used < MC_CMD_FILTER_OP_OUT_LEN) {
367283514Sarybchik		rc = EMSGSIZE;
368283514Sarybchik		goto fail3;
369283514Sarybchik	}
370283514Sarybchik
371283514Sarybchik	return (0);
372283514Sarybchik
373283514Sarybchikfail3:
374283514Sarybchik	EFSYS_PROBE(fail3);
375283514Sarybchik
376283514Sarybchikfail2:
377283514Sarybchik	EFSYS_PROBE(fail2);
378283514Sarybchikfail1:
379291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
380283514Sarybchik
381283514Sarybchik	return (rc);
382283514Sarybchik}
383283514Sarybchik
384283514Sarybchikstatic	__checkReturn	boolean_t
385293764Sarybchikef10_filter_equal(
386283514Sarybchik	__in		const efx_filter_spec_t *left,
387283514Sarybchik	__in		const efx_filter_spec_t *right)
388283514Sarybchik{
389283514Sarybchik	/* FIXME: Consider rx vs tx filters (look at efs_flags) */
390283514Sarybchik	if (left->efs_match_flags != right->efs_match_flags)
391283514Sarybchik		return (B_FALSE);
392283514Sarybchik	if (!EFX_OWORD_IS_EQUAL(left->efs_rem_host, right->efs_rem_host))
393283514Sarybchik		return (B_FALSE);
394283514Sarybchik	if (!EFX_OWORD_IS_EQUAL(left->efs_loc_host, right->efs_loc_host))
395283514Sarybchik		return (B_FALSE);
396283514Sarybchik	if (memcmp(left->efs_rem_mac, right->efs_rem_mac, EFX_MAC_ADDR_LEN))
397283514Sarybchik		return (B_FALSE);
398283514Sarybchik	if (memcmp(left->efs_loc_mac, right->efs_loc_mac, EFX_MAC_ADDR_LEN))
399283514Sarybchik		return (B_FALSE);
400283514Sarybchik	if (left->efs_rem_port != right->efs_rem_port)
401283514Sarybchik		return (B_FALSE);
402283514Sarybchik	if (left->efs_loc_port != right->efs_loc_port)
403283514Sarybchik		return (B_FALSE);
404283514Sarybchik	if (left->efs_inner_vid != right->efs_inner_vid)
405283514Sarybchik		return (B_FALSE);
406283514Sarybchik	if (left->efs_outer_vid != right->efs_outer_vid)
407283514Sarybchik		return (B_FALSE);
408283514Sarybchik	if (left->efs_ether_type != right->efs_ether_type)
409283514Sarybchik		return (B_FALSE);
410283514Sarybchik	if (left->efs_ip_proto != right->efs_ip_proto)
411283514Sarybchik		return (B_FALSE);
412283514Sarybchik
413283514Sarybchik	return (B_TRUE);
414283514Sarybchik
415283514Sarybchik}
416283514Sarybchik
417283514Sarybchikstatic	__checkReturn	boolean_t
418293764Sarybchikef10_filter_same_dest(
419283514Sarybchik	__in		const efx_filter_spec_t *left,
420283514Sarybchik	__in		const efx_filter_spec_t *right)
421283514Sarybchik{
422283514Sarybchik	if ((left->efs_flags & EFX_FILTER_FLAG_RX_RSS) &&
423283514Sarybchik	    (right->efs_flags & EFX_FILTER_FLAG_RX_RSS)) {
424283514Sarybchik		if (left->efs_rss_context == right->efs_rss_context)
425283514Sarybchik			return (B_TRUE);
426283514Sarybchik	} else if ((~(left->efs_flags) & EFX_FILTER_FLAG_RX_RSS) &&
427283514Sarybchik	    (~(right->efs_flags) & EFX_FILTER_FLAG_RX_RSS)) {
428283514Sarybchik		if (left->efs_dmaq_id == right->efs_dmaq_id)
429283514Sarybchik			return (B_TRUE);
430283514Sarybchik	}
431283514Sarybchik	return (B_FALSE);
432283514Sarybchik}
433283514Sarybchik
434283514Sarybchikstatic	__checkReturn	uint32_t
435293764Sarybchikef10_filter_hash(
436283514Sarybchik	__in		efx_filter_spec_t *spec)
437283514Sarybchik{
438283514Sarybchik	EFX_STATIC_ASSERT((sizeof (efx_filter_spec_t) % sizeof (uint32_t))
439283514Sarybchik			    == 0);
440283514Sarybchik	EFX_STATIC_ASSERT((EFX_FIELD_OFFSET(efx_filter_spec_t, efs_outer_vid) %
441283514Sarybchik			    sizeof (uint32_t)) == 0);
442283514Sarybchik
443283514Sarybchik	/*
444283514Sarybchik	 * As the area of the efx_filter_spec_t we need to hash is DWORD
445283514Sarybchik	 * aligned and an exact number of DWORDs in size we can use the
446283514Sarybchik	 * optimised efx_hash_dwords() rather than efx_hash_bytes()
447283514Sarybchik	 */
448283514Sarybchik	return (efx_hash_dwords((const uint32_t *)&spec->efs_outer_vid,
449283514Sarybchik			(sizeof (efx_filter_spec_t) -
450283514Sarybchik			EFX_FIELD_OFFSET(efx_filter_spec_t, efs_outer_vid)) /
451283514Sarybchik			sizeof (uint32_t), 0));
452283514Sarybchik}
453283514Sarybchik
454283514Sarybchik/*
455283514Sarybchik * Decide whether a filter should be exclusive or else should allow
456283514Sarybchik * delivery to additional recipients.  Currently we decide that
457283514Sarybchik * filters for specific local unicast MAC and IP addresses are
458283514Sarybchik * exclusive.
459283514Sarybchik */
460283514Sarybchikstatic	__checkReturn	boolean_t
461293764Sarybchikef10_filter_is_exclusive(
462283514Sarybchik	__in		efx_filter_spec_t *spec)
463283514Sarybchik{
464283514Sarybchik	if ((spec->efs_match_flags & EFX_FILTER_MATCH_LOC_MAC) &&
465283514Sarybchik	    !EFX_MAC_ADDR_IS_MULTICAST(spec->efs_loc_mac))
466283514Sarybchik		return (B_TRUE);
467283514Sarybchik
468283514Sarybchik	if ((spec->efs_match_flags &
469283514Sarybchik		(EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_LOC_HOST)) ==
470283514Sarybchik	    (EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_LOC_HOST)) {
471283514Sarybchik		if ((spec->efs_ether_type == EFX_ETHER_TYPE_IPV4) &&
472283514Sarybchik		    ((spec->efs_loc_host.eo_u8[0] & 0xf) != 0xe))
473283514Sarybchik			return (B_TRUE);
474283514Sarybchik		if ((spec->efs_ether_type == EFX_ETHER_TYPE_IPV6) &&
475283514Sarybchik		    (spec->efs_loc_host.eo_u8[0] != 0xff))
476283514Sarybchik			return (B_TRUE);
477283514Sarybchik	}
478283514Sarybchik
479283514Sarybchik	return (B_FALSE);
480283514Sarybchik}
481283514Sarybchik
482291436Sarybchik	__checkReturn	efx_rc_t
483293764Sarybchikef10_filter_restore(
484283514Sarybchik	__in		efx_nic_t *enp)
485283514Sarybchik{
486283514Sarybchik	int tbl_id;
487283514Sarybchik	efx_filter_spec_t *spec;
488293764Sarybchik	ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table;
489283514Sarybchik	boolean_t restoring;
490283514Sarybchik	int state;
491291436Sarybchik	efx_rc_t rc;
492283514Sarybchik
493293764Sarybchik	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
494293764Sarybchik		    enp->en_family == EFX_FAMILY_MEDFORD);
495283514Sarybchik
496293764Sarybchik	for (tbl_id = 0; tbl_id < EFX_EF10_FILTER_TBL_ROWS; tbl_id++) {
497283514Sarybchik
498283514Sarybchik		EFSYS_LOCK(enp->en_eslp, state);
499283514Sarybchik
500293764Sarybchik		spec = ef10_filter_entry_spec(eftp, tbl_id);
501283514Sarybchik		if (spec == NULL) {
502283514Sarybchik			restoring = B_FALSE;
503293764Sarybchik		} else if (ef10_filter_entry_is_busy(eftp, tbl_id)) {
504283514Sarybchik			/* Ignore busy entries. */
505283514Sarybchik			restoring = B_FALSE;
506283514Sarybchik		} else {
507293764Sarybchik			ef10_filter_set_entry_busy(eftp, tbl_id);
508283514Sarybchik			restoring = B_TRUE;
509283514Sarybchik		}
510283514Sarybchik
511283514Sarybchik		EFSYS_UNLOCK(enp->en_eslp, state);
512283514Sarybchik
513283514Sarybchik		if (restoring == B_FALSE)
514283514Sarybchik			continue;
515283514Sarybchik
516293764Sarybchik		if (ef10_filter_is_exclusive(spec)) {
517283514Sarybchik			rc = efx_mcdi_filter_op_add(enp, spec,
518283514Sarybchik			    MC_CMD_FILTER_OP_IN_OP_INSERT,
519293764Sarybchik			    &eftp->eft_entry[tbl_id].efe_handle);
520283514Sarybchik		} else {
521283514Sarybchik			rc = efx_mcdi_filter_op_add(enp, spec,
522283514Sarybchik			    MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE,
523293764Sarybchik			    &eftp->eft_entry[tbl_id].efe_handle);
524283514Sarybchik		}
525283514Sarybchik
526283514Sarybchik		if (rc != 0)
527283514Sarybchik			goto fail1;
528283514Sarybchik
529283514Sarybchik		EFSYS_LOCK(enp->en_eslp, state);
530283514Sarybchik
531293764Sarybchik		ef10_filter_set_entry_not_busy(eftp, tbl_id);
532283514Sarybchik
533283514Sarybchik		EFSYS_UNLOCK(enp->en_eslp, state);
534283514Sarybchik	}
535283514Sarybchik
536283514Sarybchik	return (0);
537283514Sarybchik
538283514Sarybchikfail1:
539291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
540283514Sarybchik
541283514Sarybchik	return (rc);
542283514Sarybchik}
543283514Sarybchik
544283514Sarybchik/*
545283514Sarybchik * An arbitrary search limit for the software hash table. As per the linux net
546283514Sarybchik * driver.
547283514Sarybchik */
548293764Sarybchik#define	EF10_FILTER_SEARCH_LIMIT 200
549283514Sarybchik
550291436Sarybchikstatic	__checkReturn	efx_rc_t
551293764Sarybchikef10_filter_add_internal(
552283514Sarybchik	__in		efx_nic_t *enp,
553283514Sarybchik	__inout		efx_filter_spec_t *spec,
554283514Sarybchik	__in		boolean_t may_replace,
555283514Sarybchik	__out_opt	uint32_t *filter_id)
556283514Sarybchik{
557291436Sarybchik	efx_rc_t rc;
558293764Sarybchik	ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table;
559283514Sarybchik	efx_filter_spec_t *saved_spec;
560283514Sarybchik	uint32_t hash;
561283514Sarybchik	unsigned int depth;
562283514Sarybchik	int ins_index;
563283514Sarybchik	boolean_t replacing = B_FALSE;
564283514Sarybchik	unsigned int i;
565283514Sarybchik	int state;
566283514Sarybchik	boolean_t locked = B_FALSE;
567283514Sarybchik
568293764Sarybchik	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
569293764Sarybchik		    enp->en_family == EFX_FAMILY_MEDFORD);
570283514Sarybchik
571283514Sarybchik#if EFSYS_OPT_RX_SCALE
572283514Sarybchik	spec->efs_rss_context = enp->en_rss_context;
573283514Sarybchik#endif
574283514Sarybchik
575293764Sarybchik	hash = ef10_filter_hash(spec);
576283514Sarybchik
577283514Sarybchik	/*
578283514Sarybchik	 * FIXME: Add support for inserting filters of different priorities
579283514Sarybchik	 * and removing lower priority multicast filters (bug 42378)
580283514Sarybchik	 */
581283514Sarybchik
582283514Sarybchik	/*
583283514Sarybchik	 * Find any existing filters with the same match tuple or
584283514Sarybchik	 * else a free slot to insert at.  If any of them are busy,
585283514Sarybchik	 * we have to wait and retry.
586283514Sarybchik	 */
587283514Sarybchik	for (;;) {
588283514Sarybchik		ins_index = -1;
589283514Sarybchik		depth = 1;
590283514Sarybchik		EFSYS_LOCK(enp->en_eslp, state);
591283514Sarybchik		locked = B_TRUE;
592283514Sarybchik
593283514Sarybchik		for (;;) {
594293764Sarybchik			i = (hash + depth) & (EFX_EF10_FILTER_TBL_ROWS - 1);
595293764Sarybchik			saved_spec = ef10_filter_entry_spec(eftp, i);
596283514Sarybchik
597283514Sarybchik			if (!saved_spec) {
598283514Sarybchik				if (ins_index < 0) {
599283514Sarybchik					ins_index = i;
600283514Sarybchik				}
601293764Sarybchik			} else if (ef10_filter_equal(spec, saved_spec)) {
602293764Sarybchik				if (ef10_filter_entry_is_busy(eftp, i))
603283514Sarybchik					break;
604283514Sarybchik				if (saved_spec->efs_priority
605283514Sarybchik					    == EFX_FILTER_PRI_AUTO) {
606283514Sarybchik					ins_index = i;
607283514Sarybchik					goto found;
608293764Sarybchik				} else if (ef10_filter_is_exclusive(spec)) {
609283514Sarybchik					if (may_replace) {
610283514Sarybchik						ins_index = i;
611283514Sarybchik						goto found;
612283514Sarybchik					} else {
613283514Sarybchik						rc = EEXIST;
614283514Sarybchik						goto fail1;
615283514Sarybchik					}
616283514Sarybchik				}
617283514Sarybchik
618283514Sarybchik				/* Leave existing */
619283514Sarybchik			}
620283514Sarybchik
621283514Sarybchik			/*
622283514Sarybchik			 * Once we reach the maximum search depth, use
623283514Sarybchik			 * the first suitable slot or return EBUSY if
624283514Sarybchik			 * there was none.
625283514Sarybchik			 */
626293764Sarybchik			if (depth == EF10_FILTER_SEARCH_LIMIT) {
627283514Sarybchik				if (ins_index < 0) {
628283514Sarybchik					rc = EBUSY;
629283514Sarybchik					goto fail2;
630283514Sarybchik				}
631283514Sarybchik				goto found;
632283514Sarybchik			}
633283514Sarybchik			depth++;
634283514Sarybchik		}
635283514Sarybchik		EFSYS_UNLOCK(enp->en_eslp, state);
636283514Sarybchik		locked = B_FALSE;
637283514Sarybchik	}
638283514Sarybchik
639283514Sarybchikfound:
640283514Sarybchik	/*
641283514Sarybchik	 * Create a software table entry if necessary, and mark it
642283514Sarybchik	 * busy.  We might yet fail to insert, but any attempt to
643283514Sarybchik	 * insert a conflicting filter while we're waiting for the
644283514Sarybchik	 * firmware must find the busy entry.
645283514Sarybchik	 */
646293764Sarybchik	saved_spec = ef10_filter_entry_spec(eftp, ins_index);
647283514Sarybchik	if (saved_spec) {
648283514Sarybchik		if (saved_spec->efs_priority == EFX_FILTER_PRI_AUTO) {
649283514Sarybchik			/* This is a filter we are refreshing */
650293764Sarybchik			ef10_filter_set_entry_not_auto_old(eftp, ins_index);
651283514Sarybchik			goto out_unlock;
652283514Sarybchik
653283514Sarybchik		}
654283514Sarybchik		replacing = B_TRUE;
655283514Sarybchik	} else {
656283514Sarybchik		EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec), saved_spec);
657283514Sarybchik		if (!saved_spec) {
658283514Sarybchik			rc = ENOMEM;
659283514Sarybchik			goto fail3;
660283514Sarybchik		}
661283514Sarybchik		*saved_spec = *spec;
662293764Sarybchik		ef10_filter_set_entry(eftp, ins_index, saved_spec);
663283514Sarybchik	}
664293764Sarybchik	ef10_filter_set_entry_busy(eftp, ins_index);
665283514Sarybchik
666283514Sarybchik	EFSYS_UNLOCK(enp->en_eslp, state);
667283514Sarybchik	locked = B_FALSE;
668283514Sarybchik
669283514Sarybchik	/*
670283514Sarybchik	 * On replacing the filter handle may change after after a successful
671283514Sarybchik	 * replace operation.
672283514Sarybchik	 */
673283514Sarybchik	if (replacing) {
674283514Sarybchik		rc = efx_mcdi_filter_op_add(enp, spec,
675283514Sarybchik		    MC_CMD_FILTER_OP_IN_OP_REPLACE,
676293764Sarybchik		    &eftp->eft_entry[ins_index].efe_handle);
677293764Sarybchik	} else if (ef10_filter_is_exclusive(spec)) {
678283514Sarybchik		rc = efx_mcdi_filter_op_add(enp, spec,
679283514Sarybchik		    MC_CMD_FILTER_OP_IN_OP_INSERT,
680293764Sarybchik		    &eftp->eft_entry[ins_index].efe_handle);
681283514Sarybchik	} else {
682283514Sarybchik		rc = efx_mcdi_filter_op_add(enp, spec,
683283514Sarybchik		    MC_CMD_FILTER_OP_IN_OP_SUBSCRIBE,
684293764Sarybchik		    &eftp->eft_entry[ins_index].efe_handle);
685283514Sarybchik	}
686283514Sarybchik
687283514Sarybchik	if (rc != 0)
688283514Sarybchik		goto fail4;
689283514Sarybchik
690283514Sarybchik	EFSYS_LOCK(enp->en_eslp, state);
691283514Sarybchik	locked = B_TRUE;
692283514Sarybchik
693283514Sarybchik	if (replacing) {
694283514Sarybchik		/* Update the fields that may differ */
695283514Sarybchik		saved_spec->efs_priority = spec->efs_priority;
696283514Sarybchik		saved_spec->efs_flags = spec->efs_flags;
697283514Sarybchik		saved_spec->efs_rss_context = spec->efs_rss_context;
698283514Sarybchik		saved_spec->efs_dmaq_id = spec->efs_dmaq_id;
699283514Sarybchik	}
700283514Sarybchik
701293764Sarybchik	ef10_filter_set_entry_not_busy(eftp, ins_index);
702283514Sarybchik
703283514Sarybchikout_unlock:
704283514Sarybchik
705283514Sarybchik	EFSYS_UNLOCK(enp->en_eslp, state);
706283514Sarybchik	locked = B_FALSE;
707283514Sarybchik
708283514Sarybchik	if (filter_id)
709283514Sarybchik		*filter_id = ins_index;
710283514Sarybchik
711283514Sarybchik	return (0);
712283514Sarybchik
713283514Sarybchikfail4:
714283514Sarybchik	EFSYS_PROBE(fail4);
715283514Sarybchik
716283514Sarybchik	if (!replacing) {
717283514Sarybchik		EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), saved_spec);
718283514Sarybchik		saved_spec = NULL;
719283514Sarybchik	}
720293764Sarybchik	ef10_filter_set_entry_not_busy(eftp, ins_index);
721293764Sarybchik	ef10_filter_set_entry(eftp, ins_index, NULL);
722283514Sarybchik
723283514Sarybchikfail3:
724283514Sarybchik	EFSYS_PROBE(fail3);
725283514Sarybchik
726283514Sarybchikfail2:
727283514Sarybchik	EFSYS_PROBE(fail2);
728283514Sarybchik
729283514Sarybchikfail1:
730291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
731283514Sarybchik
732283514Sarybchik	if (locked)
733283514Sarybchik		EFSYS_UNLOCK(enp->en_eslp, state);
734283514Sarybchik
735283514Sarybchik	return (rc);
736283514Sarybchik}
737283514Sarybchik
738291436Sarybchik	__checkReturn	efx_rc_t
739293764Sarybchikef10_filter_add(
740283514Sarybchik	__in		efx_nic_t *enp,
741283514Sarybchik	__inout		efx_filter_spec_t *spec,
742283514Sarybchik	__in		boolean_t may_replace)
743283514Sarybchik{
744291436Sarybchik	efx_rc_t rc;
745283514Sarybchik
746293764Sarybchik	rc = ef10_filter_add_internal(enp, spec, may_replace, NULL);
747283514Sarybchik	if (rc != 0)
748283514Sarybchik		goto fail1;
749283514Sarybchik
750283514Sarybchik	return (0);
751283514Sarybchik
752283514Sarybchikfail1:
753291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
754283514Sarybchik
755283514Sarybchik	return (rc);
756283514Sarybchik}
757283514Sarybchik
758283514Sarybchik
759291436Sarybchikstatic	__checkReturn	efx_rc_t
760293764Sarybchikef10_filter_delete_internal(
761283514Sarybchik	__in		efx_nic_t *enp,
762283514Sarybchik	__in		uint32_t filter_id)
763283514Sarybchik{
764291436Sarybchik	efx_rc_t rc;
765293764Sarybchik	ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table;
766283514Sarybchik	efx_filter_spec_t *spec;
767283514Sarybchik	int state;
768293764Sarybchik	uint32_t filter_idx = filter_id % EFX_EF10_FILTER_TBL_ROWS;
769283514Sarybchik
770283514Sarybchik	/*
771283514Sarybchik	 * Find the software table entry and mark it busy.  Don't
772283514Sarybchik	 * remove it yet; any attempt to update while we're waiting
773283514Sarybchik	 * for the firmware must find the busy entry.
774283514Sarybchik	 *
775283514Sarybchik	 * FIXME: What if the busy flag is never cleared?
776283514Sarybchik	 */
777283514Sarybchik	EFSYS_LOCK(enp->en_eslp, state);
778293764Sarybchik	while (ef10_filter_entry_is_busy(table, filter_idx)) {
779283514Sarybchik		EFSYS_UNLOCK(enp->en_eslp, state);
780283514Sarybchik		EFSYS_SPIN(1);
781283514Sarybchik		EFSYS_LOCK(enp->en_eslp, state);
782283514Sarybchik	}
783293764Sarybchik	if ((spec = ef10_filter_entry_spec(table, filter_idx)) != NULL) {
784293764Sarybchik		ef10_filter_set_entry_busy(table, filter_idx);
785283514Sarybchik	}
786283514Sarybchik	EFSYS_UNLOCK(enp->en_eslp, state);
787283514Sarybchik
788283514Sarybchik	if (spec == NULL) {
789283514Sarybchik		rc = ENOENT;
790283514Sarybchik		goto fail1;
791283514Sarybchik	}
792283514Sarybchik
793283514Sarybchik	/*
794283514Sarybchik	 * Try to remove the hardware filter. This may fail if the MC has
795283514Sarybchik	 * rebooted (which frees all hardware filter resources).
796283514Sarybchik	 */
797293764Sarybchik	if (ef10_filter_is_exclusive(spec)) {
798283514Sarybchik		rc = efx_mcdi_filter_op_delete(enp,
799283514Sarybchik		    MC_CMD_FILTER_OP_IN_OP_REMOVE,
800293764Sarybchik		    &table->eft_entry[filter_idx].efe_handle);
801283514Sarybchik	} else {
802283514Sarybchik		rc = efx_mcdi_filter_op_delete(enp,
803283514Sarybchik		    MC_CMD_FILTER_OP_IN_OP_UNSUBSCRIBE,
804293764Sarybchik		    &table->eft_entry[filter_idx].efe_handle);
805283514Sarybchik	}
806283514Sarybchik
807283514Sarybchik	/* Free the software table entry */
808283514Sarybchik	EFSYS_LOCK(enp->en_eslp, state);
809293764Sarybchik	ef10_filter_set_entry_not_busy(table, filter_idx);
810293764Sarybchik	ef10_filter_set_entry(table, filter_idx, NULL);
811283514Sarybchik	EFSYS_UNLOCK(enp->en_eslp, state);
812283514Sarybchik
813283514Sarybchik	EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec);
814283514Sarybchik
815283514Sarybchik	/* Check result of hardware filter removal */
816283514Sarybchik	if (rc != 0)
817283514Sarybchik		goto fail2;
818283514Sarybchik
819283514Sarybchik	return (0);
820283514Sarybchik
821283514Sarybchikfail2:
822283514Sarybchik	EFSYS_PROBE(fail2);
823283514Sarybchik
824283514Sarybchikfail1:
825291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
826283514Sarybchik
827283514Sarybchik	return (rc);
828283514Sarybchik}
829283514Sarybchik
830291436Sarybchik	__checkReturn	efx_rc_t
831293764Sarybchikef10_filter_delete(
832283514Sarybchik	__in		efx_nic_t *enp,
833283514Sarybchik	__inout		efx_filter_spec_t *spec)
834283514Sarybchik{
835291436Sarybchik	efx_rc_t rc;
836293764Sarybchik	ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table;
837283514Sarybchik	efx_filter_spec_t *saved_spec;
838283514Sarybchik	unsigned int hash;
839283514Sarybchik	unsigned int depth;
840283514Sarybchik	unsigned int i;
841283514Sarybchik	int state;
842283514Sarybchik	boolean_t locked = B_FALSE;
843283514Sarybchik
844293764Sarybchik	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
845293764Sarybchik		    enp->en_family == EFX_FAMILY_MEDFORD);
846283514Sarybchik
847293764Sarybchik	hash = ef10_filter_hash(spec);
848283514Sarybchik
849283514Sarybchik	EFSYS_LOCK(enp->en_eslp, state);
850283514Sarybchik	locked = B_TRUE;
851283514Sarybchik
852283514Sarybchik	depth = 1;
853283514Sarybchik	for (;;) {
854293764Sarybchik		i = (hash + depth) & (EFX_EF10_FILTER_TBL_ROWS - 1);
855293764Sarybchik		saved_spec = ef10_filter_entry_spec(table, i);
856293764Sarybchik		if (saved_spec && ef10_filter_equal(spec, saved_spec) &&
857293764Sarybchik		    ef10_filter_same_dest(spec, saved_spec)) {
858283514Sarybchik			break;
859283514Sarybchik		}
860293764Sarybchik		if (depth == EF10_FILTER_SEARCH_LIMIT) {
861283514Sarybchik			rc = ENOENT;
862283514Sarybchik			goto fail1;
863283514Sarybchik		}
864283514Sarybchik		depth++;
865283514Sarybchik	}
866283514Sarybchik
867283514Sarybchik	EFSYS_UNLOCK(enp->en_eslp, state);
868283514Sarybchik	locked = B_FALSE;
869283514Sarybchik
870293764Sarybchik	rc = ef10_filter_delete_internal(enp, i);
871283514Sarybchik	if (rc != 0)
872283514Sarybchik		goto fail2;
873283514Sarybchik
874283514Sarybchik	return (0);
875283514Sarybchik
876283514Sarybchikfail2:
877283514Sarybchik	EFSYS_PROBE(fail2);
878283514Sarybchik
879283514Sarybchikfail1:
880291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
881283514Sarybchik
882283514Sarybchik	if (locked)
883283514Sarybchik		EFSYS_UNLOCK(enp->en_eslp, state);
884283514Sarybchik
885283514Sarybchik	return (rc);
886283514Sarybchik}
887283514Sarybchik
888291436Sarybchikstatic	__checkReturn	efx_rc_t
889283514Sarybchikefx_mcdi_get_parser_disp_info(
890283514Sarybchik	__in		efx_nic_t *enp,
891283514Sarybchik	__out		uint32_t *list,
892283514Sarybchik	__out		size_t *length)
893283514Sarybchik{
894283514Sarybchik	efx_mcdi_req_t req;
895283514Sarybchik	uint8_t payload[MAX(MC_CMD_GET_PARSER_DISP_INFO_IN_LEN,
896283514Sarybchik			    MC_CMD_GET_PARSER_DISP_INFO_OUT_LENMAX)];
897291436Sarybchik	efx_rc_t rc;
898283514Sarybchik	uint32_t i;
899283514Sarybchik	boolean_t support_unknown_ucast = B_FALSE;
900283514Sarybchik	boolean_t support_unknown_mcast = B_FALSE;
901283514Sarybchik
902283514Sarybchik	(void) memset(payload, 0, sizeof (payload));
903283514Sarybchik	req.emr_cmd = MC_CMD_GET_PARSER_DISP_INFO;
904283514Sarybchik	req.emr_in_buf = payload;
905283514Sarybchik	req.emr_in_length = MC_CMD_GET_PARSER_DISP_INFO_IN_LEN;
906283514Sarybchik	req.emr_out_buf = payload;
907283514Sarybchik	req.emr_out_length = MC_CMD_GET_PARSER_DISP_INFO_OUT_LENMAX;
908283514Sarybchik
909283514Sarybchik	MCDI_IN_SET_DWORD(req, GET_PARSER_DISP_INFO_OUT_OP,
910283514Sarybchik	    MC_CMD_GET_PARSER_DISP_INFO_IN_OP_GET_SUPPORTED_RX_MATCHES);
911283514Sarybchik
912283514Sarybchik	efx_mcdi_execute(enp, &req);
913283514Sarybchik
914283514Sarybchik	if (req.emr_rc != 0) {
915283514Sarybchik		rc = req.emr_rc;
916283514Sarybchik		goto fail1;
917283514Sarybchik	}
918283514Sarybchik
919283514Sarybchik	*length = MCDI_OUT_DWORD(req,
920283514Sarybchik	    GET_PARSER_DISP_INFO_OUT_NUM_SUPPORTED_MATCHES);
921283514Sarybchik
922283514Sarybchik	if (req.emr_out_length_used <
923283514Sarybchik	    MC_CMD_GET_PARSER_DISP_INFO_OUT_LEN(*length)) {
924283514Sarybchik		rc = EMSGSIZE;
925283514Sarybchik		goto fail2;
926283514Sarybchik	}
927283514Sarybchik
928283514Sarybchik	memcpy(list,
929283514Sarybchik	    MCDI_OUT2(req,
930283514Sarybchik	    uint32_t,
931283514Sarybchik	    GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES),
932283514Sarybchik	    (*length) * sizeof (uint32_t));
933283514Sarybchik	EFX_STATIC_ASSERT(sizeof (uint32_t) ==
934283514Sarybchik	    MC_CMD_GET_PARSER_DISP_INFO_OUT_SUPPORTED_MATCHES_LEN);
935283514Sarybchik
936283514Sarybchik	/*
937283514Sarybchik	 * Remove UNKNOWN UCAST and MCAST flags, and if both are present, change
938283514Sarybchik	 * the lower priority one to LOC_MAC_IG.
939283514Sarybchik	 */
940283514Sarybchik	for (i = 0; i < *length; i++) {
941283514Sarybchik		if (list[i] & MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_LBN) {
942283514Sarybchik			list[i] &=
943283514Sarybchik			(~MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_UCAST_DST_LBN);
944283514Sarybchik			support_unknown_ucast = B_TRUE;
945283514Sarybchik		}
946283514Sarybchik		if (list[i] & MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_LBN) {
947283514Sarybchik			list[i] &=
948283514Sarybchik			(~MC_CMD_FILTER_OP_IN_MATCH_UNKNOWN_MCAST_DST_LBN);
949283514Sarybchik			support_unknown_mcast = B_TRUE;
950283514Sarybchik		}
951283514Sarybchik
952283514Sarybchik		if (support_unknown_ucast && support_unknown_mcast) {
953283514Sarybchik			list[i] &= EFX_FILTER_MATCH_LOC_MAC_IG;
954283514Sarybchik			break;
955283514Sarybchik		}
956283514Sarybchik	}
957283514Sarybchik
958283514Sarybchik	return (0);
959283514Sarybchik
960283514Sarybchikfail2:
961283514Sarybchik	EFSYS_PROBE(fail2);
962283514Sarybchikfail1:
963291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
964283514Sarybchik
965283514Sarybchik	return (rc);
966283514Sarybchik}
967283514Sarybchik
968291436Sarybchik	__checkReturn	efx_rc_t
969293764Sarybchikef10_filter_supported_filters(
970283514Sarybchik	__in		efx_nic_t *enp,
971283514Sarybchik	__out		uint32_t *list,
972283514Sarybchik	__out		size_t *length)
973283514Sarybchik{
974291436Sarybchik	efx_rc_t rc;
975283514Sarybchik
976283514Sarybchik	if ((rc = efx_mcdi_get_parser_disp_info(enp, list, length) != 0))
977283514Sarybchik		goto fail1;
978283514Sarybchik
979283514Sarybchik	return (0);
980283514Sarybchik
981283514Sarybchikfail1:
982291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
983283514Sarybchik
984283514Sarybchik	return (rc);
985283514Sarybchik}
986283514Sarybchik
987291436Sarybchikstatic	__checkReturn	efx_rc_t
988293764Sarybchikef10_filter_unicast_refresh(
989283514Sarybchik	__in				efx_nic_t *enp,
990283514Sarybchik	__in_ecount(6)			uint8_t const *addr,
991283514Sarybchik	__in				boolean_t all_unicst,
992283514Sarybchik	__in				efx_filter_flag_t filter_flags)
993283514Sarybchik{
994293764Sarybchik	ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table;
995283514Sarybchik	efx_filter_spec_t spec;
996291436Sarybchik	efx_rc_t rc;
997283514Sarybchik
998283514Sarybchik	if (all_unicst == B_TRUE)
999283514Sarybchik		goto use_uc_def;
1000283514Sarybchik
1001283514Sarybchik	/* Insert the filter for the local station address */
1002283514Sarybchik	efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO,
1003283514Sarybchik	    filter_flags,
1004293764Sarybchik	    eftp->eft_default_rxq);
1005283514Sarybchik	efx_filter_spec_set_eth_local(&spec, EFX_FILTER_SPEC_VID_UNSPEC, addr);
1006283514Sarybchik
1007293764Sarybchik	rc = ef10_filter_add_internal(enp, &spec, B_TRUE,
1008293764Sarybchik	    &eftp->eft_unicst_filter_index);
1009283514Sarybchik	if (rc != 0) {
1010283514Sarybchik		/*
1011283514Sarybchik		 * Fall back to an unknown filter. We may be able to subscribe
1012283514Sarybchik		 * to it even if we couldn't insert the unicast filter.
1013283514Sarybchik		 */
1014283514Sarybchik		goto use_uc_def;
1015283514Sarybchik	}
1016293764Sarybchik	eftp->eft_unicst_filter_set = B_TRUE;
1017283514Sarybchik
1018283514Sarybchik	return (0);
1019283514Sarybchik
1020283514Sarybchikuse_uc_def:
1021283514Sarybchik	/* Insert the unknown unicast filter */
1022283514Sarybchik	efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO,
1023283514Sarybchik	    filter_flags,
1024293764Sarybchik	    eftp->eft_default_rxq);
1025283514Sarybchik	efx_filter_spec_set_uc_def(&spec);
1026293764Sarybchik	rc = ef10_filter_add_internal(enp, &spec, B_TRUE,
1027293764Sarybchik	    &eftp->eft_unicst_filter_index);
1028283514Sarybchik	if (rc != 0)
1029283514Sarybchik		goto fail1;
1030283514Sarybchik
1031293764Sarybchik	eftp->eft_unicst_filter_set = B_TRUE;
1032283514Sarybchik
1033283514Sarybchik	return (0);
1034283514Sarybchik
1035283514Sarybchikfail1:
1036291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1037283514Sarybchik
1038293764Sarybchik	if (eftp->eft_unicst_filter_set != B_FALSE) {
1039293764Sarybchik		(void) ef10_filter_delete_internal(enp,
1040293764Sarybchik		    eftp->eft_unicst_filter_index);
1041283514Sarybchik
1042293764Sarybchik		eftp->eft_unicst_filter_set = B_FALSE;
1043283514Sarybchik	}
1044283514Sarybchik
1045283514Sarybchik	return (rc);
1046283514Sarybchik}
1047283514Sarybchik
1048291436Sarybchikstatic	__checkReturn	efx_rc_t
1049293764Sarybchikef10_filter_multicast_refresh(
1050283514Sarybchik	__in				efx_nic_t *enp,
1051283514Sarybchik	__in				boolean_t mulcst,
1052283514Sarybchik	__in				boolean_t all_mulcst,
1053283514Sarybchik	__in				boolean_t brdcst,
1054283514Sarybchik	__in_ecount(6*count)		uint8_t const *addrs,
1055283514Sarybchik	__in				int count,
1056283514Sarybchik	__in				efx_filter_flag_t filter_flags)
1057283514Sarybchik{
1058293764Sarybchik	ef10_filter_table_t *eftp = enp->en_filter.ef_ef10_filter_table;
1059283514Sarybchik	efx_filter_spec_t spec;
1060283514Sarybchik	uint8_t addr[6];
1061283514Sarybchik	unsigned i;
1062291436Sarybchik	efx_rc_t rc;
1063283514Sarybchik
1064283514Sarybchik	if (all_mulcst == B_TRUE)
1065283514Sarybchik		goto use_mc_def;
1066283514Sarybchik
1067283514Sarybchik	if (mulcst == B_FALSE)
1068283514Sarybchik		count = 0;
1069283514Sarybchik
1070283514Sarybchik	if (count + (brdcst ? 1 : 0) >
1071293764Sarybchik	    EFX_ARRAY_SIZE(eftp->eft_mulcst_filter_indexes)) {
1072283514Sarybchik		/* Too many MAC addresses; use unknown multicast filter */
1073283514Sarybchik		goto use_mc_def;
1074283514Sarybchik	}
1075283514Sarybchik
1076283514Sarybchik	/* Insert/renew multicast address list filters */
1077293764Sarybchik	eftp->eft_mulcst_filter_count = count;
1078293764Sarybchik	for (i = 0; i < eftp->eft_mulcst_filter_count; i++) {
1079283514Sarybchik		efx_filter_spec_init_rx(&spec,
1080283514Sarybchik		    EFX_FILTER_PRI_AUTO,
1081283514Sarybchik		    filter_flags,
1082293764Sarybchik		    eftp->eft_default_rxq);
1083283514Sarybchik
1084283514Sarybchik		efx_filter_spec_set_eth_local(&spec,
1085283514Sarybchik		    EFX_FILTER_SPEC_VID_UNSPEC,
1086283514Sarybchik		    &addrs[i * EFX_MAC_ADDR_LEN]);
1087283514Sarybchik
1088293764Sarybchik		rc = ef10_filter_add_internal(enp, &spec, B_TRUE,
1089293764Sarybchik		    &eftp->eft_mulcst_filter_indexes[i]);
1090283514Sarybchik		if (rc != 0) {
1091283514Sarybchik			/* Rollback, then use unknown multicast filter */
1092283514Sarybchik			goto rollback;
1093283514Sarybchik		}
1094283514Sarybchik	}
1095283514Sarybchik
1096283514Sarybchik	if (brdcst == B_TRUE) {
1097283514Sarybchik		/* Insert/renew broadcast address filter */
1098293764Sarybchik		eftp->eft_mulcst_filter_count++;
1099283514Sarybchik		efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO,
1100283514Sarybchik		    filter_flags,
1101293764Sarybchik		    eftp->eft_default_rxq);
1102283514Sarybchik
1103283514Sarybchik		EFX_MAC_BROADCAST_ADDR_SET(addr);
1104283514Sarybchik		efx_filter_spec_set_eth_local(&spec, EFX_FILTER_SPEC_VID_UNSPEC,
1105283514Sarybchik		    addr);
1106283514Sarybchik
1107293764Sarybchik		rc = ef10_filter_add_internal(enp, &spec, B_TRUE,
1108293764Sarybchik		    &eftp->eft_mulcst_filter_indexes[
1109293764Sarybchik			eftp->eft_mulcst_filter_count - 1]);
1110283514Sarybchik		if (rc != 0) {
1111283514Sarybchik			/* Rollback, then use unknown multicast filter */
1112283514Sarybchik			goto rollback;
1113283514Sarybchik		}
1114283514Sarybchik	}
1115283514Sarybchik
1116283514Sarybchik	return (0);
1117283514Sarybchik
1118283514Sarybchikrollback:
1119283514Sarybchik	/*
1120283514Sarybchik	 * Rollback by removing any filters we have inserted
1121283514Sarybchik	 * before inserting the unknown multicast filter.
1122283514Sarybchik	 */
1123283514Sarybchik	while (i--) {
1124293764Sarybchik		(void) ef10_filter_delete_internal(enp,
1125293764Sarybchik		    eftp->eft_mulcst_filter_indexes[i]);
1126283514Sarybchik	}
1127293764Sarybchik	eftp->eft_mulcst_filter_count = 0;
1128283514Sarybchik
1129283514Sarybchikuse_mc_def:
1130283514Sarybchik	/* Insert the unknown multicast filter */
1131283514Sarybchik	efx_filter_spec_init_rx(&spec, EFX_FILTER_PRI_AUTO,
1132283514Sarybchik	    filter_flags,
1133293764Sarybchik	    eftp->eft_default_rxq);
1134283514Sarybchik	efx_filter_spec_set_mc_def(&spec);
1135283514Sarybchik
1136293764Sarybchik	rc = ef10_filter_add_internal(enp, &spec, B_TRUE,
1137293764Sarybchik	    &eftp->eft_mulcst_filter_indexes[0]);
1138283514Sarybchik	if (rc != 0)
1139283514Sarybchik		goto fail1;
1140283514Sarybchik
1141293764Sarybchik	eftp->eft_mulcst_filter_count = 1;
1142283514Sarybchik
1143283514Sarybchik	/*
1144283514Sarybchik	 * FIXME: If brdcst == B_FALSE, add a filter to drop broadcast traffic.
1145283514Sarybchik	 */
1146283514Sarybchik
1147283514Sarybchik	return (0);
1148283514Sarybchik
1149283514Sarybchikfail1:
1150291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1151283514Sarybchik
1152283514Sarybchik	return (rc);
1153283514Sarybchik
1154283514Sarybchik}
1155283514Sarybchik
1156283514Sarybchik
1157291436Sarybchikstatic	__checkReturn	efx_rc_t
1158283514Sarybchikhunt_filter_get_workarounds(
1159283514Sarybchik	__in				efx_nic_t *enp)
1160283514Sarybchik{
1161283514Sarybchik	efx_nic_cfg_t *encp = &enp->en_nic_cfg;
1162283514Sarybchik	uint32_t implemented = 0;
1163283514Sarybchik	uint32_t enabled = 0;
1164291436Sarybchik	efx_rc_t rc;
1165283514Sarybchik
1166283514Sarybchik	rc = efx_mcdi_get_workarounds(enp, &implemented, &enabled);
1167283514Sarybchik	if (rc == 0) {
1168283514Sarybchik		/* Check if chained multicast filter support is enabled */
1169283514Sarybchik		if (implemented & enabled & MC_CMD_GET_WORKAROUNDS_OUT_BUG26807)
1170283514Sarybchik			encp->enc_bug26807_workaround = B_TRUE;
1171283514Sarybchik		else
1172283514Sarybchik			encp->enc_bug26807_workaround = B_FALSE;
1173283514Sarybchik	} else if (rc == ENOTSUP) {
1174283514Sarybchik		/*
1175283514Sarybchik		 * Firmware is too old to support GET_WORKAROUNDS, and support
1176283514Sarybchik		 * for this workaround was implemented later.
1177283514Sarybchik		 */
1178283514Sarybchik		encp->enc_bug26807_workaround = B_FALSE;
1179283514Sarybchik	} else {
1180283514Sarybchik		goto fail1;
1181283514Sarybchik	}
1182283514Sarybchik
1183283514Sarybchik	return (0);
1184283514Sarybchik
1185283514Sarybchikfail1:
1186291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1187283514Sarybchik
1188283514Sarybchik	return (rc);
1189283514Sarybchik
1190283514Sarybchik}
1191283514Sarybchik
1192283514Sarybchik
1193283514Sarybchik/*
1194283514Sarybchik * Reconfigure all filters.
1195283514Sarybchik * If all_unicst and/or all mulcst filters cannot be applied then
1196283514Sarybchik * return ENOTSUP (Note the filters for the specified addresses are
1197283514Sarybchik * still applied in this case).
1198283514Sarybchik */
1199291436Sarybchik	__checkReturn	efx_rc_t
1200293764Sarybchikef10_filter_reconfigure(
1201283514Sarybchik	__in				efx_nic_t *enp,
1202283514Sarybchik	__in_ecount(6)			uint8_t const *mac_addr,
1203283514Sarybchik	__in				boolean_t all_unicst,
1204283514Sarybchik	__in				boolean_t mulcst,
1205283514Sarybchik	__in				boolean_t all_mulcst,
1206283514Sarybchik	__in				boolean_t brdcst,
1207283514Sarybchik	__in_ecount(6*count)		uint8_t const *addrs,
1208283514Sarybchik	__in				int count)
1209283514Sarybchik{
1210293764Sarybchik	ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table;
1211283514Sarybchik	efx_filter_flag_t filter_flags;
1212283514Sarybchik	unsigned i;
1213283514Sarybchik	int all_unicst_rc;
1214283514Sarybchik	int all_mulcst_rc;
1215291436Sarybchik	efx_rc_t rc;
1216283514Sarybchik
1217293764Sarybchik	if (table->eft_default_rxq == NULL) {
1218283514Sarybchik		/*
1219283514Sarybchik		 * Filters direct traffic to the default RXQ, and so cannot be
1220283514Sarybchik		 * inserted until it is available. Any currently configured
1221283514Sarybchik		 * filters must be removed (ignore errors in case the MC
1222283514Sarybchik		 * has rebooted, which removes hardware filters).
1223283514Sarybchik		 */
1224293764Sarybchik		if (table->eft_unicst_filter_set != B_FALSE) {
1225293764Sarybchik			(void) ef10_filter_delete_internal(enp,
1226293764Sarybchik						table->eft_unicst_filter_index);
1227293764Sarybchik			table->eft_unicst_filter_set = B_FALSE;
1228283514Sarybchik		}
1229293764Sarybchik		for (i = 0; i < table->eft_mulcst_filter_count; i++) {
1230293764Sarybchik			(void) ef10_filter_delete_internal(enp,
1231293764Sarybchik					table->eft_mulcst_filter_indexes[i]);
1232283514Sarybchik		}
1233293764Sarybchik		table->eft_mulcst_filter_count = 0;
1234283514Sarybchik
1235283514Sarybchik		return (0);
1236283514Sarybchik	}
1237283514Sarybchik
1238293764Sarybchik	if (table->eft_using_rss)
1239283514Sarybchik		filter_flags = EFX_FILTER_FLAG_RX_RSS;
1240283514Sarybchik	else
1241283514Sarybchik		filter_flags = 0;
1242283514Sarybchik
1243283514Sarybchik	/* Mark old filters which may need to be removed */
1244293764Sarybchik	if (table->eft_unicst_filter_set != B_FALSE) {
1245293764Sarybchik		ef10_filter_set_entry_auto_old(table,
1246293764Sarybchik					    table->eft_unicst_filter_index);
1247283514Sarybchik	}
1248293764Sarybchik	for (i = 0; i < table->eft_mulcst_filter_count; i++) {
1249293764Sarybchik		ef10_filter_set_entry_auto_old(table,
1250293764Sarybchik					table->eft_mulcst_filter_indexes[i]);
1251283514Sarybchik	}
1252283514Sarybchik
1253283514Sarybchik	/* Insert or renew unicast filters */
1254293764Sarybchik	if ((all_unicst_rc = ef10_filter_unicast_refresh(enp, mac_addr,
1255283514Sarybchik		    all_unicst, filter_flags)) !=  0) {
1256283514Sarybchik		if (all_unicst == B_FALSE) {
1257283514Sarybchik			rc = all_unicst_rc;
1258283514Sarybchik			goto fail1;
1259283514Sarybchik		}
1260283514Sarybchik		/* Retry without all_unicast flag */
1261293764Sarybchik		rc = ef10_filter_unicast_refresh(enp, mac_addr,
1262283514Sarybchik			B_FALSE, filter_flags);
1263283514Sarybchik		if (rc != 0)
1264283514Sarybchik			goto fail2;
1265283514Sarybchik	}
1266283514Sarybchik
1267283514Sarybchik	/*
1268283514Sarybchik	 * WORKAROUND_BUG26807 controls firmware support for chained multicast
1269283514Sarybchik	 * filters, and can only be enabled or disabled when the hardware filter
1270283514Sarybchik	 * table is empty.
1271283514Sarybchik	 *
1272283514Sarybchik	 * Firmware will reset (FLR) functions which have inserted filters in
1273283514Sarybchik	 * the hardware filter table when the workaround is enabled/disabled.
1274283514Sarybchik	 * Functions without any hardware filters are not reset.
1275283514Sarybchik	 *
1276283514Sarybchik	 * Re-check if the workaround is enabled after adding unicast hardware
1277283514Sarybchik	 * filters. This ensures that encp->enc_workaround_bug26807 matches the
1278283514Sarybchik	 * firmware state, and that later changes to enable/disable the
1279283514Sarybchik	 * workaround will result in this function seeing a reset (FLR).
1280293764Sarybchik	 *
1281293764Sarybchik	 * FIXME: On Medford mulicast chaining should always be on.
1282283514Sarybchik	 */
1283283514Sarybchik	if ((rc = hunt_filter_get_workarounds(enp)) != 0)
1284283514Sarybchik		goto fail3;
1285283514Sarybchik
1286283514Sarybchik	/* Insert or renew multicast filters */
1287293764Sarybchik	if ((all_mulcst_rc = ef10_filter_multicast_refresh(enp, mulcst,
1288283514Sarybchik				all_mulcst, brdcst,
1289283514Sarybchik				addrs, count, filter_flags)) != 0) {
1290283514Sarybchik		if (all_mulcst == B_FALSE) {
1291283514Sarybchik			rc = all_mulcst_rc;
1292283514Sarybchik			goto fail4;
1293283514Sarybchik		}
1294283514Sarybchik		/* Retry without all_mulcast flag */
1295293764Sarybchik		rc = ef10_filter_multicast_refresh(enp, mulcst,
1296283514Sarybchik						B_FALSE, brdcst,
1297283514Sarybchik						addrs, count, filter_flags);
1298283514Sarybchik		if (rc != 0)
1299283514Sarybchik			goto fail5;
1300283514Sarybchik	}
1301283514Sarybchik
1302283514Sarybchik	/* Remove old filters which were not renewed */
1303293764Sarybchik	for (i = 0; i < EFX_ARRAY_SIZE(table->eft_entry); i++) {
1304293764Sarybchik		if (ef10_filter_entry_is_auto_old(table, i)) {
1305293764Sarybchik			(void) ef10_filter_delete_internal(enp, i);
1306283514Sarybchik		}
1307283514Sarybchik	}
1308283514Sarybchik
1309283514Sarybchik	/* report if any optional flags were rejected */
1310283514Sarybchik	if (((all_unicst != B_FALSE) && (all_unicst_rc != 0)) ||
1311283514Sarybchik	    ((all_mulcst != B_FALSE) && (all_mulcst_rc != 0))) {
1312283514Sarybchik		rc = ENOTSUP;
1313283514Sarybchik	}
1314283514Sarybchik
1315283514Sarybchik	return (rc);
1316283514Sarybchik
1317283514Sarybchikfail5:
1318283514Sarybchik	EFSYS_PROBE(fail5);
1319283514Sarybchikfail4:
1320283514Sarybchik	EFSYS_PROBE(fail4);
1321283514Sarybchikfail3:
1322283514Sarybchik	EFSYS_PROBE(fail3);
1323283514Sarybchikfail2:
1324283514Sarybchik	EFSYS_PROBE(fail2);
1325283514Sarybchikfail1:
1326291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1327283514Sarybchik
1328283514Sarybchik	/* Clear auto old flags */
1329293764Sarybchik	for (i = 0; i < EFX_ARRAY_SIZE(table->eft_entry); i++) {
1330293764Sarybchik		if (ef10_filter_entry_is_auto_old(table, i)) {
1331293764Sarybchik			ef10_filter_set_entry_not_auto_old(table, i);
1332283514Sarybchik		}
1333283514Sarybchik	}
1334283514Sarybchik
1335283514Sarybchik	return (rc);
1336283514Sarybchik}
1337283514Sarybchik
1338283514Sarybchik		void
1339293764Sarybchikef10_filter_get_default_rxq(
1340283514Sarybchik	__in		efx_nic_t *enp,
1341283514Sarybchik	__out		efx_rxq_t **erpp,
1342283514Sarybchik	__out		boolean_t *using_rss)
1343283514Sarybchik{
1344293764Sarybchik	ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table;
1345283514Sarybchik
1346293764Sarybchik	*erpp = table->eft_default_rxq;
1347293764Sarybchik	*using_rss = table->eft_using_rss;
1348283514Sarybchik}
1349283514Sarybchik
1350283514Sarybchik
1351283514Sarybchik		void
1352293764Sarybchikef10_filter_default_rxq_set(
1353283514Sarybchik	__in		efx_nic_t *enp,
1354283514Sarybchik	__in		efx_rxq_t *erp,
1355283514Sarybchik	__in		boolean_t using_rss)
1356283514Sarybchik{
1357293764Sarybchik	ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table;
1358283514Sarybchik
1359283514Sarybchik#if EFSYS_OPT_RX_SCALE
1360283514Sarybchik	EFSYS_ASSERT((using_rss == B_FALSE) ||
1361293754Sarybchik	    (enp->en_rss_context != EF10_RSS_CONTEXT_INVALID));
1362293764Sarybchik	table->eft_using_rss = using_rss;
1363283514Sarybchik#else
1364283514Sarybchik	EFSYS_ASSERT(using_rss == B_FALSE);
1365293764Sarybchik	table->eft_using_rss = B_FALSE;
1366283514Sarybchik#endif
1367293764Sarybchik	table->eft_default_rxq = erp;
1368283514Sarybchik}
1369283514Sarybchik
1370283514Sarybchik		void
1371293764Sarybchikef10_filter_default_rxq_clear(
1372283514Sarybchik	__in		efx_nic_t *enp)
1373283514Sarybchik{
1374293764Sarybchik	ef10_filter_table_t *table = enp->en_filter.ef_ef10_filter_table;
1375283514Sarybchik
1376293764Sarybchik	table->eft_default_rxq = NULL;
1377293764Sarybchik	table->eft_using_rss = B_FALSE;
1378283514Sarybchik}
1379283514Sarybchik
1380283514Sarybchik
1381283514Sarybchik#endif /* EFSYS_OPT_FILTER */
1382283514Sarybchik
1383283514Sarybchik#endif /* EFSYS_OPT_HUNTINGTON */
1384