efx_filter.c revision 342486
1210006Srdivacky/*-
2210006Srdivacky * Copyright (c) 2007-2016 Solarflare Communications Inc.
3210006Srdivacky * All rights reserved.
4210006Srdivacky *
5210006Srdivacky * Redistribution and use in source and binary forms, with or without
6210006Srdivacky * modification, are permitted provided that the following conditions are met:
7210006Srdivacky *
8210006Srdivacky * 1. Redistributions of source code must retain the above copyright notice,
9210006Srdivacky *    this list of conditions and the following disclaimer.
10210006Srdivacky * 2. Redistributions in binary form must reproduce the above copyright notice,
11210006Srdivacky *    this list of conditions and the following disclaimer in the documentation
12210006Srdivacky *    and/or other materials provided with the distribution.
13210006Srdivacky *
14210006Srdivacky * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15218893Sdim * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16210006Srdivacky * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17218893Sdim * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18210006Srdivacky * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19218893Sdim * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20210006Srdivacky * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21218893Sdim * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22221345Sdim * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23210006Srdivacky * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24210006Srdivacky * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25221345Sdim *
26210006Srdivacky * The views and conclusions contained in the software and documentation are
27210006Srdivacky * those of the authors and should not be interpreted as representing official
28210006Srdivacky * policies, either expressed or implied, of the FreeBSD Project.
29210006Srdivacky */
30210006Srdivacky
31210006Srdivacky#include <sys/cdefs.h>
32210006Srdivacky__FBSDID("$FreeBSD: stable/10/sys/dev/sfxge/common/efx_filter.c 342486 2018-12-26 09:41:04Z arybchik $");
33210006Srdivacky
34210006Srdivacky#include "efx.h"
35210006Srdivacky#include "efx_impl.h"
36221345Sdim
37221345Sdim
38221345Sdim#if EFSYS_OPT_FILTER
39221345Sdim
40221345Sdim#if EFSYS_OPT_SIENA
41221345Sdim
42221345Sdimstatic	__checkReturn	efx_rc_t
43221345Sdimsiena_filter_init(
44221345Sdim	__in		efx_nic_t *enp);
45221345Sdim
46221345Sdimstatic			void
47221345Sdimsiena_filter_fini(
48210006Srdivacky	__in		efx_nic_t *enp);
49210006Srdivacky
50221345Sdimstatic	__checkReturn	efx_rc_t
51221345Sdimsiena_filter_restore(
52221345Sdim	__in		efx_nic_t *enp);
53221345Sdim
54210006Srdivackystatic	__checkReturn	efx_rc_t
55221345Sdimsiena_filter_add(
56221345Sdim	__in		efx_nic_t *enp,
57221345Sdim	__inout		efx_filter_spec_t *spec,
58221345Sdim	__in		boolean_t may_replace);
59221345Sdim
60221345Sdimstatic	__checkReturn	efx_rc_t
61221345Sdimsiena_filter_delete(
62218893Sdim	__in		efx_nic_t *enp,
63221345Sdim	__inout		efx_filter_spec_t *spec);
64210006Srdivacky
65221345Sdimstatic	__checkReturn	efx_rc_t
66221345Sdimsiena_filter_supported_filters(
67221345Sdim	__in				efx_nic_t *enp,
68221345Sdim	__out_ecount(buffer_length)	uint32_t *buffer,
69221345Sdim	__in				size_t buffer_length,
70221345Sdim	__out				size_t *list_lengthp);
71221345Sdim
72221345Sdim#endif /* EFSYS_OPT_SIENA */
73221345Sdim
74221345Sdim#if EFSYS_OPT_SIENA
75221345Sdimstatic const efx_filter_ops_t	__efx_filter_siena_ops = {
76221345Sdim	siena_filter_init,		/* efo_init */
77221345Sdim	siena_filter_fini,		/* efo_fini */
78221345Sdim	siena_filter_restore,		/* efo_restore */
79221345Sdim	siena_filter_add,		/* efo_add */
80221345Sdim	siena_filter_delete,		/* efo_delete */
81221345Sdim	siena_filter_supported_filters,	/* efo_supported_filters */
82221345Sdim	NULL,				/* efo_reconfigure */
83221345Sdim};
84221345Sdim#endif /* EFSYS_OPT_SIENA */
85221345Sdim
86221345Sdim#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
87221345Sdimstatic const efx_filter_ops_t	__efx_filter_ef10_ops = {
88221345Sdim	ef10_filter_init,		/* efo_init */
89221345Sdim	ef10_filter_fini,		/* efo_fini */
90221345Sdim	ef10_filter_restore,		/* efo_restore */
91221345Sdim	ef10_filter_add,		/* efo_add */
92210006Srdivacky	ef10_filter_delete,		/* efo_delete */
93210006Srdivacky	ef10_filter_supported_filters,	/* efo_supported_filters */
94210006Srdivacky	ef10_filter_reconfigure,	/* efo_reconfigure */
95212904Sdim};
96212904Sdim#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
97212904Sdim
98221345Sdim	__checkReturn	efx_rc_t
99221345Sdimefx_filter_insert(
100221345Sdim	__in		efx_nic_t *enp,
101221345Sdim	__inout		efx_filter_spec_t *spec)
102221345Sdim{
103221345Sdim	const efx_filter_ops_t *efop = enp->en_efop;
104221345Sdim
105221345Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
106221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
107221345Sdim	EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX);
108221345Sdim
109221345Sdim	return (efop->efo_add(enp, spec, B_FALSE));
110210006Srdivacky}
111218893Sdim
112218893Sdim	__checkReturn	efx_rc_t
113210006Srdivackyefx_filter_remove(
114221345Sdim	__in		efx_nic_t *enp,
115221345Sdim	__inout		efx_filter_spec_t *spec)
116221345Sdim{
117221345Sdim	const efx_filter_ops_t *efop = enp->en_efop;
118221345Sdim
119221345Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
120221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
121221345Sdim	EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX);
122221345Sdim
123221345Sdim#if EFSYS_OPT_RX_SCALE
124221345Sdim	spec->efs_rss_context = enp->en_rss_context;
125221345Sdim#endif
126221345Sdim
127221345Sdim	return (efop->efo_delete(enp, spec));
128221345Sdim}
129221345Sdim
130221345Sdim	__checkReturn	efx_rc_t
131210006Srdivackyefx_filter_restore(
132210006Srdivacky	__in		efx_nic_t *enp)
133221345Sdim{
134210006Srdivacky	efx_rc_t rc;
135218893Sdim
136218893Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
137221345Sdim
138221345Sdim	if ((rc = enp->en_efop->efo_restore(enp)) != 0)
139221345Sdim		goto fail1;
140221345Sdim
141221345Sdim	return (0);
142221345Sdim
143221345Sdimfail1:
144210006Srdivacky	EFSYS_PROBE1(fail1, efx_rc_t, rc);
145210006Srdivacky
146210006Srdivacky	return (rc);
147210006Srdivacky}
148212904Sdim
149212904Sdim	__checkReturn	efx_rc_t
150212904Sdimefx_filter_init(
151212904Sdim	__in		efx_nic_t *enp)
152210006Srdivacky{
153210006Srdivacky	const efx_filter_ops_t *efop;
154210006Srdivacky	efx_rc_t rc;
155221345Sdim
156221345Sdim	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
157221345Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
158221345Sdim	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_FILTER));
159221345Sdim
160221345Sdim	switch (enp->en_family) {
161221345Sdim#if EFSYS_OPT_SIENA
162221345Sdim	case EFX_FAMILY_SIENA:
163221345Sdim		efop = &__efx_filter_siena_ops;
164221345Sdim		break;
165221345Sdim#endif /* EFSYS_OPT_SIENA */
166221345Sdim
167221345Sdim#if EFSYS_OPT_HUNTINGTON
168221345Sdim	case EFX_FAMILY_HUNTINGTON:
169221345Sdim		efop = &__efx_filter_ef10_ops;
170221345Sdim		break;
171221345Sdim#endif /* EFSYS_OPT_HUNTINGTON */
172221345Sdim
173221345Sdim#if EFSYS_OPT_MEDFORD
174221345Sdim	case EFX_FAMILY_MEDFORD:
175221345Sdim		efop = &__efx_filter_ef10_ops;
176221345Sdim		break;
177221345Sdim#endif /* EFSYS_OPT_MEDFORD */
178221345Sdim
179221345Sdim	default:
180221345Sdim		EFSYS_ASSERT(0);
181221345Sdim		rc = ENOTSUP;
182221345Sdim		goto fail1;
183221345Sdim	}
184221345Sdim
185221345Sdim	if ((rc = efop->efo_init(enp)) != 0)
186221345Sdim		goto fail2;
187221345Sdim
188221345Sdim	enp->en_efop = efop;
189221345Sdim	enp->en_mod_flags |= EFX_MOD_FILTER;
190221345Sdim	return (0);
191221345Sdim
192221345Sdimfail2:
193221345Sdim	EFSYS_PROBE(fail2);
194221345Sdimfail1:
195221345Sdim	EFSYS_PROBE1(fail1, efx_rc_t, rc);
196221345Sdim
197221345Sdim	enp->en_efop = NULL;
198221345Sdim	enp->en_mod_flags &= ~EFX_MOD_FILTER;
199221345Sdim	return (rc);
200221345Sdim}
201221345Sdim
202221345Sdim			void
203221345Sdimefx_filter_fini(
204221345Sdim	__in		efx_nic_t *enp)
205221345Sdim{
206221345Sdim	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
207221345Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
208221345Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
209221345Sdim
210221345Sdim	enp->en_efop->efo_fini(enp);
211221345Sdim
212221345Sdim	enp->en_efop = NULL;
213221345Sdim	enp->en_mod_flags &= ~EFX_MOD_FILTER;
214221345Sdim}
215221345Sdim
216221345Sdim/*
217221345Sdim * Query the possible combinations of match flags which can be filtered on.
218221345Sdim * These are returned as a list, of which each 32 bit element is a bitmask
219221345Sdim * formed of EFX_FILTER_MATCH flags.
220221345Sdim *
221221345Sdim * The combinations are ordered in priority from highest to lowest.
222221345Sdim *
223221345Sdim * If the provided buffer is too short to hold the list, the call with fail with
224221345Sdim * ENOSPC and *list_lengthp will be set to the buffer length required.
225221345Sdim */
226221345Sdim	__checkReturn	efx_rc_t
227221345Sdimefx_filter_supported_filters(
228221345Sdim	__in				efx_nic_t *enp,
229221345Sdim	__out_ecount(buffer_length)	uint32_t *buffer,
230221345Sdim	__in				size_t buffer_length,
231221345Sdim	__out				size_t *list_lengthp)
232221345Sdim{
233221345Sdim	efx_rc_t rc;
234221345Sdim
235221345Sdim	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
236221345Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
237221345Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
238221345Sdim	EFSYS_ASSERT(enp->en_efop->efo_supported_filters != NULL);
239221345Sdim
240221345Sdim	if (buffer == NULL) {
241221345Sdim		rc = EINVAL;
242221345Sdim		goto fail1;
243221345Sdim	}
244221345Sdim
245221345Sdim	rc = enp->en_efop->efo_supported_filters(enp, buffer, buffer_length,
246221345Sdim						    list_lengthp);
247221345Sdim	if (rc != 0)
248221345Sdim		goto fail2;
249221345Sdim
250221345Sdim	return (0);
251221345Sdim
252221345Sdimfail2:
253221345Sdim	EFSYS_PROBE(fail2);
254221345Sdimfail1:
255221345Sdim	EFSYS_PROBE1(fail1, efx_rc_t, rc);
256221345Sdim
257221345Sdim	return (rc);
258221345Sdim}
259221345Sdim
260221345Sdim	__checkReturn	efx_rc_t
261221345Sdimefx_filter_reconfigure(
262221345Sdim	__in				efx_nic_t *enp,
263221345Sdim	__in_ecount(6)			uint8_t const *mac_addr,
264221345Sdim	__in				boolean_t all_unicst,
265221345Sdim	__in				boolean_t mulcst,
266221345Sdim	__in				boolean_t all_mulcst,
267221345Sdim	__in				boolean_t brdcst,
268221345Sdim	__in_ecount(6*count)		uint8_t const *addrs,
269221345Sdim	__in				uint32_t count)
270221345Sdim{
271221345Sdim	efx_rc_t rc;
272221345Sdim
273221345Sdim	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
274221345Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
275221345Sdim	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
276221345Sdim
277221345Sdim	if (enp->en_efop->efo_reconfigure != NULL) {
278221345Sdim		if ((rc = enp->en_efop->efo_reconfigure(enp, mac_addr,
279221345Sdim							all_unicst, mulcst,
280221345Sdim							all_mulcst, brdcst,
281221345Sdim							addrs, count)) != 0)
282221345Sdim			goto fail1;
283221345Sdim	}
284221345Sdim
285221345Sdim	return (0);
286221345Sdim
287221345Sdimfail1:
288221345Sdim	EFSYS_PROBE1(fail1, efx_rc_t, rc);
289221345Sdim
290221345Sdim	return (rc);
291221345Sdim}
292221345Sdim
293221345Sdim		void
294221345Sdimefx_filter_spec_init_rx(
295221345Sdim	__out		efx_filter_spec_t *spec,
296221345Sdim	__in		efx_filter_priority_t priority,
297221345Sdim	__in		efx_filter_flags_t flags,
298221345Sdim	__in		efx_rxq_t *erp)
299221345Sdim{
300221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
301221345Sdim	EFSYS_ASSERT3P(erp, !=, NULL);
302221345Sdim	EFSYS_ASSERT((flags & ~(EFX_FILTER_FLAG_RX_RSS |
303221345Sdim				EFX_FILTER_FLAG_RX_SCATTER)) == 0);
304221345Sdim
305221345Sdim	memset(spec, 0, sizeof (*spec));
306221345Sdim	spec->efs_priority = priority;
307221345Sdim	spec->efs_flags = EFX_FILTER_FLAG_RX | flags;
308221345Sdim	spec->efs_rss_context = EFX_FILTER_SPEC_RSS_CONTEXT_DEFAULT;
309221345Sdim	spec->efs_dmaq_id = (uint16_t)erp->er_index;
310221345Sdim}
311221345Sdim
312221345Sdim		void
313221345Sdimefx_filter_spec_init_tx(
314221345Sdim	__out		efx_filter_spec_t *spec,
315221345Sdim	__in		efx_txq_t *etp)
316221345Sdim{
317221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
318221345Sdim	EFSYS_ASSERT3P(etp, !=, NULL);
319221345Sdim
320221345Sdim	memset(spec, 0, sizeof (*spec));
321221345Sdim	spec->efs_priority = EFX_FILTER_PRI_REQUIRED;
322221345Sdim	spec->efs_flags = EFX_FILTER_FLAG_TX;
323221345Sdim	spec->efs_dmaq_id = (uint16_t)etp->et_index;
324221345Sdim}
325221345Sdim
326221345Sdim
327221345Sdim/*
328221345Sdim *  Specify IPv4 host, transport protocol and port in a filter specification
329221345Sdim */
330221345Sdim__checkReturn		efx_rc_t
331221345Sdimefx_filter_spec_set_ipv4_local(
332221345Sdim	__inout		efx_filter_spec_t *spec,
333221345Sdim	__in		uint8_t proto,
334221345Sdim	__in		uint32_t host,
335221345Sdim	__in		uint16_t port)
336221345Sdim{
337221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
338221345Sdim
339221345Sdim	spec->efs_match_flags |=
340221345Sdim		EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
341221345Sdim		EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
342221345Sdim	spec->efs_ether_type = EFX_ETHER_TYPE_IPV4;
343221345Sdim	spec->efs_ip_proto = proto;
344221345Sdim	spec->efs_loc_host.eo_u32[0] = host;
345221345Sdim	spec->efs_loc_port = port;
346221345Sdim	return (0);
347221345Sdim}
348221345Sdim
349221345Sdim/*
350221345Sdim * Specify IPv4 hosts, transport protocol and ports in a filter specification
351221345Sdim */
352221345Sdim__checkReturn		efx_rc_t
353221345Sdimefx_filter_spec_set_ipv4_full(
354221345Sdim	__inout		efx_filter_spec_t *spec,
355221345Sdim	__in		uint8_t proto,
356221345Sdim	__in		uint32_t lhost,
357221345Sdim	__in		uint16_t lport,
358221345Sdim	__in		uint32_t rhost,
359221345Sdim	__in		uint16_t rport)
360221345Sdim{
361221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
362221345Sdim
363221345Sdim	spec->efs_match_flags |=
364221345Sdim		EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
365221345Sdim		EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
366221345Sdim		EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
367221345Sdim	spec->efs_ether_type = EFX_ETHER_TYPE_IPV4;
368221345Sdim	spec->efs_ip_proto = proto;
369221345Sdim	spec->efs_loc_host.eo_u32[0] = lhost;
370221345Sdim	spec->efs_loc_port = lport;
371221345Sdim	spec->efs_rem_host.eo_u32[0] = rhost;
372221345Sdim	spec->efs_rem_port = rport;
373221345Sdim	return (0);
374221345Sdim}
375221345Sdim
376221345Sdim/*
377221345Sdim * Specify local Ethernet address and/or VID in filter specification
378221345Sdim */
379221345Sdim__checkReturn		efx_rc_t
380221345Sdimefx_filter_spec_set_eth_local(
381221345Sdim	__inout		efx_filter_spec_t *spec,
382221345Sdim	__in		uint16_t vid,
383221345Sdim	__in		const uint8_t *addr)
384221345Sdim{
385221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
386221345Sdim	EFSYS_ASSERT3P(addr, !=, NULL);
387221345Sdim
388221345Sdim	if (vid == EFX_FILTER_SPEC_VID_UNSPEC && addr == NULL)
389221345Sdim		return (EINVAL);
390221345Sdim
391221345Sdim	if (vid != EFX_FILTER_SPEC_VID_UNSPEC) {
392221345Sdim		spec->efs_match_flags |= EFX_FILTER_MATCH_OUTER_VID;
393221345Sdim		spec->efs_outer_vid = vid;
394221345Sdim	}
395221345Sdim	if (addr != NULL) {
396221345Sdim		spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC;
397221345Sdim		memcpy(spec->efs_loc_mac, addr, EFX_MAC_ADDR_LEN);
398221345Sdim	}
399221345Sdim	return (0);
400221345Sdim}
401221345Sdim
402221345Sdim			void
403221345Sdimefx_filter_spec_set_ether_type(
404221345Sdim	__inout		efx_filter_spec_t *spec,
405221345Sdim	__in		uint16_t ether_type)
406221345Sdim{
407221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
408221345Sdim
409221345Sdim	spec->efs_ether_type = ether_type;
410221345Sdim	spec->efs_match_flags |= EFX_FILTER_MATCH_ETHER_TYPE;
411221345Sdim}
412221345Sdim
413221345Sdim/*
414221345Sdim * Specify matching otherwise-unmatched unicast in a filter specification
415221345Sdim */
416221345Sdim__checkReturn		efx_rc_t
417221345Sdimefx_filter_spec_set_uc_def(
418221345Sdim	__inout		efx_filter_spec_t *spec)
419221345Sdim{
420221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
421221345Sdim
422221345Sdim	spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_UCAST_DST;
423221345Sdim	return (0);
424221345Sdim}
425221345Sdim
426221345Sdim/*
427221345Sdim * Specify matching otherwise-unmatched multicast in a filter specification
428221345Sdim */
429221345Sdim__checkReturn		efx_rc_t
430221345Sdimefx_filter_spec_set_mc_def(
431221345Sdim	__inout		efx_filter_spec_t *spec)
432221345Sdim{
433221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
434221345Sdim
435221345Sdim	spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_MCAST_DST;
436221345Sdim	return (0);
437221345Sdim}
438221345Sdim
439221345Sdim
440221345Sdim__checkReturn		efx_rc_t
441221345Sdimefx_filter_spec_set_encap_type(
442221345Sdim	__inout		efx_filter_spec_t *spec,
443221345Sdim	__in		efx_tunnel_protocol_t encap_type,
444221345Sdim	__in		efx_filter_inner_frame_match_t inner_frame_match)
445221345Sdim{
446221345Sdim	uint32_t match_flags = 0;
447221345Sdim	uint8_t ip_proto;
448221345Sdim	efx_rc_t rc;
449221345Sdim
450221345Sdim	EFSYS_ASSERT3P(spec, !=, NULL);
451221345Sdim
452221345Sdim	switch (encap_type) {
453221345Sdim	case EFX_TUNNEL_PROTOCOL_VXLAN:
454221345Sdim	case EFX_TUNNEL_PROTOCOL_GENEVE:
455221345Sdim		ip_proto = EFX_IPPROTO_UDP;
456221345Sdim		break;
457221345Sdim	case EFX_TUNNEL_PROTOCOL_NVGRE:
458221345Sdim		ip_proto = EFX_IPPROTO_GRE;
459221345Sdim		break;
460221345Sdim	default:
461221345Sdim		EFSYS_ASSERT(0);
462221345Sdim		rc = EINVAL;
463221345Sdim		goto fail1;
464221345Sdim	}
465221345Sdim
466221345Sdim	switch (inner_frame_match) {
467221345Sdim	case EFX_FILTER_INNER_FRAME_MATCH_UNKNOWN_MCAST_DST:
468221345Sdim		match_flags |= EFX_FILTER_MATCH_IFRM_UNKNOWN_MCAST_DST;
469221345Sdim		break;
470221345Sdim	case EFX_FILTER_INNER_FRAME_MATCH_UNKNOWN_UCAST_DST:
471221345Sdim		match_flags |= EFX_FILTER_MATCH_IFRM_UNKNOWN_UCAST_DST;
472221345Sdim		break;
473221345Sdim	case EFX_FILTER_INNER_FRAME_MATCH_OTHER:
474221345Sdim		/* This is for when specific inner frames are to be matched. */
475221345Sdim		break;
476221345Sdim	default:
477221345Sdim		EFSYS_ASSERT(0);
478221345Sdim		rc = EINVAL;
479221345Sdim		goto fail2;
480221345Sdim	}
481221345Sdim
482221345Sdim	spec->efs_encap_type = encap_type;
483221345Sdim	spec->efs_ip_proto = ip_proto;
484221345Sdim	spec->efs_match_flags |= (match_flags | EFX_FILTER_MATCH_IP_PROTO);
485221345Sdim
486221345Sdim	return (0);
487221345Sdim
488221345Sdimfail2:
489221345Sdim	EFSYS_PROBE(fail2);
490221345Sdimfail1:
491221345Sdim	EFSYS_PROBE1(fail1, efx_rc_t, rc);
492221345Sdim
493221345Sdim	return (rc);
494221345Sdim}
495221345Sdim
496221345Sdim
497221345Sdim
498221345Sdim#if EFSYS_OPT_SIENA
499221345Sdim
500221345Sdim/*
501221345Sdim * "Fudge factors" - difference between programmed value and actual depth.
502221345Sdim * Due to pipelined implementation we need to program H/W with a value that
503221345Sdim * is larger than the hop limit we want.
504221345Sdim */
505221345Sdim#define	FILTER_CTL_SRCH_FUDGE_WILD 3
506221345Sdim#define	FILTER_CTL_SRCH_FUDGE_FULL 1
507221345Sdim
508221345Sdim/*
509221345Sdim * Hard maximum hop limit.  Hardware will time-out beyond 200-something.
510221345Sdim * We also need to avoid infinite loops in efx_filter_search() when the
511221345Sdim * table is full.
512221345Sdim */
513221345Sdim#define	FILTER_CTL_SRCH_MAX 200
514221345Sdim
515221345Sdimstatic	__checkReturn	efx_rc_t
516221345Sdimsiena_filter_spec_from_gen_spec(
517221345Sdim	__out		siena_filter_spec_t *sf_spec,
518221345Sdim	__in		efx_filter_spec_t *gen_spec)
519221345Sdim{
520221345Sdim	efx_rc_t rc;
521221345Sdim	boolean_t is_full = B_FALSE;
522221345Sdim
523221345Sdim	if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX)
524221345Sdim		EFSYS_ASSERT3U(gen_spec->efs_flags, ==, EFX_FILTER_FLAG_TX);
525221345Sdim	else
526221345Sdim		EFSYS_ASSERT3U(gen_spec->efs_flags, &, EFX_FILTER_FLAG_RX);
527221345Sdim
528221345Sdim	/* Falconsiena only has one RSS context */
529221345Sdim	if ((gen_spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) &&
530221345Sdim	    gen_spec->efs_rss_context != 0) {
531221345Sdim		rc = EINVAL;
532221345Sdim		goto fail1;
533221345Sdim	}
534221345Sdim
535221345Sdim	sf_spec->sfs_flags = gen_spec->efs_flags;
536221345Sdim	sf_spec->sfs_dmaq_id = gen_spec->efs_dmaq_id;
537221345Sdim
538221345Sdim	switch (gen_spec->efs_match_flags) {
539221345Sdim	case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
540221345Sdim	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
541221345Sdim	    EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT:
542221345Sdim		is_full = B_TRUE;
543221345Sdim		/* Fall through */
544221345Sdim	case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
545221345Sdim	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT: {
546221345Sdim		uint32_t rhost, host1, host2;
547221345Sdim		uint16_t rport, port1, port2;
548221345Sdim
549221345Sdim		if (gen_spec->efs_ether_type != EFX_ETHER_TYPE_IPV4) {
550221345Sdim			rc = ENOTSUP;
551221345Sdim			goto fail2;
552221345Sdim		}
553221345Sdim		if (gen_spec->efs_loc_port == 0 ||
554221345Sdim		    (is_full && gen_spec->efs_rem_port == 0)) {
555221345Sdim			rc = EINVAL;
556221345Sdim			goto fail3;
557221345Sdim		}
558221345Sdim		switch (gen_spec->efs_ip_proto) {
559221345Sdim		case EFX_IPPROTO_TCP:
560221345Sdim			if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
561221345Sdim				sf_spec->sfs_type = (is_full ?
562221345Sdim				    EFX_SIENA_FILTER_TX_TCP_FULL :
563221345Sdim				    EFX_SIENA_FILTER_TX_TCP_WILD);
564221345Sdim			} else {
565221345Sdim				sf_spec->sfs_type = (is_full ?
566221345Sdim				    EFX_SIENA_FILTER_RX_TCP_FULL :
567221345Sdim				    EFX_SIENA_FILTER_RX_TCP_WILD);
568221345Sdim			}
569221345Sdim			break;
570221345Sdim		case EFX_IPPROTO_UDP:
571221345Sdim			if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
572221345Sdim				sf_spec->sfs_type = (is_full ?
573221345Sdim				    EFX_SIENA_FILTER_TX_UDP_FULL :
574221345Sdim				    EFX_SIENA_FILTER_TX_UDP_WILD);
575221345Sdim			} else {
576221345Sdim				sf_spec->sfs_type = (is_full ?
577221345Sdim				    EFX_SIENA_FILTER_RX_UDP_FULL :
578221345Sdim				    EFX_SIENA_FILTER_RX_UDP_WILD);
579221345Sdim			}
580221345Sdim			break;
581221345Sdim		default:
582221345Sdim			rc = ENOTSUP;
583221345Sdim			goto fail4;
584221345Sdim		}
585221345Sdim		/*
586221345Sdim		 * The filter is constructed in terms of source and destination,
587221345Sdim		 * with the odd wrinkle that the ports are swapped in a UDP
588221345Sdim		 * wildcard filter. We need to convert from local and remote
589221345Sdim		 * addresses (zero for a wildcard).
590221345Sdim		 */
591221345Sdim		rhost = is_full ? gen_spec->efs_rem_host.eo_u32[0] : 0;
592221345Sdim		rport = is_full ? gen_spec->efs_rem_port : 0;
593219077Sdim		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
594221345Sdim			host1 = gen_spec->efs_loc_host.eo_u32[0];
595221345Sdim			host2 = rhost;
596221345Sdim		} else {
597221345Sdim			host1 = rhost;
598218893Sdim			host2 = gen_spec->efs_loc_host.eo_u32[0];
599221345Sdim		}
600210006Srdivacky		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
601210006Srdivacky			if (sf_spec->sfs_type ==
602210006Srdivacky			    EFX_SIENA_FILTER_TX_UDP_WILD) {
603221345Sdim				port1 = rport;
604210006Srdivacky				port2 = gen_spec->efs_loc_port;
605210006Srdivacky			} else {
606210006Srdivacky				port1 = gen_spec->efs_loc_port;
607210006Srdivacky				port2 = rport;
608210006Srdivacky			}
609218893Sdim		} else {
610221345Sdim			if (sf_spec->sfs_type ==
611221345Sdim			    EFX_SIENA_FILTER_RX_UDP_WILD) {
612221345Sdim				port1 = gen_spec->efs_loc_port;
613221345Sdim				port2 = rport;
614221345Sdim			} else {
615221345Sdim				port1 = rport;
616221345Sdim				port2 = gen_spec->efs_loc_port;
617221345Sdim			}
618221345Sdim		}
619221345Sdim		sf_spec->sfs_dword[0] = (host1 << 16) | port1;
620210006Srdivacky		sf_spec->sfs_dword[1] = (port2 << 16) | (host1 >> 16);
621210006Srdivacky		sf_spec->sfs_dword[2] = host2;
622210006Srdivacky		break;
623210006Srdivacky	}
624221345Sdim
625221345Sdim	case EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_OUTER_VID:
626210006Srdivacky		is_full = B_TRUE;
627210006Srdivacky		/* Fall through */
628221345Sdim	case EFX_FILTER_MATCH_LOC_MAC:
629210006Srdivacky		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
630210006Srdivacky			sf_spec->sfs_type = (is_full ?
631210006Srdivacky			    EFX_SIENA_FILTER_TX_MAC_FULL :
632210006Srdivacky			    EFX_SIENA_FILTER_TX_MAC_WILD);
633221345Sdim		} else {
634210006Srdivacky			sf_spec->sfs_type = (is_full ?
635210006Srdivacky			    EFX_SIENA_FILTER_RX_MAC_FULL :
636210006Srdivacky			    EFX_SIENA_FILTER_RX_MAC_WILD);
637210006Srdivacky		}
638210006Srdivacky		sf_spec->sfs_dword[0] = is_full ? gen_spec->efs_outer_vid : 0;
639210006Srdivacky		sf_spec->sfs_dword[1] =
640218893Sdim		    gen_spec->efs_loc_mac[2] << 24 |
641218893Sdim		    gen_spec->efs_loc_mac[3] << 16 |
642218893Sdim		    gen_spec->efs_loc_mac[4] <<  8 |
643218893Sdim		    gen_spec->efs_loc_mac[5];
644221345Sdim		sf_spec->sfs_dword[2] =
645218893Sdim		    gen_spec->efs_loc_mac[0] << 8 |
646218893Sdim		    gen_spec->efs_loc_mac[1];
647218893Sdim		break;
648210006Srdivacky
649221345Sdim	default:
650210006Srdivacky		EFSYS_ASSERT(B_FALSE);
651210006Srdivacky		rc = ENOTSUP;
652210006Srdivacky		goto fail5;
653221345Sdim	}
654221345Sdim
655218893Sdim	return (0);
656221345Sdim
657210006Srdivackyfail5:
658210006Srdivacky	EFSYS_PROBE(fail5);
659210006Srdivackyfail4:
660210006Srdivacky	EFSYS_PROBE(fail4);
661221345Sdimfail3:
662218893Sdim	EFSYS_PROBE(fail3);
663210006Srdivackyfail2:
664210006Srdivacky	EFSYS_PROBE(fail2);
665210006Srdivackyfail1:
666210006Srdivacky	EFSYS_PROBE1(fail1, efx_rc_t, rc);
667210006Srdivacky
668221345Sdim	return (rc);
669210006Srdivacky}
670210006Srdivacky
671210006Srdivacky/*
672210006Srdivacky * The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit
673210006Srdivacky * key derived from the n-tuple.
674218893Sdim */
675210006Srdivackystatic			uint16_t
676210006Srdivackysiena_filter_tbl_hash(
677221345Sdim	__in		uint32_t key)
678221345Sdim{
679218893Sdim	uint16_t tmp;
680218893Sdim
681221345Sdim	/* First 16 rounds */
682210006Srdivacky	tmp = 0x1fff ^ (uint16_t)(key >> 16);
683221345Sdim	tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
684210006Srdivacky	tmp = tmp ^ tmp >> 9;
685221345Sdim
686221345Sdim	/* Last 16 rounds */
687221345Sdim	tmp = tmp ^ tmp << 13 ^ (uint16_t)(key & 0xffff);
688221345Sdim	tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
689221345Sdim	tmp = tmp ^ tmp >> 9;
690221345Sdim
691221345Sdim	return (tmp);
692221345Sdim}
693210006Srdivacky
694210006Srdivacky/*
695210006Srdivacky * To allow for hash collisions, filter search continues at these
696210006Srdivacky * increments from the first possible entry selected by the hash.
697221345Sdim */
698221345Sdimstatic			uint16_t
699221345Sdimsiena_filter_tbl_increment(
700221345Sdim	__in		uint32_t key)
701221345Sdim{
702221345Sdim	return ((uint16_t)(key * 2 - 1));
703221345Sdim}
704221345Sdim
705221345Sdimstatic	__checkReturn	boolean_t
706221345Sdimsiena_filter_test_used(
707221345Sdim	__in		siena_filter_tbl_t *sftp,
708221345Sdim	__in		unsigned int index)
709221345Sdim{
710221345Sdim	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
711221345Sdim	return ((sftp->sft_bitmap[index / 32] & (1 << (index % 32))) != 0);
712210006Srdivacky}
713210006Srdivacky
714221345Sdimstatic			void
715221345Sdimsiena_filter_set_used(
716221345Sdim	__in		siena_filter_tbl_t *sftp,
717210006Srdivacky	__in		unsigned int index)
718221345Sdim{
719221345Sdim	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
720210006Srdivacky	sftp->sft_bitmap[index / 32] |= (1 << (index % 32));
721221345Sdim	++sftp->sft_used;
722221345Sdim}
723221345Sdim
724221345Sdimstatic			void
725221345Sdimsiena_filter_clear_used(
726210006Srdivacky	__in		siena_filter_tbl_t *sftp,
727210006Srdivacky	__in		unsigned int index)
728221345Sdim{
729221345Sdim	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
730221345Sdim	sftp->sft_bitmap[index / 32] &= ~(1 << (index % 32));
731221345Sdim
732221345Sdim	--sftp->sft_used;
733210006Srdivacky	EFSYS_ASSERT3U(sftp->sft_used, >=, 0);
734221345Sdim}
735210006Srdivacky
736210006Srdivacky
737221345Sdimstatic			siena_filter_tbl_id_t
738221345Sdimsiena_filter_tbl_id(
739221345Sdim	__in		siena_filter_type_t type)
740221345Sdim{
741221345Sdim	siena_filter_tbl_id_t tbl_id;
742221345Sdim
743221345Sdim	switch (type) {
744212904Sdim	case EFX_SIENA_FILTER_RX_TCP_FULL:
745221345Sdim	case EFX_SIENA_FILTER_RX_TCP_WILD:
746221345Sdim	case EFX_SIENA_FILTER_RX_UDP_FULL:
747221345Sdim	case EFX_SIENA_FILTER_RX_UDP_WILD:
748212904Sdim		tbl_id = EFX_SIENA_FILTER_TBL_RX_IP;
749212904Sdim		break;
750212904Sdim
751221345Sdim	case EFX_SIENA_FILTER_RX_MAC_FULL:
752212904Sdim	case EFX_SIENA_FILTER_RX_MAC_WILD:
753212904Sdim		tbl_id = EFX_SIENA_FILTER_TBL_RX_MAC;
754212904Sdim		break;
755221345Sdim
756212904Sdim	case EFX_SIENA_FILTER_TX_TCP_FULL:
757212904Sdim	case EFX_SIENA_FILTER_TX_TCP_WILD:
758212904Sdim	case EFX_SIENA_FILTER_TX_UDP_FULL:
759212904Sdim	case EFX_SIENA_FILTER_TX_UDP_WILD:
760210006Srdivacky		tbl_id = EFX_SIENA_FILTER_TBL_TX_IP;
761218893Sdim		break;
762218893Sdim
763218893Sdim	case EFX_SIENA_FILTER_TX_MAC_FULL:
764218893Sdim	case EFX_SIENA_FILTER_TX_MAC_WILD:
765210006Srdivacky		tbl_id = EFX_SIENA_FILTER_TBL_TX_MAC;
766218893Sdim		break;
767218893Sdim
768210006Srdivacky	default:
769210006Srdivacky		EFSYS_ASSERT(B_FALSE);
770210006Srdivacky		tbl_id = EFX_SIENA_FILTER_NTBLS;
771210006Srdivacky		break;
772210006Srdivacky	}
773210006Srdivacky	return (tbl_id);
774210006Srdivacky}
775210006Srdivacky
776210006Srdivackystatic			void
777210006Srdivackysiena_filter_reset_search_depth(
778210006Srdivacky	__inout		siena_filter_t *sfp,
779218893Sdim	__in		siena_filter_tbl_id_t tbl_id)
780218893Sdim{
781218893Sdim	switch (tbl_id) {
782210006Srdivacky	case EFX_SIENA_FILTER_TBL_RX_IP:
783210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] = 0;
784210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] = 0;
785210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] = 0;
786210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] = 0;
787218893Sdim		break;
788221345Sdim
789221345Sdim	case EFX_SIENA_FILTER_TBL_RX_MAC:
790210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] = 0;
791210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] = 0;
792221345Sdim		break;
793218893Sdim
794221345Sdim	case EFX_SIENA_FILTER_TBL_TX_IP:
795210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] = 0;
796210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] = 0;
797210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] = 0;
798210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] = 0;
799210006Srdivacky		break;
800210006Srdivacky
801210006Srdivacky	case EFX_SIENA_FILTER_TBL_TX_MAC:
802221345Sdim		sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] = 0;
803210006Srdivacky		sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] = 0;
804210006Srdivacky		break;
805221345Sdim
806221345Sdim	default:
807210006Srdivacky		EFSYS_ASSERT(B_FALSE);
808221345Sdim		break;
809221345Sdim	}
810210006Srdivacky}
811218893Sdim
812221345Sdimstatic			void
813210006Srdivackysiena_filter_push_rx_limits(
814210006Srdivacky	__in		efx_nic_t *enp)
815210006Srdivacky{
816210006Srdivacky	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
817221345Sdim	efx_oword_t oword;
818221345Sdim
819210006Srdivacky	EFX_BAR_READO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
820221345Sdim
821221345Sdim	EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_FULL_SRCH_LIMIT,
822210006Srdivacky	    sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] +
823221345Sdim	    FILTER_CTL_SRCH_FUDGE_FULL);
824221345Sdim	EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_WILD_SRCH_LIMIT,
825210006Srdivacky	    sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] +
826221345Sdim	    FILTER_CTL_SRCH_FUDGE_WILD);
827210006Srdivacky	EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_FULL_SRCH_LIMIT,
828210006Srdivacky	    sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] +
829210006Srdivacky	    FILTER_CTL_SRCH_FUDGE_FULL);
830221345Sdim	EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_WILD_SRCH_LIMIT,
831221345Sdim	    sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] +
832221345Sdim	    FILTER_CTL_SRCH_FUDGE_WILD);
833210006Srdivacky
834221345Sdim	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC].sft_size) {
835221345Sdim		EFX_SET_OWORD_FIELD(oword,
836210006Srdivacky		    FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT,
837210006Srdivacky		    sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] +
838210006Srdivacky		    FILTER_CTL_SRCH_FUDGE_FULL);
839210006Srdivacky		EFX_SET_OWORD_FIELD(oword,
840210006Srdivacky		    FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT,
841210006Srdivacky		    sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] +
842210006Srdivacky		    FILTER_CTL_SRCH_FUDGE_WILD);
843210006Srdivacky	}
844221345Sdim
845210006Srdivacky	EFX_BAR_WRITEO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
846210006Srdivacky}
847210006Srdivacky
848210006Srdivackystatic			void
849210006Srdivackysiena_filter_push_tx_limits(
850210006Srdivacky	__in		efx_nic_t *enp)
851210006Srdivacky{
852210006Srdivacky	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
853210006Srdivacky	efx_oword_t oword;
854210006Srdivacky
855210006Srdivacky	EFX_BAR_READO(enp, FR_AZ_TX_CFG_REG, &oword);
856221345Sdim
857221345Sdim	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP].sft_size != 0) {
858221345Sdim		EFX_SET_OWORD_FIELD(oword,
859221345Sdim		    FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE,
860212904Sdim		    sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] +
861221345Sdim		    FILTER_CTL_SRCH_FUDGE_FULL);
862212904Sdim		EFX_SET_OWORD_FIELD(oword,
863212904Sdim		    FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE,
864210006Srdivacky		    sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] +
865210006Srdivacky		    FILTER_CTL_SRCH_FUDGE_WILD);
866210006Srdivacky		EFX_SET_OWORD_FIELD(oword,
867221345Sdim		    FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE,
868210006Srdivacky		    sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] +
869221345Sdim		    FILTER_CTL_SRCH_FUDGE_FULL);
870221345Sdim		EFX_SET_OWORD_FIELD(oword,
871221345Sdim		    FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE,
872221345Sdim		    sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] +
873221345Sdim		    FILTER_CTL_SRCH_FUDGE_WILD);
874221345Sdim	}
875221345Sdim
876221345Sdim	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC].sft_size != 0) {
877221345Sdim		EFX_SET_OWORD_FIELD(
878221345Sdim			oword, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE,
879221345Sdim			sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] +
880221345Sdim			FILTER_CTL_SRCH_FUDGE_FULL);
881221345Sdim		EFX_SET_OWORD_FIELD(
882221345Sdim			oword, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE,
883221345Sdim			sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] +
884221345Sdim			FILTER_CTL_SRCH_FUDGE_WILD);
885221345Sdim	}
886221345Sdim
887221345Sdim	EFX_BAR_WRITEO(enp, FR_AZ_TX_CFG_REG, &oword);
888221345Sdim}
889221345Sdim
890221345Sdim/* Build a filter entry and return its n-tuple key. */
891221345Sdimstatic	__checkReturn	uint32_t
892221345Sdimsiena_filter_build(
893221345Sdim	__out		efx_oword_t *filter,
894221345Sdim	__in		siena_filter_spec_t *spec)
895210006Srdivacky{
896210006Srdivacky	uint32_t dword3;
897210006Srdivacky	uint32_t key;
898210006Srdivacky	uint8_t  type  = spec->sfs_type;
899210006Srdivacky	uint32_t flags = spec->sfs_flags;
900210006Srdivacky
901221345Sdim	switch (siena_filter_tbl_id(type)) {
902210006Srdivacky	case EFX_SIENA_FILTER_TBL_RX_IP: {
903210006Srdivacky		boolean_t is_udp = (type == EFX_SIENA_FILTER_RX_UDP_FULL ||
904210006Srdivacky		    type == EFX_SIENA_FILTER_RX_UDP_WILD);
905221345Sdim		EFX_POPULATE_OWORD_7(*filter,
906210006Srdivacky		    FRF_BZ_RSS_EN,
907210006Srdivacky		    (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
908210006Srdivacky		    FRF_BZ_SCATTER_EN,
909210006Srdivacky		    (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
910210006Srdivacky		    FRF_AZ_TCP_UDP, is_udp,
911218893Sdim		    FRF_AZ_RXQ_ID, spec->sfs_dmaq_id,
912210006Srdivacky		    EFX_DWORD_2, spec->sfs_dword[2],
913210006Srdivacky		    EFX_DWORD_1, spec->sfs_dword[1],
914210006Srdivacky		    EFX_DWORD_0, spec->sfs_dword[0]);
915210006Srdivacky		dword3 = is_udp;
916210006Srdivacky		break;
917210006Srdivacky	}
918210006Srdivacky
919210006Srdivacky	case EFX_SIENA_FILTER_TBL_RX_MAC: {
920221345Sdim		boolean_t is_wild = (type == EFX_SIENA_FILTER_RX_MAC_WILD);
921210006Srdivacky		EFX_POPULATE_OWORD_7(*filter,
922210006Srdivacky		    FRF_CZ_RMFT_RSS_EN,
923210006Srdivacky		    (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
924221345Sdim		    FRF_CZ_RMFT_SCATTER_EN,
925210006Srdivacky		    (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
926210006Srdivacky		    FRF_CZ_RMFT_RXQ_ID, spec->sfs_dmaq_id,
927210006Srdivacky		    FRF_CZ_RMFT_WILDCARD_MATCH, is_wild,
928210006Srdivacky		    FRF_CZ_RMFT_DEST_MAC_DW1, spec->sfs_dword[2],
929221345Sdim		    FRF_CZ_RMFT_DEST_MAC_DW0, spec->sfs_dword[1],
930221345Sdim		    FRF_CZ_RMFT_VLAN_ID, spec->sfs_dword[0]);
931221345Sdim		dword3 = is_wild;
932221345Sdim		break;
933221345Sdim	}
934221345Sdim
935221345Sdim	case EFX_SIENA_FILTER_TBL_TX_IP: {
936221345Sdim		boolean_t is_udp = (type == EFX_SIENA_FILTER_TX_UDP_FULL ||
937221345Sdim		    type == EFX_SIENA_FILTER_TX_UDP_WILD);
938221345Sdim		EFX_POPULATE_OWORD_5(*filter,
939221345Sdim		    FRF_CZ_TIFT_TCP_UDP, is_udp,
940221345Sdim		    FRF_CZ_TIFT_TXQ_ID, spec->sfs_dmaq_id,
941221345Sdim		    EFX_DWORD_2, spec->sfs_dword[2],
942221345Sdim		    EFX_DWORD_1, spec->sfs_dword[1],
943221345Sdim		    EFX_DWORD_0, spec->sfs_dword[0]);
944221345Sdim		dword3 = is_udp | spec->sfs_dmaq_id << 1;
945221345Sdim		break;
946221345Sdim	}
947221345Sdim
948221345Sdim	case EFX_SIENA_FILTER_TBL_TX_MAC: {
949221345Sdim		boolean_t is_wild = (type == EFX_SIENA_FILTER_TX_MAC_WILD);
950221345Sdim		EFX_POPULATE_OWORD_5(*filter,
951221345Sdim		    FRF_CZ_TMFT_TXQ_ID, spec->sfs_dmaq_id,
952221345Sdim		    FRF_CZ_TMFT_WILDCARD_MATCH, is_wild,
953221345Sdim		    FRF_CZ_TMFT_SRC_MAC_DW1, spec->sfs_dword[2],
954221345Sdim		    FRF_CZ_TMFT_SRC_MAC_DW0, spec->sfs_dword[1],
955221345Sdim		    FRF_CZ_TMFT_VLAN_ID, spec->sfs_dword[0]);
956221345Sdim		dword3 = is_wild | spec->sfs_dmaq_id << 1;
957221345Sdim		break;
958221345Sdim	}
959221345Sdim
960221345Sdim	default:
961221345Sdim		EFSYS_ASSERT(B_FALSE);
962221345Sdim		return (0);
963221345Sdim	}
964221345Sdim
965221345Sdim	key =
966221345Sdim	    spec->sfs_dword[0] ^
967221345Sdim	    spec->sfs_dword[1] ^
968221345Sdim	    spec->sfs_dword[2] ^
969221345Sdim	    dword3;
970221345Sdim
971221345Sdim	return (key);
972221345Sdim}
973221345Sdim
974221345Sdimstatic	__checkReturn		efx_rc_t
975221345Sdimsiena_filter_push_entry(
976221345Sdim	__inout			efx_nic_t *enp,
977221345Sdim	__in			siena_filter_type_t type,
978221345Sdim	__in			int index,
979221345Sdim	__in			efx_oword_t *eop)
980221345Sdim{
981221345Sdim	efx_rc_t rc;
982221345Sdim
983221345Sdim	switch (type) {
984221345Sdim	case EFX_SIENA_FILTER_RX_TCP_FULL:
985221345Sdim	case EFX_SIENA_FILTER_RX_TCP_WILD:
986221345Sdim	case EFX_SIENA_FILTER_RX_UDP_FULL:
987221345Sdim	case EFX_SIENA_FILTER_RX_UDP_WILD:
988221345Sdim		EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_FILTER_TBL0, index,
989221345Sdim		    eop, B_TRUE);
990221345Sdim		break;
991221345Sdim
992221345Sdim	case EFX_SIENA_FILTER_RX_MAC_FULL:
993221345Sdim	case EFX_SIENA_FILTER_RX_MAC_WILD:
994221345Sdim		EFX_BAR_TBL_WRITEO(enp, FR_CZ_RX_MAC_FILTER_TBL0, index,
995221345Sdim		    eop, B_TRUE);
996221345Sdim		break;
997221345Sdim
998221345Sdim	case EFX_SIENA_FILTER_TX_TCP_FULL:
999221345Sdim	case EFX_SIENA_FILTER_TX_TCP_WILD:
1000221345Sdim	case EFX_SIENA_FILTER_TX_UDP_FULL:
1001	case EFX_SIENA_FILTER_TX_UDP_WILD:
1002		EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_FILTER_TBL0, index,
1003		    eop, B_TRUE);
1004		break;
1005
1006	case EFX_SIENA_FILTER_TX_MAC_FULL:
1007	case EFX_SIENA_FILTER_TX_MAC_WILD:
1008		EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_MAC_FILTER_TBL0, index,
1009		    eop, B_TRUE);
1010		break;
1011
1012	default:
1013		EFSYS_ASSERT(B_FALSE);
1014		rc = ENOTSUP;
1015		goto fail1;
1016	}
1017	return (0);
1018
1019fail1:
1020	return (rc);
1021}
1022
1023
1024static	__checkReturn	boolean_t
1025siena_filter_equal(
1026	__in		const siena_filter_spec_t *left,
1027	__in		const siena_filter_spec_t *right)
1028{
1029	siena_filter_tbl_id_t tbl_id;
1030
1031	tbl_id = siena_filter_tbl_id(left->sfs_type);
1032
1033
1034	if (left->sfs_type != right->sfs_type)
1035		return (B_FALSE);
1036
1037	if (memcmp(left->sfs_dword, right->sfs_dword,
1038		sizeof (left->sfs_dword)))
1039		return (B_FALSE);
1040
1041	if ((tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
1042		tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC) &&
1043	    left->sfs_dmaq_id != right->sfs_dmaq_id)
1044		return (B_FALSE);
1045
1046	return (B_TRUE);
1047}
1048
1049static	__checkReturn	efx_rc_t
1050siena_filter_search(
1051	__in		siena_filter_tbl_t *sftp,
1052	__in		siena_filter_spec_t *spec,
1053	__in		uint32_t key,
1054	__in		boolean_t for_insert,
1055	__out		int *filter_index,
1056	__out		unsigned int *depth_required)
1057{
1058	unsigned int hash, incr, filter_idx, depth;
1059
1060	hash = siena_filter_tbl_hash(key);
1061	incr = siena_filter_tbl_increment(key);
1062
1063	filter_idx = hash & (sftp->sft_size - 1);
1064	depth = 1;
1065
1066	for (;;) {
1067		/*
1068		 * Return success if entry is used and matches this spec
1069		 * or entry is unused and we are trying to insert.
1070		 */
1071		if (siena_filter_test_used(sftp, filter_idx) ?
1072		    siena_filter_equal(spec,
1073		    &sftp->sft_spec[filter_idx]) :
1074		    for_insert) {
1075			*filter_index = filter_idx;
1076			*depth_required = depth;
1077			return (0);
1078		}
1079
1080		/* Return failure if we reached the maximum search depth */
1081		if (depth == FILTER_CTL_SRCH_MAX)
1082			return (for_insert ? EBUSY : ENOENT);
1083
1084		filter_idx = (filter_idx + incr) & (sftp->sft_size - 1);
1085		++depth;
1086	}
1087}
1088
1089static			void
1090siena_filter_clear_entry(
1091	__in		efx_nic_t *enp,
1092	__in		siena_filter_tbl_t *sftp,
1093	__in		int index)
1094{
1095	efx_oword_t filter;
1096
1097	if (siena_filter_test_used(sftp, index)) {
1098		siena_filter_clear_used(sftp, index);
1099
1100		EFX_ZERO_OWORD(filter);
1101		siena_filter_push_entry(enp,
1102		    sftp->sft_spec[index].sfs_type,
1103		    index, &filter);
1104
1105		memset(&sftp->sft_spec[index],
1106		    0, sizeof (sftp->sft_spec[0]));
1107	}
1108}
1109
1110			void
1111siena_filter_tbl_clear(
1112	__in		efx_nic_t *enp,
1113	__in		siena_filter_tbl_id_t tbl_id)
1114{
1115	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1116	siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
1117	int index;
1118	efsys_lock_state_t state;
1119
1120	EFSYS_LOCK(enp->en_eslp, state);
1121
1122	for (index = 0; index < sftp->sft_size; ++index) {
1123		siena_filter_clear_entry(enp, sftp, index);
1124	}
1125
1126	if (sftp->sft_used == 0)
1127		siena_filter_reset_search_depth(sfp, tbl_id);
1128
1129	EFSYS_UNLOCK(enp->en_eslp, state);
1130}
1131
1132static	__checkReturn	efx_rc_t
1133siena_filter_init(
1134	__in		efx_nic_t *enp)
1135{
1136	siena_filter_t *sfp;
1137	siena_filter_tbl_t *sftp;
1138	int tbl_id;
1139	efx_rc_t rc;
1140
1141	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (siena_filter_t), sfp);
1142
1143	if (!sfp) {
1144		rc = ENOMEM;
1145		goto fail1;
1146	}
1147
1148	enp->en_filter.ef_siena_filter = sfp;
1149
1150	switch (enp->en_family) {
1151	case EFX_FAMILY_SIENA:
1152		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_IP];
1153		sftp->sft_size = FR_AZ_RX_FILTER_TBL0_ROWS;
1154
1155		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC];
1156		sftp->sft_size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS;
1157
1158		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP];
1159		sftp->sft_size = FR_CZ_TX_FILTER_TBL0_ROWS;
1160
1161		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC];
1162		sftp->sft_size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS;
1163		break;
1164
1165	default:
1166		rc = ENOTSUP;
1167		goto fail2;
1168	}
1169
1170	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1171		unsigned int bitmap_size;
1172
1173		sftp = &sfp->sf_tbl[tbl_id];
1174		if (sftp->sft_size == 0)
1175			continue;
1176
1177		EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
1178		    sizeof (uint32_t));
1179		bitmap_size =
1180		    (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
1181
1182		EFSYS_KMEM_ALLOC(enp->en_esip, bitmap_size, sftp->sft_bitmap);
1183		if (!sftp->sft_bitmap) {
1184			rc = ENOMEM;
1185			goto fail3;
1186		}
1187
1188		EFSYS_KMEM_ALLOC(enp->en_esip,
1189		    sftp->sft_size * sizeof (*sftp->sft_spec),
1190		    sftp->sft_spec);
1191		if (!sftp->sft_spec) {
1192			rc = ENOMEM;
1193			goto fail4;
1194		}
1195		memset(sftp->sft_spec, 0,
1196		    sftp->sft_size * sizeof (*sftp->sft_spec));
1197	}
1198
1199	return (0);
1200
1201fail4:
1202	EFSYS_PROBE(fail4);
1203
1204fail3:
1205	EFSYS_PROBE(fail3);
1206
1207fail2:
1208	EFSYS_PROBE(fail2);
1209	siena_filter_fini(enp);
1210
1211fail1:
1212	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1213	return (rc);
1214}
1215
1216static			void
1217siena_filter_fini(
1218	__in		efx_nic_t *enp)
1219{
1220	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1221	siena_filter_tbl_id_t tbl_id;
1222
1223	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1224	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
1225
1226	if (sfp == NULL)
1227		return;
1228
1229	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1230		siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
1231		unsigned int bitmap_size;
1232
1233		EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
1234		    sizeof (uint32_t));
1235		bitmap_size =
1236		    (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
1237
1238		if (sftp->sft_bitmap != NULL) {
1239			EFSYS_KMEM_FREE(enp->en_esip, bitmap_size,
1240			    sftp->sft_bitmap);
1241			sftp->sft_bitmap = NULL;
1242		}
1243
1244		if (sftp->sft_spec != NULL) {
1245			EFSYS_KMEM_FREE(enp->en_esip, sftp->sft_size *
1246			    sizeof (*sftp->sft_spec), sftp->sft_spec);
1247			sftp->sft_spec = NULL;
1248		}
1249	}
1250
1251	EFSYS_KMEM_FREE(enp->en_esip, sizeof (siena_filter_t),
1252	    enp->en_filter.ef_siena_filter);
1253}
1254
1255/* Restore filter state after a reset */
1256static	__checkReturn	efx_rc_t
1257siena_filter_restore(
1258	__in		efx_nic_t *enp)
1259{
1260	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1261	siena_filter_tbl_id_t tbl_id;
1262	siena_filter_tbl_t *sftp;
1263	siena_filter_spec_t *spec;
1264	efx_oword_t filter;
1265	int filter_idx;
1266	efsys_lock_state_t state;
1267	uint32_t key;
1268	efx_rc_t rc;
1269
1270	EFSYS_LOCK(enp->en_eslp, state);
1271
1272	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1273		sftp = &sfp->sf_tbl[tbl_id];
1274		for (filter_idx = 0;
1275			filter_idx < sftp->sft_size;
1276			filter_idx++) {
1277			if (!siena_filter_test_used(sftp, filter_idx))
1278				continue;
1279
1280			spec = &sftp->sft_spec[filter_idx];
1281			if ((key = siena_filter_build(&filter, spec)) == 0) {
1282				rc = EINVAL;
1283				goto fail1;
1284			}
1285			if ((rc = siena_filter_push_entry(enp,
1286				    spec->sfs_type, filter_idx, &filter)) != 0)
1287				goto fail2;
1288		}
1289	}
1290
1291	siena_filter_push_rx_limits(enp);
1292	siena_filter_push_tx_limits(enp);
1293
1294	EFSYS_UNLOCK(enp->en_eslp, state);
1295
1296	return (0);
1297
1298fail2:
1299	EFSYS_PROBE(fail2);
1300
1301fail1:
1302	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1303
1304	EFSYS_UNLOCK(enp->en_eslp, state);
1305
1306	return (rc);
1307}
1308
1309static	 __checkReturn	efx_rc_t
1310siena_filter_add(
1311	__in		efx_nic_t *enp,
1312	__inout		efx_filter_spec_t *spec,
1313	__in		boolean_t may_replace)
1314{
1315	efx_rc_t rc;
1316	siena_filter_spec_t sf_spec;
1317	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1318	siena_filter_tbl_id_t tbl_id;
1319	siena_filter_tbl_t *sftp;
1320	siena_filter_spec_t *saved_sf_spec;
1321	efx_oword_t filter;
1322	int filter_idx;
1323	unsigned int depth;
1324	efsys_lock_state_t state;
1325	uint32_t key;
1326
1327
1328	EFSYS_ASSERT3P(spec, !=, NULL);
1329
1330	if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
1331		goto fail1;
1332
1333	tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
1334	sftp = &sfp->sf_tbl[tbl_id];
1335
1336	if (sftp->sft_size == 0) {
1337		rc = EINVAL;
1338		goto fail2;
1339	}
1340
1341	key = siena_filter_build(&filter, &sf_spec);
1342
1343	EFSYS_LOCK(enp->en_eslp, state);
1344
1345	rc = siena_filter_search(sftp, &sf_spec, key, B_TRUE,
1346	    &filter_idx, &depth);
1347	if (rc != 0)
1348		goto fail3;
1349
1350	EFSYS_ASSERT3U(filter_idx, <, sftp->sft_size);
1351	saved_sf_spec = &sftp->sft_spec[filter_idx];
1352
1353	if (siena_filter_test_used(sftp, filter_idx)) {
1354		if (may_replace == B_FALSE) {
1355			rc = EEXIST;
1356			goto fail4;
1357		}
1358	}
1359	siena_filter_set_used(sftp, filter_idx);
1360	*saved_sf_spec = sf_spec;
1361
1362	if (sfp->sf_depth[sf_spec.sfs_type] < depth) {
1363		sfp->sf_depth[sf_spec.sfs_type] = depth;
1364		if (tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
1365		    tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC)
1366			siena_filter_push_tx_limits(enp);
1367		else
1368			siena_filter_push_rx_limits(enp);
1369	}
1370
1371	siena_filter_push_entry(enp, sf_spec.sfs_type,
1372	    filter_idx, &filter);
1373
1374	EFSYS_UNLOCK(enp->en_eslp, state);
1375	return (0);
1376
1377fail4:
1378	EFSYS_PROBE(fail4);
1379
1380fail3:
1381	EFSYS_UNLOCK(enp->en_eslp, state);
1382	EFSYS_PROBE(fail3);
1383
1384fail2:
1385	EFSYS_PROBE(fail2);
1386
1387fail1:
1388	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1389	return (rc);
1390}
1391
1392static	 __checkReturn	efx_rc_t
1393siena_filter_delete(
1394	__in		efx_nic_t *enp,
1395	__inout		efx_filter_spec_t *spec)
1396{
1397	efx_rc_t rc;
1398	siena_filter_spec_t sf_spec;
1399	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1400	siena_filter_tbl_id_t tbl_id;
1401	siena_filter_tbl_t *sftp;
1402	efx_oword_t filter;
1403	int filter_idx;
1404	unsigned int depth;
1405	efsys_lock_state_t state;
1406	uint32_t key;
1407
1408	EFSYS_ASSERT3P(spec, !=, NULL);
1409
1410	if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
1411		goto fail1;
1412
1413	tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
1414	sftp = &sfp->sf_tbl[tbl_id];
1415
1416	key = siena_filter_build(&filter, &sf_spec);
1417
1418	EFSYS_LOCK(enp->en_eslp, state);
1419
1420	rc = siena_filter_search(sftp, &sf_spec, key, B_FALSE,
1421	    &filter_idx, &depth);
1422	if (rc != 0)
1423		goto fail2;
1424
1425	siena_filter_clear_entry(enp, sftp, filter_idx);
1426	if (sftp->sft_used == 0)
1427		siena_filter_reset_search_depth(sfp, tbl_id);
1428
1429	EFSYS_UNLOCK(enp->en_eslp, state);
1430	return (0);
1431
1432fail2:
1433	EFSYS_UNLOCK(enp->en_eslp, state);
1434	EFSYS_PROBE(fail2);
1435
1436fail1:
1437	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1438	return (rc);
1439}
1440
1441#define	SIENA_MAX_SUPPORTED_MATCHES 4
1442
1443static	__checkReturn	efx_rc_t
1444siena_filter_supported_filters(
1445	__in				efx_nic_t *enp,
1446	__out_ecount(buffer_length)	uint32_t *buffer,
1447	__in				size_t buffer_length,
1448	__out				size_t *list_lengthp)
1449{
1450	uint32_t index = 0;
1451	uint32_t rx_matches[SIENA_MAX_SUPPORTED_MATCHES];
1452	size_t list_length;
1453	efx_rc_t rc;
1454
1455	rx_matches[index++] =
1456	    EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
1457	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
1458	    EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
1459
1460	rx_matches[index++] =
1461	    EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
1462	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
1463
1464	if (enp->en_features & EFX_FEATURE_MAC_HEADER_FILTERS) {
1465		rx_matches[index++] =
1466		    EFX_FILTER_MATCH_OUTER_VID | EFX_FILTER_MATCH_LOC_MAC;
1467
1468		rx_matches[index++] = EFX_FILTER_MATCH_LOC_MAC;
1469	}
1470
1471	EFSYS_ASSERT3U(index, <=, SIENA_MAX_SUPPORTED_MATCHES);
1472	list_length = index;
1473
1474	*list_lengthp = list_length;
1475
1476	if (buffer_length < list_length) {
1477		rc = ENOSPC;
1478		goto fail1;
1479	}
1480
1481	memcpy(buffer, rx_matches, list_length * sizeof (rx_matches[0]));
1482
1483	return (0);
1484
1485fail1:
1486	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1487
1488	return (rc);
1489}
1490
1491#undef MAX_SUPPORTED
1492
1493#endif /* EFSYS_OPT_SIENA */
1494
1495#endif /* EFSYS_OPT_FILTER */
1496