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