2 * Copyright (c) 2013 Chelsio Communications, Inc.
4 * Written by: Navdeep Parhar <np@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
32 #include "opt_inet6.h"
34 #include <sys/param.h>
36 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sys/sockio.h>
42 #include <net/ethernet.h>
44 #include <net/if_clone.h>
45 #include <net/if_types.h>
47 #include "common/common.h"
48 #include "common/t4_msg.h"
49 #include "common/t4_regs.h"
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.
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.
75 * The order in which all the locks involved should be acquired are:
78 * (begin synch op and let go of the above two)
83 static struct sx t4_trace_lock;
84 static const char *t4_cloner_name = "tXnex";
85 static struct if_clone *t4_cloner;
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 *);
95 /* match name (request/response) */
98 int lock; /* set to 1 to returned sc locked. */
104 match_name(struct adapter *sc, void *arg)
106 struct match_rr *mrr = arg;
108 if (strcmp(device_get_nameunit(sc->dev), mrr->name) != 0)
111 KASSERT(mrr->sc == NULL, ("%s: multiple matches (%p, %p) for %s",
112 __func__, mrr->sc, sc, mrr->name));
116 mrr->rc = begin_synchronized_op(mrr->sc, NULL, 0, "t4clon");
122 t4_cloner_match(struct if_clone *ifc, const char *name)
125 if (strncmp(name, "t4nex", 5) != 0 &&
126 strncmp(name, "t5nex", 5) != 0)
128 if (name[5] < '0' || name[5] > '9')
134 t4_cloner_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
140 const uint8_t lla[ETHER_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
146 t4_iterate(match_name, &mrr);
152 KASSERT(sc != NULL, ("%s: name (%s) matched but softc is NULL",
154 ASSERT_SYNCHRONIZED_OP(sc);
156 sx_xlock(&t4_trace_lock);
158 if (sc->ifp != NULL) {
162 if (sc->traceq < 0) {
169 rc = ifc_alloc_unit(ifc, &unit);
173 ifp = if_alloc(IFT_ETHER);
175 ifc_free_unit(ifc, unit);
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);
197 mtx_lock(&sc->ifp_lock);
200 mtx_unlock(&sc->ifp_lock);
202 sx_xunlock(&t4_trace_lock);
203 end_synchronized_op(sc, 0);
208 t4_cloner_destroy(struct if_clone *ifc, struct ifnet *ifp)
211 int unit = ifp->if_dunit;
213 sx_xlock(&t4_trace_lock);
216 mtx_lock(&sc->ifp_lock);
218 ifp->if_softc = NULL;
219 mtx_unlock(&sc->ifp_lock);
220 ifmedia_removeall(&sc->media);
224 ifc_free_unit(ifc, unit);
225 sx_xunlock(&t4_trace_lock);
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);
240 t4_tracer_modunload()
243 if (t4_cloner != NULL) {
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.
251 if_clone_detach(t4_cloner);
253 sx_destroy(&t4_trace_lock);
257 t4_tracer_port_detach(struct adapter *sc)
260 sx_xlock(&t4_trace_lock);
261 if (sc->ifp != NULL) {
262 mtx_lock(&sc->ifp_lock);
263 sc->ifp->if_softc = NULL;
265 mtx_unlock(&sc->ifp_lock);
267 ifmedia_removeall(&sc->media);
268 sx_xunlock(&t4_trace_lock);
272 t4_get_tracer(struct adapter *sc, struct t4_tracer *t)
275 struct trace_params tp;
277 if (t->idx >= NTRACE) {
284 rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
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);
293 t->enabled = enabled;
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;
303 /* convert channel to port iff 0 <= port < 8. */
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;
309 t->tp.port = tp.port;
319 end_synchronized_op(sc, LOCK_HELD);
325 t4_set_tracer(struct adapter *sc, struct t4_tracer *t)
328 struct trace_params tp, *tpp;
330 if (t->idx >= NTRACE)
333 rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
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.
344 if (isset(&sc->tracer_valid, t->idx))
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) {
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;
366 /* convert port to channel iff 0 <= port < 8. */
367 if (t->tp.port < 4) {
368 if (sc->port[t->tp.port] == NULL) {
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) {
378 tp.port = sc->port[t->tp.port - 4]->tx_chan + 4;
383 rc = -t4_set_trace_filter(sc, tpp, t->idx, 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,
391 setbit(&sc->tracer_enabled, t->idx);
393 clrbit(&sc->tracer_enabled, t->idx);
394 if (sc->tracer_enabled == 0) {
395 t4_set_reg_field(sc, A_MPS_TRC_CFG,
401 end_synchronized_op(sc, LOCK_HELD);
407 t4_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
409 struct adapter *sc = iq->adapter;
412 KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
415 mtx_lock(&sc->ifp_lock);
418 m_adj(m, sizeof(struct cpl_trace_pkt));
419 m->m_pkthdr.rcvif = ifp;
420 ETHER_BPF_MTAP(ifp, m);
422 mtx_unlock(&sc->ifp_lock);
429 t5_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
431 struct adapter *sc = iq->adapter;
434 KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
437 mtx_lock(&sc->ifp_lock);
440 m_adj(m, sizeof(struct cpl_t5_trace_pkt));
441 m->m_pkthdr.rcvif = ifp;
442 ETHER_BPF_MTAP(ifp, m);
444 mtx_unlock(&sc->ifp_lock);
452 tracer_init(void *arg)
459 tracer_ioctl(struct ifnet *ifp, unsigned long cmd, caddr_t data)
463 struct ifreq *ifr = (struct ifreq *)data;
474 sx_xlock(&t4_trace_lock);
479 rc = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
480 sx_xunlock(&t4_trace_lock);
483 rc = ether_ioctl(ifp, cmd, data);
490 tracer_transmit(struct ifnet *ifp, struct mbuf *m)
498 tracer_qflush(struct ifnet *ifp)
505 tracer_media_change(struct ifnet *ifp)
512 tracer_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
515 ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;