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