efx_filter.c revision 311494
1/*-
2 * Copyright (c) 2007-2016 Solarflare Communications Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 *    this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 *    this list of conditions and the following disclaimer in the documentation
12 *    and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * The views and conclusions contained in the software and documentation are
27 * those of the authors and should not be interpreted as representing official
28 * policies, either expressed or implied, of the FreeBSD Project.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: stable/10/sys/dev/sfxge/common/efx_filter.c 311494 2017-01-06 07:29:54Z arybchik $");
33
34#include "efx.h"
35#include "efx_impl.h"
36
37
38#if EFSYS_OPT_FILTER
39
40#if EFSYS_OPT_SIENA
41
42static	__checkReturn	efx_rc_t
43siena_filter_init(
44	__in		efx_nic_t *enp);
45
46static			void
47siena_filter_fini(
48	__in		efx_nic_t *enp);
49
50static	__checkReturn	efx_rc_t
51siena_filter_restore(
52	__in		efx_nic_t *enp);
53
54static	__checkReturn	efx_rc_t
55siena_filter_add(
56	__in		efx_nic_t *enp,
57	__inout		efx_filter_spec_t *spec,
58	__in		boolean_t may_replace);
59
60static	__checkReturn	efx_rc_t
61siena_filter_delete(
62	__in		efx_nic_t *enp,
63	__inout		efx_filter_spec_t *spec);
64
65static	__checkReturn	efx_rc_t
66siena_filter_supported_filters(
67	__in				efx_nic_t *enp,
68	__out_ecount(buffer_length)	uint32_t *buffer,
69	__in				size_t buffer_length,
70	__out				size_t *list_lengthp);
71
72#endif /* EFSYS_OPT_SIENA */
73
74#if EFSYS_OPT_SIENA
75static const efx_filter_ops_t	__efx_filter_siena_ops = {
76	siena_filter_init,		/* efo_init */
77	siena_filter_fini,		/* efo_fini */
78	siena_filter_restore,		/* efo_restore */
79	siena_filter_add,		/* efo_add */
80	siena_filter_delete,		/* efo_delete */
81	siena_filter_supported_filters,	/* efo_supported_filters */
82	NULL,				/* efo_reconfigure */
83};
84#endif /* EFSYS_OPT_SIENA */
85
86#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
87static const efx_filter_ops_t	__efx_filter_ef10_ops = {
88	ef10_filter_init,		/* efo_init */
89	ef10_filter_fini,		/* efo_fini */
90	ef10_filter_restore,		/* efo_restore */
91	ef10_filter_add,		/* efo_add */
92	ef10_filter_delete,		/* efo_delete */
93	ef10_filter_supported_filters,	/* efo_supported_filters */
94	ef10_filter_reconfigure,	/* efo_reconfigure */
95};
96#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
97
98	__checkReturn	efx_rc_t
99efx_filter_insert(
100	__in		efx_nic_t *enp,
101	__inout		efx_filter_spec_t *spec)
102{
103	const efx_filter_ops_t *efop = enp->en_efop;
104
105	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
106	EFSYS_ASSERT3P(spec, !=, NULL);
107	EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX);
108
109	return (efop->efo_add(enp, spec, B_FALSE));
110}
111
112	__checkReturn	efx_rc_t
113efx_filter_remove(
114	__in		efx_nic_t *enp,
115	__inout		efx_filter_spec_t *spec)
116{
117	const efx_filter_ops_t *efop = enp->en_efop;
118
119	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
120	EFSYS_ASSERT3P(spec, !=, NULL);
121	EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX);
122
123#if EFSYS_OPT_RX_SCALE
124	spec->efs_rss_context = enp->en_rss_context;
125#endif
126
127	return (efop->efo_delete(enp, spec));
128}
129
130	__checkReturn	efx_rc_t
131efx_filter_restore(
132	__in		efx_nic_t *enp)
133{
134	efx_rc_t rc;
135
136	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
137
138	if ((rc = enp->en_efop->efo_restore(enp)) != 0)
139		goto fail1;
140
141	return (0);
142
143fail1:
144	EFSYS_PROBE1(fail1, efx_rc_t, rc);
145
146	return (rc);
147}
148
149	__checkReturn	efx_rc_t
150efx_filter_init(
151	__in		efx_nic_t *enp)
152{
153	const efx_filter_ops_t *efop;
154	efx_rc_t rc;
155
156	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
157	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
158	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_FILTER));
159
160	switch (enp->en_family) {
161#if EFSYS_OPT_SIENA
162	case EFX_FAMILY_SIENA:
163		efop = &__efx_filter_siena_ops;
164		break;
165#endif /* EFSYS_OPT_SIENA */
166
167#if EFSYS_OPT_HUNTINGTON
168	case EFX_FAMILY_HUNTINGTON:
169		efop = &__efx_filter_ef10_ops;
170		break;
171#endif /* EFSYS_OPT_HUNTINGTON */
172
173#if EFSYS_OPT_MEDFORD
174	case EFX_FAMILY_MEDFORD:
175		efop = &__efx_filter_ef10_ops;
176		break;
177#endif /* EFSYS_OPT_MEDFORD */
178
179	default:
180		EFSYS_ASSERT(0);
181		rc = ENOTSUP;
182		goto fail1;
183	}
184
185	if ((rc = efop->efo_init(enp)) != 0)
186		goto fail2;
187
188	enp->en_efop = efop;
189	enp->en_mod_flags |= EFX_MOD_FILTER;
190	return (0);
191
192fail2:
193	EFSYS_PROBE(fail2);
194fail1:
195	EFSYS_PROBE1(fail1, efx_rc_t, rc);
196
197	enp->en_efop = NULL;
198	enp->en_mod_flags &= ~EFX_MOD_FILTER;
199	return (rc);
200}
201
202			void
203efx_filter_fini(
204	__in		efx_nic_t *enp)
205{
206	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
207	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
208	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
209
210	enp->en_efop->efo_fini(enp);
211
212	enp->en_efop = NULL;
213	enp->en_mod_flags &= ~EFX_MOD_FILTER;
214}
215
216/*
217 * Query the possible combinations of match flags which can be filtered on.
218 * These are returned as a list, of which each 32 bit element is a bitmask
219 * formed of EFX_FILTER_MATCH flags.
220 *
221 * The combinations are ordered in priority from highest to lowest.
222 *
223 * If the provided buffer is too short to hold the list, the call with fail with
224 * ENOSPC and *list_lengthp will be set to the buffer length required.
225 */
226	__checkReturn	efx_rc_t
227efx_filter_supported_filters(
228	__in				efx_nic_t *enp,
229	__out_ecount(buffer_length)	uint32_t *buffer,
230	__in				size_t buffer_length,
231	__out				size_t *list_lengthp)
232{
233	efx_rc_t rc;
234
235	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
236	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
237	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
238	EFSYS_ASSERT(enp->en_efop->efo_supported_filters != NULL);
239
240	if (buffer == NULL) {
241		rc = EINVAL;
242		goto fail1;
243	}
244
245	rc = enp->en_efop->efo_supported_filters(enp, buffer, buffer_length,
246						    list_lengthp);
247	if (rc != 0)
248		goto fail2;
249
250	return (0);
251
252fail2:
253	EFSYS_PROBE(fail2);
254fail1:
255	EFSYS_PROBE1(fail1, efx_rc_t, rc);
256
257	return (rc);
258}
259
260	__checkReturn	efx_rc_t
261efx_filter_reconfigure(
262	__in				efx_nic_t *enp,
263	__in_ecount(6)			uint8_t const *mac_addr,
264	__in				boolean_t all_unicst,
265	__in				boolean_t mulcst,
266	__in				boolean_t all_mulcst,
267	__in				boolean_t brdcst,
268	__in_ecount(6*count)		uint8_t const *addrs,
269	__in				uint32_t count)
270{
271	efx_rc_t rc;
272
273	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
274	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
275	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
276
277	if (enp->en_efop->efo_reconfigure != NULL) {
278		if ((rc = enp->en_efop->efo_reconfigure(enp, mac_addr,
279							all_unicst, mulcst,
280							all_mulcst, brdcst,
281							addrs, count)) != 0)
282			goto fail1;
283	}
284
285	return (0);
286
287fail1:
288	EFSYS_PROBE1(fail1, efx_rc_t, rc);
289
290	return (rc);
291}
292
293		void
294efx_filter_spec_init_rx(
295	__out		efx_filter_spec_t *spec,
296	__in		efx_filter_priority_t priority,
297	__in		efx_filter_flags_t flags,
298	__in		efx_rxq_t *erp)
299{
300	EFSYS_ASSERT3P(spec, !=, NULL);
301	EFSYS_ASSERT3P(erp, !=, NULL);
302	EFSYS_ASSERT((flags & ~(EFX_FILTER_FLAG_RX_RSS |
303				EFX_FILTER_FLAG_RX_SCATTER)) == 0);
304
305	memset(spec, 0, sizeof (*spec));
306	spec->efs_priority = priority;
307	spec->efs_flags = EFX_FILTER_FLAG_RX | flags;
308	spec->efs_rss_context = EFX_FILTER_SPEC_RSS_CONTEXT_DEFAULT;
309	spec->efs_dmaq_id = (uint16_t)erp->er_index;
310}
311
312		void
313efx_filter_spec_init_tx(
314	__out		efx_filter_spec_t *spec,
315	__in		efx_txq_t *etp)
316{
317	EFSYS_ASSERT3P(spec, !=, NULL);
318	EFSYS_ASSERT3P(etp, !=, NULL);
319
320	memset(spec, 0, sizeof (*spec));
321	spec->efs_priority = EFX_FILTER_PRI_REQUIRED;
322	spec->efs_flags = EFX_FILTER_FLAG_TX;
323	spec->efs_dmaq_id = (uint16_t)etp->et_index;
324}
325
326
327/*
328 *  Specify IPv4 host, transport protocol and port in a filter specification
329 */
330__checkReturn		efx_rc_t
331efx_filter_spec_set_ipv4_local(
332	__inout		efx_filter_spec_t *spec,
333	__in		uint8_t proto,
334	__in		uint32_t host,
335	__in		uint16_t port)
336{
337	EFSYS_ASSERT3P(spec, !=, NULL);
338
339	spec->efs_match_flags |=
340		EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
341		EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
342	spec->efs_ether_type = EFX_ETHER_TYPE_IPV4;
343	spec->efs_ip_proto = proto;
344	spec->efs_loc_host.eo_u32[0] = host;
345	spec->efs_loc_port = port;
346	return (0);
347}
348
349/*
350 * Specify IPv4 hosts, transport protocol and ports in a filter specification
351 */
352__checkReturn		efx_rc_t
353efx_filter_spec_set_ipv4_full(
354	__inout		efx_filter_spec_t *spec,
355	__in		uint8_t proto,
356	__in		uint32_t lhost,
357	__in		uint16_t lport,
358	__in		uint32_t rhost,
359	__in		uint16_t rport)
360{
361	EFSYS_ASSERT3P(spec, !=, NULL);
362
363	spec->efs_match_flags |=
364		EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
365		EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
366		EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
367	spec->efs_ether_type = EFX_ETHER_TYPE_IPV4;
368	spec->efs_ip_proto = proto;
369	spec->efs_loc_host.eo_u32[0] = lhost;
370	spec->efs_loc_port = lport;
371	spec->efs_rem_host.eo_u32[0] = rhost;
372	spec->efs_rem_port = rport;
373	return (0);
374}
375
376/*
377 * Specify local Ethernet address and/or VID in filter specification
378 */
379__checkReturn		efx_rc_t
380efx_filter_spec_set_eth_local(
381	__inout		efx_filter_spec_t *spec,
382	__in		uint16_t vid,
383	__in		const uint8_t *addr)
384{
385	EFSYS_ASSERT3P(spec, !=, NULL);
386	EFSYS_ASSERT3P(addr, !=, NULL);
387
388	if (vid == EFX_FILTER_SPEC_VID_UNSPEC && addr == NULL)
389		return (EINVAL);
390
391	if (vid != EFX_FILTER_SPEC_VID_UNSPEC) {
392		spec->efs_match_flags |= EFX_FILTER_MATCH_OUTER_VID;
393		spec->efs_outer_vid = vid;
394	}
395	if (addr != NULL) {
396		spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC;
397		memcpy(spec->efs_loc_mac, addr, EFX_MAC_ADDR_LEN);
398	}
399	return (0);
400}
401
402/*
403 * Specify matching otherwise-unmatched unicast in a filter specification
404 */
405__checkReturn		efx_rc_t
406efx_filter_spec_set_uc_def(
407	__inout		efx_filter_spec_t *spec)
408{
409	EFSYS_ASSERT3P(spec, !=, NULL);
410
411	spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_UCAST_DST;
412	return (0);
413}
414
415/*
416 * Specify matching otherwise-unmatched multicast in a filter specification
417 */
418__checkReturn		efx_rc_t
419efx_filter_spec_set_mc_def(
420	__inout		efx_filter_spec_t *spec)
421{
422	EFSYS_ASSERT3P(spec, !=, NULL);
423
424	spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_MCAST_DST;
425	return (0);
426}
427
428
429
430#if EFSYS_OPT_SIENA
431
432/*
433 * "Fudge factors" - difference between programmed value and actual depth.
434 * Due to pipelined implementation we need to program H/W with a value that
435 * is larger than the hop limit we want.
436 */
437#define	FILTER_CTL_SRCH_FUDGE_WILD 3
438#define	FILTER_CTL_SRCH_FUDGE_FULL 1
439
440/*
441 * Hard maximum hop limit.  Hardware will time-out beyond 200-something.
442 * We also need to avoid infinite loops in efx_filter_search() when the
443 * table is full.
444 */
445#define	FILTER_CTL_SRCH_MAX 200
446
447static	__checkReturn	efx_rc_t
448siena_filter_spec_from_gen_spec(
449	__out		siena_filter_spec_t *sf_spec,
450	__in		efx_filter_spec_t *gen_spec)
451{
452	efx_rc_t rc;
453	boolean_t is_full = B_FALSE;
454
455	if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX)
456		EFSYS_ASSERT3U(gen_spec->efs_flags, ==, EFX_FILTER_FLAG_TX);
457	else
458		EFSYS_ASSERT3U(gen_spec->efs_flags, &, EFX_FILTER_FLAG_RX);
459
460	/* Falconsiena only has one RSS context */
461	if ((gen_spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) &&
462	    gen_spec->efs_rss_context != 0) {
463		rc = EINVAL;
464		goto fail1;
465	}
466
467	sf_spec->sfs_flags = gen_spec->efs_flags;
468	sf_spec->sfs_dmaq_id = gen_spec->efs_dmaq_id;
469
470	switch (gen_spec->efs_match_flags) {
471	case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
472	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
473	    EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT:
474		is_full = B_TRUE;
475		/* Fall through */
476	case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
477	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT: {
478		uint32_t rhost, host1, host2;
479		uint16_t rport, port1, port2;
480
481		if (gen_spec->efs_ether_type != EFX_ETHER_TYPE_IPV4) {
482			rc = ENOTSUP;
483			goto fail2;
484		}
485		if (gen_spec->efs_loc_port == 0 ||
486		    (is_full && gen_spec->efs_rem_port == 0)) {
487			rc = EINVAL;
488			goto fail3;
489		}
490		switch (gen_spec->efs_ip_proto) {
491		case EFX_IPPROTO_TCP:
492			if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
493				sf_spec->sfs_type = (is_full ?
494				    EFX_SIENA_FILTER_TX_TCP_FULL :
495				    EFX_SIENA_FILTER_TX_TCP_WILD);
496			} else {
497				sf_spec->sfs_type = (is_full ?
498				    EFX_SIENA_FILTER_RX_TCP_FULL :
499				    EFX_SIENA_FILTER_RX_TCP_WILD);
500			}
501			break;
502		case EFX_IPPROTO_UDP:
503			if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
504				sf_spec->sfs_type = (is_full ?
505				    EFX_SIENA_FILTER_TX_UDP_FULL :
506				    EFX_SIENA_FILTER_TX_UDP_WILD);
507			} else {
508				sf_spec->sfs_type = (is_full ?
509				    EFX_SIENA_FILTER_RX_UDP_FULL :
510				    EFX_SIENA_FILTER_RX_UDP_WILD);
511			}
512			break;
513		default:
514			rc = ENOTSUP;
515			goto fail4;
516		}
517		/*
518		 * The filter is constructed in terms of source and destination,
519		 * with the odd wrinkle that the ports are swapped in a UDP
520		 * wildcard filter. We need to convert from local and remote
521		 * addresses (zero for a wildcard).
522		 */
523		rhost = is_full ? gen_spec->efs_rem_host.eo_u32[0] : 0;
524		rport = is_full ? gen_spec->efs_rem_port : 0;
525		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
526			host1 = gen_spec->efs_loc_host.eo_u32[0];
527			host2 = rhost;
528		} else {
529			host1 = rhost;
530			host2 = gen_spec->efs_loc_host.eo_u32[0];
531		}
532		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
533			if (sf_spec->sfs_type ==
534			    EFX_SIENA_FILTER_TX_UDP_WILD) {
535				port1 = rport;
536				port2 = gen_spec->efs_loc_port;
537			} else {
538				port1 = gen_spec->efs_loc_port;
539				port2 = rport;
540			}
541		} else {
542			if (sf_spec->sfs_type ==
543			    EFX_SIENA_FILTER_RX_UDP_WILD) {
544				port1 = gen_spec->efs_loc_port;
545				port2 = rport;
546			} else {
547				port1 = rport;
548				port2 = gen_spec->efs_loc_port;
549			}
550		}
551		sf_spec->sfs_dword[0] = (host1 << 16) | port1;
552		sf_spec->sfs_dword[1] = (port2 << 16) | (host1 >> 16);
553		sf_spec->sfs_dword[2] = host2;
554		break;
555	}
556
557	case EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_OUTER_VID:
558		is_full = B_TRUE;
559		/* Fall through */
560	case EFX_FILTER_MATCH_LOC_MAC:
561		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
562			sf_spec->sfs_type = (is_full ?
563			    EFX_SIENA_FILTER_TX_MAC_FULL :
564			    EFX_SIENA_FILTER_TX_MAC_WILD);
565		} else {
566			sf_spec->sfs_type = (is_full ?
567			    EFX_SIENA_FILTER_RX_MAC_FULL :
568			    EFX_SIENA_FILTER_RX_MAC_WILD);
569		}
570		sf_spec->sfs_dword[0] = is_full ? gen_spec->efs_outer_vid : 0;
571		sf_spec->sfs_dword[1] =
572		    gen_spec->efs_loc_mac[2] << 24 |
573		    gen_spec->efs_loc_mac[3] << 16 |
574		    gen_spec->efs_loc_mac[4] <<  8 |
575		    gen_spec->efs_loc_mac[5];
576		sf_spec->sfs_dword[2] =
577		    gen_spec->efs_loc_mac[0] << 8 |
578		    gen_spec->efs_loc_mac[1];
579		break;
580
581	default:
582		EFSYS_ASSERT(B_FALSE);
583		rc = ENOTSUP;
584		goto fail5;
585	}
586
587	return (0);
588
589fail5:
590	EFSYS_PROBE(fail5);
591fail4:
592	EFSYS_PROBE(fail4);
593fail3:
594	EFSYS_PROBE(fail3);
595fail2:
596	EFSYS_PROBE(fail2);
597fail1:
598	EFSYS_PROBE1(fail1, efx_rc_t, rc);
599
600	return (rc);
601}
602
603/*
604 * The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit
605 * key derived from the n-tuple.
606 */
607static			uint16_t
608siena_filter_tbl_hash(
609	__in		uint32_t key)
610{
611	uint16_t tmp;
612
613	/* First 16 rounds */
614	tmp = 0x1fff ^ (uint16_t)(key >> 16);
615	tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
616	tmp = tmp ^ tmp >> 9;
617
618	/* Last 16 rounds */
619	tmp = tmp ^ tmp << 13 ^ (uint16_t)(key & 0xffff);
620	tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
621	tmp = tmp ^ tmp >> 9;
622
623	return (tmp);
624}
625
626/*
627 * To allow for hash collisions, filter search continues at these
628 * increments from the first possible entry selected by the hash.
629 */
630static			uint16_t
631siena_filter_tbl_increment(
632	__in		uint32_t key)
633{
634	return ((uint16_t)(key * 2 - 1));
635}
636
637static	__checkReturn	boolean_t
638siena_filter_test_used(
639	__in		siena_filter_tbl_t *sftp,
640	__in		unsigned int index)
641{
642	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
643	return ((sftp->sft_bitmap[index / 32] & (1 << (index % 32))) != 0);
644}
645
646static			void
647siena_filter_set_used(
648	__in		siena_filter_tbl_t *sftp,
649	__in		unsigned int index)
650{
651	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
652	sftp->sft_bitmap[index / 32] |= (1 << (index % 32));
653	++sftp->sft_used;
654}
655
656static			void
657siena_filter_clear_used(
658	__in		siena_filter_tbl_t *sftp,
659	__in		unsigned int index)
660{
661	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
662	sftp->sft_bitmap[index / 32] &= ~(1 << (index % 32));
663
664	--sftp->sft_used;
665	EFSYS_ASSERT3U(sftp->sft_used, >=, 0);
666}
667
668
669static			siena_filter_tbl_id_t
670siena_filter_tbl_id(
671	__in		siena_filter_type_t type)
672{
673	siena_filter_tbl_id_t tbl_id;
674
675	switch (type) {
676	case EFX_SIENA_FILTER_RX_TCP_FULL:
677	case EFX_SIENA_FILTER_RX_TCP_WILD:
678	case EFX_SIENA_FILTER_RX_UDP_FULL:
679	case EFX_SIENA_FILTER_RX_UDP_WILD:
680		tbl_id = EFX_SIENA_FILTER_TBL_RX_IP;
681		break;
682
683	case EFX_SIENA_FILTER_RX_MAC_FULL:
684	case EFX_SIENA_FILTER_RX_MAC_WILD:
685		tbl_id = EFX_SIENA_FILTER_TBL_RX_MAC;
686		break;
687
688	case EFX_SIENA_FILTER_TX_TCP_FULL:
689	case EFX_SIENA_FILTER_TX_TCP_WILD:
690	case EFX_SIENA_FILTER_TX_UDP_FULL:
691	case EFX_SIENA_FILTER_TX_UDP_WILD:
692		tbl_id = EFX_SIENA_FILTER_TBL_TX_IP;
693		break;
694
695	case EFX_SIENA_FILTER_TX_MAC_FULL:
696	case EFX_SIENA_FILTER_TX_MAC_WILD:
697		tbl_id = EFX_SIENA_FILTER_TBL_TX_MAC;
698		break;
699
700	default:
701		EFSYS_ASSERT(B_FALSE);
702		tbl_id = EFX_SIENA_FILTER_NTBLS;
703		break;
704	}
705	return (tbl_id);
706}
707
708static			void
709siena_filter_reset_search_depth(
710	__inout		siena_filter_t *sfp,
711	__in		siena_filter_tbl_id_t tbl_id)
712{
713	switch (tbl_id) {
714	case EFX_SIENA_FILTER_TBL_RX_IP:
715		sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] = 0;
716		sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] = 0;
717		sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] = 0;
718		sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] = 0;
719		break;
720
721	case EFX_SIENA_FILTER_TBL_RX_MAC:
722		sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] = 0;
723		sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] = 0;
724		break;
725
726	case EFX_SIENA_FILTER_TBL_TX_IP:
727		sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] = 0;
728		sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] = 0;
729		sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] = 0;
730		sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] = 0;
731		break;
732
733	case EFX_SIENA_FILTER_TBL_TX_MAC:
734		sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] = 0;
735		sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] = 0;
736		break;
737
738	default:
739		EFSYS_ASSERT(B_FALSE);
740		break;
741	}
742}
743
744static			void
745siena_filter_push_rx_limits(
746	__in		efx_nic_t *enp)
747{
748	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
749	efx_oword_t oword;
750
751	EFX_BAR_READO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
752
753	EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_FULL_SRCH_LIMIT,
754	    sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] +
755	    FILTER_CTL_SRCH_FUDGE_FULL);
756	EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_WILD_SRCH_LIMIT,
757	    sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] +
758	    FILTER_CTL_SRCH_FUDGE_WILD);
759	EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_FULL_SRCH_LIMIT,
760	    sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] +
761	    FILTER_CTL_SRCH_FUDGE_FULL);
762	EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_WILD_SRCH_LIMIT,
763	    sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] +
764	    FILTER_CTL_SRCH_FUDGE_WILD);
765
766	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC].sft_size) {
767		EFX_SET_OWORD_FIELD(oword,
768		    FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT,
769		    sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] +
770		    FILTER_CTL_SRCH_FUDGE_FULL);
771		EFX_SET_OWORD_FIELD(oword,
772		    FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT,
773		    sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] +
774		    FILTER_CTL_SRCH_FUDGE_WILD);
775	}
776
777	EFX_BAR_WRITEO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
778}
779
780static			void
781siena_filter_push_tx_limits(
782	__in		efx_nic_t *enp)
783{
784	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
785	efx_oword_t oword;
786
787	EFX_BAR_READO(enp, FR_AZ_TX_CFG_REG, &oword);
788
789	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP].sft_size != 0) {
790		EFX_SET_OWORD_FIELD(oword,
791		    FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE,
792		    sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] +
793		    FILTER_CTL_SRCH_FUDGE_FULL);
794		EFX_SET_OWORD_FIELD(oword,
795		    FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE,
796		    sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] +
797		    FILTER_CTL_SRCH_FUDGE_WILD);
798		EFX_SET_OWORD_FIELD(oword,
799		    FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE,
800		    sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] +
801		    FILTER_CTL_SRCH_FUDGE_FULL);
802		EFX_SET_OWORD_FIELD(oword,
803		    FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE,
804		    sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] +
805		    FILTER_CTL_SRCH_FUDGE_WILD);
806	}
807
808	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC].sft_size != 0) {
809		EFX_SET_OWORD_FIELD(
810			oword, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE,
811			sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] +
812			FILTER_CTL_SRCH_FUDGE_FULL);
813		EFX_SET_OWORD_FIELD(
814			oword, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE,
815			sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] +
816			FILTER_CTL_SRCH_FUDGE_WILD);
817	}
818
819	EFX_BAR_WRITEO(enp, FR_AZ_TX_CFG_REG, &oword);
820}
821
822/* Build a filter entry and return its n-tuple key. */
823static	__checkReturn	uint32_t
824siena_filter_build(
825	__out		efx_oword_t *filter,
826	__in		siena_filter_spec_t *spec)
827{
828	uint32_t dword3;
829	uint32_t key;
830	uint8_t  type  = spec->sfs_type;
831	uint32_t flags = spec->sfs_flags;
832
833	switch (siena_filter_tbl_id(type)) {
834	case EFX_SIENA_FILTER_TBL_RX_IP: {
835		boolean_t is_udp = (type == EFX_SIENA_FILTER_RX_UDP_FULL ||
836		    type == EFX_SIENA_FILTER_RX_UDP_WILD);
837		EFX_POPULATE_OWORD_7(*filter,
838		    FRF_BZ_RSS_EN,
839		    (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
840		    FRF_BZ_SCATTER_EN,
841		    (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
842		    FRF_AZ_TCP_UDP, is_udp,
843		    FRF_AZ_RXQ_ID, spec->sfs_dmaq_id,
844		    EFX_DWORD_2, spec->sfs_dword[2],
845		    EFX_DWORD_1, spec->sfs_dword[1],
846		    EFX_DWORD_0, spec->sfs_dword[0]);
847		dword3 = is_udp;
848		break;
849	}
850
851	case EFX_SIENA_FILTER_TBL_RX_MAC: {
852		boolean_t is_wild = (type == EFX_SIENA_FILTER_RX_MAC_WILD);
853		EFX_POPULATE_OWORD_7(*filter,
854		    FRF_CZ_RMFT_RSS_EN,
855		    (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
856		    FRF_CZ_RMFT_SCATTER_EN,
857		    (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
858		    FRF_CZ_RMFT_RXQ_ID, spec->sfs_dmaq_id,
859		    FRF_CZ_RMFT_WILDCARD_MATCH, is_wild,
860		    FRF_CZ_RMFT_DEST_MAC_DW1, spec->sfs_dword[2],
861		    FRF_CZ_RMFT_DEST_MAC_DW0, spec->sfs_dword[1],
862		    FRF_CZ_RMFT_VLAN_ID, spec->sfs_dword[0]);
863		dword3 = is_wild;
864		break;
865	}
866
867	case EFX_SIENA_FILTER_TBL_TX_IP: {
868		boolean_t is_udp = (type == EFX_SIENA_FILTER_TX_UDP_FULL ||
869		    type == EFX_SIENA_FILTER_TX_UDP_WILD);
870		EFX_POPULATE_OWORD_5(*filter,
871		    FRF_CZ_TIFT_TCP_UDP, is_udp,
872		    FRF_CZ_TIFT_TXQ_ID, spec->sfs_dmaq_id,
873		    EFX_DWORD_2, spec->sfs_dword[2],
874		    EFX_DWORD_1, spec->sfs_dword[1],
875		    EFX_DWORD_0, spec->sfs_dword[0]);
876		dword3 = is_udp | spec->sfs_dmaq_id << 1;
877		break;
878	}
879
880	case EFX_SIENA_FILTER_TBL_TX_MAC: {
881		boolean_t is_wild = (type == EFX_SIENA_FILTER_TX_MAC_WILD);
882		EFX_POPULATE_OWORD_5(*filter,
883		    FRF_CZ_TMFT_TXQ_ID, spec->sfs_dmaq_id,
884		    FRF_CZ_TMFT_WILDCARD_MATCH, is_wild,
885		    FRF_CZ_TMFT_SRC_MAC_DW1, spec->sfs_dword[2],
886		    FRF_CZ_TMFT_SRC_MAC_DW0, spec->sfs_dword[1],
887		    FRF_CZ_TMFT_VLAN_ID, spec->sfs_dword[0]);
888		dword3 = is_wild | spec->sfs_dmaq_id << 1;
889		break;
890	}
891
892	default:
893		EFSYS_ASSERT(B_FALSE);
894		return (0);
895	}
896
897	key =
898	    spec->sfs_dword[0] ^
899	    spec->sfs_dword[1] ^
900	    spec->sfs_dword[2] ^
901	    dword3;
902
903	return (key);
904}
905
906static	__checkReturn		efx_rc_t
907siena_filter_push_entry(
908	__inout			efx_nic_t *enp,
909	__in			siena_filter_type_t type,
910	__in			int index,
911	__in			efx_oword_t *eop)
912{
913	efx_rc_t rc;
914
915	switch (type) {
916	case EFX_SIENA_FILTER_RX_TCP_FULL:
917	case EFX_SIENA_FILTER_RX_TCP_WILD:
918	case EFX_SIENA_FILTER_RX_UDP_FULL:
919	case EFX_SIENA_FILTER_RX_UDP_WILD:
920		EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_FILTER_TBL0, index,
921		    eop, B_TRUE);
922		break;
923
924	case EFX_SIENA_FILTER_RX_MAC_FULL:
925	case EFX_SIENA_FILTER_RX_MAC_WILD:
926		EFX_BAR_TBL_WRITEO(enp, FR_CZ_RX_MAC_FILTER_TBL0, index,
927		    eop, B_TRUE);
928		break;
929
930	case EFX_SIENA_FILTER_TX_TCP_FULL:
931	case EFX_SIENA_FILTER_TX_TCP_WILD:
932	case EFX_SIENA_FILTER_TX_UDP_FULL:
933	case EFX_SIENA_FILTER_TX_UDP_WILD:
934		EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_FILTER_TBL0, index,
935		    eop, B_TRUE);
936		break;
937
938	case EFX_SIENA_FILTER_TX_MAC_FULL:
939	case EFX_SIENA_FILTER_TX_MAC_WILD:
940		EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_MAC_FILTER_TBL0, index,
941		    eop, B_TRUE);
942		break;
943
944	default:
945		EFSYS_ASSERT(B_FALSE);
946		rc = ENOTSUP;
947		goto fail1;
948	}
949	return (0);
950
951fail1:
952	return (rc);
953}
954
955
956static	__checkReturn	boolean_t
957siena_filter_equal(
958	__in		const siena_filter_spec_t *left,
959	__in		const siena_filter_spec_t *right)
960{
961	siena_filter_tbl_id_t tbl_id;
962
963	tbl_id = siena_filter_tbl_id(left->sfs_type);
964
965
966	if (left->sfs_type != right->sfs_type)
967		return (B_FALSE);
968
969	if (memcmp(left->sfs_dword, right->sfs_dword,
970		sizeof (left->sfs_dword)))
971		return (B_FALSE);
972
973	if ((tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
974		tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC) &&
975	    left->sfs_dmaq_id != right->sfs_dmaq_id)
976		return (B_FALSE);
977
978	return (B_TRUE);
979}
980
981static	__checkReturn	efx_rc_t
982siena_filter_search(
983	__in		siena_filter_tbl_t *sftp,
984	__in		siena_filter_spec_t *spec,
985	__in		uint32_t key,
986	__in		boolean_t for_insert,
987	__out		int *filter_index,
988	__out		unsigned int *depth_required)
989{
990	unsigned int hash, incr, filter_idx, depth;
991
992	hash = siena_filter_tbl_hash(key);
993	incr = siena_filter_tbl_increment(key);
994
995	filter_idx = hash & (sftp->sft_size - 1);
996	depth = 1;
997
998	for (;;) {
999		/*
1000		 * Return success if entry is used and matches this spec
1001		 * or entry is unused and we are trying to insert.
1002		 */
1003		if (siena_filter_test_used(sftp, filter_idx) ?
1004		    siena_filter_equal(spec,
1005		    &sftp->sft_spec[filter_idx]) :
1006		    for_insert) {
1007			*filter_index = filter_idx;
1008			*depth_required = depth;
1009			return (0);
1010		}
1011
1012		/* Return failure if we reached the maximum search depth */
1013		if (depth == FILTER_CTL_SRCH_MAX)
1014			return (for_insert ? EBUSY : ENOENT);
1015
1016		filter_idx = (filter_idx + incr) & (sftp->sft_size - 1);
1017		++depth;
1018	}
1019}
1020
1021static			void
1022siena_filter_clear_entry(
1023	__in		efx_nic_t *enp,
1024	__in		siena_filter_tbl_t *sftp,
1025	__in		int index)
1026{
1027	efx_oword_t filter;
1028
1029	if (siena_filter_test_used(sftp, index)) {
1030		siena_filter_clear_used(sftp, index);
1031
1032		EFX_ZERO_OWORD(filter);
1033		siena_filter_push_entry(enp,
1034		    sftp->sft_spec[index].sfs_type,
1035		    index, &filter);
1036
1037		memset(&sftp->sft_spec[index],
1038		    0, sizeof (sftp->sft_spec[0]));
1039	}
1040}
1041
1042			void
1043siena_filter_tbl_clear(
1044	__in		efx_nic_t *enp,
1045	__in		siena_filter_tbl_id_t tbl_id)
1046{
1047	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1048	siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
1049	int index;
1050	efsys_lock_state_t state;
1051
1052	EFSYS_LOCK(enp->en_eslp, state);
1053
1054	for (index = 0; index < sftp->sft_size; ++index) {
1055		siena_filter_clear_entry(enp, sftp, index);
1056	}
1057
1058	if (sftp->sft_used == 0)
1059		siena_filter_reset_search_depth(sfp, tbl_id);
1060
1061	EFSYS_UNLOCK(enp->en_eslp, state);
1062}
1063
1064static	__checkReturn	efx_rc_t
1065siena_filter_init(
1066	__in		efx_nic_t *enp)
1067{
1068	siena_filter_t *sfp;
1069	siena_filter_tbl_t *sftp;
1070	int tbl_id;
1071	efx_rc_t rc;
1072
1073	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (siena_filter_t), sfp);
1074
1075	if (!sfp) {
1076		rc = ENOMEM;
1077		goto fail1;
1078	}
1079
1080	enp->en_filter.ef_siena_filter = sfp;
1081
1082	switch (enp->en_family) {
1083	case EFX_FAMILY_SIENA:
1084		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_IP];
1085		sftp->sft_size = FR_AZ_RX_FILTER_TBL0_ROWS;
1086
1087		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC];
1088		sftp->sft_size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS;
1089
1090		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP];
1091		sftp->sft_size = FR_CZ_TX_FILTER_TBL0_ROWS;
1092
1093		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC];
1094		sftp->sft_size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS;
1095		break;
1096
1097	default:
1098		rc = ENOTSUP;
1099		goto fail2;
1100	}
1101
1102	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1103		unsigned int bitmap_size;
1104
1105		sftp = &sfp->sf_tbl[tbl_id];
1106		if (sftp->sft_size == 0)
1107			continue;
1108
1109		EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
1110		    sizeof (uint32_t));
1111		bitmap_size =
1112		    (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
1113
1114		EFSYS_KMEM_ALLOC(enp->en_esip, bitmap_size, sftp->sft_bitmap);
1115		if (!sftp->sft_bitmap) {
1116			rc = ENOMEM;
1117			goto fail3;
1118		}
1119
1120		EFSYS_KMEM_ALLOC(enp->en_esip,
1121		    sftp->sft_size * sizeof (*sftp->sft_spec),
1122		    sftp->sft_spec);
1123		if (!sftp->sft_spec) {
1124			rc = ENOMEM;
1125			goto fail4;
1126		}
1127		memset(sftp->sft_spec, 0,
1128		    sftp->sft_size * sizeof (*sftp->sft_spec));
1129	}
1130
1131	return (0);
1132
1133fail4:
1134	EFSYS_PROBE(fail4);
1135
1136fail3:
1137	EFSYS_PROBE(fail3);
1138
1139fail2:
1140	EFSYS_PROBE(fail2);
1141	siena_filter_fini(enp);
1142
1143fail1:
1144	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1145	return (rc);
1146}
1147
1148static			void
1149siena_filter_fini(
1150	__in		efx_nic_t *enp)
1151{
1152	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1153	siena_filter_tbl_id_t tbl_id;
1154
1155	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1156	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
1157
1158	if (sfp == NULL)
1159		return;
1160
1161	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1162		siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
1163		unsigned int bitmap_size;
1164
1165		EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
1166		    sizeof (uint32_t));
1167		bitmap_size =
1168		    (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
1169
1170		if (sftp->sft_bitmap != NULL) {
1171			EFSYS_KMEM_FREE(enp->en_esip, bitmap_size,
1172			    sftp->sft_bitmap);
1173			sftp->sft_bitmap = NULL;
1174		}
1175
1176		if (sftp->sft_spec != NULL) {
1177			EFSYS_KMEM_FREE(enp->en_esip, sftp->sft_size *
1178			    sizeof (*sftp->sft_spec), sftp->sft_spec);
1179			sftp->sft_spec = NULL;
1180		}
1181	}
1182
1183	EFSYS_KMEM_FREE(enp->en_esip, sizeof (siena_filter_t),
1184	    enp->en_filter.ef_siena_filter);
1185}
1186
1187/* Restore filter state after a reset */
1188static	__checkReturn	efx_rc_t
1189siena_filter_restore(
1190	__in		efx_nic_t *enp)
1191{
1192	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1193	siena_filter_tbl_id_t tbl_id;
1194	siena_filter_tbl_t *sftp;
1195	siena_filter_spec_t *spec;
1196	efx_oword_t filter;
1197	int filter_idx;
1198	efsys_lock_state_t state;
1199	uint32_t key;
1200	efx_rc_t rc;
1201
1202	EFSYS_LOCK(enp->en_eslp, state);
1203
1204	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1205		sftp = &sfp->sf_tbl[tbl_id];
1206		for (filter_idx = 0;
1207			filter_idx < sftp->sft_size;
1208			filter_idx++) {
1209			if (!siena_filter_test_used(sftp, filter_idx))
1210				continue;
1211
1212			spec = &sftp->sft_spec[filter_idx];
1213			if ((key = siena_filter_build(&filter, spec)) == 0) {
1214				rc = EINVAL;
1215				goto fail1;
1216			}
1217			if ((rc = siena_filter_push_entry(enp,
1218				    spec->sfs_type, filter_idx, &filter)) != 0)
1219				goto fail2;
1220		}
1221	}
1222
1223	siena_filter_push_rx_limits(enp);
1224	siena_filter_push_tx_limits(enp);
1225
1226	EFSYS_UNLOCK(enp->en_eslp, state);
1227
1228	return (0);
1229
1230fail2:
1231	EFSYS_PROBE(fail2);
1232
1233fail1:
1234	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1235
1236	EFSYS_UNLOCK(enp->en_eslp, state);
1237
1238	return (rc);
1239}
1240
1241static	 __checkReturn	efx_rc_t
1242siena_filter_add(
1243	__in		efx_nic_t *enp,
1244	__inout		efx_filter_spec_t *spec,
1245	__in		boolean_t may_replace)
1246{
1247	efx_rc_t rc;
1248	siena_filter_spec_t sf_spec;
1249	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1250	siena_filter_tbl_id_t tbl_id;
1251	siena_filter_tbl_t *sftp;
1252	siena_filter_spec_t *saved_sf_spec;
1253	efx_oword_t filter;
1254	int filter_idx;
1255	unsigned int depth;
1256	efsys_lock_state_t state;
1257	uint32_t key;
1258
1259
1260	EFSYS_ASSERT3P(spec, !=, NULL);
1261
1262	if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
1263		goto fail1;
1264
1265	tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
1266	sftp = &sfp->sf_tbl[tbl_id];
1267
1268	if (sftp->sft_size == 0) {
1269		rc = EINVAL;
1270		goto fail2;
1271	}
1272
1273	key = siena_filter_build(&filter, &sf_spec);
1274
1275	EFSYS_LOCK(enp->en_eslp, state);
1276
1277	rc = siena_filter_search(sftp, &sf_spec, key, B_TRUE,
1278	    &filter_idx, &depth);
1279	if (rc != 0)
1280		goto fail3;
1281
1282	EFSYS_ASSERT3U(filter_idx, <, sftp->sft_size);
1283	saved_sf_spec = &sftp->sft_spec[filter_idx];
1284
1285	if (siena_filter_test_used(sftp, filter_idx)) {
1286		if (may_replace == B_FALSE) {
1287			rc = EEXIST;
1288			goto fail4;
1289		}
1290	}
1291	siena_filter_set_used(sftp, filter_idx);
1292	*saved_sf_spec = sf_spec;
1293
1294	if (sfp->sf_depth[sf_spec.sfs_type] < depth) {
1295		sfp->sf_depth[sf_spec.sfs_type] = depth;
1296		if (tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
1297		    tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC)
1298			siena_filter_push_tx_limits(enp);
1299		else
1300			siena_filter_push_rx_limits(enp);
1301	}
1302
1303	siena_filter_push_entry(enp, sf_spec.sfs_type,
1304	    filter_idx, &filter);
1305
1306	EFSYS_UNLOCK(enp->en_eslp, state);
1307	return (0);
1308
1309fail4:
1310	EFSYS_PROBE(fail4);
1311
1312fail3:
1313	EFSYS_UNLOCK(enp->en_eslp, state);
1314	EFSYS_PROBE(fail3);
1315
1316fail2:
1317	EFSYS_PROBE(fail2);
1318
1319fail1:
1320	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1321	return (rc);
1322}
1323
1324static	 __checkReturn	efx_rc_t
1325siena_filter_delete(
1326	__in		efx_nic_t *enp,
1327	__inout		efx_filter_spec_t *spec)
1328{
1329	efx_rc_t rc;
1330	siena_filter_spec_t sf_spec;
1331	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1332	siena_filter_tbl_id_t tbl_id;
1333	siena_filter_tbl_t *sftp;
1334	efx_oword_t filter;
1335	int filter_idx;
1336	unsigned int depth;
1337	efsys_lock_state_t state;
1338	uint32_t key;
1339
1340	EFSYS_ASSERT3P(spec, !=, NULL);
1341
1342	if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
1343		goto fail1;
1344
1345	tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
1346	sftp = &sfp->sf_tbl[tbl_id];
1347
1348	key = siena_filter_build(&filter, &sf_spec);
1349
1350	EFSYS_LOCK(enp->en_eslp, state);
1351
1352	rc = siena_filter_search(sftp, &sf_spec, key, B_FALSE,
1353	    &filter_idx, &depth);
1354	if (rc != 0)
1355		goto fail2;
1356
1357	siena_filter_clear_entry(enp, sftp, filter_idx);
1358	if (sftp->sft_used == 0)
1359		siena_filter_reset_search_depth(sfp, tbl_id);
1360
1361	EFSYS_UNLOCK(enp->en_eslp, state);
1362	return (0);
1363
1364fail2:
1365	EFSYS_UNLOCK(enp->en_eslp, state);
1366	EFSYS_PROBE(fail2);
1367
1368fail1:
1369	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1370	return (rc);
1371}
1372
1373#define	SIENA_MAX_SUPPORTED_MATCHES 4
1374
1375static	__checkReturn	efx_rc_t
1376siena_filter_supported_filters(
1377	__in				efx_nic_t *enp,
1378	__out_ecount(buffer_length)	uint32_t *buffer,
1379	__in				size_t buffer_length,
1380	__out				size_t *list_lengthp)
1381{
1382	uint32_t index = 0;
1383	uint32_t rx_matches[SIENA_MAX_SUPPORTED_MATCHES];
1384	size_t list_length;
1385	efx_rc_t rc;
1386
1387	rx_matches[index++] =
1388	    EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
1389	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
1390	    EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
1391
1392	rx_matches[index++] =
1393	    EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
1394	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
1395
1396	if (enp->en_features & EFX_FEATURE_MAC_HEADER_FILTERS) {
1397		rx_matches[index++] =
1398		    EFX_FILTER_MATCH_OUTER_VID | EFX_FILTER_MATCH_LOC_MAC;
1399
1400		rx_matches[index++] = EFX_FILTER_MATCH_LOC_MAC;
1401	}
1402
1403	EFSYS_ASSERT3U(index, <=, SIENA_MAX_SUPPORTED_MATCHES);
1404	list_length = index;
1405
1406	*list_lengthp = list_length;
1407
1408	if (buffer_length < list_length) {
1409		rc = ENOSPC;
1410		goto fail1;
1411	}
1412
1413	memcpy(buffer, rx_matches, list_length * sizeof (rx_matches[0]));
1414
1415	return (0);
1416
1417fail1:
1418	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1419
1420	return (rc);
1421}
1422
1423#undef MAX_SUPPORTED
1424
1425#endif /* EFSYS_OPT_SIENA */
1426
1427#endif /* EFSYS_OPT_FILTER */
1428