]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/cxgbe/t4_clip.c
Fix powerpc LINT build
[FreeBSD/FreeBSD.git] / sys / dev / cxgbe / t4_clip.c
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 (last_vnet == (uintptr_t)vi->ifp->if_vnet)
219                         continue;
220
221                 /* XXX: races with if_vmove */
222                 CURVNET_SET(vi->ifp->if_vnet);
223                 CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
224                         lip = &ia->ia_addr.sin6_addr;
225
226                         KASSERT(!IN6_IS_ADDR_MULTICAST(lip),
227                             ("%s: mcast address in in6_ifaddr list", __func__));
228
229                         if (IN6_IS_ADDR_LOOPBACK(lip))
230                                 continue;
231                         if (IN6_IS_SCOPE_EMBED(lip)) {
232                                 /* Remove the embedded scope */
233                                 tlip = *lip;
234                                 lip = &tlip;
235                                 in6_clearscope(lip);
236                         }
237                         /*
238                          * XXX: how to weed out the link local address for the
239                          * loopback interface?  It's fe80::1 usually (always?).
240                          */
241
242                         /*
243                          * If it's in the main list then we already know it's
244                          * not stale.
245                          */
246                         TAILQ_FOREACH(ce, &sc->clip_table, link) {
247                                 if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
248                                         goto next;
249                         }
250
251                         /*
252                          * If it's in the stale list we should move it to the
253                          * main list.
254                          */
255                         TAILQ_FOREACH(ce, &stale, link) {
256                                 if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip)) {
257                                         TAILQ_REMOVE(&stale, ce, link);
258                                         TAILQ_INSERT_TAIL(&sc->clip_table, ce,
259                                             link);
260                                         goto next;
261                                 }
262                         }
263
264                         /* A new IP6 address; add it to the CLIP table */
265                         ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT);
266                         memcpy(&ce->lip, lip, sizeof(ce->lip));
267                         ce->refcount = 0;
268                         rc = add_lip(sc, lip);
269                         if (rc == 0)
270                                 TAILQ_INSERT_TAIL(&sc->clip_table, ce, link);
271                         else {
272                                 char ip[INET6_ADDRSTRLEN];
273
274                                 inet_ntop(AF_INET6, &ce->lip, &ip[0],
275                                     sizeof(ip));
276                                 log(LOG_ERR, "%s: could not add %s (%d)\n",
277                                     __func__, ip, rc);
278                                 free(ce, M_CXGBE);
279                         }
280 next:
281                         continue;
282                 }
283                 CURVNET_RESTORE();
284                 last_vnet = (uintptr_t)vi->ifp->if_vnet;
285         }
286
287         /*
288          * Remove stale addresses (those no longer in V_in6_ifaddrhead) that are
289          * no longer referenced by the driver.
290          */
291         TAILQ_FOREACH_SAFE(ce, &stale, link, ce_temp) {
292                 if (ce->refcount == 0) {
293                         rc = delete_lip(sc, &ce->lip);
294                         if (rc == 0) {
295                                 TAILQ_REMOVE(&stale, ce, link);
296                                 free(ce, M_CXGBE);
297                         } else {
298                                 char ip[INET6_ADDRSTRLEN];
299
300                                 inet_ntop(AF_INET6, &ce->lip, &ip[0],
301                                     sizeof(ip));
302                                 log(LOG_ERR, "%s: could not delete %s (%d)\n",
303                                     __func__, ip, rc);
304                         }
305                 }
306         }
307         /* The ones that are still referenced need to stay in the CLIP table */
308         TAILQ_CONCAT(&sc->clip_table, &stale, link);
309
310         sc->clip_gen = gen;
311 done:
312         mtx_unlock(&sc->clip_table_lock);
313         IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
314 }
315
316 void
317 t4_destroy_clip_table(struct adapter *sc)
318 {
319         struct clip_entry *ce, *ce_temp;
320
321         if (mtx_initialized(&sc->clip_table_lock)) {
322                 mtx_lock(&sc->clip_table_lock);
323                 TAILQ_FOREACH_SAFE(ce, &sc->clip_table, link, ce_temp) {
324                         KASSERT(ce->refcount == 0,
325                             ("%s: CLIP entry %p still in use (%d)", __func__,
326                             ce, ce->refcount));
327                         TAILQ_REMOVE(&sc->clip_table, ce, link);
328 #if 0
329                         delete_lip(sc, &ce->lip);
330 #endif
331                         free(ce, M_CXGBE);
332                 }
333                 mtx_unlock(&sc->clip_table_lock);
334                 mtx_destroy(&sc->clip_table_lock);
335         }
336 }
337
338 static void
339 t4_tom_ifaddr_event(void *arg __unused, struct ifnet *ifp)
340 {
341
342         atomic_add_rel_int(&in6_ifaddr_gen, 1);
343         taskqueue_enqueue_timeout(taskqueue_thread, &clip_task, -hz / 4);
344 }
345
346 int
347 sysctl_clip(SYSCTL_HANDLER_ARGS)
348 {
349         struct adapter *sc = arg1;
350         struct clip_entry *ce;
351         struct sbuf *sb;
352         int rc, header = 0;
353         char ip[INET6_ADDRSTRLEN];
354
355         rc = sysctl_wire_old_buffer(req, 0);
356         if (rc != 0)
357                 return (rc);
358
359         sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
360         if (sb == NULL)
361                 return (ENOMEM);
362
363         mtx_lock(&sc->clip_table_lock);
364         TAILQ_FOREACH(ce, &sc->clip_table, link) {
365                 if (header == 0) {
366                         sbuf_printf(sb, "%-40s %-5s", "IP address", "Users");
367                         header = 1;
368                 }
369                 inet_ntop(AF_INET6, &ce->lip, &ip[0], sizeof(ip));
370
371                 sbuf_printf(sb, "\n%-40s %5u", ip, ce->refcount);
372         }
373         mtx_unlock(&sc->clip_table_lock);
374
375         rc = sbuf_finish(sb);
376         sbuf_delete(sb);
377
378         return (rc);
379 }
380
381 void
382 t4_clip_modload(void)
383 {
384
385         TIMEOUT_TASK_INIT(taskqueue_thread, &clip_task, 0, t4_clip_task, NULL);
386         ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event,
387             t4_tom_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY);
388 }
389
390 void
391 t4_clip_modunload(void)
392 {
393
394         EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_evhandler);
395         taskqueue_cancel_timeout(taskqueue_thread, &clip_task, NULL);
396 }
397 #endif