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