t4_tracer.c revision 253691
1253691Snp/*-
2253691Snp * Copyright (c) 2013 Chelsio Communications, Inc.
3253691Snp * All rights reserved.
4253691Snp * Written by: Navdeep Parhar <np@FreeBSD.org>
5253691Snp *
6253691Snp * Redistribution and use in source and binary forms, with or without
7253691Snp * modification, are permitted provided that the following conditions
8253691Snp * are met:
9253691Snp * 1. Redistributions of source code must retain the above copyright
10253691Snp *    notice, this list of conditions and the following disclaimer.
11253691Snp * 2. Redistributions in binary form must reproduce the above copyright
12253691Snp *    notice, this list of conditions and the following disclaimer in the
13253691Snp *    documentation and/or other materials provided with the distribution.
14253691Snp *
15253691Snp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16253691Snp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17253691Snp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18253691Snp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19253691Snp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20253691Snp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21253691Snp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22253691Snp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23253691Snp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24253691Snp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25253691Snp * SUCH DAMAGE.
26253691Snp */
27253691Snp
28253691Snp#include <sys/cdefs.h>
29253691Snp__FBSDID("$FreeBSD: head/sys/dev/cxgbe/t4_tracer.c 253691 2013-07-26 22:04:11Z np $");
30253691Snp
31253691Snp#include "opt_inet.h"
32253691Snp#include "opt_inet6.h"
33253691Snp
34253691Snp#include <sys/param.h>
35253691Snp#include <sys/lock.h>
36253691Snp#include <sys/types.h>
37253691Snp#include <sys/mbuf.h>
38253691Snp#include <sys/socket.h>
39253691Snp#include <sys/sockio.h>
40253691Snp#include <sys/sx.h>
41253691Snp#include <net/bpf.h>
42253691Snp#include <net/ethernet.h>
43253691Snp#include <net/if.h>
44253691Snp#include <net/if_clone.h>
45253691Snp#include <net/if_types.h>
46253691Snp
47253691Snp#include "common/common.h"
48253691Snp#include "common/t4_msg.h"
49253691Snp#include "common/t4_regs.h"
50253691Snp#include "t4_ioctl.h"
51253691Snp
52253691Snp/*
53253691Snp * Locking notes
54253691Snp * =============
55253691Snp *
56253691Snp * An interface cloner is registered during mod_load and it can be used to
57253691Snp * create or destroy the tracing ifnet for an adapter at any time.  It is
58253691Snp * possible for the cloned interface to outlive the adapter (adapter disappears
59253691Snp * in t4_detach but the tracing ifnet may live till mod_unload when removal of
60253691Snp * the cloner finally destroys any remaining cloned interfaces).  When tracing
61253691Snp * filters are active, this ifnet is also receiving data.  There are potential
62253691Snp * bad races between ifnet create, ifnet destroy, ifnet rx, ifnet ioctl,
63253691Snp * cxgbe_detach/t4_detach, mod_unload.
64253691Snp *
65253691Snp * a) The driver selects an iq for tracing (sc->traceq) inside a synch op.  The
66253691Snp *    iq is destroyed inside a synch op too (and sc->traceq updated).
67253691Snp * b) The cloner looks for an adapter that matches the name of the ifnet it's
68253691Snp *    been asked to create, starts a synch op on that adapter, and proceeds only
69253691Snp *    if the adapter has a tracing iq.
70253691Snp * c) The cloned ifnet and the adapter are coupled to each other via
71253691Snp *    ifp->if_softc and sc->ifp.  These can be modified only with the global
72253691Snp *    t4_trace_lock sx as well as the sc->ifp_lock mutex held.  Holding either
73253691Snp *    of these will prevent any change.
74253691Snp *
75253691Snp * The order in which all the locks involved should be acquired are:
76253691Snp * t4_list_lock
77253691Snp * adapter lock
78253691Snp * (begin synch op and let go of the above two)
79253691Snp * t4_trace_lock
80253691Snp * sc->ifp_lock
81253691Snp */
82253691Snp
83253691Snpstatic struct sx t4_trace_lock;
84253691Snpstatic const char *t4_cloner_name = "tXnex";
85253691Snpstatic struct if_clone *t4_cloner;
86253691Snp
87253691Snp/* tracer ifnet routines.  mostly no-ops. */
88253691Snpstatic void tracer_init(void *);
89253691Snpstatic int tracer_ioctl(struct ifnet *, unsigned long, caddr_t);
90253691Snpstatic int tracer_transmit(struct ifnet *, struct mbuf *);
91253691Snpstatic void tracer_qflush(struct ifnet *);
92253691Snpstatic int tracer_media_change(struct ifnet *);
93253691Snpstatic void tracer_media_status(struct ifnet *, struct ifmediareq *);
94253691Snp
95253691Snp/* match name (request/response) */
96253691Snpstruct match_rr {
97253691Snp	const char *name;
98253691Snp	int lock;	/* set to 1 to returned sc locked. */
99253691Snp	struct adapter *sc;
100253691Snp	int rc;
101253691Snp};
102253691Snp
103253691Snpstatic void
104253691Snpmatch_name(struct adapter *sc, void *arg)
105253691Snp{
106253691Snp	struct match_rr *mrr = arg;
107253691Snp
108253691Snp	if (strcmp(device_get_nameunit(sc->dev), mrr->name) != 0)
109253691Snp		return;
110253691Snp
111253691Snp	KASSERT(mrr->sc == NULL, ("%s: multiple matches (%p, %p) for %s",
112253691Snp	    __func__, mrr->sc, sc, mrr->name));
113253691Snp
114253691Snp	mrr->sc = sc;
115253691Snp	if (mrr->lock)
116253691Snp		mrr->rc = begin_synchronized_op(mrr->sc, NULL, 0, "t4clon");
117253691Snp	else
118253691Snp		mrr->rc = 0;
119253691Snp}
120253691Snp
121253691Snpstatic int
122253691Snpt4_cloner_match(struct if_clone *ifc, const char *name)
123253691Snp{
124253691Snp	struct match_rr mrr;
125253691Snp
126253691Snp	mrr.name = name;
127253691Snp	mrr.lock = 0;
128253691Snp	mrr.sc = NULL;
129253691Snp	t4_iterate(match_name, &mrr);
130253691Snp
131253691Snp	return (mrr.sc != NULL);
132253691Snp}
133253691Snp
134253691Snpstatic int
135253691Snpt4_cloner_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
136253691Snp{
137253691Snp	struct match_rr mrr;
138253691Snp	struct adapter *sc;
139253691Snp	struct ifnet *ifp;
140253691Snp	int rc, unit;
141253691Snp	const uint8_t lla[ETHER_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
142253691Snp
143253691Snp	mrr.name = name;
144253691Snp	mrr.lock = 1;
145253691Snp	mrr.sc = NULL;
146253691Snp	mrr.rc = ENOENT;
147253691Snp	t4_iterate(match_name, &mrr);
148253691Snp
149253691Snp	if (mrr.rc != 0)
150253691Snp		return (mrr.rc);
151253691Snp	sc = mrr.sc;
152253691Snp
153253691Snp	KASSERT(sc != NULL, ("%s: name (%s) matched but softc is NULL",
154253691Snp	    __func__, name));
155253691Snp	ASSERT_SYNCHRONIZED_OP(sc);
156253691Snp
157253691Snp	sx_xlock(&t4_trace_lock);
158253691Snp
159253691Snp	if (sc->ifp != NULL) {
160253691Snp		rc = EEXIST;
161253691Snp		goto done;
162253691Snp	}
163253691Snp	if (sc->traceq < 0) {
164253691Snp		rc = EAGAIN;
165253691Snp		goto done;
166253691Snp	}
167253691Snp
168253691Snp
169253691Snp	unit = -1;
170253691Snp	rc = ifc_alloc_unit(ifc, &unit);
171253691Snp	if (rc != 0)
172253691Snp		goto done;
173253691Snp
174253691Snp	ifp = if_alloc(IFT_ETHER);
175253691Snp	if (ifp == NULL) {
176253691Snp		ifc_free_unit(ifc, unit);
177253691Snp		rc = ENOMEM;
178253691Snp		goto done;
179253691Snp	}
180253691Snp
181253691Snp	/* Note that if_xname is not <if_dname><if_dunit>. */
182253691Snp	strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname));
183253691Snp	ifp->if_dname = t4_cloner_name;
184253691Snp	ifp->if_dunit = unit;
185253691Snp	ifp->if_init = tracer_init;
186253691Snp	ifp->if_flags = IFF_SIMPLEX | IFF_DRV_RUNNING;
187253691Snp	ifp->if_ioctl = tracer_ioctl;
188253691Snp	ifp->if_transmit = tracer_transmit;
189253691Snp	ifp->if_qflush = tracer_qflush;
190253691Snp	ifp->if_capabilities = IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU;
191253691Snp	ifmedia_init(&sc->media, IFM_IMASK, tracer_media_change,
192253691Snp	    tracer_media_status);
193253691Snp	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE, 0, NULL);
194253691Snp	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE);
195253691Snp	ether_ifattach(ifp, lla);
196253691Snp	if_up(ifp);
197253691Snp
198253691Snp	mtx_lock(&sc->ifp_lock);
199253691Snp	ifp->if_softc = sc;
200253691Snp	sc->ifp = ifp;
201253691Snp	mtx_unlock(&sc->ifp_lock);
202253691Snpdone:
203253691Snp	sx_xunlock(&t4_trace_lock);
204253691Snp	end_synchronized_op(sc, 0);
205253691Snp	return (rc);
206253691Snp}
207253691Snp
208253691Snpstatic int
209253691Snpt4_cloner_destroy(struct if_clone *ifc, struct ifnet *ifp)
210253691Snp{
211253691Snp	struct adapter *sc;
212253691Snp	int unit = ifp->if_dunit;
213253691Snp
214253691Snp	sx_xlock(&t4_trace_lock);
215253691Snp	sc = ifp->if_softc;
216253691Snp	if (sc != NULL) {
217253691Snp		mtx_lock(&sc->ifp_lock);
218253691Snp		sc->ifp = NULL;
219253691Snp		ifp->if_softc = NULL;
220253691Snp		mtx_unlock(&sc->ifp_lock);
221253691Snp		ifmedia_removeall(&sc->media);
222253691Snp	}
223253691Snp	ether_ifdetach(ifp);
224253691Snp	if_free(ifp);
225253691Snp	ifc_free_unit(ifc, unit);
226253691Snp	sx_xunlock(&t4_trace_lock);
227253691Snp
228253691Snp	return (0);
229253691Snp}
230253691Snp
231253691Snpvoid
232253691Snpt4_tracer_modload()
233253691Snp{
234253691Snp
235253691Snp	sx_init(&t4_trace_lock, "T4/T5 tracer lock");
236253691Snp	t4_cloner = if_clone_advanced(t4_cloner_name, 0, t4_cloner_match,
237253691Snp	    t4_cloner_create, t4_cloner_destroy);
238253691Snp}
239253691Snp
240253691Snpvoid
241253691Snpt4_tracer_modunload()
242253691Snp{
243253691Snp
244253691Snp	if (t4_cloner != NULL) {
245253691Snp		/*
246253691Snp		 * The module is being unloaded so the nexus drivers have
247253691Snp		 * detached.  The tracing interfaces can not outlive the nexus
248253691Snp		 * (ifp->if_softc is the nexus) and must have been destroyed
249253691Snp		 * already.  XXX: but if_clone is opaque to us and we can't
250253691Snp		 * assert LIST_EMPTY(&t4_cloner->ifc_iflist) at this time.
251253691Snp		 */
252253691Snp		if_clone_detach(t4_cloner);
253253691Snp	}
254253691Snp	sx_destroy(&t4_trace_lock);
255253691Snp}
256253691Snp
257253691Snpvoid
258253691Snpt4_tracer_port_detach(struct adapter *sc)
259253691Snp{
260253691Snp
261253691Snp	sx_xlock(&t4_trace_lock);
262253691Snp	if (sc->ifp != NULL) {
263253691Snp		mtx_lock(&sc->ifp_lock);
264253691Snp		sc->ifp->if_softc = NULL;
265253691Snp		sc->ifp = NULL;
266253691Snp		mtx_unlock(&sc->ifp_lock);
267253691Snp	}
268253691Snp	ifmedia_removeall(&sc->media);
269253691Snp	sx_xunlock(&t4_trace_lock);
270253691Snp}
271253691Snp
272253691Snpint
273253691Snpt4_get_tracer(struct adapter *sc, struct t4_tracer *t)
274253691Snp{
275253691Snp	int rc, i, enabled;
276253691Snp	struct trace_params tp;
277253691Snp
278253691Snp	if (t->idx >= NTRACE) {
279253691Snp		t->idx = 0xff;
280253691Snp		t->enabled = 0;
281253691Snp		t->valid = 0;
282253691Snp		return (0);
283253691Snp	}
284253691Snp
285253691Snp	rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
286253691Snp	    "t4gett");
287253691Snp	if (rc)
288253691Snp		return (rc);
289253691Snp
290253691Snp	for (i = t->idx; i < NTRACE; i++) {
291253691Snp		if (isset(&sc->tracer_valid, t->idx)) {
292253691Snp			t4_get_trace_filter(sc, &tp, i, &enabled);
293253691Snp			t->idx = i;
294253691Snp			t->enabled = enabled;
295253691Snp			t->valid = 1;
296253691Snp			memcpy(&t->tp.data[0], &tp.data[0], sizeof(t->tp.data));
297253691Snp			memcpy(&t->tp.mask[0], &tp.mask[0], sizeof(t->tp.mask));
298253691Snp			t->tp.snap_len = tp.snap_len;
299253691Snp			t->tp.min_len = tp.min_len;
300253691Snp			t->tp.skip_ofst = tp.skip_ofst;
301253691Snp			t->tp.skip_len = tp.skip_len;
302253691Snp			t->tp.invert = tp.invert;
303253691Snp
304253691Snp			/* convert channel to port iff 0 <= port < 8. */
305253691Snp			if (tp.port < 4)
306253691Snp				t->tp.port = sc->chan_map[tp.port];
307253691Snp			else if (tp.port < 8)
308253691Snp				t->tp.port = sc->chan_map[tp.port - 4] + 4;
309253691Snp			else
310253691Snp				t->tp.port = tp.port;
311253691Snp
312253691Snp			goto done;
313253691Snp		}
314253691Snp	}
315253691Snp
316253691Snp	t->idx = 0xff;
317253691Snp	t->enabled = 0;
318253691Snp	t->valid = 0;
319253691Snpdone:
320253691Snp	end_synchronized_op(sc, LOCK_HELD);
321253691Snp
322253691Snp	return (rc);
323253691Snp}
324253691Snp
325253691Snpint
326253691Snpt4_set_tracer(struct adapter *sc, struct t4_tracer *t)
327253691Snp{
328253691Snp	int rc;
329253691Snp	struct trace_params tp, *tpp;
330253691Snp
331253691Snp	if (t->idx >= NTRACE)
332253691Snp		return (EINVAL);
333253691Snp
334253691Snp	rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
335253691Snp	    "t4sett");
336253691Snp	if (rc)
337253691Snp		return (rc);
338253691Snp
339253691Snp	/*
340253691Snp	 * If no tracing filter is specified this time then check if the filter
341253691Snp	 * at the index is valid anyway because it was set previously.  If so
342253691Snp	 * then this is a legitimate enable/disable operation.
343253691Snp	 */
344253691Snp	if (t->valid == 0) {
345253691Snp		if (isset(&sc->tracer_valid, t->idx))
346253691Snp			tpp = NULL;
347253691Snp		else
348253691Snp			rc = EINVAL;
349253691Snp		goto done;
350253691Snp	}
351253691Snp
352253691Snp	if (t->tp.port > 19 || t->tp.snap_len > 9600 ||
353253691Snp	    t->tp.min_len > M_TFMINPKTSIZE || t->tp.skip_len > M_TFLENGTH ||
354253691Snp	    t->tp.skip_ofst > M_TFOFFSET) {
355253691Snp		rc = EINVAL;
356253691Snp		goto done;
357253691Snp	}
358253691Snp
359253691Snp	memcpy(&tp.data[0], &t->tp.data[0], sizeof(tp.data));
360253691Snp	memcpy(&tp.mask[0], &t->tp.mask[0], sizeof(tp.mask));
361253691Snp	tp.snap_len = t->tp.snap_len;
362253691Snp	tp.min_len = t->tp.min_len;
363253691Snp	tp.skip_ofst = t->tp.skip_ofst;
364253691Snp	tp.skip_len = t->tp.skip_len;
365253691Snp	tp.invert = !!t->tp.invert;
366253691Snp
367253691Snp	/* convert port to channel iff 0 <= port < 8. */
368253691Snp	if (t->tp.port < 4) {
369253691Snp		if (sc->port[t->tp.port] == NULL) {
370253691Snp			rc = EINVAL;
371253691Snp			goto done;
372253691Snp		}
373253691Snp		tp.port = sc->port[t->tp.port]->tx_chan;
374253691Snp	} else if (t->tp.port < 8) {
375253691Snp		if (sc->port[t->tp.port - 4] == NULL) {
376253691Snp			rc = EINVAL;
377253691Snp			goto done;
378253691Snp		}
379253691Snp		tp.port = sc->port[t->tp.port - 4]->tx_chan + 4;
380253691Snp	}
381253691Snp	tpp = &tp;
382253691Snpdone:
383253691Snp	if (rc == 0) {
384253691Snp		rc = -t4_set_trace_filter(sc, tpp, t->idx, t->enabled);
385253691Snp		if (rc == 0) {
386253691Snp			if (t->enabled) {
387253691Snp				setbit(&sc->tracer_valid, t->idx);
388253691Snp				if (sc->tracer_enabled == 0) {
389253691Snp					t4_set_reg_field(sc, A_MPS_TRC_CFG,
390253691Snp					    F_TRCEN, F_TRCEN);
391253691Snp				}
392253691Snp				setbit(&sc->tracer_enabled, t->idx);
393253691Snp			} else {
394253691Snp				clrbit(&sc->tracer_enabled, t->idx);
395253691Snp				if (sc->tracer_enabled == 0) {
396253691Snp					t4_set_reg_field(sc, A_MPS_TRC_CFG,
397253691Snp					    F_TRCEN, 0);
398253691Snp				}
399253691Snp			}
400253691Snp		}
401253691Snp	}
402253691Snp	end_synchronized_op(sc, LOCK_HELD);
403253691Snp
404253691Snp	return (rc);
405253691Snp}
406253691Snp
407253691Snpint
408253691Snpt4_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
409253691Snp{
410253691Snp	struct adapter *sc = iq->adapter;
411253691Snp	struct ifnet *ifp;
412253691Snp
413253691Snp	KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
414253691Snp	    rss->opcode));
415253691Snp
416253691Snp	mtx_lock(&sc->ifp_lock);
417253691Snp	ifp = sc->ifp;
418253691Snp	if (sc->ifp) {
419253691Snp		m_adj(m, sizeof(struct cpl_trace_pkt));
420253691Snp		m->m_pkthdr.rcvif = ifp;
421253691Snp		ETHER_BPF_MTAP(ifp, m);
422253691Snp	}
423253691Snp	mtx_unlock(&sc->ifp_lock);
424253691Snp	m_freem(m);
425253691Snp
426253691Snp	return (0);
427253691Snp}
428253691Snp
429253691Snpint
430253691Snpt5_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
431253691Snp{
432253691Snp	struct adapter *sc = iq->adapter;
433253691Snp	struct ifnet *ifp;
434253691Snp
435253691Snp	KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
436253691Snp	    rss->opcode));
437253691Snp
438253691Snp	mtx_lock(&sc->ifp_lock);
439253691Snp	ifp = sc->ifp;
440253691Snp	if (ifp != NULL) {
441253691Snp		m_adj(m, sizeof(struct cpl_t5_trace_pkt));
442253691Snp		m->m_pkthdr.rcvif = ifp;
443253691Snp		ETHER_BPF_MTAP(ifp, m);
444253691Snp	}
445253691Snp	mtx_unlock(&sc->ifp_lock);
446253691Snp	m_freem(m);
447253691Snp
448253691Snp	return (0);
449253691Snp}
450253691Snp
451253691Snp
452253691Snpstatic void
453253691Snptracer_init(void *arg)
454253691Snp{
455253691Snp
456253691Snp	return;
457253691Snp}
458253691Snp
459253691Snpstatic int
460253691Snptracer_ioctl(struct ifnet *ifp, unsigned long cmd, caddr_t data)
461253691Snp{
462253691Snp	int rc = 0;
463253691Snp	struct adapter *sc;
464253691Snp	struct ifreq *ifr = (struct ifreq *)data;
465253691Snp
466253691Snp	switch (cmd) {
467253691Snp	case SIOCSIFMTU:
468253691Snp	case SIOCSIFFLAGS:
469253691Snp	case SIOCADDMULTI:
470253691Snp	case SIOCDELMULTI:
471253691Snp	case SIOCSIFCAP:
472253691Snp		break;
473253691Snp	case SIOCSIFMEDIA:
474253691Snp	case SIOCGIFMEDIA:
475253691Snp		sx_xlock(&t4_trace_lock);
476253691Snp		sc = ifp->if_softc;
477253691Snp		if (sc == NULL)
478253691Snp			rc = EIO;
479253691Snp		else
480253691Snp			rc = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
481253691Snp		sx_xunlock(&t4_trace_lock);
482253691Snp		break;
483253691Snp	default:
484253691Snp		rc = ether_ioctl(ifp, cmd, data);
485253691Snp	}
486253691Snp
487253691Snp	return (rc);
488253691Snp}
489253691Snp
490253691Snpstatic int
491253691Snptracer_transmit(struct ifnet *ifp, struct mbuf *m)
492253691Snp{
493253691Snp
494253691Snp	m_freem(m);
495253691Snp	return (0);
496253691Snp}
497253691Snp
498253691Snpstatic void
499253691Snptracer_qflush(struct ifnet *ifp)
500253691Snp{
501253691Snp
502253691Snp	return;
503253691Snp}
504253691Snp
505253691Snpstatic int
506253691Snptracer_media_change(struct ifnet *ifp)
507253691Snp{
508253691Snp
509253691Snp	return (EOPNOTSUPP);
510253691Snp}
511253691Snp
512253691Snpstatic void
513253691Snptracer_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
514253691Snp{
515253691Snp
516253691Snp	ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
517253691Snp
518253691Snp	return;
519253691Snp}
520