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