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