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
clip_hashfn(struct in6_addr * addr)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 *
alloc_clip_db_entry(struct in6_addr * in6)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 *
alloc_clip_entry(struct clip_db_entry * cde)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 *
lookup_clip_db_entry(struct in6_addr * in6,bool add)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 *
lookup_clip_entry(struct adapter * sc,struct in6_addr * in6,bool add)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
add_lip(struct adapter * sc,struct in6_addr * lip,int16_t * idx)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
del_lip(struct adapter * sc,struct in6_addr * lip)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 *
t4_get_clip_entry(struct adapter * sc,struct in6_addr * in6,bool add)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
t4_hold_clip_entry(struct adapter * sc,struct clip_entry * ce)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
release_clip_entry_locked(struct adapter * sc,struct clip_entry * ce)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
t4_release_clip_entry(struct adapter * sc,struct clip_entry * ce)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
t4_release_clip_addr(struct adapter * sc,struct in6_addr * in6)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
t4_init_clip_table(struct adapter * sc)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
update_clip_db(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
t4_clip_db_task(void * arg,int count)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
update_sw_clip_table(struct adapter * sc)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
update_hw_clip_table(struct adapter * sc)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
update_clip_table(struct adapter * sc,void * arg __unused)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
t4_clip_task(void * sc,int count)684 t4_clip_task(void *sc, int count)
685 {
686 update_clip_table(sc, NULL);
687 }
688
689 void
t4_destroy_clip_table(struct adapter * sc)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
t4_ifaddr_event(void * arg __unused,if_t ifp,struct ifaddr * ifa,int event)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
sysctl_clip(SYSCTL_HANDLER_ARGS)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
sysctl_clip_db(SYSCTL_HANDLER_ARGS)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
sysctl_clip_db_auto(SYSCTL_HANDLER_ARGS)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
t4_clip_modload(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
t4_clip_modunload(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
874