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