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