xref: /freebsd/sys/dev/cxgbe/t4_clip.c (revision 45d5c284)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2012 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 #if defined(INET6)
54 static int add_lip(struct adapter *, struct in6_addr *);
55 static int delete_lip(struct adapter *, struct in6_addr *);
56 static struct clip_entry *search_lip(struct adapter *, struct in6_addr *);
57 static void update_clip(struct adapter *, void *);
58 static void t4_clip_task(void *, int);
59 static void update_clip_table(struct adapter *);
60 
61 static int in6_ifaddr_gen;
62 static eventhandler_tag ifaddr_evhandler;
63 static struct timeout_task clip_task;
64 
65 static int
66 add_lip(struct adapter *sc, struct in6_addr *lip)
67 {
68         struct fw_clip_cmd c;
69 
70 	ASSERT_SYNCHRONIZED_OP(sc);
71 	mtx_assert(&sc->clip_table_lock, MA_OWNED);
72 
73         memset(&c, 0, sizeof(c));
74 	c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
75 	    F_FW_CMD_WRITE);
76         c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
77         c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
78         c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
79 
80 	return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
81 }
82 
83 static int
84 delete_lip(struct adapter *sc, struct in6_addr *lip)
85 {
86 	struct fw_clip_cmd c;
87 
88 	ASSERT_SYNCHRONIZED_OP(sc);
89 	mtx_assert(&sc->clip_table_lock, MA_OWNED);
90 
91 	memset(&c, 0, sizeof(c));
92 	c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
93 	    F_FW_CMD_READ);
94         c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
95         c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
96         c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
97 
98 	return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
99 }
100 
101 static struct clip_entry *
102 search_lip(struct adapter *sc, struct in6_addr *lip)
103 {
104 	struct clip_entry *ce;
105 
106 	mtx_assert(&sc->clip_table_lock, MA_OWNED);
107 
108 	TAILQ_FOREACH(ce, &sc->clip_table, link) {
109 		if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
110 			return (ce);
111 	}
112 
113 	return (NULL);
114 }
115 #endif
116 
117 struct clip_entry *
118 t4_hold_lip(struct adapter *sc, struct in6_addr *lip, struct clip_entry *ce)
119 {
120 
121 #ifdef INET6
122 	mtx_lock(&sc->clip_table_lock);
123 	if (ce == NULL)
124 		ce = search_lip(sc, lip);
125 	if (ce != NULL)
126 		ce->refcount++;
127 	mtx_unlock(&sc->clip_table_lock);
128 
129 	return (ce);
130 #else
131 	return (NULL);
132 #endif
133 }
134 
135 void
136 t4_release_lip(struct adapter *sc, struct clip_entry *ce)
137 {
138 
139 #ifdef INET6
140 	mtx_lock(&sc->clip_table_lock);
141 	KASSERT(search_lip(sc, &ce->lip) == ce,
142 	    ("%s: CLIP entry %p p not in CLIP table.", __func__, ce));
143 	KASSERT(ce->refcount > 0,
144 	    ("%s: CLIP entry %p has refcount 0", __func__, ce));
145 	--ce->refcount;
146 	mtx_unlock(&sc->clip_table_lock);
147 #endif
148 }
149 
150 #ifdef INET6
151 void
152 t4_init_clip_table(struct adapter *sc)
153 {
154 
155 	mtx_init(&sc->clip_table_lock, "CLIP table lock", NULL, MTX_DEF);
156 	TAILQ_INIT(&sc->clip_table);
157 	sc->clip_gen = -1;
158 
159 	/*
160 	 * Don't bother forcing an update of the clip table when the
161 	 * adapter is initialized.  Before an interface can be used it
162 	 * must be assigned an address which will trigger the event
163 	 * handler to update the table.
164 	 */
165 }
166 
167 static void
168 update_clip(struct adapter *sc, void *arg __unused)
169 {
170 
171 	if (begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4clip"))
172 		return;
173 
174 	if (mtx_initialized(&sc->clip_table_lock))
175 		update_clip_table(sc);
176 
177 	end_synchronized_op(sc, LOCK_HELD);
178 }
179 
180 static void
181 t4_clip_task(void *arg, int count)
182 {
183 
184 	t4_iterate(update_clip, NULL);
185 }
186 
187 static void
188 update_clip_table(struct adapter *sc)
189 {
190 	struct rm_priotracker in6_ifa_tracker;
191 	struct in6_ifaddr *ia;
192 	struct in6_addr *lip, tlip;
193 	TAILQ_HEAD(, clip_entry) stale;
194 	struct clip_entry *ce, *ce_temp;
195 	struct vi_info *vi;
196 	int rc, gen, i, j;
197 	uintptr_t last_vnet;
198 
199 	ASSERT_SYNCHRONIZED_OP(sc);
200 
201 	IN6_IFADDR_RLOCK(&in6_ifa_tracker);
202 	mtx_lock(&sc->clip_table_lock);
203 
204 	gen = atomic_load_acq_int(&in6_ifaddr_gen);
205 	if (gen == sc->clip_gen)
206 		goto done;
207 
208 	TAILQ_INIT(&stale);
209 	TAILQ_CONCAT(&stale, &sc->clip_table, link);
210 
211 	/*
212 	 * last_vnet optimizes the common cases where all if_vnet = NULL (no
213 	 * VIMAGE) or all if_vnet = vnet0.
214 	 */
215 	last_vnet = (uintptr_t)(-1);
216 	for_each_port(sc, i)
217 	for_each_vi(sc->port[i], j, vi) {
218 		if (IS_DOOMED(vi))
219 			continue;
220 
221 		if (last_vnet == (uintptr_t)vi->ifp->if_vnet)
222 			continue;
223 
224 		/* XXX: races with if_vmove */
225 		CURVNET_SET(vi->ifp->if_vnet);
226 		CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
227 			lip = &ia->ia_addr.sin6_addr;
228 
229 			KASSERT(!IN6_IS_ADDR_MULTICAST(lip),
230 			    ("%s: mcast address in in6_ifaddr list", __func__));
231 
232 			if (IN6_IS_ADDR_LOOPBACK(lip))
233 				continue;
234 			if (IN6_IS_SCOPE_EMBED(lip)) {
235 				/* Remove the embedded scope */
236 				tlip = *lip;
237 				lip = &tlip;
238 				in6_clearscope(lip);
239 			}
240 			/*
241 			 * XXX: how to weed out the link local address for the
242 			 * loopback interface?  It's fe80::1 usually (always?).
243 			 */
244 
245 			/*
246 			 * If it's in the main list then we already know it's
247 			 * not stale.
248 			 */
249 			TAILQ_FOREACH(ce, &sc->clip_table, link) {
250 				if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
251 					goto next;
252 			}
253 
254 			/*
255 			 * If it's in the stale list we should move it to the
256 			 * main list.
257 			 */
258 			TAILQ_FOREACH(ce, &stale, link) {
259 				if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip)) {
260 					TAILQ_REMOVE(&stale, ce, link);
261 					TAILQ_INSERT_TAIL(&sc->clip_table, ce,
262 					    link);
263 					goto next;
264 				}
265 			}
266 
267 			/* A new IP6 address; add it to the CLIP table */
268 			ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT);
269 			memcpy(&ce->lip, lip, sizeof(ce->lip));
270 			ce->refcount = 0;
271 			rc = add_lip(sc, lip);
272 			if (rc == 0)
273 				TAILQ_INSERT_TAIL(&sc->clip_table, ce, link);
274 			else {
275 				char ip[INET6_ADDRSTRLEN];
276 
277 				inet_ntop(AF_INET6, &ce->lip, &ip[0],
278 				    sizeof(ip));
279 				if (sc->flags & KERN_TLS_ON ||
280 				    sc->active_ulds != 0) {
281 					log(LOG_ERR,
282 					    "%s: could not add %s (%d)\n",
283 					    __func__, ip, rc);
284 				}
285 				free(ce, M_CXGBE);
286 			}
287 next:
288 			continue;
289 		}
290 		CURVNET_RESTORE();
291 		last_vnet = (uintptr_t)vi->ifp->if_vnet;
292 	}
293 
294 	/*
295 	 * Remove stale addresses (those no longer in V_in6_ifaddrhead) that are
296 	 * no longer referenced by the driver.
297 	 */
298 	TAILQ_FOREACH_SAFE(ce, &stale, link, ce_temp) {
299 		if (ce->refcount == 0) {
300 			rc = delete_lip(sc, &ce->lip);
301 			if (rc == 0) {
302 				TAILQ_REMOVE(&stale, ce, link);
303 				free(ce, M_CXGBE);
304 			} else {
305 				char ip[INET6_ADDRSTRLEN];
306 
307 				inet_ntop(AF_INET6, &ce->lip, &ip[0],
308 				    sizeof(ip));
309 				log(LOG_ERR, "%s: could not delete %s (%d)\n",
310 				    __func__, ip, rc);
311 			}
312 		}
313 	}
314 	/* The ones that are still referenced need to stay in the CLIP table */
315 	TAILQ_CONCAT(&sc->clip_table, &stale, link);
316 
317 	sc->clip_gen = gen;
318 done:
319 	mtx_unlock(&sc->clip_table_lock);
320 	IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
321 }
322 
323 void
324 t4_destroy_clip_table(struct adapter *sc)
325 {
326 	struct clip_entry *ce, *ce_temp;
327 
328 	if (mtx_initialized(&sc->clip_table_lock)) {
329 		mtx_lock(&sc->clip_table_lock);
330 		TAILQ_FOREACH_SAFE(ce, &sc->clip_table, link, ce_temp) {
331 			KASSERT(ce->refcount == 0,
332 			    ("%s: CLIP entry %p still in use (%d)", __func__,
333 			    ce, ce->refcount));
334 			TAILQ_REMOVE(&sc->clip_table, ce, link);
335 #if 0
336 			delete_lip(sc, &ce->lip);
337 #endif
338 			free(ce, M_CXGBE);
339 		}
340 		mtx_unlock(&sc->clip_table_lock);
341 		mtx_destroy(&sc->clip_table_lock);
342 	}
343 }
344 
345 static void
346 t4_tom_ifaddr_event(void *arg __unused, struct ifnet *ifp)
347 {
348 
349 	atomic_add_rel_int(&in6_ifaddr_gen, 1);
350 	taskqueue_enqueue_timeout(taskqueue_thread, &clip_task, -hz / 4);
351 }
352 
353 int
354 sysctl_clip(SYSCTL_HANDLER_ARGS)
355 {
356 	struct adapter *sc = arg1;
357 	struct clip_entry *ce;
358 	struct sbuf *sb;
359 	int rc, header = 0;
360 	char ip[INET6_ADDRSTRLEN];
361 
362 	rc = sysctl_wire_old_buffer(req, 0);
363 	if (rc != 0)
364 		return (rc);
365 
366 	sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
367 	if (sb == NULL)
368 		return (ENOMEM);
369 
370 	mtx_lock(&sc->clip_table_lock);
371 	TAILQ_FOREACH(ce, &sc->clip_table, link) {
372 		if (header == 0) {
373 			sbuf_printf(sb, "%-40s %-5s", "IP address", "Users");
374 			header = 1;
375 		}
376 		inet_ntop(AF_INET6, &ce->lip, &ip[0], sizeof(ip));
377 
378 		sbuf_printf(sb, "\n%-40s %5u", ip, ce->refcount);
379 	}
380 	mtx_unlock(&sc->clip_table_lock);
381 
382 	rc = sbuf_finish(sb);
383 	sbuf_delete(sb);
384 
385 	return (rc);
386 }
387 
388 void
389 t4_clip_modload(void)
390 {
391 
392 	TIMEOUT_TASK_INIT(taskqueue_thread, &clip_task, 0, t4_clip_task, NULL);
393 	ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event,
394 	    t4_tom_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY);
395 }
396 
397 void
398 t4_clip_modunload(void)
399 {
400 
401 	EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_evhandler);
402 	taskqueue_cancel_timeout(taskqueue_thread, &clip_task, NULL);
403 }
404 #endif
405