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