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