efx_filter.c revision 311076
1/*-
2 * Copyright (c) 2007-2016 Solarflare Communications Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 *    this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 *    this list of conditions and the following disclaimer in the documentation
12 *    and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * The views and conclusions contained in the software and documentation are
27 * those of the authors and should not be interpreted as representing official
28 * policies, either expressed or implied, of the FreeBSD Project.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: stable/10/sys/dev/sfxge/common/efx_filter.c 311076 2017-01-02 09:28:07Z arybchik $");
33
34#include "efx.h"
35#include "efx_impl.h"
36
37
38#if EFSYS_OPT_FILTER
39
40#if EFSYS_OPT_SIENA
41
42static	__checkReturn	efx_rc_t
43siena_filter_init(
44	__in		efx_nic_t *enp);
45
46static			void
47siena_filter_fini(
48	__in		efx_nic_t *enp);
49
50static	__checkReturn	efx_rc_t
51siena_filter_restore(
52	__in		efx_nic_t *enp);
53
54static	__checkReturn	efx_rc_t
55siena_filter_add(
56	__in		efx_nic_t *enp,
57	__inout		efx_filter_spec_t *spec,
58	__in		boolean_t may_replace);
59
60static	__checkReturn	efx_rc_t
61siena_filter_delete(
62	__in		efx_nic_t *enp,
63	__inout		efx_filter_spec_t *spec);
64
65static	__checkReturn	efx_rc_t
66siena_filter_supported_filters(
67	__in		efx_nic_t *enp,
68	__out		uint32_t *list,
69	__out		size_t *length);
70
71#endif /* EFSYS_OPT_SIENA */
72
73#if EFSYS_OPT_SIENA
74static const efx_filter_ops_t	__efx_filter_siena_ops = {
75	siena_filter_init,		/* efo_init */
76	siena_filter_fini,		/* efo_fini */
77	siena_filter_restore,		/* efo_restore */
78	siena_filter_add,		/* efo_add */
79	siena_filter_delete,		/* efo_delete */
80	siena_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 const 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	const 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	const 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	const 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_siena_ops;
166		break;
167#endif /* EFSYS_OPT_SIENA */
168
169#if EFSYS_OPT_HUNTINGTON
170	case EFX_FAMILY_HUNTINGTON:
171		efop = &__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_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
431siena_filter_spec_from_gen_spec(
432	__out		siena_filter_spec_t *sf_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	sf_spec->sfs_flags = gen_spec->efs_flags;
451	sf_spec->sfs_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				sf_spec->sfs_type = (is_full ?
477				    EFX_SIENA_FILTER_TX_TCP_FULL :
478				    EFX_SIENA_FILTER_TX_TCP_WILD);
479			} else {
480				sf_spec->sfs_type = (is_full ?
481				    EFX_SIENA_FILTER_RX_TCP_FULL :
482				    EFX_SIENA_FILTER_RX_TCP_WILD);
483			}
484			break;
485		case EFX_IPPROTO_UDP:
486			if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
487				sf_spec->sfs_type = (is_full ?
488				    EFX_SIENA_FILTER_TX_UDP_FULL :
489				    EFX_SIENA_FILTER_TX_UDP_WILD);
490			} else {
491				sf_spec->sfs_type = (is_full ?
492				    EFX_SIENA_FILTER_RX_UDP_FULL :
493				    EFX_SIENA_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 (sf_spec->sfs_type ==
517			    EFX_SIENA_FILTER_TX_UDP_WILD) {
518				port1 = rport;
519				port2 = gen_spec->efs_loc_port;
520			} else {
521				port1 = gen_spec->efs_loc_port;
522				port2 = rport;
523			}
524		} else {
525			if (sf_spec->sfs_type ==
526			    EFX_SIENA_FILTER_RX_UDP_WILD) {
527				port1 = gen_spec->efs_loc_port;
528				port2 = rport;
529			} else {
530				port1 = rport;
531				port2 = gen_spec->efs_loc_port;
532			}
533		}
534		sf_spec->sfs_dword[0] = (host1 << 16) | port1;
535		sf_spec->sfs_dword[1] = (port2 << 16) | (host1 >> 16);
536		sf_spec->sfs_dword[2] = host2;
537		break;
538	}
539
540	case EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_OUTER_VID:
541		is_full = B_TRUE;
542		/* Fall through */
543	case EFX_FILTER_MATCH_LOC_MAC:
544		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
545			sf_spec->sfs_type = (is_full ?
546			    EFX_SIENA_FILTER_TX_MAC_FULL :
547			    EFX_SIENA_FILTER_TX_MAC_WILD);
548		} else {
549			sf_spec->sfs_type = (is_full ?
550			    EFX_SIENA_FILTER_RX_MAC_FULL :
551			    EFX_SIENA_FILTER_RX_MAC_WILD);
552		}
553		sf_spec->sfs_dword[0] = is_full ? gen_spec->efs_outer_vid : 0;
554		sf_spec->sfs_dword[1] =
555		    gen_spec->efs_loc_mac[2] << 24 |
556		    gen_spec->efs_loc_mac[3] << 16 |
557		    gen_spec->efs_loc_mac[4] <<  8 |
558		    gen_spec->efs_loc_mac[5];
559		sf_spec->sfs_dword[2] =
560		    gen_spec->efs_loc_mac[0] << 8 |
561		    gen_spec->efs_loc_mac[1];
562		break;
563
564	default:
565		EFSYS_ASSERT(B_FALSE);
566		rc = ENOTSUP;
567		goto fail5;
568	}
569
570	return (0);
571
572fail5:
573	EFSYS_PROBE(fail5);
574fail4:
575	EFSYS_PROBE(fail4);
576fail3:
577	EFSYS_PROBE(fail3);
578fail2:
579	EFSYS_PROBE(fail2);
580fail1:
581	EFSYS_PROBE1(fail1, efx_rc_t, rc);
582
583	return (rc);
584}
585
586/*
587 * The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit
588 * key derived from the n-tuple.
589 */
590static			uint16_t
591siena_filter_tbl_hash(
592	__in		uint32_t key)
593{
594	uint16_t tmp;
595
596	/* First 16 rounds */
597	tmp = 0x1fff ^ (uint16_t)(key >> 16);
598	tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
599	tmp = tmp ^ tmp >> 9;
600
601	/* Last 16 rounds */
602	tmp = tmp ^ tmp << 13 ^ (uint16_t)(key & 0xffff);
603	tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
604	tmp = tmp ^ tmp >> 9;
605
606	return (tmp);
607}
608
609/*
610 * To allow for hash collisions, filter search continues at these
611 * increments from the first possible entry selected by the hash.
612 */
613static			uint16_t
614siena_filter_tbl_increment(
615	__in		uint32_t key)
616{
617	return ((uint16_t)(key * 2 - 1));
618}
619
620static	__checkReturn	boolean_t
621siena_filter_test_used(
622	__in		siena_filter_tbl_t *sftp,
623	__in		unsigned int index)
624{
625	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
626	return ((sftp->sft_bitmap[index / 32] & (1 << (index % 32))) != 0);
627}
628
629static			void
630siena_filter_set_used(
631	__in		siena_filter_tbl_t *sftp,
632	__in		unsigned int index)
633{
634	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
635	sftp->sft_bitmap[index / 32] |= (1 << (index % 32));
636	++sftp->sft_used;
637}
638
639static			void
640siena_filter_clear_used(
641	__in		siena_filter_tbl_t *sftp,
642	__in		unsigned int index)
643{
644	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
645	sftp->sft_bitmap[index / 32] &= ~(1 << (index % 32));
646
647	--sftp->sft_used;
648	EFSYS_ASSERT3U(sftp->sft_used, >=, 0);
649}
650
651
652static			siena_filter_tbl_id_t
653siena_filter_tbl_id(
654	__in		siena_filter_type_t type)
655{
656	siena_filter_tbl_id_t tbl_id;
657
658	switch (type) {
659	case EFX_SIENA_FILTER_RX_TCP_FULL:
660	case EFX_SIENA_FILTER_RX_TCP_WILD:
661	case EFX_SIENA_FILTER_RX_UDP_FULL:
662	case EFX_SIENA_FILTER_RX_UDP_WILD:
663		tbl_id = EFX_SIENA_FILTER_TBL_RX_IP;
664		break;
665
666	case EFX_SIENA_FILTER_RX_MAC_FULL:
667	case EFX_SIENA_FILTER_RX_MAC_WILD:
668		tbl_id = EFX_SIENA_FILTER_TBL_RX_MAC;
669		break;
670
671	case EFX_SIENA_FILTER_TX_TCP_FULL:
672	case EFX_SIENA_FILTER_TX_TCP_WILD:
673	case EFX_SIENA_FILTER_TX_UDP_FULL:
674	case EFX_SIENA_FILTER_TX_UDP_WILD:
675		tbl_id = EFX_SIENA_FILTER_TBL_TX_IP;
676		break;
677
678	case EFX_SIENA_FILTER_TX_MAC_FULL:
679	case EFX_SIENA_FILTER_TX_MAC_WILD:
680		tbl_id = EFX_SIENA_FILTER_TBL_TX_MAC;
681		break;
682
683	default:
684		EFSYS_ASSERT(B_FALSE);
685		tbl_id = EFX_SIENA_FILTER_NTBLS;
686		break;
687	}
688	return (tbl_id);
689}
690
691static			void
692siena_filter_reset_search_depth(
693	__inout		siena_filter_t *sfp,
694	__in		siena_filter_tbl_id_t tbl_id)
695{
696	switch (tbl_id) {
697	case EFX_SIENA_FILTER_TBL_RX_IP:
698		sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] = 0;
699		sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] = 0;
700		sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] = 0;
701		sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] = 0;
702		break;
703
704	case EFX_SIENA_FILTER_TBL_RX_MAC:
705		sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] = 0;
706		sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] = 0;
707		break;
708
709	case EFX_SIENA_FILTER_TBL_TX_IP:
710		sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] = 0;
711		sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] = 0;
712		sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] = 0;
713		sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] = 0;
714		break;
715
716	case EFX_SIENA_FILTER_TBL_TX_MAC:
717		sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] = 0;
718		sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] = 0;
719		break;
720
721	default:
722		EFSYS_ASSERT(B_FALSE);
723		break;
724	}
725}
726
727static			void
728siena_filter_push_rx_limits(
729	__in		efx_nic_t *enp)
730{
731	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
732	efx_oword_t oword;
733
734	EFX_BAR_READO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
735
736	EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_FULL_SRCH_LIMIT,
737	    sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] +
738	    FILTER_CTL_SRCH_FUDGE_FULL);
739	EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_WILD_SRCH_LIMIT,
740	    sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] +
741	    FILTER_CTL_SRCH_FUDGE_WILD);
742	EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_FULL_SRCH_LIMIT,
743	    sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] +
744	    FILTER_CTL_SRCH_FUDGE_FULL);
745	EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_WILD_SRCH_LIMIT,
746	    sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] +
747	    FILTER_CTL_SRCH_FUDGE_WILD);
748
749	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC].sft_size) {
750		EFX_SET_OWORD_FIELD(oword,
751		    FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT,
752		    sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] +
753		    FILTER_CTL_SRCH_FUDGE_FULL);
754		EFX_SET_OWORD_FIELD(oword,
755		    FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT,
756		    sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] +
757		    FILTER_CTL_SRCH_FUDGE_WILD);
758	}
759
760	EFX_BAR_WRITEO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
761}
762
763static			void
764siena_filter_push_tx_limits(
765	__in		efx_nic_t *enp)
766{
767	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
768	efx_oword_t oword;
769
770	EFX_BAR_READO(enp, FR_AZ_TX_CFG_REG, &oword);
771
772	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP].sft_size != 0) {
773		EFX_SET_OWORD_FIELD(oword,
774		    FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE,
775		    sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] +
776		    FILTER_CTL_SRCH_FUDGE_FULL);
777		EFX_SET_OWORD_FIELD(oword,
778		    FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE,
779		    sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] +
780		    FILTER_CTL_SRCH_FUDGE_WILD);
781		EFX_SET_OWORD_FIELD(oword,
782		    FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE,
783		    sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] +
784		    FILTER_CTL_SRCH_FUDGE_FULL);
785		EFX_SET_OWORD_FIELD(oword,
786		    FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE,
787		    sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] +
788		    FILTER_CTL_SRCH_FUDGE_WILD);
789	}
790
791	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC].sft_size != 0) {
792		EFX_SET_OWORD_FIELD(
793			oword, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE,
794			sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] +
795			FILTER_CTL_SRCH_FUDGE_FULL);
796		EFX_SET_OWORD_FIELD(
797			oword, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE,
798			sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] +
799			FILTER_CTL_SRCH_FUDGE_WILD);
800	}
801
802	EFX_BAR_WRITEO(enp, FR_AZ_TX_CFG_REG, &oword);
803}
804
805/* Build a filter entry and return its n-tuple key. */
806static	__checkReturn	uint32_t
807siena_filter_build(
808	__out		efx_oword_t *filter,
809	__in		siena_filter_spec_t *spec)
810{
811	uint32_t dword3;
812	uint32_t key;
813	uint8_t  type  = spec->sfs_type;
814	uint32_t flags = spec->sfs_flags;
815
816	switch (siena_filter_tbl_id(type)) {
817	case EFX_SIENA_FILTER_TBL_RX_IP: {
818		boolean_t is_udp = (type == EFX_SIENA_FILTER_RX_UDP_FULL ||
819		    type == EFX_SIENA_FILTER_RX_UDP_WILD);
820		EFX_POPULATE_OWORD_7(*filter,
821		    FRF_BZ_RSS_EN,
822		    (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
823		    FRF_BZ_SCATTER_EN,
824		    (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
825		    FRF_AZ_TCP_UDP, is_udp,
826		    FRF_AZ_RXQ_ID, spec->sfs_dmaq_id,
827		    EFX_DWORD_2, spec->sfs_dword[2],
828		    EFX_DWORD_1, spec->sfs_dword[1],
829		    EFX_DWORD_0, spec->sfs_dword[0]);
830		dword3 = is_udp;
831		break;
832	}
833
834	case EFX_SIENA_FILTER_TBL_RX_MAC: {
835		boolean_t is_wild = (type == EFX_SIENA_FILTER_RX_MAC_WILD);
836		EFX_POPULATE_OWORD_7(*filter,
837		    FRF_CZ_RMFT_RSS_EN,
838		    (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
839		    FRF_CZ_RMFT_SCATTER_EN,
840		    (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
841		    FRF_CZ_RMFT_RXQ_ID, spec->sfs_dmaq_id,
842		    FRF_CZ_RMFT_WILDCARD_MATCH, is_wild,
843		    FRF_CZ_RMFT_DEST_MAC_DW1, spec->sfs_dword[2],
844		    FRF_CZ_RMFT_DEST_MAC_DW0, spec->sfs_dword[1],
845		    FRF_CZ_RMFT_VLAN_ID, spec->sfs_dword[0]);
846		dword3 = is_wild;
847		break;
848	}
849
850	case EFX_SIENA_FILTER_TBL_TX_IP: {
851		boolean_t is_udp = (type == EFX_SIENA_FILTER_TX_UDP_FULL ||
852		    type == EFX_SIENA_FILTER_TX_UDP_WILD);
853		EFX_POPULATE_OWORD_5(*filter,
854		    FRF_CZ_TIFT_TCP_UDP, is_udp,
855		    FRF_CZ_TIFT_TXQ_ID, spec->sfs_dmaq_id,
856		    EFX_DWORD_2, spec->sfs_dword[2],
857		    EFX_DWORD_1, spec->sfs_dword[1],
858		    EFX_DWORD_0, spec->sfs_dword[0]);
859		dword3 = is_udp | spec->sfs_dmaq_id << 1;
860		break;
861	}
862
863	case EFX_SIENA_FILTER_TBL_TX_MAC: {
864		boolean_t is_wild = (type == EFX_SIENA_FILTER_TX_MAC_WILD);
865		EFX_POPULATE_OWORD_5(*filter,
866		    FRF_CZ_TMFT_TXQ_ID, spec->sfs_dmaq_id,
867		    FRF_CZ_TMFT_WILDCARD_MATCH, is_wild,
868		    FRF_CZ_TMFT_SRC_MAC_DW1, spec->sfs_dword[2],
869		    FRF_CZ_TMFT_SRC_MAC_DW0, spec->sfs_dword[1],
870		    FRF_CZ_TMFT_VLAN_ID, spec->sfs_dword[0]);
871		dword3 = is_wild | spec->sfs_dmaq_id << 1;
872		break;
873	}
874
875	default:
876		EFSYS_ASSERT(B_FALSE);
877		return (0);
878	}
879
880	key =
881	    spec->sfs_dword[0] ^
882	    spec->sfs_dword[1] ^
883	    spec->sfs_dword[2] ^
884	    dword3;
885
886	return (key);
887}
888
889static	__checkReturn		efx_rc_t
890siena_filter_push_entry(
891	__inout			efx_nic_t *enp,
892	__in			siena_filter_type_t type,
893	__in			int index,
894	__in			efx_oword_t *eop)
895{
896	efx_rc_t rc;
897
898	switch (type) {
899	case EFX_SIENA_FILTER_RX_TCP_FULL:
900	case EFX_SIENA_FILTER_RX_TCP_WILD:
901	case EFX_SIENA_FILTER_RX_UDP_FULL:
902	case EFX_SIENA_FILTER_RX_UDP_WILD:
903		EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_FILTER_TBL0, index,
904		    eop, B_TRUE);
905		break;
906
907	case EFX_SIENA_FILTER_RX_MAC_FULL:
908	case EFX_SIENA_FILTER_RX_MAC_WILD:
909		EFX_BAR_TBL_WRITEO(enp, FR_CZ_RX_MAC_FILTER_TBL0, index,
910		    eop, B_TRUE);
911		break;
912
913	case EFX_SIENA_FILTER_TX_TCP_FULL:
914	case EFX_SIENA_FILTER_TX_TCP_WILD:
915	case EFX_SIENA_FILTER_TX_UDP_FULL:
916	case EFX_SIENA_FILTER_TX_UDP_WILD:
917		EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_FILTER_TBL0, index,
918		    eop, B_TRUE);
919		break;
920
921	case EFX_SIENA_FILTER_TX_MAC_FULL:
922	case EFX_SIENA_FILTER_TX_MAC_WILD:
923		EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_MAC_FILTER_TBL0, index,
924		    eop, B_TRUE);
925		break;
926
927	default:
928		EFSYS_ASSERT(B_FALSE);
929		rc = ENOTSUP;
930		goto fail1;
931	}
932	return (0);
933
934fail1:
935	return (rc);
936}
937
938
939static	__checkReturn	boolean_t
940siena_filter_equal(
941	__in		const siena_filter_spec_t *left,
942	__in		const siena_filter_spec_t *right)
943{
944	siena_filter_tbl_id_t tbl_id;
945
946	tbl_id = siena_filter_tbl_id(left->sfs_type);
947
948
949	if (left->sfs_type != right->sfs_type)
950		return (B_FALSE);
951
952	if (memcmp(left->sfs_dword, right->sfs_dword,
953		sizeof (left->sfs_dword)))
954		return (B_FALSE);
955
956	if ((tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
957		tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC) &&
958	    left->sfs_dmaq_id != right->sfs_dmaq_id)
959		return (B_FALSE);
960
961	return (B_TRUE);
962}
963
964static	__checkReturn	efx_rc_t
965siena_filter_search(
966	__in		siena_filter_tbl_t *sftp,
967	__in		siena_filter_spec_t *spec,
968	__in		uint32_t key,
969	__in		boolean_t for_insert,
970	__out		int *filter_index,
971	__out		unsigned int *depth_required)
972{
973	unsigned int hash, incr, filter_idx, depth;
974
975	hash = siena_filter_tbl_hash(key);
976	incr = siena_filter_tbl_increment(key);
977
978	filter_idx = hash & (sftp->sft_size - 1);
979	depth = 1;
980
981	for (;;) {
982		/*
983		 * Return success if entry is used and matches this spec
984		 * or entry is unused and we are trying to insert.
985		 */
986		if (siena_filter_test_used(sftp, filter_idx) ?
987		    siena_filter_equal(spec,
988		    &sftp->sft_spec[filter_idx]) :
989		    for_insert) {
990			*filter_index = filter_idx;
991			*depth_required = depth;
992			return (0);
993		}
994
995		/* Return failure if we reached the maximum search depth */
996		if (depth == FILTER_CTL_SRCH_MAX)
997			return (for_insert ? EBUSY : ENOENT);
998
999		filter_idx = (filter_idx + incr) & (sftp->sft_size - 1);
1000		++depth;
1001	}
1002}
1003
1004static			void
1005siena_filter_clear_entry(
1006	__in		efx_nic_t *enp,
1007	__in		siena_filter_tbl_t *sftp,
1008	__in		int index)
1009{
1010	efx_oword_t filter;
1011
1012	if (siena_filter_test_used(sftp, index)) {
1013		siena_filter_clear_used(sftp, index);
1014
1015		EFX_ZERO_OWORD(filter);
1016		siena_filter_push_entry(enp,
1017		    sftp->sft_spec[index].sfs_type,
1018		    index, &filter);
1019
1020		memset(&sftp->sft_spec[index],
1021		    0, sizeof (sftp->sft_spec[0]));
1022	}
1023}
1024
1025			void
1026siena_filter_tbl_clear(
1027	__in		efx_nic_t *enp,
1028	__in		siena_filter_tbl_id_t tbl_id)
1029{
1030	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1031	siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
1032	int index;
1033	int state;
1034
1035	EFSYS_LOCK(enp->en_eslp, state);
1036
1037	for (index = 0; index < sftp->sft_size; ++index) {
1038		siena_filter_clear_entry(enp, sftp, index);
1039	}
1040
1041	if (sftp->sft_used == 0)
1042		siena_filter_reset_search_depth(sfp, tbl_id);
1043
1044	EFSYS_UNLOCK(enp->en_eslp, state);
1045}
1046
1047static	__checkReturn	efx_rc_t
1048siena_filter_init(
1049	__in		efx_nic_t *enp)
1050{
1051	siena_filter_t *sfp;
1052	siena_filter_tbl_t *sftp;
1053	int tbl_id;
1054	efx_rc_t rc;
1055
1056	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (siena_filter_t), sfp);
1057
1058	if (!sfp) {
1059		rc = ENOMEM;
1060		goto fail1;
1061	}
1062
1063	enp->en_filter.ef_siena_filter = sfp;
1064
1065	switch (enp->en_family) {
1066	case EFX_FAMILY_SIENA:
1067		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_IP];
1068		sftp->sft_size = FR_AZ_RX_FILTER_TBL0_ROWS;
1069
1070		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC];
1071		sftp->sft_size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS;
1072
1073		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP];
1074		sftp->sft_size = FR_CZ_TX_FILTER_TBL0_ROWS;
1075
1076		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC];
1077		sftp->sft_size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS;
1078		break;
1079
1080	default:
1081		rc = ENOTSUP;
1082		goto fail2;
1083	}
1084
1085	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1086		unsigned int bitmap_size;
1087
1088		sftp = &sfp->sf_tbl[tbl_id];
1089		if (sftp->sft_size == 0)
1090			continue;
1091
1092		EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
1093		    sizeof (uint32_t));
1094		bitmap_size =
1095		    (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
1096
1097		EFSYS_KMEM_ALLOC(enp->en_esip, bitmap_size, sftp->sft_bitmap);
1098		if (!sftp->sft_bitmap) {
1099			rc = ENOMEM;
1100			goto fail3;
1101		}
1102
1103		EFSYS_KMEM_ALLOC(enp->en_esip,
1104		    sftp->sft_size * sizeof (*sftp->sft_spec),
1105		    sftp->sft_spec);
1106		if (!sftp->sft_spec) {
1107			rc = ENOMEM;
1108			goto fail4;
1109		}
1110		memset(sftp->sft_spec, 0,
1111		    sftp->sft_size * sizeof (*sftp->sft_spec));
1112	}
1113
1114	return (0);
1115
1116fail4:
1117	EFSYS_PROBE(fail4);
1118
1119fail3:
1120	EFSYS_PROBE(fail3);
1121
1122fail2:
1123	EFSYS_PROBE(fail2);
1124	siena_filter_fini(enp);
1125
1126fail1:
1127	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1128	return (rc);
1129}
1130
1131static			void
1132siena_filter_fini(
1133	__in		efx_nic_t *enp)
1134{
1135	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1136	siena_filter_tbl_id_t tbl_id;
1137
1138	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1139	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
1140
1141	if (sfp == NULL)
1142		return;
1143
1144	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1145		siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
1146		unsigned int bitmap_size;
1147
1148		EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
1149		    sizeof (uint32_t));
1150		bitmap_size =
1151		    (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
1152
1153		if (sftp->sft_bitmap != NULL) {
1154			EFSYS_KMEM_FREE(enp->en_esip, bitmap_size,
1155			    sftp->sft_bitmap);
1156			sftp->sft_bitmap = NULL;
1157		}
1158
1159		if (sftp->sft_spec != NULL) {
1160			EFSYS_KMEM_FREE(enp->en_esip, sftp->sft_size *
1161			    sizeof (*sftp->sft_spec), sftp->sft_spec);
1162			sftp->sft_spec = NULL;
1163		}
1164	}
1165
1166	EFSYS_KMEM_FREE(enp->en_esip, sizeof (siena_filter_t),
1167	    enp->en_filter.ef_siena_filter);
1168}
1169
1170/* Restore filter state after a reset */
1171static	__checkReturn	efx_rc_t
1172siena_filter_restore(
1173	__in		efx_nic_t *enp)
1174{
1175	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1176	siena_filter_tbl_id_t tbl_id;
1177	siena_filter_tbl_t *sftp;
1178	siena_filter_spec_t *spec;
1179	efx_oword_t filter;
1180	int filter_idx;
1181	int state;
1182	uint32_t key;
1183	efx_rc_t rc;
1184
1185	EFSYS_LOCK(enp->en_eslp, state);
1186
1187	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1188		sftp = &sfp->sf_tbl[tbl_id];
1189		for (filter_idx = 0;
1190			filter_idx < sftp->sft_size;
1191			filter_idx++) {
1192			if (!siena_filter_test_used(sftp, filter_idx))
1193				continue;
1194
1195			spec = &sftp->sft_spec[filter_idx];
1196			if ((key = siena_filter_build(&filter, spec)) == 0) {
1197				rc = EINVAL;
1198				goto fail1;
1199			}
1200			if ((rc = siena_filter_push_entry(enp,
1201				    spec->sfs_type, filter_idx, &filter)) != 0)
1202				goto fail2;
1203		}
1204	}
1205
1206	siena_filter_push_rx_limits(enp);
1207	siena_filter_push_tx_limits(enp);
1208
1209	EFSYS_UNLOCK(enp->en_eslp, state);
1210
1211	return (0);
1212
1213fail2:
1214	EFSYS_PROBE(fail2);
1215
1216fail1:
1217	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1218
1219	EFSYS_UNLOCK(enp->en_eslp, state);
1220
1221	return (rc);
1222}
1223
1224static	 __checkReturn	efx_rc_t
1225siena_filter_add(
1226	__in		efx_nic_t *enp,
1227	__inout		efx_filter_spec_t *spec,
1228	__in		boolean_t may_replace)
1229{
1230	efx_rc_t rc;
1231	siena_filter_spec_t sf_spec;
1232	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1233	siena_filter_tbl_id_t tbl_id;
1234	siena_filter_tbl_t *sftp;
1235	siena_filter_spec_t *saved_sf_spec;
1236	efx_oword_t filter;
1237	int filter_idx;
1238	unsigned int depth;
1239	int state;
1240	uint32_t key;
1241
1242
1243	EFSYS_ASSERT3P(spec, !=, NULL);
1244
1245	if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
1246		goto fail1;
1247
1248	tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
1249	sftp = &sfp->sf_tbl[tbl_id];
1250
1251	if (sftp->sft_size == 0) {
1252		rc = EINVAL;
1253		goto fail2;
1254	}
1255
1256	key = siena_filter_build(&filter, &sf_spec);
1257
1258	EFSYS_LOCK(enp->en_eslp, state);
1259
1260	rc = siena_filter_search(sftp, &sf_spec, key, B_TRUE,
1261	    &filter_idx, &depth);
1262	if (rc != 0)
1263		goto fail3;
1264
1265	EFSYS_ASSERT3U(filter_idx, <, sftp->sft_size);
1266	saved_sf_spec = &sftp->sft_spec[filter_idx];
1267
1268	if (siena_filter_test_used(sftp, filter_idx)) {
1269		if (may_replace == B_FALSE) {
1270			rc = EEXIST;
1271			goto fail4;
1272		}
1273	}
1274	siena_filter_set_used(sftp, filter_idx);
1275	*saved_sf_spec = sf_spec;
1276
1277	if (sfp->sf_depth[sf_spec.sfs_type] < depth) {
1278		sfp->sf_depth[sf_spec.sfs_type] = depth;
1279		if (tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
1280		    tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC)
1281			siena_filter_push_tx_limits(enp);
1282		else
1283			siena_filter_push_rx_limits(enp);
1284	}
1285
1286	siena_filter_push_entry(enp, sf_spec.sfs_type,
1287	    filter_idx, &filter);
1288
1289	EFSYS_UNLOCK(enp->en_eslp, state);
1290	return (0);
1291
1292fail4:
1293	EFSYS_PROBE(fail4);
1294
1295fail3:
1296	EFSYS_UNLOCK(enp->en_eslp, state);
1297	EFSYS_PROBE(fail3);
1298
1299fail2:
1300	EFSYS_PROBE(fail2);
1301
1302fail1:
1303	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1304	return (rc);
1305}
1306
1307static	 __checkReturn	efx_rc_t
1308siena_filter_delete(
1309	__in		efx_nic_t *enp,
1310	__inout		efx_filter_spec_t *spec)
1311{
1312	efx_rc_t rc;
1313	siena_filter_spec_t sf_spec;
1314	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1315	siena_filter_tbl_id_t tbl_id;
1316	siena_filter_tbl_t *sftp;
1317	efx_oword_t filter;
1318	int filter_idx;
1319	unsigned int depth;
1320	int state;
1321	uint32_t key;
1322
1323	EFSYS_ASSERT3P(spec, !=, NULL);
1324
1325	if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
1326		goto fail1;
1327
1328	tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
1329	sftp = &sfp->sf_tbl[tbl_id];
1330
1331	key = siena_filter_build(&filter, &sf_spec);
1332
1333	EFSYS_LOCK(enp->en_eslp, state);
1334
1335	rc = siena_filter_search(sftp, &sf_spec, key, B_FALSE,
1336	    &filter_idx, &depth);
1337	if (rc != 0)
1338		goto fail2;
1339
1340	siena_filter_clear_entry(enp, sftp, filter_idx);
1341	if (sftp->sft_used == 0)
1342		siena_filter_reset_search_depth(sfp, tbl_id);
1343
1344	EFSYS_UNLOCK(enp->en_eslp, state);
1345	return (0);
1346
1347fail2:
1348	EFSYS_UNLOCK(enp->en_eslp, state);
1349	EFSYS_PROBE(fail2);
1350
1351fail1:
1352	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1353	return (rc);
1354}
1355
1356#define	MAX_SUPPORTED 4
1357
1358static	__checkReturn	efx_rc_t
1359siena_filter_supported_filters(
1360	__in		efx_nic_t *enp,
1361	__out		uint32_t *list,
1362	__out		size_t *length)
1363{
1364	int index = 0;
1365	uint32_t rx_matches[MAX_SUPPORTED];
1366	efx_rc_t rc;
1367
1368	if (list == NULL) {
1369		rc = EINVAL;
1370		goto fail1;
1371	}
1372
1373	rx_matches[index++] =
1374	    EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
1375	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
1376	    EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
1377
1378	rx_matches[index++] =
1379	    EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
1380	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
1381
1382	if (enp->en_features & EFX_FEATURE_MAC_HEADER_FILTERS) {
1383		rx_matches[index++] =
1384		    EFX_FILTER_MATCH_OUTER_VID | EFX_FILTER_MATCH_LOC_MAC;
1385
1386		rx_matches[index++] = EFX_FILTER_MATCH_LOC_MAC;
1387	}
1388
1389	EFSYS_ASSERT3U(index, <=, MAX_SUPPORTED);
1390
1391	*length = index;
1392	memcpy(list, rx_matches, *length);
1393
1394	return (0);
1395
1396fail1:
1397
1398	return (rc);
1399}
1400
1401#undef MAX_SUPPORTED
1402
1403#endif /* EFSYS_OPT_SIENA */
1404
1405#endif /* EFSYS_OPT_FILTER */
1406