]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/cxgbe/t4_clip.c
Merge llvm-project release/16.x llvmorg-16.0.6-0-g7cbf1a259152
[FreeBSD/FreeBSD.git] / sys / dev / cxgbe / t4_clip.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2012-2021 Chelsio Communications, Inc.
5  * All rights reserved.
6  * Written by: Navdeep Parhar <np@FreeBSD.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include "opt_inet.h"
34 #include "opt_inet6.h"
35
36 #include <sys/types.h>
37 #include <sys/ck.h>
38 #include <sys/eventhandler.h>
39 #include <sys/malloc.h>
40 #include <sys/rmlock.h>
41 #include <sys/sbuf.h>
42 #include <sys/socket.h>
43 #include <sys/taskqueue.h>
44 #include <net/if.h>
45 #include <net/if_var.h>
46 #include <netinet/in.h>
47 #include <netinet6/in6_var.h>
48 #include <netinet6/scope6_var.h>
49
50 #include "common/common.h"
51 #include "t4_clip.h"
52
53 /*
54  * Code to deal with the Compressed Local IPv6 (CLIP) table in the ASIC.
55  *
56  * The driver maintains a global CLIP database (clip_db) of IPv6 addresses and a
57  * per-adapter CLIP table (sc->clip_table) with entries that point to an IPv6 in
58  * the clip_db.  All access is protected by a single global lock (clip_db_lock).
59  * The correct lock order is clip lock before synchronized op.
60  *
61  * By default (hw.cxgbe.clip_db_auto=1) all local IPv6 addresses are added to
62  * the db.  Addresses are also added on-demand when the driver allocates an
63  * entry for a filter, TOE tid, etc.  krn_ref counts the number of times an
64  * address appears in the system.  adp_ref counts the number of adapters that
65  * have that address in their CLIP table.  If both are 0 then the entry is
66  * evicted from the db.  Consumers of the CLIP table entry (filters, TOE tids)
67  * are tracked in ce->refcount.  Driver ioctls let external consumers add/remove
68  * addresses from the CLIP table.
69  */
70
71 #if defined(INET6)
72 struct clip_db_entry {
73         LIST_ENTRY(clip_db_entry) link; /* clip_db hash linkage */
74         struct in6_addr lip;
75         u_int krn_ref;  /* # of times this IP6 appears in list of all IP6 */
76         u_int adp_ref;  /* # of adapters with this IP6 in their CLIP */
77         u_int tmp_ref;  /* Used only during refresh */
78 };
79
80 struct clip_entry {
81         LIST_ENTRY(clip_entry) link;    /* clip_table hash linkage */
82         TAILQ_ENTRY(clip_entry) plink;  /* clip_pending linkage */
83         struct clip_db_entry *cde;
84         int16_t clip_idx;               /* index in the hw table */
85         bool pending;                   /* in clip_pending list */
86         int refcount;
87 };
88
89 static eventhandler_tag ifaddr_evhandler;
90 static struct mtx clip_db_lock;
91 static LIST_HEAD(, clip_db_entry) *clip_db;
92 static u_long clip_db_mask;
93 static int clip_db_gen;
94 static struct task clip_db_task;
95
96 static int add_lip(struct adapter *, struct in6_addr *, int16_t *);
97 static int del_lip(struct adapter *, struct in6_addr *);
98 static void t4_clip_db_task(void *, int);
99 static void t4_clip_task(void *, int);
100 static void update_clip_db(void);
101 static int update_sw_clip_table(struct adapter *);
102 static int update_hw_clip_table(struct adapter *);
103 static void update_clip_table(struct adapter *, void *);
104 static int sysctl_clip_db(SYSCTL_HANDLER_ARGS);
105 static int sysctl_clip_db_auto(SYSCTL_HANDLER_ARGS);
106 static struct clip_db_entry *lookup_clip_db_entry(struct in6_addr *, bool);
107 static struct clip_entry *lookup_clip_entry(struct adapter *, struct in6_addr *,
108     bool);
109
110 SYSCTL_PROC(_hw_cxgbe, OID_AUTO, clip_db, CTLTYPE_STRING | CTLFLAG_RD |
111     CTLFLAG_SKIP | CTLFLAG_MPSAFE, NULL, 0, sysctl_clip_db, "A",
112     "CLIP database");
113
114 int t4_clip_db_auto = 1;
115 SYSCTL_PROC(_hw_cxgbe, OID_AUTO, clip_db_auto, CTLTYPE_INT | CTLFLAG_RWTUN |
116     CTLFLAG_MPSAFE, NULL, 0, sysctl_clip_db_auto, "I",
117     "Add local IPs to CLIP db automatically (0 = no, 1 = yes)");
118
119 static inline uint32_t
120 clip_hashfn(struct in6_addr *addr)
121 {
122         return (fnv_32_buf(addr, sizeof(*addr), FNV1_32_INIT) & clip_db_mask);
123 }
124
125 static inline struct clip_db_entry *
126 alloc_clip_db_entry(struct in6_addr *in6)
127 {
128         struct clip_db_entry *cde;
129
130         cde = malloc(sizeof(*cde), M_CXGBE, M_NOWAIT | M_ZERO);
131         if (__predict_true(cde != NULL))
132                 memcpy(&cde->lip, in6, sizeof(cde->lip));
133
134         return (cde);
135 }
136
137 static inline struct clip_entry *
138 alloc_clip_entry(struct clip_db_entry *cde)
139 {
140         struct clip_entry *ce;
141
142         mtx_assert(&clip_db_lock, MA_OWNED);
143
144         ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT | M_ZERO);
145         if (__predict_true(ce != NULL)) {
146                 ce->cde = cde;
147                 cde->adp_ref++;
148                 ce->clip_idx = -1;
149         }
150
151         return (ce);
152 }
153
154 /*
155  * Look up the IP6 address in the CLIP db.  If add is set then an entry for the
156  * IP6 will be added to the db.
157  */
158 static struct clip_db_entry *
159 lookup_clip_db_entry(struct in6_addr *in6, bool add)
160 {
161         struct clip_db_entry *cde;
162         const int bucket = clip_hashfn(in6);
163
164         mtx_assert(&clip_db_lock, MA_OWNED);
165
166         LIST_FOREACH(cde, &clip_db[bucket], link) {
167                 if (IN6_ARE_ADDR_EQUAL(&cde->lip, in6))
168                         return (cde);
169         }
170
171         /* Not found.  Create a new entry if requested. */
172         if (add) {
173                 cde = alloc_clip_db_entry(in6);
174                 if (cde != NULL)
175                         LIST_INSERT_HEAD(&clip_db[bucket], cde, link);
176         }
177
178         return (cde);
179 }
180
181 /*
182  * Look up the IP6 address in the CLIP db.  If add is set then an entry for the
183  * IP6 will be added to the db.
184  */
185 static struct clip_entry *
186 lookup_clip_entry(struct adapter *sc, struct in6_addr *in6, bool add)
187 {
188         struct clip_db_entry *cde;
189         struct clip_entry *ce;
190         const int bucket = clip_hashfn(in6);
191
192         mtx_assert(&clip_db_lock, MA_OWNED);
193
194         cde = lookup_clip_db_entry(in6, add);
195         if (cde == NULL)
196                 return (NULL);
197
198         LIST_FOREACH(ce, &sc->clip_table[bucket], link) {
199                 if (ce->cde == cde)
200                         return (ce);
201         }
202
203         /* Not found.  Create a new entry if requested. */
204         if (add) {
205                 ce = alloc_clip_entry(cde);
206                 if (ce != NULL) {
207                         LIST_INSERT_HEAD(&sc->clip_table[bucket], ce, link);
208                         TAILQ_INSERT_TAIL(&sc->clip_pending, ce, plink);
209                         ce->pending = true;
210                 }
211         }
212
213         return (ce);
214 }
215
216 static int
217 add_lip(struct adapter *sc, struct in6_addr *lip, int16_t *idx)
218 {
219         struct fw_clip_cmd c;
220         int rc;
221
222         ASSERT_SYNCHRONIZED_OP(sc);
223
224         memset(&c, 0, sizeof(c));
225         c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
226             F_FW_CMD_WRITE);
227         c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
228         c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
229         c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
230
231         rc = -t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c);
232         if (rc == 0 && idx != NULL)
233                 *idx = G_FW_CLIP_CMD_INDEX(ntohl(c.alloc_to_len16));
234         return (rc);
235 }
236
237 static int
238 del_lip(struct adapter *sc, struct in6_addr *lip)
239 {
240         struct fw_clip_cmd c;
241
242         ASSERT_SYNCHRONIZED_OP(sc);
243
244         memset(&c, 0, sizeof(c));
245         c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
246             F_FW_CMD_READ);
247         c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
248         c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
249         c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
250
251         return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
252 }
253 #endif
254
255 struct clip_entry *
256 t4_get_clip_entry(struct adapter *sc, struct in6_addr *in6, bool add)
257 {
258 #ifdef INET6
259         struct clip_entry *ce;
260         bool schedule = false;
261
262         mtx_lock(&clip_db_lock);
263         ce = lookup_clip_entry(sc, in6, add);
264         if (ce != NULL) {
265                 MPASS(ce->cde->adp_ref > 0);
266                 if (++ce->refcount == 1 && ce->pending && ce->clip_idx != -1) {
267                         /*
268                          * Valid entry that was waiting to be deleted.  It is in
269                          * use now so take it off the pending list.
270                          */
271                         TAILQ_REMOVE(&sc->clip_pending, ce, plink);
272                         ce->pending = false;
273                 }
274                 if (ce->clip_idx == -1 && update_hw_clip_table(sc) != 0)
275                         schedule = true;
276         }
277         mtx_unlock(&clip_db_lock);
278         if (schedule)
279                 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task, 0);
280
281         return (ce);
282 #else
283         return (NULL);
284 #endif
285 }
286
287 void
288 t4_hold_clip_entry(struct adapter *sc, struct clip_entry *ce)
289 {
290 #ifdef INET6
291         MPASS(ce != NULL);
292         MPASS(ce->cde->adp_ref > 0);
293
294         mtx_lock(&clip_db_lock);
295         MPASS(ce->refcount > 0); /* Caller should already have a reference */
296         ce->refcount++;
297         mtx_unlock(&clip_db_lock);
298 #endif
299 }
300
301 #ifdef INET6
302 static void
303 release_clip_entry_locked(struct adapter *sc, struct clip_entry *ce)
304 {
305         struct clip_db_entry *cde;
306
307         mtx_assert(&clip_db_lock, MA_OWNED);
308         MPASS(ce->refcount > 0);
309         cde = ce->cde;
310         MPASS(cde->adp_ref > 0);
311         if (--ce->refcount == 0 && cde->krn_ref == 0) {
312                 if (ce->clip_idx == -1) {
313                         /* Was never written to the hardware. */
314                         MPASS(ce->pending);
315                         TAILQ_REMOVE(&sc->clip_pending, ce, plink);
316                         LIST_REMOVE(ce, link);
317                         free(ce, M_CXGBE);
318                         if (--cde->adp_ref == 0) {
319                                 LIST_REMOVE(cde, link);
320                                 free(cde, M_CXGBE);
321                         }
322                 } else {
323                         /*
324                          * Valid entry is now unused, add to the pending list
325                          * for deletion.  Its refcount was 1 on entry so it
326                          * can't already be pending.
327                          */
328                         MPASS(!ce->pending);
329                         TAILQ_INSERT_HEAD(&sc->clip_pending, ce, plink);
330                         ce->pending = true;
331                 }
332         }
333 }
334 #endif
335
336 void
337 t4_release_clip_entry(struct adapter *sc, struct clip_entry *ce)
338 {
339 #ifdef INET6
340         MPASS(ce != NULL);
341
342         mtx_lock(&clip_db_lock);
343         release_clip_entry_locked(sc, ce);
344         /*
345          * This isn't a manual release via the ioctl.  No need to update the
346          * hw right now even if the release resulted in the entry being queued
347          * for deletion.
348          */
349         mtx_unlock(&clip_db_lock);
350 #endif
351 }
352
353 int
354 t4_release_clip_addr(struct adapter *sc, struct in6_addr *in6)
355 {
356         int rc = ENOTSUP;
357 #ifdef INET6
358         struct clip_entry *ce;
359         bool schedule = false;
360
361         mtx_lock(&clip_db_lock);
362         ce = lookup_clip_entry(sc, in6, false);
363         if (ce == NULL)
364                 rc = ENOENT;
365         else if (ce->refcount == 0)
366                 rc = EIO;
367         else {
368                 release_clip_entry_locked(sc, ce);
369                 if (update_hw_clip_table(sc) != 0)
370                         schedule = true;
371                 rc = 0;
372         }
373         mtx_unlock(&clip_db_lock);
374         if (schedule)
375                 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task, 0);
376 #endif
377         return (rc);
378 }
379
380 #ifdef INET6
381 void
382 t4_init_clip_table(struct adapter *sc)
383 {
384         TAILQ_INIT(&sc->clip_pending);
385         TIMEOUT_TASK_INIT(taskqueue_thread, &sc->clip_task, 0, t4_clip_task, sc);
386         sc->clip_gen = -1;
387         sc->clip_table = hashinit(CLIP_HASH_SIZE, M_CXGBE, &sc->clip_mask);
388
389         /* Both the hashes must use the same bucket for the same key. */
390         if (sc->clip_table != NULL)
391                 MPASS(sc->clip_mask == clip_db_mask);
392         /*
393          * Don't bother forcing an update of the clip table when the
394          * adapter is initialized.  Before an interface can be used it
395          * must be assigned an address which will trigger the event
396          * handler to update the table.
397          */
398 }
399
400 /*
401  * Returns true if any additions or deletions were made to the CLIP DB.
402  */
403 static void
404 update_clip_db(void)
405 {
406         VNET_ITERATOR_DECL(vnet_iter);
407         struct rm_priotracker in6_ifa_tracker;
408         struct in6_addr *in6, tin6;
409         struct in6_ifaddr *ia;
410         struct clip_db_entry *cde, *cde_tmp;
411         int i, addel;
412
413         VNET_LIST_RLOCK();
414         IN6_IFADDR_RLOCK(&in6_ifa_tracker);
415         mtx_lock(&clip_db_lock);
416         VNET_FOREACH(vnet_iter) {
417                 CURVNET_SET_QUIET(vnet_iter);
418                 CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
419                         if (if_getflags(ia->ia_ifp) & IFF_LOOPBACK)
420                                 continue;
421                         in6 = &ia->ia_addr.sin6_addr;
422                         KASSERT(!IN6_IS_ADDR_MULTICAST(in6),
423                             ("%s: mcast address in in6_ifaddr list", __func__));
424                         if (IN6_IS_ADDR_LOOPBACK(in6))
425                                 continue;
426
427                         if (IN6_IS_SCOPE_EMBED(in6)) {
428                                 tin6 = *in6;
429                                 in6 = &tin6;
430                                 in6_clearscope(in6);
431                         }
432                         cde = lookup_clip_db_entry(in6, true);
433                         if (cde == NULL)
434                                 continue;
435                         cde->tmp_ref++;
436                 }
437                 CURVNET_RESTORE();
438         }
439
440         addel = 0;
441         for (i = 0; i <= clip_db_mask; i++) {
442                 LIST_FOREACH_SAFE(cde, &clip_db[i], link, cde_tmp) {
443                         if (cde->krn_ref == 0 && cde->tmp_ref > 0) {
444                                 addel++;        /* IP6 addr added. */
445                         } else if (cde->krn_ref > 0 && cde->tmp_ref == 0) {
446                                 if (cde->adp_ref == 0) {
447                                         LIST_REMOVE(cde, link);
448                                         free(cde, M_CXGBE);
449                                         continue;
450                                 }
451                                 addel++;        /* IP6 addr deleted. */
452                         }
453                         cde->krn_ref = cde->tmp_ref;
454                         cde->tmp_ref = 0;
455                 }
456         }
457         if (addel > 0)
458                 clip_db_gen++;
459         mtx_unlock(&clip_db_lock);
460         IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
461         VNET_LIST_RUNLOCK();
462
463 }
464
465 /*
466  * Update the CLIP db and then update the CLIP tables on all the adapters.
467  */
468 static void
469 t4_clip_db_task(void *arg, int count)
470 {
471         update_clip_db();
472         t4_iterate(update_clip_table, NULL);
473 }
474
475 /*
476  * Refresh the sw CLIP table for this adapter from the global CLIP db.  Entries
477  * that need to be added or deleted from the hardware CLIP table are placed on a
478  * pending list but the hardware is not touched.  The pending list is something
479  * reasonable even if this fails so it's ok to apply that to the hardware.
480  */
481 static int
482 update_sw_clip_table(struct adapter *sc)
483 {
484         struct clip_db_entry *cde;
485         struct clip_entry *ce, *ce_temp;
486         int i;
487         bool found;
488
489         mtx_assert(&clip_db_lock, MA_OWNED);
490
491         /*
492          * We are about to rebuild the pending list from scratch.  Deletions are
493          * placed before additions because that's how we want to submit them to
494          * the hardware.
495          */
496         TAILQ_INIT(&sc->clip_pending);
497
498         /*
499          * Walk the sw CLIP table first.  We want to reset every entry's pending
500          * status as we're rebuilding the pending list.
501          */
502         for (i = 0; i <= clip_db_mask; i++) {
503                 LIST_FOREACH_SAFE(ce, &sc->clip_table[i], link, ce_temp) {
504                         cde = ce->cde;
505                         MPASS(cde->adp_ref > 0);
506                         if (ce->refcount != 0 || cde->krn_ref != 0) {
507                                 /*
508                                  * Entry should stay in the CLIP.
509                                  */
510
511                                 if (ce->clip_idx != -1) {
512                                         ce->pending = false;
513                                 } else {
514                                         /* Was never added, carry forward. */
515                                         MPASS(ce->pending);
516                                         TAILQ_INSERT_TAIL(&sc->clip_pending, ce,
517                                             plink);
518                                 }
519                                 continue;
520                         }
521
522                         /*
523                          * Entry should be removed from the CLIP.
524                          */
525
526                         if (ce->clip_idx != -1) {
527                                 ce->pending = true;
528                                 TAILQ_INSERT_HEAD(&sc->clip_pending, ce, plink);
529                         } else {
530                                 /* Was never added, free right now. */
531                                 MPASS(ce->pending);
532                                 LIST_REMOVE(ce, link);
533                                 free(ce, M_CXGBE);
534                                 if (--cde->adp_ref == 0) {
535                                         LIST_REMOVE(cde, link);
536                                         free(cde, M_CXGBE);
537                                 }
538                         }
539                 }
540         }
541
542         for (i = 0; i <= clip_db_mask; i++) {
543                 LIST_FOREACH(cde, &clip_db[i], link) {
544                         if (cde->krn_ref == 0)
545                                 continue;
546
547                         found = false;
548                         LIST_FOREACH(ce, &sc->clip_table[i], link) {
549                                 if (ce->cde == cde) {
550                                         found = true;
551                                         break;
552                                 }
553                         }
554                         if (found)
555                                 continue;
556                         ce = alloc_clip_entry(cde);
557                         if (ce == NULL)
558                                 return (ENOMEM);
559                         LIST_INSERT_HEAD(&sc->clip_table[i], ce, link);
560                         TAILQ_INSERT_TAIL(&sc->clip_pending, ce, plink);
561                         ce->pending = true;
562                 }
563         }
564
565         sc->clip_gen = clip_db_gen;
566         return (0);
567 }
568
569 static int
570 update_hw_clip_table(struct adapter *sc)
571 {
572         struct clip_db_entry *cde;
573         struct clip_entry *ce;
574         int rc;
575         char ip[INET6_ADDRSTRLEN];
576
577         mtx_assert(&clip_db_lock, MA_OWNED);
578         rc = begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4clip");
579         if (rc != 0)
580                 return (rc);
581         if (hw_off_limits(sc))
582                 goto done;      /* with rc = 0, we don't want to reschedule. */
583         while (!TAILQ_EMPTY(&sc->clip_pending)) {
584                 ce = TAILQ_FIRST(&sc->clip_pending);
585                 MPASS(ce->pending);
586                 cde = ce->cde;
587                 MPASS(cde->adp_ref > 0);
588
589                 if (ce->clip_idx == -1) {
590                         /*
591                          * Entry was queued for addition to the HW CLIP.
592                          */
593
594                         if (ce->refcount == 0 && cde->krn_ref == 0) {
595                                 /* No need to add to HW CLIP. */
596                                 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
597                                 LIST_REMOVE(ce, link);
598                                 free(ce, M_CXGBE);
599                                 if (--cde->adp_ref == 0) {
600                                         LIST_REMOVE(cde, link);
601                                         free(cde, M_CXGBE);
602                                 }
603                         } else {
604                                 /* Add to the HW CLIP. */
605                                 rc = add_lip(sc, &cde->lip, &ce->clip_idx);
606                                 if (rc == FW_ENOMEM) {
607                                         /* CLIP full, no point in retrying. */
608                                         rc = 0;
609                                         goto done;
610                                 }
611                                 if (rc != 0) {
612                                         inet_ntop(AF_INET6, &cde->lip, &ip[0],
613                                             sizeof(ip));
614                                         CH_ERR(sc, "add_lip(%s) failed: %d\n",
615                                             ip, rc);
616                                         goto done;
617                                 }
618                                 MPASS(ce->clip_idx != -1);
619                                 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
620                                 ce->pending = false;
621                         }
622                 } else {
623                         /*
624                          * Entry was queued for deletion from the HW CLIP.
625                          */
626
627                         if (ce->refcount == 0 && cde->krn_ref == 0) {
628                                 /*
629                                  * Delete from the HW CLIP.  Delete should never
630                                  * fail so we always log an error.  But if the
631                                  * failure is that the entry wasn't found in the
632                                  * CLIP then we carry on as if it was deleted.
633                                  */
634                                 rc = del_lip(sc, &cde->lip);
635                                 if (rc != 0)
636                                         CH_ERR(sc, "del_lip(%s) failed: %d\n",
637                                             ip, rc);
638                                 if (rc == FW_EPROTO)
639                                         rc = 0;
640                                 if (rc != 0)
641                                         goto done;
642
643                                 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
644                                 LIST_REMOVE(ce, link);
645                                 free(ce, M_CXGBE);
646                                 if (--cde->adp_ref == 0) {
647                                         LIST_REMOVE(cde, link);
648                                         free(cde, M_CXGBE);
649                                 }
650                         } else {
651                                 /* No need to delete from HW CLIP. */
652                                 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
653                                 ce->pending = false;
654                         }
655                 }
656         }
657 done:
658         end_synchronized_op(sc, LOCK_HELD);
659         return (rc);
660 }
661
662 static void
663 update_clip_table(struct adapter *sc, void *arg __unused)
664 {
665         bool reschedule;
666
667         if (sc->clip_table == NULL)
668                 return;
669
670         reschedule = false;
671         mtx_lock(&clip_db_lock);
672         if (sc->clip_gen != clip_db_gen && update_sw_clip_table(sc) != 0)
673                 reschedule = true;
674         if (!TAILQ_EMPTY(&sc->clip_pending) && update_hw_clip_table(sc) != 0)
675                 reschedule = true;
676         mtx_unlock(&clip_db_lock);
677         if (reschedule)
678                 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task,
679                     -hz / 4);
680 }
681
682 /*
683  * Update the CLIP table of the specified adapter.
684  */
685 static void
686 t4_clip_task(void *sc, int count)
687 {
688         update_clip_table(sc, NULL);
689 }
690
691 void
692 t4_destroy_clip_table(struct adapter *sc)
693 {
694         struct clip_entry *ce, *ce_temp;
695         int i;
696
697         mtx_lock(&clip_db_lock);
698         if (sc->clip_table == NULL)
699                 goto done;              /* CLIP was never initialized. */
700         for (i = 0; i <= sc->clip_mask; i++) {
701                 LIST_FOREACH_SAFE(ce, &sc->clip_table[i], link, ce_temp) {
702                         MPASS(ce->refcount == 0);
703                         MPASS(ce->cde->adp_ref > 0);
704 #if 0
705                         del_lip(sc, &ce->lip);
706 #endif
707                         LIST_REMOVE(ce, link);
708                         if (--ce->cde->adp_ref == 0 && ce->cde->krn_ref == 0) {
709                                 LIST_REMOVE(ce->cde, link);
710                                 free(ce->cde, M_CXGBE);
711                         }
712                         free(ce, M_CXGBE);
713                 }
714         }
715         hashdestroy(sc->clip_table, M_CXGBE, sc->clip_mask);
716         sc->clip_table = NULL;
717 done:
718         mtx_unlock(&clip_db_lock);
719 }
720
721 static void
722 t4_ifaddr_event(void *arg __unused, if_t ifp, struct ifaddr *ifa,
723     int event)
724 {
725         struct in6_addr *in6;
726
727         if (t4_clip_db_auto == 0)
728                 return;         /* Automatic updates not allowed. */
729         if (ifa->ifa_addr->sa_family != AF_INET6)
730                 return;
731         if (if_getflags(ifp) & IFF_LOOPBACK)
732                 return;
733         in6 = &((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr;
734         if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_MULTICAST(in6))
735                 return;
736
737         taskqueue_enqueue(taskqueue_thread, &clip_db_task);
738 }
739
740 int
741 sysctl_clip(SYSCTL_HANDLER_ARGS)
742 {
743         struct adapter *sc = arg1;
744         struct clip_entry *ce;
745         struct sbuf *sb;
746         int i, rc, header = 0;
747         char ip[INET6_ADDRSTRLEN];
748
749         rc = sysctl_wire_old_buffer(req, 0);
750         if (rc != 0)
751                 return (rc);
752
753         sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
754         if (sb == NULL)
755                 return (ENOMEM);
756
757         mtx_lock(&clip_db_lock);
758         for (i = 0; i <= sc->clip_mask; i++) {
759                 LIST_FOREACH(ce, &sc->clip_table[i], link) {
760                         if (header == 0) {
761                                 sbuf_printf(sb, "%-4s %-4s %s", "Indx", "Refs",
762                                     "IP address");
763                                 header = 1;
764                         }
765                         inet_ntop(AF_INET6, &ce->cde->lip, &ip[0], sizeof(ip));
766                         if (ce->clip_idx == -1) {
767                                 sbuf_printf(sb, "\n%-4s %-4d %s", "-",
768                                     ce->refcount, ip);
769                         } else {
770                                 sbuf_printf(sb, "\n%-4d %-4d %s", ce->clip_idx,
771                                     ce->refcount, ip);
772                         }
773                 }
774         }
775         mtx_unlock(&clip_db_lock);
776
777         rc = sbuf_finish(sb);
778         sbuf_delete(sb);
779
780         return (rc);
781 }
782
783 static int
784 sysctl_clip_db(SYSCTL_HANDLER_ARGS)
785 {
786         struct clip_db_entry *cde;
787         struct sbuf *sb;
788         int i, rc, header = 0;
789         char ip[INET6_ADDRSTRLEN];
790
791         rc = sysctl_wire_old_buffer(req, 0);
792         if (rc != 0)
793                 return (rc);
794
795         sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
796         if (sb == NULL)
797                 return (ENOMEM);
798
799         mtx_lock(&clip_db_lock);
800         for (i = 0; i <= clip_db_mask; i++) {
801                 LIST_FOREACH(cde, &clip_db[i], link) {
802                         MPASS(cde->tmp_ref == 0);
803                         if (header == 0) {
804                                 sbuf_printf(sb, "%-4s %-4s %s", "Kref", "Aref",
805                                     "IP address");
806                                 header = 1;
807                         }
808                         inet_ntop(AF_INET6, &cde->lip, &ip[0], sizeof(ip));
809                         sbuf_printf(sb, "\n%-4d %-4d %s", cde->krn_ref,
810                             cde->adp_ref, ip);
811                 }
812         }
813         mtx_unlock(&clip_db_lock);
814
815         rc = sbuf_finish(sb);
816         sbuf_delete(sb);
817
818         return (rc);
819 }
820
821 static int
822 sysctl_clip_db_auto(SYSCTL_HANDLER_ARGS)
823 {
824         int rc, val;
825
826         val = t4_clip_db_auto;
827         rc = sysctl_handle_int(oidp, &val, 0, req);
828         if (rc != 0 || req->newptr == NULL)
829                 return (rc);
830
831         if (val == 0 || val == 1)
832                 t4_clip_db_auto = val;
833         else {
834                 /*
835                  * Writing a value other than 0 or 1 forces a one-time update of
836                  * the clip_db directly in the sysctl and not in some taskqueue.
837                  */
838                 t4_clip_db_task(NULL, 0);
839         }
840
841         return (0);
842 }
843
844 void
845 t4_clip_modload(void)
846 {
847         mtx_init(&clip_db_lock, "clip_db", NULL, MTX_DEF);
848         clip_db = hashinit(CLIP_HASH_SIZE, M_CXGBE, &clip_db_mask);
849         TASK_INIT(&clip_db_task, 0, t4_clip_db_task, NULL);
850         ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event_ext,
851             t4_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY);
852 }
853
854 void
855 t4_clip_modunload(void)
856 {
857         struct clip_db_entry *cde;
858         int i;
859
860         EVENTHANDLER_DEREGISTER(ifaddr_event_ext, ifaddr_evhandler);
861         taskqueue_drain(taskqueue_thread, &clip_db_task);
862         mtx_lock(&clip_db_lock);
863         for (i = 0; i <= clip_db_mask; i++) {
864                 while ((cde = LIST_FIRST(&clip_db[i])) != NULL) {
865                         MPASS(cde->tmp_ref == 0);
866                         MPASS(cde->adp_ref == 0);
867                         LIST_REMOVE(cde, link);
868                         free(cde, M_CXGBE);
869                 }
870         }
871         mtx_unlock(&clip_db_lock);
872         hashdestroy(clip_db, M_CXGBE, clip_db_mask);
873         mtx_destroy(&clip_db_lock);
874 }
875 #endif