2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2012-2021 Chelsio Communications, Inc.
6 * Written by: Navdeep Parhar <np@FreeBSD.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
34 #include "opt_inet6.h"
36 #include <sys/types.h>
38 #include <sys/eventhandler.h>
39 #include <sys/malloc.h>
40 #include <sys/rmlock.h>
42 #include <sys/socket.h>
43 #include <sys/taskqueue.h>
45 #include <net/if_var.h>
46 #include <netinet/in.h>
47 #include <netinet6/in6_var.h>
48 #include <netinet6/scope6_var.h>
50 #include "common/common.h"
54 * Code to deal with the Compressed Local IPv6 (CLIP) table in the ASIC.
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.
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.
72 struct clip_db_entry {
73 LIST_ENTRY(clip_db_entry) link; /* clip_db hash linkage */
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 */
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 */
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;
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 *,
110 SYSCTL_PROC(_hw_cxgbe, OID_AUTO, clip_db, CTLTYPE_STRING | CTLFLAG_RD |
111 CTLFLAG_SKIP | CTLFLAG_MPSAFE, NULL, 0, sysctl_clip_db, "A",
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)");
119 static inline uint32_t
120 clip_hashfn(struct in6_addr *addr)
122 return (fnv_32_buf(addr, sizeof(*addr), FNV1_32_INIT) & clip_db_mask);
125 static inline struct clip_db_entry *
126 alloc_clip_db_entry(struct in6_addr *in6)
128 struct clip_db_entry *cde;
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));
137 static inline struct clip_entry *
138 alloc_clip_entry(struct clip_db_entry *cde)
140 struct clip_entry *ce;
142 mtx_assert(&clip_db_lock, MA_OWNED);
144 ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT | M_ZERO);
145 if (__predict_true(ce != NULL)) {
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.
158 static struct clip_db_entry *
159 lookup_clip_db_entry(struct in6_addr *in6, bool add)
161 struct clip_db_entry *cde;
162 const int bucket = clip_hashfn(in6);
164 mtx_assert(&clip_db_lock, MA_OWNED);
166 LIST_FOREACH(cde, &clip_db[bucket], link) {
167 if (IN6_ARE_ADDR_EQUAL(&cde->lip, in6))
171 /* Not found. Create a new entry if requested. */
173 cde = alloc_clip_db_entry(in6);
175 LIST_INSERT_HEAD(&clip_db[bucket], cde, link);
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.
185 static struct clip_entry *
186 lookup_clip_entry(struct adapter *sc, struct in6_addr *in6, bool add)
188 struct clip_db_entry *cde;
189 struct clip_entry *ce;
190 const int bucket = clip_hashfn(in6);
192 mtx_assert(&clip_db_lock, MA_OWNED);
194 cde = lookup_clip_db_entry(in6, add);
198 LIST_FOREACH(ce, &sc->clip_table[bucket], link) {
203 /* Not found. Create a new entry if requested. */
205 ce = alloc_clip_entry(cde);
207 LIST_INSERT_HEAD(&sc->clip_table[bucket], ce, link);
208 TAILQ_INSERT_TAIL(&sc->clip_pending, ce, plink);
217 add_lip(struct adapter *sc, struct in6_addr *lip, int16_t *idx)
219 struct fw_clip_cmd c;
222 ASSERT_SYNCHRONIZED_OP(sc);
224 memset(&c, 0, sizeof(c));
225 c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
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];
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));
238 del_lip(struct adapter *sc, struct in6_addr *lip)
240 struct fw_clip_cmd c;
242 ASSERT_SYNCHRONIZED_OP(sc);
244 memset(&c, 0, sizeof(c));
245 c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
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];
251 return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
256 t4_get_clip_entry(struct adapter *sc, struct in6_addr *in6, bool add)
259 struct clip_entry *ce;
260 bool schedule = false;
262 mtx_lock(&clip_db_lock);
263 ce = lookup_clip_entry(sc, in6, add);
265 MPASS(ce->cde->adp_ref > 0);
266 if (++ce->refcount == 1 && ce->pending && ce->clip_idx != -1) {
268 * Valid entry that was waiting to be deleted. It is in
269 * use now so take it off the pending list.
271 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
274 if (ce->clip_idx == -1 && update_hw_clip_table(sc) != 0)
277 mtx_unlock(&clip_db_lock);
279 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task, 0);
288 t4_hold_clip_entry(struct adapter *sc, struct clip_entry *ce)
292 MPASS(ce->cde->adp_ref > 0);
294 mtx_lock(&clip_db_lock);
295 MPASS(ce->refcount > 0); /* Caller should already have a reference */
297 mtx_unlock(&clip_db_lock);
303 release_clip_entry_locked(struct adapter *sc, struct clip_entry *ce)
305 struct clip_db_entry *cde;
307 mtx_assert(&clip_db_lock, MA_OWNED);
308 MPASS(ce->refcount > 0);
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. */
315 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
316 LIST_REMOVE(ce, link);
318 if (--cde->adp_ref == 0) {
319 LIST_REMOVE(cde, link);
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.
329 TAILQ_INSERT_HEAD(&sc->clip_pending, ce, plink);
337 t4_release_clip_entry(struct adapter *sc, struct clip_entry *ce)
342 mtx_lock(&clip_db_lock);
343 release_clip_entry_locked(sc, ce);
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
349 mtx_unlock(&clip_db_lock);
354 t4_release_clip_addr(struct adapter *sc, struct in6_addr *in6)
358 struct clip_entry *ce;
359 bool schedule = false;
361 mtx_lock(&clip_db_lock);
362 ce = lookup_clip_entry(sc, in6, false);
365 else if (ce->refcount == 0)
368 release_clip_entry_locked(sc, ce);
369 if (update_hw_clip_table(sc) != 0)
373 mtx_unlock(&clip_db_lock);
375 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task, 0);
382 t4_init_clip_table(struct adapter *sc)
384 TAILQ_INIT(&sc->clip_pending);
385 TIMEOUT_TASK_INIT(taskqueue_thread, &sc->clip_task, 0, t4_clip_task, sc);
387 sc->clip_table = hashinit(CLIP_HASH_SIZE, M_CXGBE, &sc->clip_mask);
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);
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.
401 * Returns true if any additions or deletions were made to the CLIP DB.
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;
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)
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))
427 if (IN6_IS_SCOPE_EMBED(in6)) {
432 cde = lookup_clip_db_entry(in6, true);
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);
451 addel++; /* IP6 addr deleted. */
453 cde->krn_ref = cde->tmp_ref;
459 mtx_unlock(&clip_db_lock);
460 IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
466 * Update the CLIP db and then update the CLIP tables on all the adapters.
469 t4_clip_db_task(void *arg, int count)
472 t4_iterate(update_clip_table, NULL);
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.
482 update_sw_clip_table(struct adapter *sc)
484 struct clip_db_entry *cde;
485 struct clip_entry *ce, *ce_temp;
489 mtx_assert(&clip_db_lock, MA_OWNED);
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
496 TAILQ_INIT(&sc->clip_pending);
499 * Walk the sw CLIP table first. We want to reset every entry's pending
500 * status as we're rebuilding the pending list.
502 for (i = 0; i <= clip_db_mask; i++) {
503 LIST_FOREACH_SAFE(ce, &sc->clip_table[i], link, ce_temp) {
505 MPASS(cde->adp_ref > 0);
506 if (ce->refcount != 0 || cde->krn_ref != 0) {
508 * Entry should stay in the CLIP.
511 if (ce->clip_idx != -1) {
514 /* Was never added, carry forward. */
516 TAILQ_INSERT_TAIL(&sc->clip_pending, ce,
523 * Entry should be removed from the CLIP.
526 if (ce->clip_idx != -1) {
528 TAILQ_INSERT_HEAD(&sc->clip_pending, ce, plink);
530 /* Was never added, free right now. */
532 LIST_REMOVE(ce, link);
534 if (--cde->adp_ref == 0) {
535 LIST_REMOVE(cde, link);
542 for (i = 0; i <= clip_db_mask; i++) {
543 LIST_FOREACH(cde, &clip_db[i], link) {
544 if (cde->krn_ref == 0)
548 LIST_FOREACH(ce, &sc->clip_table[i], link) {
549 if (ce->cde == cde) {
556 ce = alloc_clip_entry(cde);
559 LIST_INSERT_HEAD(&sc->clip_table[i], ce, link);
560 TAILQ_INSERT_TAIL(&sc->clip_pending, ce, plink);
565 sc->clip_gen = clip_db_gen;
570 update_hw_clip_table(struct adapter *sc)
572 struct clip_db_entry *cde;
573 struct clip_entry *ce;
575 char ip[INET6_ADDRSTRLEN];
577 mtx_assert(&clip_db_lock, MA_OWNED);
578 rc = begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4clip");
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);
587 MPASS(cde->adp_ref > 0);
589 if (ce->clip_idx == -1) {
591 * Entry was queued for addition to the HW CLIP.
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);
599 if (--cde->adp_ref == 0) {
600 LIST_REMOVE(cde, link);
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. */
612 inet_ntop(AF_INET6, &cde->lip, &ip[0],
614 CH_ERR(sc, "add_lip(%s) failed: %d\n",
618 MPASS(ce->clip_idx != -1);
619 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
624 * Entry was queued for deletion from the HW CLIP.
627 if (ce->refcount == 0 && cde->krn_ref == 0) {
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.
634 rc = del_lip(sc, &cde->lip);
636 CH_ERR(sc, "del_lip(%s) failed: %d\n",
643 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
644 LIST_REMOVE(ce, link);
646 if (--cde->adp_ref == 0) {
647 LIST_REMOVE(cde, link);
651 /* No need to delete from HW CLIP. */
652 TAILQ_REMOVE(&sc->clip_pending, ce, plink);
658 end_synchronized_op(sc, LOCK_HELD);
663 update_clip_table(struct adapter *sc, void *arg __unused)
667 if (sc->clip_table == NULL)
671 mtx_lock(&clip_db_lock);
672 if (sc->clip_gen != clip_db_gen && update_sw_clip_table(sc) != 0)
674 if (!TAILQ_EMPTY(&sc->clip_pending) && update_hw_clip_table(sc) != 0)
676 mtx_unlock(&clip_db_lock);
678 taskqueue_enqueue_timeout(taskqueue_thread, &sc->clip_task,
683 * Update the CLIP table of the specified adapter.
686 t4_clip_task(void *sc, int count)
688 update_clip_table(sc, NULL);
692 t4_destroy_clip_table(struct adapter *sc)
694 struct clip_entry *ce, *ce_temp;
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);
705 del_lip(sc, &ce->lip);
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);
715 hashdestroy(sc->clip_table, M_CXGBE, sc->clip_mask);
716 sc->clip_table = NULL;
718 mtx_unlock(&clip_db_lock);
722 t4_ifaddr_event(void *arg __unused, if_t ifp, struct ifaddr *ifa,
725 struct in6_addr *in6;
727 if (t4_clip_db_auto == 0)
728 return; /* Automatic updates not allowed. */
729 if (ifa->ifa_addr->sa_family != AF_INET6)
731 if (if_getflags(ifp) & IFF_LOOPBACK)
733 in6 = &((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr;
734 if (IN6_IS_ADDR_LOOPBACK(in6) || IN6_IS_ADDR_MULTICAST(in6))
737 taskqueue_enqueue(taskqueue_thread, &clip_db_task);
741 sysctl_clip(SYSCTL_HANDLER_ARGS)
743 struct adapter *sc = arg1;
744 struct clip_entry *ce;
746 int i, rc, header = 0;
747 char ip[INET6_ADDRSTRLEN];
749 rc = sysctl_wire_old_buffer(req, 0);
753 sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
757 mtx_lock(&clip_db_lock);
758 for (i = 0; i <= sc->clip_mask; i++) {
759 LIST_FOREACH(ce, &sc->clip_table[i], link) {
761 sbuf_printf(sb, "%-4s %-4s %s", "Indx", "Refs",
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", "-",
770 sbuf_printf(sb, "\n%-4d %-4d %s", ce->clip_idx,
775 mtx_unlock(&clip_db_lock);
777 rc = sbuf_finish(sb);
784 sysctl_clip_db(SYSCTL_HANDLER_ARGS)
786 struct clip_db_entry *cde;
788 int i, rc, header = 0;
789 char ip[INET6_ADDRSTRLEN];
791 rc = sysctl_wire_old_buffer(req, 0);
795 sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
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);
804 sbuf_printf(sb, "%-4s %-4s %s", "Kref", "Aref",
808 inet_ntop(AF_INET6, &cde->lip, &ip[0], sizeof(ip));
809 sbuf_printf(sb, "\n%-4d %-4d %s", cde->krn_ref,
813 mtx_unlock(&clip_db_lock);
815 rc = sbuf_finish(sb);
822 sysctl_clip_db_auto(SYSCTL_HANDLER_ARGS)
826 val = t4_clip_db_auto;
827 rc = sysctl_handle_int(oidp, &val, 0, req);
828 if (rc != 0 || req->newptr == NULL)
831 if (val == 0 || val == 1)
832 t4_clip_db_auto = val;
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.
838 t4_clip_db_task(NULL, 0);
845 t4_clip_modload(void)
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);
855 t4_clip_modunload(void)
857 struct clip_db_entry *cde;
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);
871 mtx_unlock(&clip_db_lock);
872 hashdestroy(clip_db, M_CXGBE, clip_db_mask);
873 mtx_destroy(&clip_db_lock);