]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/cxgbe/t4_tracer.c
cxgbe(4): Update firmwares to 1.27.5.0
[FreeBSD/FreeBSD.git] / sys / dev / cxgbe / t4_tracer.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013 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/param.h>
35 #include <sys/eventhandler.h>
36 #include <sys/lock.h>
37 #include <sys/types.h>
38 #include <sys/mbuf.h>
39 #include <sys/socket.h>
40 #include <sys/sockio.h>
41 #include <sys/sx.h>
42 #include <net/bpf.h>
43 #include <net/ethernet.h>
44 #include <net/if.h>
45 #include <net/if_clone.h>
46 #include <net/if_types.h>
47
48 #include "common/common.h"
49 #include "common/t4_msg.h"
50 #include "common/t4_regs.h"
51 #include "t4_ioctl.h"
52
53 /*
54  * Locking notes
55  * =============
56  *
57  * An interface cloner is registered during mod_load and it can be used to
58  * create or destroy the tracing ifnet for an adapter at any time.  It is
59  * possible for the cloned interface to outlive the adapter (adapter disappears
60  * in t4_detach but the tracing ifnet may live till mod_unload when removal of
61  * the cloner finally destroys any remaining cloned interfaces).  When tracing
62  * filters are active, this ifnet is also receiving data.  There are potential
63  * bad races between ifnet create, ifnet destroy, ifnet rx, ifnet ioctl,
64  * cxgbe_detach/t4_detach, mod_unload.
65  *
66  * a) The driver selects an iq for tracing (sc->traceq) inside a synch op.  The
67  *    iq is destroyed inside a synch op too (and sc->traceq updated).
68  * b) The cloner looks for an adapter that matches the name of the ifnet it's
69  *    been asked to create, starts a synch op on that adapter, and proceeds only
70  *    if the adapter has a tracing iq.
71  * c) The cloned ifnet and the adapter are coupled to each other via
72  *    ifp->if_softc and sc->ifp.  These can be modified only with the global
73  *    t4_trace_lock sx as well as the sc->ifp_lock mutex held.  Holding either
74  *    of these will prevent any change.
75  *
76  * The order in which all the locks involved should be acquired are:
77  * t4_list_lock
78  * adapter lock
79  * (begin synch op and let go of the above two)
80  * t4_trace_lock
81  * sc->ifp_lock
82  */
83
84 static struct sx t4_trace_lock;
85 static const char *t4_cloner_name = "tXnex";
86 static struct if_clone *t4_cloner;
87
88 /* tracer ifnet routines.  mostly no-ops. */
89 static void tracer_init(void *);
90 static int tracer_ioctl(if_t, unsigned long, caddr_t);
91 static int tracer_transmit(if_t, struct mbuf *);
92 static void tracer_qflush(if_t);
93 static int tracer_media_change(if_t);
94 static void tracer_media_status(if_t, struct ifmediareq *);
95
96 /* match name (request/response) */
97 struct match_rr {
98         const char *name;
99         int lock;       /* set to 1 to returned sc locked. */
100         struct adapter *sc;
101         int rc;
102 };
103
104 static void
105 match_name(struct adapter *sc, void *arg)
106 {
107         struct match_rr *mrr = arg;
108
109         if (strcmp(device_get_nameunit(sc->dev), mrr->name) != 0)
110                 return;
111
112         KASSERT(mrr->sc == NULL, ("%s: multiple matches (%p, %p) for %s",
113             __func__, mrr->sc, sc, mrr->name));
114
115         mrr->sc = sc;
116         if (mrr->lock)
117                 mrr->rc = begin_synchronized_op(mrr->sc, NULL, 0, "t4clon");
118         else
119                 mrr->rc = 0;
120 }
121
122 static int
123 t4_cloner_match(struct if_clone *ifc, const char *name)
124 {
125
126         if (strncmp(name, "t4nex", 5) != 0 &&
127             strncmp(name, "t5nex", 5) != 0 &&
128             strncmp(name, "t6nex", 5) != 0)
129                 return (0);
130         if (name[5] < '0' || name[5] > '9')
131                 return (0);
132         return (1);
133 }
134
135 static int
136 t4_cloner_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
137 {
138         struct match_rr mrr;
139         struct adapter *sc;
140         if_t ifp;
141         int rc;
142         const uint8_t lla[ETHER_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
143
144         mrr.name = name;
145         mrr.lock = 1;
146         mrr.sc = NULL;
147         mrr.rc = ENOENT;
148         t4_iterate(match_name, &mrr);
149
150         if (mrr.rc != 0)
151                 return (mrr.rc);
152         sc = mrr.sc;
153
154         KASSERT(sc != NULL, ("%s: name (%s) matched but softc is NULL",
155             __func__, name));
156         ASSERT_SYNCHRONIZED_OP(sc);
157
158         sx_xlock(&t4_trace_lock);
159
160         if (sc->ifp != NULL) {
161                 rc = EEXIST;
162                 goto done;
163         }
164         if (sc->traceq < 0) {
165                 rc = EAGAIN;
166                 goto done;
167         }
168
169         ifp = if_alloc(IFT_ETHER);
170         if (ifp == NULL) {
171                 rc = ENOMEM;
172                 goto done;
173         }
174
175         /* Note that if_xname is identical to the nexus nameunit */
176         if_initname(ifp, name, -1);
177         if_setdname(ifp, t4_cloner_name);
178         if_setinitfn(ifp, tracer_init);
179         if_setflags(ifp, IFF_SIMPLEX | IFF_DRV_RUNNING);
180         if_setioctlfn(ifp, tracer_ioctl);
181         if_settransmitfn(ifp, tracer_transmit);
182         if_setqflushfn(ifp, tracer_qflush);
183         if_setcapabilities(ifp, IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU);
184         ifmedia_init(&sc->media, IFM_IMASK, tracer_media_change,
185             tracer_media_status);
186         ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE, 0, NULL);
187         ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE);
188         sx_xunlock(&t4_trace_lock);
189         ether_ifattach(ifp, lla);
190         sx_xlock(&t4_trace_lock);
191         mtx_lock(&sc->ifp_lock);
192         if_setsoftc(ifp, sc);
193         sc->ifp = ifp;
194         mtx_unlock(&sc->ifp_lock);
195         rc = 0;
196 done:
197         sx_xunlock(&t4_trace_lock);
198         end_synchronized_op(sc, 0);
199         return (rc);
200 }
201
202 static int
203 t4_cloner_destroy(struct if_clone *ifc, if_t ifp)
204 {
205         struct adapter *sc;
206
207         sx_xlock(&t4_trace_lock);
208         sc = if_getsoftc(ifp);
209         if (sc != NULL) {
210                 mtx_lock(&sc->ifp_lock);
211                 sc->ifp = NULL;
212                 if_setsoftc(ifp, NULL);
213                 mtx_unlock(&sc->ifp_lock);
214                 ifmedia_removeall(&sc->media);
215         }
216         sx_xunlock(&t4_trace_lock);
217         ether_ifdetach(ifp);
218         if_free(ifp);
219
220         return (0);
221 }
222
223 void
224 t4_tracer_modload(void)
225 {
226
227         sx_init(&t4_trace_lock, "T4/T5 tracer lock");
228         t4_cloner = if_clone_advanced(t4_cloner_name, 0, t4_cloner_match,
229             t4_cloner_create, t4_cloner_destroy);
230 }
231
232 void
233 t4_tracer_modunload(void)
234 {
235
236         if (t4_cloner != NULL) {
237                 /*
238                  * The module is being unloaded so the nexus drivers have
239                  * detached.  The tracing interfaces can not outlive the nexus
240                  * (ifp->if_softc is the nexus) and must have been destroyed
241                  * already.  XXX: but if_clone is opaque to us and we can't
242                  * assert LIST_EMPTY(&t4_cloner->ifc_iflist) at this time.
243                  */
244                 if_clone_detach(t4_cloner);
245         }
246         sx_destroy(&t4_trace_lock);
247 }
248
249 void
250 t4_tracer_port_detach(struct adapter *sc)
251 {
252
253         sx_xlock(&t4_trace_lock);
254         if (sc->ifp != NULL) {
255                 mtx_lock(&sc->ifp_lock);
256                 if_setsoftc(sc->ifp, NULL);
257                 sc->ifp = NULL;
258                 mtx_unlock(&sc->ifp_lock);
259         }
260         ifmedia_removeall(&sc->media);
261         sx_xunlock(&t4_trace_lock);
262 }
263
264 int
265 t4_get_tracer(struct adapter *sc, struct t4_tracer *t)
266 {
267         int rc, i, enabled;
268         struct trace_params tp;
269
270         if (t->idx >= NTRACE) {
271                 t->idx = 0xff;
272                 t->enabled = 0;
273                 t->valid = 0;
274                 return (0);
275         }
276
277         rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
278             "t4gett");
279         if (rc)
280                 return (rc);
281
282         if (hw_off_limits(sc)) {
283                 rc = ENXIO;
284                 goto done;
285         }
286
287         for (i = t->idx; i < NTRACE; i++) {
288                 if (isset(&sc->tracer_valid, t->idx)) {
289                         t4_get_trace_filter(sc, &tp, i, &enabled);
290                         t->idx = i;
291                         t->enabled = enabled;
292                         t->valid = 1;
293                         memcpy(&t->tp.data[0], &tp.data[0], sizeof(t->tp.data));
294                         memcpy(&t->tp.mask[0], &tp.mask[0], sizeof(t->tp.mask));
295                         t->tp.snap_len = tp.snap_len;
296                         t->tp.min_len = tp.min_len;
297                         t->tp.skip_ofst = tp.skip_ofst;
298                         t->tp.skip_len = tp.skip_len;
299                         t->tp.invert = tp.invert;
300
301                         /* convert channel to port iff 0 <= port < 8. */
302                         if (tp.port < 4)
303                                 t->tp.port = sc->chan_map[tp.port];
304                         else if (tp.port < 8)
305                                 t->tp.port = sc->chan_map[tp.port - 4] + 4;
306                         else
307                                 t->tp.port = tp.port;
308
309                         goto done;
310                 }
311         }
312
313         t->idx = 0xff;
314         t->enabled = 0;
315         t->valid = 0;
316 done:
317         end_synchronized_op(sc, LOCK_HELD);
318
319         return (rc);
320 }
321
322 int
323 t4_set_tracer(struct adapter *sc, struct t4_tracer *t)
324 {
325         int rc;
326         struct trace_params tp, *tpp;
327
328         if (t->idx >= NTRACE)
329                 return (EINVAL);
330
331         rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
332             "t4sett");
333         if (rc)
334                 return (rc);
335
336         if (hw_off_limits(sc)) {
337                 rc = ENXIO;
338                 goto done;
339         }
340
341         /*
342          * If no tracing filter is specified this time then check if the filter
343          * at the index is valid anyway because it was set previously.  If so
344          * then this is a legitimate enable/disable operation.
345          */
346         if (t->valid == 0) {
347                 if (isset(&sc->tracer_valid, t->idx))
348                         tpp = NULL;
349                 else
350                         rc = EINVAL;
351                 goto done;
352         }
353
354         if (t->tp.port > 19 || t->tp.snap_len > 9600 ||
355             t->tp.min_len > M_TFMINPKTSIZE || t->tp.skip_len > M_TFLENGTH ||
356             t->tp.skip_ofst > M_TFOFFSET) {
357                 rc = EINVAL;
358                 goto done;
359         }
360
361         memcpy(&tp.data[0], &t->tp.data[0], sizeof(tp.data));
362         memcpy(&tp.mask[0], &t->tp.mask[0], sizeof(tp.mask));
363         tp.snap_len = t->tp.snap_len;
364         tp.min_len = t->tp.min_len;
365         tp.skip_ofst = t->tp.skip_ofst;
366         tp.skip_len = t->tp.skip_len;
367         tp.invert = !!t->tp.invert;
368
369         /* convert port to channel iff 0 <= port < 8. */
370         if (t->tp.port < 4) {
371                 if (sc->port[t->tp.port] == NULL) {
372                         rc = EINVAL;
373                         goto done;
374                 }
375                 tp.port = sc->port[t->tp.port]->tx_chan;
376         } else if (t->tp.port < 8) {
377                 if (sc->port[t->tp.port - 4] == NULL) {
378                         rc = EINVAL;
379                         goto done;
380                 }
381                 tp.port = sc->port[t->tp.port - 4]->tx_chan + 4;
382         } else
383                 tp.port = t->tp.port;
384         tpp = &tp;
385 done:
386         if (rc == 0) {
387                 rc = -t4_set_trace_filter(sc, tpp, t->idx, t->enabled);
388                 if (rc == 0) {
389                         if (t->enabled) {
390                                 setbit(&sc->tracer_valid, t->idx);
391                                 if (sc->tracer_enabled == 0) {
392                                         t4_set_reg_field(sc, A_MPS_TRC_CFG,
393                                             F_TRCEN, F_TRCEN);
394                                 }
395                                 setbit(&sc->tracer_enabled, t->idx);
396                         } else {
397                                 clrbit(&sc->tracer_enabled, t->idx);
398                                 if (sc->tracer_enabled == 0) {
399                                         t4_set_reg_field(sc, A_MPS_TRC_CFG,
400                                             F_TRCEN, 0);
401                                 }
402                         }
403                 }
404         }
405         end_synchronized_op(sc, LOCK_HELD);
406
407         return (rc);
408 }
409
410 int
411 t4_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
412 {
413         struct adapter *sc = iq->adapter;
414         if_t ifp;
415
416         KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
417             rss->opcode));
418
419         mtx_lock(&sc->ifp_lock);
420         ifp = sc->ifp;
421         if (sc->ifp) {
422                 m_adj(m, sizeof(struct cpl_trace_pkt));
423                 m->m_pkthdr.rcvif = ifp;
424                 ETHER_BPF_MTAP(ifp, m);
425         }
426         mtx_unlock(&sc->ifp_lock);
427         m_freem(m);
428
429         return (0);
430 }
431
432 int
433 t5_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
434 {
435         struct adapter *sc = iq->adapter;
436         if_t ifp;
437
438         KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
439             rss->opcode));
440
441         mtx_lock(&sc->ifp_lock);
442         ifp = sc->ifp;
443         if (ifp != NULL) {
444                 m_adj(m, sizeof(struct cpl_t5_trace_pkt));
445                 m->m_pkthdr.rcvif = ifp;
446                 ETHER_BPF_MTAP(ifp, m);
447         }
448         mtx_unlock(&sc->ifp_lock);
449         m_freem(m);
450
451         return (0);
452 }
453
454
455 static void
456 tracer_init(void *arg)
457 {
458
459         return;
460 }
461
462 static int
463 tracer_ioctl(if_t ifp, unsigned long cmd, caddr_t data)
464 {
465         int rc = 0;
466         struct adapter *sc;
467         struct ifreq *ifr = (struct ifreq *)data;
468
469         switch (cmd) {
470         case SIOCSIFMTU:
471         case SIOCSIFFLAGS:
472         case SIOCADDMULTI:
473         case SIOCDELMULTI:
474         case SIOCSIFCAP:
475                 break;
476         case SIOCSIFMEDIA:
477         case SIOCGIFMEDIA:
478         case SIOCGIFXMEDIA:
479                 sx_xlock(&t4_trace_lock);
480                 sc = if_getsoftc(ifp);
481                 if (sc == NULL)
482                         rc = EIO;
483                 else
484                         rc = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
485                 sx_xunlock(&t4_trace_lock);
486                 break;
487         default:
488                 rc = ether_ioctl(ifp, cmd, data);
489         }
490
491         return (rc);
492 }
493
494 static int
495 tracer_transmit(if_t ifp, struct mbuf *m)
496 {
497
498         m_freem(m);
499         return (0);
500 }
501
502 static void
503 tracer_qflush(if_t ifp)
504 {
505
506         return;
507 }
508
509 static int
510 tracer_media_change(if_t ifp)
511 {
512
513         return (EOPNOTSUPP);
514 }
515
516 static void
517 tracer_media_status(if_t ifp, struct ifmediareq *ifmr)
518 {
519
520         ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
521
522         return;
523 }