1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2007-2016 Solarflare Communications Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 *    this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 *    this list of conditions and the following disclaimer in the documentation
14 *    and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * The views and conclusions contained in the software and documentation are
29 * those of the authors and should not be interpreted as representing official
30 * policies, either expressed or implied, of the FreeBSD Project.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD$");
35
36#include "efx.h"
37#include "efx_impl.h"
38
39#if EFSYS_OPT_FILTER
40
41#if EFSYS_OPT_SIENA
42
43static	__checkReturn	efx_rc_t
44siena_filter_init(
45	__in		efx_nic_t *enp);
46
47static			void
48siena_filter_fini(
49	__in		efx_nic_t *enp);
50
51static	__checkReturn	efx_rc_t
52siena_filter_restore(
53	__in		efx_nic_t *enp);
54
55static	__checkReturn	efx_rc_t
56siena_filter_add(
57	__in		efx_nic_t *enp,
58	__inout		efx_filter_spec_t *spec,
59	__in		boolean_t may_replace);
60
61static	__checkReturn	efx_rc_t
62siena_filter_delete(
63	__in		efx_nic_t *enp,
64	__inout		efx_filter_spec_t *spec);
65
66static	__checkReturn	efx_rc_t
67siena_filter_supported_filters(
68	__in				efx_nic_t *enp,
69	__out_ecount(buffer_length)	uint32_t *buffer,
70	__in				size_t buffer_length,
71	__out				size_t *list_lengthp);
72
73#endif /* EFSYS_OPT_SIENA */
74
75#if EFSYS_OPT_SIENA
76static const efx_filter_ops_t	__efx_filter_siena_ops = {
77	siena_filter_init,		/* efo_init */
78	siena_filter_fini,		/* efo_fini */
79	siena_filter_restore,		/* efo_restore */
80	siena_filter_add,		/* efo_add */
81	siena_filter_delete,		/* efo_delete */
82	siena_filter_supported_filters,	/* efo_supported_filters */
83	NULL,				/* efo_reconfigure */
84};
85#endif /* EFSYS_OPT_SIENA */
86
87#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
88static const efx_filter_ops_t	__efx_filter_ef10_ops = {
89	ef10_filter_init,		/* efo_init */
90	ef10_filter_fini,		/* efo_fini */
91	ef10_filter_restore,		/* efo_restore */
92	ef10_filter_add,		/* efo_add */
93	ef10_filter_delete,		/* efo_delete */
94	ef10_filter_supported_filters,	/* efo_supported_filters */
95	ef10_filter_reconfigure,	/* efo_reconfigure */
96};
97#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
98
99	__checkReturn	efx_rc_t
100efx_filter_insert(
101	__in		efx_nic_t *enp,
102	__inout		efx_filter_spec_t *spec)
103{
104	const efx_filter_ops_t *efop = enp->en_efop;
105	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
106	efx_rc_t rc;
107
108	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
109	EFSYS_ASSERT3P(spec, !=, NULL);
110	EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX);
111
112	if ((spec->efs_flags & EFX_FILTER_FLAG_ACTION_MARK) &&
113	    !encp->enc_filter_action_mark_supported) {
114		rc = ENOTSUP;
115		goto fail1;
116	}
117
118	if ((spec->efs_flags & EFX_FILTER_FLAG_ACTION_FLAG) &&
119	    !encp->enc_filter_action_flag_supported) {
120		rc = ENOTSUP;
121		goto fail2;
122	}
123
124	return (efop->efo_add(enp, spec, B_FALSE));
125
126fail2:
127	EFSYS_PROBE(fail2);
128fail1:
129	EFSYS_PROBE1(fail1, efx_rc_t, rc);
130
131	return (rc);
132}
133
134	__checkReturn	efx_rc_t
135efx_filter_remove(
136	__in		efx_nic_t *enp,
137	__inout		efx_filter_spec_t *spec)
138{
139	const efx_filter_ops_t *efop = enp->en_efop;
140
141	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
142	EFSYS_ASSERT3P(spec, !=, NULL);
143	EFSYS_ASSERT3U(spec->efs_flags, &, EFX_FILTER_FLAG_RX);
144
145	return (efop->efo_delete(enp, spec));
146}
147
148	__checkReturn	efx_rc_t
149efx_filter_restore(
150	__in		efx_nic_t *enp)
151{
152	efx_rc_t rc;
153
154	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
155
156	if ((rc = enp->en_efop->efo_restore(enp)) != 0)
157		goto fail1;
158
159	return (0);
160
161fail1:
162	EFSYS_PROBE1(fail1, efx_rc_t, rc);
163
164	return (rc);
165}
166
167	__checkReturn	efx_rc_t
168efx_filter_init(
169	__in		efx_nic_t *enp)
170{
171	const efx_filter_ops_t *efop;
172	efx_rc_t rc;
173
174	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
175	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
176	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_FILTER));
177
178	switch (enp->en_family) {
179#if EFSYS_OPT_SIENA
180	case EFX_FAMILY_SIENA:
181		efop = &__efx_filter_siena_ops;
182		break;
183#endif /* EFSYS_OPT_SIENA */
184
185#if EFSYS_OPT_HUNTINGTON
186	case EFX_FAMILY_HUNTINGTON:
187		efop = &__efx_filter_ef10_ops;
188		break;
189#endif /* EFSYS_OPT_HUNTINGTON */
190
191#if EFSYS_OPT_MEDFORD
192	case EFX_FAMILY_MEDFORD:
193		efop = &__efx_filter_ef10_ops;
194		break;
195#endif /* EFSYS_OPT_MEDFORD */
196
197#if EFSYS_OPT_MEDFORD2
198	case EFX_FAMILY_MEDFORD2:
199		efop = &__efx_filter_ef10_ops;
200		break;
201#endif /* EFSYS_OPT_MEDFORD2 */
202
203	default:
204		EFSYS_ASSERT(0);
205		rc = ENOTSUP;
206		goto fail1;
207	}
208
209	if ((rc = efop->efo_init(enp)) != 0)
210		goto fail2;
211
212	enp->en_efop = efop;
213	enp->en_mod_flags |= EFX_MOD_FILTER;
214	return (0);
215
216fail2:
217	EFSYS_PROBE(fail2);
218fail1:
219	EFSYS_PROBE1(fail1, efx_rc_t, rc);
220
221	enp->en_efop = NULL;
222	enp->en_mod_flags &= ~EFX_MOD_FILTER;
223	return (rc);
224}
225
226			void
227efx_filter_fini(
228	__in		efx_nic_t *enp)
229{
230	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
231	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
232	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
233
234	enp->en_efop->efo_fini(enp);
235
236	enp->en_efop = NULL;
237	enp->en_mod_flags &= ~EFX_MOD_FILTER;
238}
239
240/*
241 * Query the possible combinations of match flags which can be filtered on.
242 * These are returned as a list, of which each 32 bit element is a bitmask
243 * formed of EFX_FILTER_MATCH flags.
244 *
245 * The combinations are ordered in priority from highest to lowest.
246 *
247 * If the provided buffer is too short to hold the list, the call with fail with
248 * ENOSPC and *list_lengthp will be set to the buffer length required.
249 */
250	__checkReturn	efx_rc_t
251efx_filter_supported_filters(
252	__in				efx_nic_t *enp,
253	__out_ecount(buffer_length)	uint32_t *buffer,
254	__in				size_t buffer_length,
255	__out				size_t *list_lengthp)
256{
257	efx_rc_t rc;
258
259	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
260	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
261	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
262	EFSYS_ASSERT(enp->en_efop->efo_supported_filters != NULL);
263
264	if (buffer == NULL) {
265		rc = EINVAL;
266		goto fail1;
267	}
268
269	rc = enp->en_efop->efo_supported_filters(enp, buffer, buffer_length,
270						    list_lengthp);
271	if (rc != 0)
272		goto fail2;
273
274	return (0);
275
276fail2:
277	EFSYS_PROBE(fail2);
278fail1:
279	EFSYS_PROBE1(fail1, efx_rc_t, rc);
280
281	return (rc);
282}
283
284	__checkReturn	efx_rc_t
285efx_filter_reconfigure(
286	__in				efx_nic_t *enp,
287	__in_ecount(6)			uint8_t const *mac_addr,
288	__in				boolean_t all_unicst,
289	__in				boolean_t mulcst,
290	__in				boolean_t all_mulcst,
291	__in				boolean_t brdcst,
292	__in_ecount(6*count)		uint8_t const *addrs,
293	__in				uint32_t count)
294{
295	efx_rc_t rc;
296
297	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
298	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
299	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_FILTER);
300
301	if (enp->en_efop->efo_reconfigure != NULL) {
302		if ((rc = enp->en_efop->efo_reconfigure(enp, mac_addr,
303							all_unicst, mulcst,
304							all_mulcst, brdcst,
305							addrs, count)) != 0)
306			goto fail1;
307	}
308
309	return (0);
310
311fail1:
312	EFSYS_PROBE1(fail1, efx_rc_t, rc);
313
314	return (rc);
315}
316
317		void
318efx_filter_spec_init_rx(
319	__out		efx_filter_spec_t *spec,
320	__in		efx_filter_priority_t priority,
321	__in		efx_filter_flags_t flags,
322	__in		efx_rxq_t *erp)
323{
324	EFSYS_ASSERT3P(spec, !=, NULL);
325	EFSYS_ASSERT3P(erp, !=, NULL);
326	EFSYS_ASSERT((flags & ~(EFX_FILTER_FLAG_RX_RSS |
327				EFX_FILTER_FLAG_RX_SCATTER)) == 0);
328
329	memset(spec, 0, sizeof (*spec));
330	spec->efs_priority = priority;
331	spec->efs_flags = EFX_FILTER_FLAG_RX | flags;
332	spec->efs_rss_context = EFX_RSS_CONTEXT_DEFAULT;
333	spec->efs_dmaq_id = (uint16_t)erp->er_index;
334}
335
336		void
337efx_filter_spec_init_tx(
338	__out		efx_filter_spec_t *spec,
339	__in		efx_txq_t *etp)
340{
341	EFSYS_ASSERT3P(spec, !=, NULL);
342	EFSYS_ASSERT3P(etp, !=, NULL);
343
344	memset(spec, 0, sizeof (*spec));
345	spec->efs_priority = EFX_FILTER_PRI_REQUIRED;
346	spec->efs_flags = EFX_FILTER_FLAG_TX;
347	spec->efs_dmaq_id = (uint16_t)etp->et_index;
348}
349
350/*
351 *  Specify IPv4 host, transport protocol and port in a filter specification
352 */
353__checkReturn		efx_rc_t
354efx_filter_spec_set_ipv4_local(
355	__inout		efx_filter_spec_t *spec,
356	__in		uint8_t proto,
357	__in		uint32_t host,
358	__in		uint16_t port)
359{
360	EFSYS_ASSERT3P(spec, !=, NULL);
361
362	spec->efs_match_flags |=
363		EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
364		EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
365	spec->efs_ether_type = EFX_ETHER_TYPE_IPV4;
366	spec->efs_ip_proto = proto;
367	spec->efs_loc_host.eo_u32[0] = host;
368	spec->efs_loc_port = port;
369	return (0);
370}
371
372/*
373 * Specify IPv4 hosts, transport protocol and ports in a filter specification
374 */
375__checkReturn		efx_rc_t
376efx_filter_spec_set_ipv4_full(
377	__inout		efx_filter_spec_t *spec,
378	__in		uint8_t proto,
379	__in		uint32_t lhost,
380	__in		uint16_t lport,
381	__in		uint32_t rhost,
382	__in		uint16_t rport)
383{
384	EFSYS_ASSERT3P(spec, !=, NULL);
385
386	spec->efs_match_flags |=
387		EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
388		EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
389		EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
390	spec->efs_ether_type = EFX_ETHER_TYPE_IPV4;
391	spec->efs_ip_proto = proto;
392	spec->efs_loc_host.eo_u32[0] = lhost;
393	spec->efs_loc_port = lport;
394	spec->efs_rem_host.eo_u32[0] = rhost;
395	spec->efs_rem_port = rport;
396	return (0);
397}
398
399/*
400 * Specify local Ethernet address and/or VID in filter specification
401 */
402__checkReturn		efx_rc_t
403efx_filter_spec_set_eth_local(
404	__inout		efx_filter_spec_t *spec,
405	__in		uint16_t vid,
406	__in		const uint8_t *addr)
407{
408	EFSYS_ASSERT3P(spec, !=, NULL);
409	EFSYS_ASSERT3P(addr, !=, NULL);
410
411	if (vid == EFX_FILTER_SPEC_VID_UNSPEC && addr == NULL)
412		return (EINVAL);
413
414	if (vid != EFX_FILTER_SPEC_VID_UNSPEC) {
415		spec->efs_match_flags |= EFX_FILTER_MATCH_OUTER_VID;
416		spec->efs_outer_vid = vid;
417	}
418	if (addr != NULL) {
419		spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC;
420		memcpy(spec->efs_loc_mac, addr, EFX_MAC_ADDR_LEN);
421	}
422	return (0);
423}
424
425			void
426efx_filter_spec_set_ether_type(
427	__inout		efx_filter_spec_t *spec,
428	__in		uint16_t ether_type)
429{
430	EFSYS_ASSERT3P(spec, !=, NULL);
431
432	spec->efs_ether_type = ether_type;
433	spec->efs_match_flags |= EFX_FILTER_MATCH_ETHER_TYPE;
434}
435
436/*
437 * Specify matching otherwise-unmatched unicast in a filter specification
438 */
439__checkReturn		efx_rc_t
440efx_filter_spec_set_uc_def(
441	__inout		efx_filter_spec_t *spec)
442{
443	EFSYS_ASSERT3P(spec, !=, NULL);
444
445	spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_UCAST_DST;
446	return (0);
447}
448
449/*
450 * Specify matching otherwise-unmatched multicast in a filter specification
451 */
452__checkReturn		efx_rc_t
453efx_filter_spec_set_mc_def(
454	__inout		efx_filter_spec_t *spec)
455{
456	EFSYS_ASSERT3P(spec, !=, NULL);
457
458	spec->efs_match_flags |= EFX_FILTER_MATCH_UNKNOWN_MCAST_DST;
459	return (0);
460}
461
462__checkReturn		efx_rc_t
463efx_filter_spec_set_encap_type(
464	__inout		efx_filter_spec_t *spec,
465	__in		efx_tunnel_protocol_t encap_type,
466	__in		efx_filter_inner_frame_match_t inner_frame_match)
467{
468	uint32_t match_flags = EFX_FILTER_MATCH_ENCAP_TYPE;
469	uint8_t ip_proto;
470	efx_rc_t rc;
471
472	EFSYS_ASSERT3P(spec, !=, NULL);
473
474	switch (encap_type) {
475	case EFX_TUNNEL_PROTOCOL_VXLAN:
476	case EFX_TUNNEL_PROTOCOL_GENEVE:
477		ip_proto = EFX_IPPROTO_UDP;
478		break;
479	case EFX_TUNNEL_PROTOCOL_NVGRE:
480		ip_proto = EFX_IPPROTO_GRE;
481		break;
482	default:
483		EFSYS_ASSERT(0);
484		rc = EINVAL;
485		goto fail1;
486	}
487
488	switch (inner_frame_match) {
489	case EFX_FILTER_INNER_FRAME_MATCH_UNKNOWN_MCAST_DST:
490		match_flags |= EFX_FILTER_MATCH_IFRM_UNKNOWN_MCAST_DST;
491		break;
492	case EFX_FILTER_INNER_FRAME_MATCH_UNKNOWN_UCAST_DST:
493		match_flags |= EFX_FILTER_MATCH_IFRM_UNKNOWN_UCAST_DST;
494		break;
495	case EFX_FILTER_INNER_FRAME_MATCH_OTHER:
496		/* This is for when specific inner frames are to be matched. */
497		break;
498	default:
499		EFSYS_ASSERT(0);
500		rc = EINVAL;
501		goto fail2;
502	}
503
504	spec->efs_encap_type = encap_type;
505	spec->efs_ip_proto = ip_proto;
506	spec->efs_match_flags |= (match_flags | EFX_FILTER_MATCH_IP_PROTO);
507
508	return (0);
509
510fail2:
511	EFSYS_PROBE(fail2);
512fail1:
513	EFSYS_PROBE1(fail1, efx_rc_t, rc);
514
515	return (rc);
516}
517
518/*
519 * Specify inner and outer Ethernet address and VNI or VSID in tunnel filter
520 * specification.
521 */
522static	__checkReturn	efx_rc_t
523efx_filter_spec_set_tunnel(
524	__inout	efx_filter_spec_t *spec,
525	__in		efx_tunnel_protocol_t encap_type,
526	__in		const uint8_t *vni_or_vsid,
527	__in		const uint8_t *inner_addr,
528	__in		const uint8_t *outer_addr)
529{
530	efx_rc_t rc;
531
532	EFSYS_ASSERT3P(spec, !=, NULL);
533	EFSYS_ASSERT3P(vni_or_vsid, !=, NULL);
534	EFSYS_ASSERT3P(inner_addr, !=, NULL);
535	EFSYS_ASSERT3P(outer_addr, !=, NULL);
536
537	switch (encap_type) {
538	case EFX_TUNNEL_PROTOCOL_VXLAN:
539	case EFX_TUNNEL_PROTOCOL_GENEVE:
540	case EFX_TUNNEL_PROTOCOL_NVGRE:
541		break;
542	default:
543		rc = EINVAL;
544		goto fail1;
545	}
546
547	if ((inner_addr == NULL) && (outer_addr == NULL)) {
548		rc = EINVAL;
549		goto fail2;
550	}
551
552	if (vni_or_vsid != NULL) {
553		spec->efs_match_flags |= EFX_FILTER_MATCH_VNI_OR_VSID;
554		memcpy(spec->efs_vni_or_vsid, vni_or_vsid, EFX_VNI_OR_VSID_LEN);
555	}
556	if (outer_addr != NULL) {
557		spec->efs_match_flags |= EFX_FILTER_MATCH_LOC_MAC;
558		memcpy(spec->efs_loc_mac, outer_addr, EFX_MAC_ADDR_LEN);
559	}
560	if (inner_addr != NULL) {
561		spec->efs_match_flags |= EFX_FILTER_MATCH_IFRM_LOC_MAC;
562		memcpy(spec->efs_ifrm_loc_mac, inner_addr, EFX_MAC_ADDR_LEN);
563	}
564
565	spec->efs_match_flags |= EFX_FILTER_MATCH_ENCAP_TYPE;
566	spec->efs_encap_type = encap_type;
567
568	return (0);
569
570fail2:
571	EFSYS_PROBE(fail2);
572fail1:
573	EFSYS_PROBE1(fail1, efx_rc_t, rc);
574
575	return (rc);
576}
577
578/*
579 * Specify inner and outer Ethernet address and VNI in VXLAN filter
580 * specification.
581 */
582__checkReturn		efx_rc_t
583efx_filter_spec_set_vxlan(
584	__inout		efx_filter_spec_t *spec,
585	__in		const uint8_t *vni,
586	__in		const uint8_t *inner_addr,
587	__in		const uint8_t *outer_addr)
588{
589	return efx_filter_spec_set_tunnel(spec, EFX_TUNNEL_PROTOCOL_VXLAN,
590	    vni, inner_addr, outer_addr);
591}
592
593/*
594 * Specify inner and outer Ethernet address and VNI in Geneve filter
595 * specification.
596 */
597__checkReturn		efx_rc_t
598efx_filter_spec_set_geneve(
599	__inout		efx_filter_spec_t *spec,
600	__in		const uint8_t *vni,
601	__in		const uint8_t *inner_addr,
602	__in		const uint8_t *outer_addr)
603{
604	return efx_filter_spec_set_tunnel(spec, EFX_TUNNEL_PROTOCOL_GENEVE,
605	    vni, inner_addr, outer_addr);
606}
607
608/*
609 * Specify inner and outer Ethernet address and vsid in NVGRE filter
610 * specification.
611 */
612__checkReturn		efx_rc_t
613efx_filter_spec_set_nvgre(
614	__inout		efx_filter_spec_t *spec,
615	__in		const uint8_t *vsid,
616	__in		const uint8_t *inner_addr,
617	__in		const uint8_t *outer_addr)
618{
619	return efx_filter_spec_set_tunnel(spec, EFX_TUNNEL_PROTOCOL_NVGRE,
620	    vsid, inner_addr, outer_addr);
621}
622
623#if EFSYS_OPT_RX_SCALE
624	__checkReturn	efx_rc_t
625efx_filter_spec_set_rss_context(
626	__inout		efx_filter_spec_t *spec,
627	__in		uint32_t rss_context)
628{
629	efx_rc_t rc;
630
631	EFSYS_ASSERT3P(spec, !=, NULL);
632
633	/* The filter must have been created with EFX_FILTER_FLAG_RX_RSS. */
634	if ((spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) == 0) {
635		rc = EINVAL;
636		goto fail1;
637	}
638
639	spec->efs_rss_context = rss_context;
640
641	return (0);
642
643fail1:
644	EFSYS_PROBE1(fail1, efx_rc_t, rc);
645
646	return (rc);
647}
648#endif
649
650#if EFSYS_OPT_SIENA
651
652/*
653 * "Fudge factors" - difference between programmed value and actual depth.
654 * Due to pipelined implementation we need to program H/W with a value that
655 * is larger than the hop limit we want.
656 */
657#define	FILTER_CTL_SRCH_FUDGE_WILD 3
658#define	FILTER_CTL_SRCH_FUDGE_FULL 1
659
660/*
661 * Hard maximum hop limit.  Hardware will time-out beyond 200-something.
662 * We also need to avoid infinite loops in efx_filter_search() when the
663 * table is full.
664 */
665#define	FILTER_CTL_SRCH_MAX 200
666
667static	__checkReturn	efx_rc_t
668siena_filter_spec_from_gen_spec(
669	__out		siena_filter_spec_t *sf_spec,
670	__in		efx_filter_spec_t *gen_spec)
671{
672	efx_rc_t rc;
673	boolean_t is_full = B_FALSE;
674
675	if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX)
676		EFSYS_ASSERT3U(gen_spec->efs_flags, ==, EFX_FILTER_FLAG_TX);
677	else
678		EFSYS_ASSERT3U(gen_spec->efs_flags, &, EFX_FILTER_FLAG_RX);
679
680	/* Siena only has one RSS context */
681	if ((gen_spec->efs_flags & EFX_FILTER_FLAG_RX_RSS) &&
682	    gen_spec->efs_rss_context != EFX_RSS_CONTEXT_DEFAULT) {
683		rc = EINVAL;
684		goto fail1;
685	}
686
687	sf_spec->sfs_flags = gen_spec->efs_flags;
688	sf_spec->sfs_dmaq_id = gen_spec->efs_dmaq_id;
689
690	switch (gen_spec->efs_match_flags) {
691	case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
692	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
693	    EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT:
694		is_full = B_TRUE;
695		/* Fall through */
696	case EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
697	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT: {
698		uint32_t rhost, host1, host2;
699		uint16_t rport, port1, port2;
700
701		if (gen_spec->efs_ether_type != EFX_ETHER_TYPE_IPV4) {
702			rc = ENOTSUP;
703			goto fail2;
704		}
705		if (gen_spec->efs_loc_port == 0 ||
706		    (is_full && gen_spec->efs_rem_port == 0)) {
707			rc = EINVAL;
708			goto fail3;
709		}
710		switch (gen_spec->efs_ip_proto) {
711		case EFX_IPPROTO_TCP:
712			if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
713				sf_spec->sfs_type = (is_full ?
714				    EFX_SIENA_FILTER_TX_TCP_FULL :
715				    EFX_SIENA_FILTER_TX_TCP_WILD);
716			} else {
717				sf_spec->sfs_type = (is_full ?
718				    EFX_SIENA_FILTER_RX_TCP_FULL :
719				    EFX_SIENA_FILTER_RX_TCP_WILD);
720			}
721			break;
722		case EFX_IPPROTO_UDP:
723			if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
724				sf_spec->sfs_type = (is_full ?
725				    EFX_SIENA_FILTER_TX_UDP_FULL :
726				    EFX_SIENA_FILTER_TX_UDP_WILD);
727			} else {
728				sf_spec->sfs_type = (is_full ?
729				    EFX_SIENA_FILTER_RX_UDP_FULL :
730				    EFX_SIENA_FILTER_RX_UDP_WILD);
731			}
732			break;
733		default:
734			rc = ENOTSUP;
735			goto fail4;
736		}
737		/*
738		 * The filter is constructed in terms of source and destination,
739		 * with the odd wrinkle that the ports are swapped in a UDP
740		 * wildcard filter. We need to convert from local and remote
741		 * addresses (zero for a wildcard).
742		 */
743		rhost = is_full ? gen_spec->efs_rem_host.eo_u32[0] : 0;
744		rport = is_full ? gen_spec->efs_rem_port : 0;
745		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
746			host1 = gen_spec->efs_loc_host.eo_u32[0];
747			host2 = rhost;
748		} else {
749			host1 = rhost;
750			host2 = gen_spec->efs_loc_host.eo_u32[0];
751		}
752		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
753			if (sf_spec->sfs_type ==
754			    EFX_SIENA_FILTER_TX_UDP_WILD) {
755				port1 = rport;
756				port2 = gen_spec->efs_loc_port;
757			} else {
758				port1 = gen_spec->efs_loc_port;
759				port2 = rport;
760			}
761		} else {
762			if (sf_spec->sfs_type ==
763			    EFX_SIENA_FILTER_RX_UDP_WILD) {
764				port1 = gen_spec->efs_loc_port;
765				port2 = rport;
766			} else {
767				port1 = rport;
768				port2 = gen_spec->efs_loc_port;
769			}
770		}
771		sf_spec->sfs_dword[0] = (host1 << 16) | port1;
772		sf_spec->sfs_dword[1] = (port2 << 16) | (host1 >> 16);
773		sf_spec->sfs_dword[2] = host2;
774		break;
775	}
776
777	case EFX_FILTER_MATCH_LOC_MAC | EFX_FILTER_MATCH_OUTER_VID:
778		is_full = B_TRUE;
779		/* Fall through */
780	case EFX_FILTER_MATCH_LOC_MAC:
781		if (gen_spec->efs_flags & EFX_FILTER_FLAG_TX) {
782			sf_spec->sfs_type = (is_full ?
783			    EFX_SIENA_FILTER_TX_MAC_FULL :
784			    EFX_SIENA_FILTER_TX_MAC_WILD);
785		} else {
786			sf_spec->sfs_type = (is_full ?
787			    EFX_SIENA_FILTER_RX_MAC_FULL :
788			    EFX_SIENA_FILTER_RX_MAC_WILD);
789		}
790		sf_spec->sfs_dword[0] = is_full ? gen_spec->efs_outer_vid : 0;
791		sf_spec->sfs_dword[1] =
792		    gen_spec->efs_loc_mac[2] << 24 |
793		    gen_spec->efs_loc_mac[3] << 16 |
794		    gen_spec->efs_loc_mac[4] <<  8 |
795		    gen_spec->efs_loc_mac[5];
796		sf_spec->sfs_dword[2] =
797		    gen_spec->efs_loc_mac[0] << 8 |
798		    gen_spec->efs_loc_mac[1];
799		break;
800
801	default:
802		EFSYS_ASSERT(B_FALSE);
803		rc = ENOTSUP;
804		goto fail5;
805	}
806
807	return (0);
808
809fail5:
810	EFSYS_PROBE(fail5);
811fail4:
812	EFSYS_PROBE(fail4);
813fail3:
814	EFSYS_PROBE(fail3);
815fail2:
816	EFSYS_PROBE(fail2);
817fail1:
818	EFSYS_PROBE1(fail1, efx_rc_t, rc);
819
820	return (rc);
821}
822
823/*
824 * The filter hash function is LFSR polynomial x^16 + x^3 + 1 of a 32-bit
825 * key derived from the n-tuple.
826 */
827static			uint16_t
828siena_filter_tbl_hash(
829	__in		uint32_t key)
830{
831	uint16_t tmp;
832
833	/* First 16 rounds */
834	tmp = 0x1fff ^ (uint16_t)(key >> 16);
835	tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
836	tmp = tmp ^ tmp >> 9;
837
838	/* Last 16 rounds */
839	tmp = tmp ^ tmp << 13 ^ (uint16_t)(key & 0xffff);
840	tmp = tmp ^ tmp >> 3 ^ tmp >> 6;
841	tmp = tmp ^ tmp >> 9;
842
843	return (tmp);
844}
845
846/*
847 * To allow for hash collisions, filter search continues at these
848 * increments from the first possible entry selected by the hash.
849 */
850static			uint16_t
851siena_filter_tbl_increment(
852	__in		uint32_t key)
853{
854	return ((uint16_t)(key * 2 - 1));
855}
856
857static	__checkReturn	boolean_t
858siena_filter_test_used(
859	__in		siena_filter_tbl_t *sftp,
860	__in		unsigned int index)
861{
862	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
863	return ((sftp->sft_bitmap[index / 32] & (1 << (index % 32))) != 0);
864}
865
866static			void
867siena_filter_set_used(
868	__in		siena_filter_tbl_t *sftp,
869	__in		unsigned int index)
870{
871	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
872	sftp->sft_bitmap[index / 32] |= (1 << (index % 32));
873	++sftp->sft_used;
874}
875
876static			void
877siena_filter_clear_used(
878	__in		siena_filter_tbl_t *sftp,
879	__in		unsigned int index)
880{
881	EFSYS_ASSERT3P(sftp->sft_bitmap, !=, NULL);
882	sftp->sft_bitmap[index / 32] &= ~(1 << (index % 32));
883
884	--sftp->sft_used;
885	EFSYS_ASSERT3U(sftp->sft_used, >=, 0);
886}
887
888static			siena_filter_tbl_id_t
889siena_filter_tbl_id(
890	__in		siena_filter_type_t type)
891{
892	siena_filter_tbl_id_t tbl_id;
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		tbl_id = EFX_SIENA_FILTER_TBL_RX_IP;
900		break;
901
902	case EFX_SIENA_FILTER_RX_MAC_FULL:
903	case EFX_SIENA_FILTER_RX_MAC_WILD:
904		tbl_id = EFX_SIENA_FILTER_TBL_RX_MAC;
905		break;
906
907	case EFX_SIENA_FILTER_TX_TCP_FULL:
908	case EFX_SIENA_FILTER_TX_TCP_WILD:
909	case EFX_SIENA_FILTER_TX_UDP_FULL:
910	case EFX_SIENA_FILTER_TX_UDP_WILD:
911		tbl_id = EFX_SIENA_FILTER_TBL_TX_IP;
912		break;
913
914	case EFX_SIENA_FILTER_TX_MAC_FULL:
915	case EFX_SIENA_FILTER_TX_MAC_WILD:
916		tbl_id = EFX_SIENA_FILTER_TBL_TX_MAC;
917		break;
918
919	default:
920		EFSYS_ASSERT(B_FALSE);
921		tbl_id = EFX_SIENA_FILTER_NTBLS;
922		break;
923	}
924	return (tbl_id);
925}
926
927static			void
928siena_filter_reset_search_depth(
929	__inout		siena_filter_t *sfp,
930	__in		siena_filter_tbl_id_t tbl_id)
931{
932	switch (tbl_id) {
933	case EFX_SIENA_FILTER_TBL_RX_IP:
934		sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] = 0;
935		sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] = 0;
936		sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] = 0;
937		sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] = 0;
938		break;
939
940	case EFX_SIENA_FILTER_TBL_RX_MAC:
941		sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] = 0;
942		sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] = 0;
943		break;
944
945	case EFX_SIENA_FILTER_TBL_TX_IP:
946		sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] = 0;
947		sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] = 0;
948		sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] = 0;
949		sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] = 0;
950		break;
951
952	case EFX_SIENA_FILTER_TBL_TX_MAC:
953		sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] = 0;
954		sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] = 0;
955		break;
956
957	default:
958		EFSYS_ASSERT(B_FALSE);
959		break;
960	}
961}
962
963static			void
964siena_filter_push_rx_limits(
965	__in		efx_nic_t *enp)
966{
967	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
968	efx_oword_t oword;
969
970	EFX_BAR_READO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
971
972	EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_FULL_SRCH_LIMIT,
973	    sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_FULL] +
974	    FILTER_CTL_SRCH_FUDGE_FULL);
975	EFX_SET_OWORD_FIELD(oword, FRF_AZ_TCP_WILD_SRCH_LIMIT,
976	    sfp->sf_depth[EFX_SIENA_FILTER_RX_TCP_WILD] +
977	    FILTER_CTL_SRCH_FUDGE_WILD);
978	EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_FULL_SRCH_LIMIT,
979	    sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_FULL] +
980	    FILTER_CTL_SRCH_FUDGE_FULL);
981	EFX_SET_OWORD_FIELD(oword, FRF_AZ_UDP_WILD_SRCH_LIMIT,
982	    sfp->sf_depth[EFX_SIENA_FILTER_RX_UDP_WILD] +
983	    FILTER_CTL_SRCH_FUDGE_WILD);
984
985	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC].sft_size) {
986		EFX_SET_OWORD_FIELD(oword,
987		    FRF_CZ_ETHERNET_FULL_SEARCH_LIMIT,
988		    sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_FULL] +
989		    FILTER_CTL_SRCH_FUDGE_FULL);
990		EFX_SET_OWORD_FIELD(oword,
991		    FRF_CZ_ETHERNET_WILDCARD_SEARCH_LIMIT,
992		    sfp->sf_depth[EFX_SIENA_FILTER_RX_MAC_WILD] +
993		    FILTER_CTL_SRCH_FUDGE_WILD);
994	}
995
996	EFX_BAR_WRITEO(enp, FR_AZ_RX_FILTER_CTL_REG, &oword);
997}
998
999static			void
1000siena_filter_push_tx_limits(
1001	__in		efx_nic_t *enp)
1002{
1003	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1004	efx_oword_t oword;
1005
1006	EFX_BAR_READO(enp, FR_AZ_TX_CFG_REG, &oword);
1007
1008	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP].sft_size != 0) {
1009		EFX_SET_OWORD_FIELD(oword,
1010		    FRF_CZ_TX_TCPIP_FILTER_FULL_SEARCH_RANGE,
1011		    sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_FULL] +
1012		    FILTER_CTL_SRCH_FUDGE_FULL);
1013		EFX_SET_OWORD_FIELD(oword,
1014		    FRF_CZ_TX_TCPIP_FILTER_WILD_SEARCH_RANGE,
1015		    sfp->sf_depth[EFX_SIENA_FILTER_TX_TCP_WILD] +
1016		    FILTER_CTL_SRCH_FUDGE_WILD);
1017		EFX_SET_OWORD_FIELD(oword,
1018		    FRF_CZ_TX_UDPIP_FILTER_FULL_SEARCH_RANGE,
1019		    sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_FULL] +
1020		    FILTER_CTL_SRCH_FUDGE_FULL);
1021		EFX_SET_OWORD_FIELD(oword,
1022		    FRF_CZ_TX_UDPIP_FILTER_WILD_SEARCH_RANGE,
1023		    sfp->sf_depth[EFX_SIENA_FILTER_TX_UDP_WILD] +
1024		    FILTER_CTL_SRCH_FUDGE_WILD);
1025	}
1026
1027	if (sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC].sft_size != 0) {
1028		EFX_SET_OWORD_FIELD(
1029			oword, FRF_CZ_TX_ETH_FILTER_FULL_SEARCH_RANGE,
1030			sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_FULL] +
1031			FILTER_CTL_SRCH_FUDGE_FULL);
1032		EFX_SET_OWORD_FIELD(
1033			oword, FRF_CZ_TX_ETH_FILTER_WILD_SEARCH_RANGE,
1034			sfp->sf_depth[EFX_SIENA_FILTER_TX_MAC_WILD] +
1035			FILTER_CTL_SRCH_FUDGE_WILD);
1036	}
1037
1038	EFX_BAR_WRITEO(enp, FR_AZ_TX_CFG_REG, &oword);
1039}
1040
1041/* Build a filter entry and return its n-tuple key. */
1042static	__checkReturn	uint32_t
1043siena_filter_build(
1044	__out		efx_oword_t *filter,
1045	__in		siena_filter_spec_t *spec)
1046{
1047	uint32_t dword3;
1048	uint32_t key;
1049	uint8_t  type  = spec->sfs_type;
1050	uint32_t flags = spec->sfs_flags;
1051
1052	switch (siena_filter_tbl_id(type)) {
1053	case EFX_SIENA_FILTER_TBL_RX_IP: {
1054		boolean_t is_udp = (type == EFX_SIENA_FILTER_RX_UDP_FULL ||
1055		    type == EFX_SIENA_FILTER_RX_UDP_WILD);
1056		EFX_POPULATE_OWORD_7(*filter,
1057		    FRF_BZ_RSS_EN,
1058		    (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
1059		    FRF_BZ_SCATTER_EN,
1060		    (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
1061		    FRF_AZ_TCP_UDP, is_udp,
1062		    FRF_AZ_RXQ_ID, spec->sfs_dmaq_id,
1063		    EFX_DWORD_2, spec->sfs_dword[2],
1064		    EFX_DWORD_1, spec->sfs_dword[1],
1065		    EFX_DWORD_0, spec->sfs_dword[0]);
1066		dword3 = is_udp;
1067		break;
1068	}
1069
1070	case EFX_SIENA_FILTER_TBL_RX_MAC: {
1071		boolean_t is_wild = (type == EFX_SIENA_FILTER_RX_MAC_WILD);
1072		EFX_POPULATE_OWORD_7(*filter,
1073		    FRF_CZ_RMFT_RSS_EN,
1074		    (flags & EFX_FILTER_FLAG_RX_RSS) ? 1 : 0,
1075		    FRF_CZ_RMFT_SCATTER_EN,
1076		    (flags & EFX_FILTER_FLAG_RX_SCATTER) ? 1 : 0,
1077		    FRF_CZ_RMFT_RXQ_ID, spec->sfs_dmaq_id,
1078		    FRF_CZ_RMFT_WILDCARD_MATCH, is_wild,
1079		    FRF_CZ_RMFT_DEST_MAC_DW1, spec->sfs_dword[2],
1080		    FRF_CZ_RMFT_DEST_MAC_DW0, spec->sfs_dword[1],
1081		    FRF_CZ_RMFT_VLAN_ID, spec->sfs_dword[0]);
1082		dword3 = is_wild;
1083		break;
1084	}
1085
1086	case EFX_SIENA_FILTER_TBL_TX_IP: {
1087		boolean_t is_udp = (type == EFX_SIENA_FILTER_TX_UDP_FULL ||
1088		    type == EFX_SIENA_FILTER_TX_UDP_WILD);
1089		EFX_POPULATE_OWORD_5(*filter,
1090		    FRF_CZ_TIFT_TCP_UDP, is_udp,
1091		    FRF_CZ_TIFT_TXQ_ID, spec->sfs_dmaq_id,
1092		    EFX_DWORD_2, spec->sfs_dword[2],
1093		    EFX_DWORD_1, spec->sfs_dword[1],
1094		    EFX_DWORD_0, spec->sfs_dword[0]);
1095		dword3 = is_udp | spec->sfs_dmaq_id << 1;
1096		break;
1097	}
1098
1099	case EFX_SIENA_FILTER_TBL_TX_MAC: {
1100		boolean_t is_wild = (type == EFX_SIENA_FILTER_TX_MAC_WILD);
1101		EFX_POPULATE_OWORD_5(*filter,
1102		    FRF_CZ_TMFT_TXQ_ID, spec->sfs_dmaq_id,
1103		    FRF_CZ_TMFT_WILDCARD_MATCH, is_wild,
1104		    FRF_CZ_TMFT_SRC_MAC_DW1, spec->sfs_dword[2],
1105		    FRF_CZ_TMFT_SRC_MAC_DW0, spec->sfs_dword[1],
1106		    FRF_CZ_TMFT_VLAN_ID, spec->sfs_dword[0]);
1107		dword3 = is_wild | spec->sfs_dmaq_id << 1;
1108		break;
1109	}
1110
1111	default:
1112		EFSYS_ASSERT(B_FALSE);
1113		EFX_ZERO_OWORD(*filter);
1114		return (0);
1115	}
1116
1117	key =
1118	    spec->sfs_dword[0] ^
1119	    spec->sfs_dword[1] ^
1120	    spec->sfs_dword[2] ^
1121	    dword3;
1122
1123	return (key);
1124}
1125
1126static	__checkReturn		efx_rc_t
1127siena_filter_push_entry(
1128	__inout			efx_nic_t *enp,
1129	__in			siena_filter_type_t type,
1130	__in			int index,
1131	__in			efx_oword_t *eop)
1132{
1133	efx_rc_t rc;
1134
1135	switch (type) {
1136	case EFX_SIENA_FILTER_RX_TCP_FULL:
1137	case EFX_SIENA_FILTER_RX_TCP_WILD:
1138	case EFX_SIENA_FILTER_RX_UDP_FULL:
1139	case EFX_SIENA_FILTER_RX_UDP_WILD:
1140		EFX_BAR_TBL_WRITEO(enp, FR_AZ_RX_FILTER_TBL0, index,
1141		    eop, B_TRUE);
1142		break;
1143
1144	case EFX_SIENA_FILTER_RX_MAC_FULL:
1145	case EFX_SIENA_FILTER_RX_MAC_WILD:
1146		EFX_BAR_TBL_WRITEO(enp, FR_CZ_RX_MAC_FILTER_TBL0, index,
1147		    eop, B_TRUE);
1148		break;
1149
1150	case EFX_SIENA_FILTER_TX_TCP_FULL:
1151	case EFX_SIENA_FILTER_TX_TCP_WILD:
1152	case EFX_SIENA_FILTER_TX_UDP_FULL:
1153	case EFX_SIENA_FILTER_TX_UDP_WILD:
1154		EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_FILTER_TBL0, index,
1155		    eop, B_TRUE);
1156		break;
1157
1158	case EFX_SIENA_FILTER_TX_MAC_FULL:
1159	case EFX_SIENA_FILTER_TX_MAC_WILD:
1160		EFX_BAR_TBL_WRITEO(enp, FR_CZ_TX_MAC_FILTER_TBL0, index,
1161		    eop, B_TRUE);
1162		break;
1163
1164	default:
1165		EFSYS_ASSERT(B_FALSE);
1166		rc = ENOTSUP;
1167		goto fail1;
1168	}
1169	return (0);
1170
1171fail1:
1172	return (rc);
1173}
1174
1175static	__checkReturn	boolean_t
1176siena_filter_equal(
1177	__in		const siena_filter_spec_t *left,
1178	__in		const siena_filter_spec_t *right)
1179{
1180	siena_filter_tbl_id_t tbl_id;
1181
1182	tbl_id = siena_filter_tbl_id(left->sfs_type);
1183
1184	if (left->sfs_type != right->sfs_type)
1185		return (B_FALSE);
1186
1187	if (memcmp(left->sfs_dword, right->sfs_dword,
1188		sizeof (left->sfs_dword)))
1189		return (B_FALSE);
1190
1191	if ((tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
1192		tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC) &&
1193	    left->sfs_dmaq_id != right->sfs_dmaq_id)
1194		return (B_FALSE);
1195
1196	return (B_TRUE);
1197}
1198
1199static	__checkReturn	efx_rc_t
1200siena_filter_search(
1201	__in		siena_filter_tbl_t *sftp,
1202	__in		siena_filter_spec_t *spec,
1203	__in		uint32_t key,
1204	__in		boolean_t for_insert,
1205	__out		int *filter_index,
1206	__out		unsigned int *depth_required)
1207{
1208	unsigned int hash, incr, filter_idx, depth;
1209
1210	hash = siena_filter_tbl_hash(key);
1211	incr = siena_filter_tbl_increment(key);
1212
1213	filter_idx = hash & (sftp->sft_size - 1);
1214	depth = 1;
1215
1216	for (;;) {
1217		/*
1218		 * Return success if entry is used and matches this spec
1219		 * or entry is unused and we are trying to insert.
1220		 */
1221		if (siena_filter_test_used(sftp, filter_idx) ?
1222		    siena_filter_equal(spec,
1223		    &sftp->sft_spec[filter_idx]) :
1224		    for_insert) {
1225			*filter_index = filter_idx;
1226			*depth_required = depth;
1227			return (0);
1228		}
1229
1230		/* Return failure if we reached the maximum search depth */
1231		if (depth == FILTER_CTL_SRCH_MAX)
1232			return (for_insert ? EBUSY : ENOENT);
1233
1234		filter_idx = (filter_idx + incr) & (sftp->sft_size - 1);
1235		++depth;
1236	}
1237}
1238
1239static			void
1240siena_filter_clear_entry(
1241	__in		efx_nic_t *enp,
1242	__in		siena_filter_tbl_t *sftp,
1243	__in		int index)
1244{
1245	efx_oword_t filter;
1246
1247	if (siena_filter_test_used(sftp, index)) {
1248		siena_filter_clear_used(sftp, index);
1249
1250		EFX_ZERO_OWORD(filter);
1251		siena_filter_push_entry(enp,
1252		    sftp->sft_spec[index].sfs_type,
1253		    index, &filter);
1254
1255		memset(&sftp->sft_spec[index],
1256		    0, sizeof (sftp->sft_spec[0]));
1257	}
1258}
1259
1260			void
1261siena_filter_tbl_clear(
1262	__in		efx_nic_t *enp,
1263	__in		siena_filter_tbl_id_t tbl_id)
1264{
1265	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1266	siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
1267	int index;
1268	efsys_lock_state_t state;
1269
1270	EFSYS_LOCK(enp->en_eslp, state);
1271
1272	for (index = 0; index < sftp->sft_size; ++index) {
1273		siena_filter_clear_entry(enp, sftp, index);
1274	}
1275
1276	if (sftp->sft_used == 0)
1277		siena_filter_reset_search_depth(sfp, tbl_id);
1278
1279	EFSYS_UNLOCK(enp->en_eslp, state);
1280}
1281
1282static	__checkReturn	efx_rc_t
1283siena_filter_init(
1284	__in		efx_nic_t *enp)
1285{
1286	siena_filter_t *sfp;
1287	siena_filter_tbl_t *sftp;
1288	int tbl_id;
1289	efx_rc_t rc;
1290
1291	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (siena_filter_t), sfp);
1292
1293	if (!sfp) {
1294		rc = ENOMEM;
1295		goto fail1;
1296	}
1297
1298	enp->en_filter.ef_siena_filter = sfp;
1299
1300	switch (enp->en_family) {
1301	case EFX_FAMILY_SIENA:
1302		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_IP];
1303		sftp->sft_size = FR_AZ_RX_FILTER_TBL0_ROWS;
1304
1305		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_RX_MAC];
1306		sftp->sft_size = FR_CZ_RX_MAC_FILTER_TBL0_ROWS;
1307
1308		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_IP];
1309		sftp->sft_size = FR_CZ_TX_FILTER_TBL0_ROWS;
1310
1311		sftp = &sfp->sf_tbl[EFX_SIENA_FILTER_TBL_TX_MAC];
1312		sftp->sft_size = FR_CZ_TX_MAC_FILTER_TBL0_ROWS;
1313		break;
1314
1315	default:
1316		rc = ENOTSUP;
1317		goto fail2;
1318	}
1319
1320	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1321		unsigned int bitmap_size;
1322
1323		sftp = &sfp->sf_tbl[tbl_id];
1324		if (sftp->sft_size == 0)
1325			continue;
1326
1327		EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
1328		    sizeof (uint32_t));
1329		bitmap_size =
1330		    (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
1331
1332		EFSYS_KMEM_ALLOC(enp->en_esip, bitmap_size, sftp->sft_bitmap);
1333		if (!sftp->sft_bitmap) {
1334			rc = ENOMEM;
1335			goto fail3;
1336		}
1337
1338		EFSYS_KMEM_ALLOC(enp->en_esip,
1339		    sftp->sft_size * sizeof (*sftp->sft_spec),
1340		    sftp->sft_spec);
1341		if (!sftp->sft_spec) {
1342			rc = ENOMEM;
1343			goto fail4;
1344		}
1345		memset(sftp->sft_spec, 0,
1346		    sftp->sft_size * sizeof (*sftp->sft_spec));
1347	}
1348
1349	return (0);
1350
1351fail4:
1352	EFSYS_PROBE(fail4);
1353
1354fail3:
1355	EFSYS_PROBE(fail3);
1356
1357fail2:
1358	EFSYS_PROBE(fail2);
1359	siena_filter_fini(enp);
1360
1361fail1:
1362	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1363	return (rc);
1364}
1365
1366static			void
1367siena_filter_fini(
1368	__in		efx_nic_t *enp)
1369{
1370	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1371	siena_filter_tbl_id_t tbl_id;
1372
1373	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1374	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
1375
1376	if (sfp == NULL)
1377		return;
1378
1379	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1380		siena_filter_tbl_t *sftp = &sfp->sf_tbl[tbl_id];
1381		unsigned int bitmap_size;
1382
1383		EFX_STATIC_ASSERT(sizeof (sftp->sft_bitmap[0]) ==
1384		    sizeof (uint32_t));
1385		bitmap_size =
1386		    (sftp->sft_size + (sizeof (uint32_t) * 8) - 1) / 8;
1387
1388		if (sftp->sft_bitmap != NULL) {
1389			EFSYS_KMEM_FREE(enp->en_esip, bitmap_size,
1390			    sftp->sft_bitmap);
1391			sftp->sft_bitmap = NULL;
1392		}
1393
1394		if (sftp->sft_spec != NULL) {
1395			EFSYS_KMEM_FREE(enp->en_esip, sftp->sft_size *
1396			    sizeof (*sftp->sft_spec), sftp->sft_spec);
1397			sftp->sft_spec = NULL;
1398		}
1399	}
1400
1401	EFSYS_KMEM_FREE(enp->en_esip, sizeof (siena_filter_t),
1402	    enp->en_filter.ef_siena_filter);
1403}
1404
1405/* Restore filter state after a reset */
1406static	__checkReturn	efx_rc_t
1407siena_filter_restore(
1408	__in		efx_nic_t *enp)
1409{
1410	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1411	siena_filter_tbl_id_t tbl_id;
1412	siena_filter_tbl_t *sftp;
1413	siena_filter_spec_t *spec;
1414	efx_oword_t filter;
1415	int filter_idx;
1416	efsys_lock_state_t state;
1417	uint32_t key;
1418	efx_rc_t rc;
1419
1420	EFSYS_LOCK(enp->en_eslp, state);
1421
1422	for (tbl_id = 0; tbl_id < EFX_SIENA_FILTER_NTBLS; tbl_id++) {
1423		sftp = &sfp->sf_tbl[tbl_id];
1424		for (filter_idx = 0;
1425			filter_idx < sftp->sft_size;
1426			filter_idx++) {
1427			if (!siena_filter_test_used(sftp, filter_idx))
1428				continue;
1429
1430			spec = &sftp->sft_spec[filter_idx];
1431			if ((key = siena_filter_build(&filter, spec)) == 0) {
1432				rc = EINVAL;
1433				goto fail1;
1434			}
1435			if ((rc = siena_filter_push_entry(enp,
1436				    spec->sfs_type, filter_idx, &filter)) != 0)
1437				goto fail2;
1438		}
1439	}
1440
1441	siena_filter_push_rx_limits(enp);
1442	siena_filter_push_tx_limits(enp);
1443
1444	EFSYS_UNLOCK(enp->en_eslp, state);
1445
1446	return (0);
1447
1448fail2:
1449	EFSYS_PROBE(fail2);
1450
1451fail1:
1452	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1453
1454	EFSYS_UNLOCK(enp->en_eslp, state);
1455
1456	return (rc);
1457}
1458
1459static	 __checkReturn	efx_rc_t
1460siena_filter_add(
1461	__in		efx_nic_t *enp,
1462	__inout		efx_filter_spec_t *spec,
1463	__in		boolean_t may_replace)
1464{
1465	efx_rc_t rc;
1466	siena_filter_spec_t sf_spec;
1467	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1468	siena_filter_tbl_id_t tbl_id;
1469	siena_filter_tbl_t *sftp;
1470	siena_filter_spec_t *saved_sf_spec;
1471	efx_oword_t filter;
1472	int filter_idx;
1473	unsigned int depth;
1474	efsys_lock_state_t state;
1475	uint32_t key;
1476
1477	EFSYS_ASSERT3P(spec, !=, NULL);
1478
1479	if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
1480		goto fail1;
1481
1482	tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
1483	sftp = &sfp->sf_tbl[tbl_id];
1484
1485	if (sftp->sft_size == 0) {
1486		rc = EINVAL;
1487		goto fail2;
1488	}
1489
1490	key = siena_filter_build(&filter, &sf_spec);
1491
1492	EFSYS_LOCK(enp->en_eslp, state);
1493
1494	rc = siena_filter_search(sftp, &sf_spec, key, B_TRUE,
1495	    &filter_idx, &depth);
1496	if (rc != 0)
1497		goto fail3;
1498
1499	EFSYS_ASSERT3U(filter_idx, <, sftp->sft_size);
1500	saved_sf_spec = &sftp->sft_spec[filter_idx];
1501
1502	if (siena_filter_test_used(sftp, filter_idx)) {
1503		if (may_replace == B_FALSE) {
1504			rc = EEXIST;
1505			goto fail4;
1506		}
1507	}
1508	siena_filter_set_used(sftp, filter_idx);
1509	*saved_sf_spec = sf_spec;
1510
1511	if (sfp->sf_depth[sf_spec.sfs_type] < depth) {
1512		sfp->sf_depth[sf_spec.sfs_type] = depth;
1513		if (tbl_id == EFX_SIENA_FILTER_TBL_TX_IP ||
1514		    tbl_id == EFX_SIENA_FILTER_TBL_TX_MAC)
1515			siena_filter_push_tx_limits(enp);
1516		else
1517			siena_filter_push_rx_limits(enp);
1518	}
1519
1520	siena_filter_push_entry(enp, sf_spec.sfs_type,
1521	    filter_idx, &filter);
1522
1523	EFSYS_UNLOCK(enp->en_eslp, state);
1524	return (0);
1525
1526fail4:
1527	EFSYS_PROBE(fail4);
1528
1529fail3:
1530	EFSYS_UNLOCK(enp->en_eslp, state);
1531	EFSYS_PROBE(fail3);
1532
1533fail2:
1534	EFSYS_PROBE(fail2);
1535
1536fail1:
1537	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1538	return (rc);
1539}
1540
1541static	 __checkReturn	efx_rc_t
1542siena_filter_delete(
1543	__in		efx_nic_t *enp,
1544	__inout		efx_filter_spec_t *spec)
1545{
1546	efx_rc_t rc;
1547	siena_filter_spec_t sf_spec;
1548	siena_filter_t *sfp = enp->en_filter.ef_siena_filter;
1549	siena_filter_tbl_id_t tbl_id;
1550	siena_filter_tbl_t *sftp;
1551	efx_oword_t filter;
1552	int filter_idx;
1553	unsigned int depth;
1554	efsys_lock_state_t state;
1555	uint32_t key;
1556
1557	EFSYS_ASSERT3P(spec, !=, NULL);
1558
1559	if ((rc = siena_filter_spec_from_gen_spec(&sf_spec, spec)) != 0)
1560		goto fail1;
1561
1562	tbl_id = siena_filter_tbl_id(sf_spec.sfs_type);
1563	sftp = &sfp->sf_tbl[tbl_id];
1564
1565	key = siena_filter_build(&filter, &sf_spec);
1566
1567	EFSYS_LOCK(enp->en_eslp, state);
1568
1569	rc = siena_filter_search(sftp, &sf_spec, key, B_FALSE,
1570	    &filter_idx, &depth);
1571	if (rc != 0)
1572		goto fail2;
1573
1574	siena_filter_clear_entry(enp, sftp, filter_idx);
1575	if (sftp->sft_used == 0)
1576		siena_filter_reset_search_depth(sfp, tbl_id);
1577
1578	EFSYS_UNLOCK(enp->en_eslp, state);
1579	return (0);
1580
1581fail2:
1582	EFSYS_UNLOCK(enp->en_eslp, state);
1583	EFSYS_PROBE(fail2);
1584
1585fail1:
1586	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1587	return (rc);
1588}
1589
1590#define	SIENA_MAX_SUPPORTED_MATCHES 4
1591
1592static	__checkReturn	efx_rc_t
1593siena_filter_supported_filters(
1594	__in				efx_nic_t *enp,
1595	__out_ecount(buffer_length)	uint32_t *buffer,
1596	__in				size_t buffer_length,
1597	__out				size_t *list_lengthp)
1598{
1599	uint32_t index = 0;
1600	uint32_t rx_matches[SIENA_MAX_SUPPORTED_MATCHES];
1601	size_t list_length;
1602	efx_rc_t rc;
1603
1604	rx_matches[index++] =
1605	    EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
1606	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT |
1607	    EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT;
1608
1609	rx_matches[index++] =
1610	    EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO |
1611	    EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT;
1612
1613	if (enp->en_features & EFX_FEATURE_MAC_HEADER_FILTERS) {
1614		rx_matches[index++] =
1615		    EFX_FILTER_MATCH_OUTER_VID | EFX_FILTER_MATCH_LOC_MAC;
1616
1617		rx_matches[index++] = EFX_FILTER_MATCH_LOC_MAC;
1618	}
1619
1620	EFSYS_ASSERT3U(index, <=, SIENA_MAX_SUPPORTED_MATCHES);
1621	list_length = index;
1622
1623	*list_lengthp = list_length;
1624
1625	if (buffer_length < list_length) {
1626		rc = ENOSPC;
1627		goto fail1;
1628	}
1629
1630	memcpy(buffer, rx_matches, list_length * sizeof (rx_matches[0]));
1631
1632	return (0);
1633
1634fail1:
1635	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1636
1637	return (rc);
1638}
1639
1640#undef MAX_SUPPORTED
1641
1642#endif /* EFSYS_OPT_SIENA */
1643
1644#endif /* EFSYS_OPT_FILTER */
1645