xref: /freebsd/sys/dev/cxgbe/t4_clip.c (revision 315ee00f)
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
874