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