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)
70struct 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
78struct 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
87static eventhandler_tag ifaddr_evhandler;
88static struct mtx clip_db_lock;
89static LIST_HEAD(, clip_db_entry) *clip_db;
90static u_long clip_db_mask;
91static int clip_db_gen;
92static struct task clip_db_task;
93
94static int add_lip(struct adapter *, struct in6_addr *, int16_t *);
95static int del_lip(struct adapter *, struct in6_addr *);
96static void t4_clip_db_task(void *, int);
97static void t4_clip_task(void *, int);
98static void update_clip_db(void);
99static int update_sw_clip_table(struct adapter *);
100static int update_hw_clip_table(struct adapter *);
101static void update_clip_table(struct adapter *, void *);
102static int sysctl_clip_db(SYSCTL_HANDLER_ARGS);
103static int sysctl_clip_db_auto(SYSCTL_HANDLER_ARGS);
104static struct clip_db_entry *lookup_clip_db_entry(struct in6_addr *, bool);
105static struct clip_entry *lookup_clip_entry(struct adapter *, struct in6_addr *,
106    bool);
107
108SYSCTL_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
112int t4_clip_db_auto = 1;
113SYSCTL_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
117static inline uint32_t
118clip_hashfn(struct in6_addr *addr)
119{
120	return (fnv_32_buf(addr, sizeof(*addr), FNV1_32_INIT) & clip_db_mask);
121}
122
123static inline struct clip_db_entry *
124alloc_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
135static inline struct clip_entry *
136alloc_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 */
156static struct clip_db_entry *
157lookup_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 */
183static struct clip_entry *
184lookup_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
214static int
215add_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
235static int
236del_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
253struct clip_entry *
254t4_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
285void
286t4_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
300static void
301release_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
334void
335t4_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
351int
352t4_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
379void
380t4_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 */
401static void
402update_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 */
466static void
467t4_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 */
479static int
480update_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
567static int
568update_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	}
655done:
656	end_synchronized_op(sc, LOCK_HELD);
657	return (rc);
658}
659
660static void
661update_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 */
683static void
684t4_clip_task(void *sc, int count)
685{
686	update_clip_table(sc, NULL);
687}
688
689void
690t4_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;
715done:
716	mtx_unlock(&clip_db_lock);
717}
718
719static void
720t4_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
738int
739sysctl_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
781static int
782sysctl_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
819static int
820sysctl_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
842void
843t4_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
852void
853t4_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