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