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