t4_tracer.c revision 255011
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 255011 2013-08-28 23:15:05Z 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
125255006Snp	if (strncmp(name, "t4nex", 5) != 0 &&
126255006Snp	    strncmp(name, "t5nex", 5) != 0)
127255006Snp		return (0);
128255006Snp	if (name[5] < '0' || name[5] > '9')
129255006Snp		return (0);
130255006Snp	return (1);
131253691Snp}
132253691Snp
133253691Snpstatic int
134253691Snpt4_cloner_create(struct if_clone *ifc, char *name, size_t len, caddr_t params)
135253691Snp{
136253691Snp	struct match_rr mrr;
137253691Snp	struct adapter *sc;
138253691Snp	struct ifnet *ifp;
139253691Snp	int rc, unit;
140253691Snp	const uint8_t lla[ETHER_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
141253691Snp
142253691Snp	mrr.name = name;
143253691Snp	mrr.lock = 1;
144253691Snp	mrr.sc = NULL;
145253691Snp	mrr.rc = ENOENT;
146253691Snp	t4_iterate(match_name, &mrr);
147253691Snp
148253691Snp	if (mrr.rc != 0)
149253691Snp		return (mrr.rc);
150253691Snp	sc = mrr.sc;
151253691Snp
152253691Snp	KASSERT(sc != NULL, ("%s: name (%s) matched but softc is NULL",
153253691Snp	    __func__, name));
154253691Snp	ASSERT_SYNCHRONIZED_OP(sc);
155253691Snp
156253691Snp	sx_xlock(&t4_trace_lock);
157253691Snp
158253691Snp	if (sc->ifp != NULL) {
159253691Snp		rc = EEXIST;
160253691Snp		goto done;
161253691Snp	}
162253691Snp	if (sc->traceq < 0) {
163253691Snp		rc = EAGAIN;
164253691Snp		goto done;
165253691Snp	}
166253691Snp
167253691Snp
168253691Snp	unit = -1;
169253691Snp	rc = ifc_alloc_unit(ifc, &unit);
170253691Snp	if (rc != 0)
171253691Snp		goto done;
172253691Snp
173253691Snp	ifp = if_alloc(IFT_ETHER);
174253691Snp	if (ifp == NULL) {
175253691Snp		ifc_free_unit(ifc, unit);
176253691Snp		rc = ENOMEM;
177253691Snp		goto done;
178253691Snp	}
179253691Snp
180253691Snp	/* Note that if_xname is not <if_dname><if_dunit>. */
181253691Snp	strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname));
182253691Snp	ifp->if_dname = t4_cloner_name;
183253691Snp	ifp->if_dunit = unit;
184253691Snp	ifp->if_init = tracer_init;
185253691Snp	ifp->if_flags = IFF_SIMPLEX | IFF_DRV_RUNNING;
186253691Snp	ifp->if_ioctl = tracer_ioctl;
187253691Snp	ifp->if_transmit = tracer_transmit;
188253691Snp	ifp->if_qflush = tracer_qflush;
189253691Snp	ifp->if_capabilities = IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU;
190253691Snp	ifmedia_init(&sc->media, IFM_IMASK, tracer_media_change,
191253691Snp	    tracer_media_status);
192253691Snp	ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE, 0, NULL);
193253691Snp	ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE);
194253691Snp	ether_ifattach(ifp, lla);
195253691Snp	if_up(ifp);
196253691Snp
197253691Snp	mtx_lock(&sc->ifp_lock);
198253691Snp	ifp->if_softc = sc;
199253691Snp	sc->ifp = ifp;
200253691Snp	mtx_unlock(&sc->ifp_lock);
201253691Snpdone:
202253691Snp	sx_xunlock(&t4_trace_lock);
203253691Snp	end_synchronized_op(sc, 0);
204253691Snp	return (rc);
205253691Snp}
206253691Snp
207253691Snpstatic int
208253691Snpt4_cloner_destroy(struct if_clone *ifc, struct ifnet *ifp)
209253691Snp{
210253691Snp	struct adapter *sc;
211253691Snp	int unit = ifp->if_dunit;
212253691Snp
213253691Snp	sx_xlock(&t4_trace_lock);
214253691Snp	sc = ifp->if_softc;
215253691Snp	if (sc != NULL) {
216253691Snp		mtx_lock(&sc->ifp_lock);
217253691Snp		sc->ifp = NULL;
218253691Snp		ifp->if_softc = NULL;
219253691Snp		mtx_unlock(&sc->ifp_lock);
220253691Snp		ifmedia_removeall(&sc->media);
221253691Snp	}
222253691Snp	ether_ifdetach(ifp);
223253691Snp	if_free(ifp);
224253691Snp	ifc_free_unit(ifc, unit);
225253691Snp	sx_xunlock(&t4_trace_lock);
226253691Snp
227253691Snp	return (0);
228253691Snp}
229253691Snp
230253691Snpvoid
231253691Snpt4_tracer_modload()
232253691Snp{
233253691Snp
234253691Snp	sx_init(&t4_trace_lock, "T4/T5 tracer lock");
235253691Snp	t4_cloner = if_clone_advanced(t4_cloner_name, 0, t4_cloner_match,
236253691Snp	    t4_cloner_create, t4_cloner_destroy);
237253691Snp}
238253691Snp
239253691Snpvoid
240253691Snpt4_tracer_modunload()
241253691Snp{
242253691Snp
243253691Snp	if (t4_cloner != NULL) {
244253691Snp		/*
245253691Snp		 * The module is being unloaded so the nexus drivers have
246253691Snp		 * detached.  The tracing interfaces can not outlive the nexus
247253691Snp		 * (ifp->if_softc is the nexus) and must have been destroyed
248253691Snp		 * already.  XXX: but if_clone is opaque to us and we can't
249253691Snp		 * assert LIST_EMPTY(&t4_cloner->ifc_iflist) at this time.
250253691Snp		 */
251253691Snp		if_clone_detach(t4_cloner);
252253691Snp	}
253253691Snp	sx_destroy(&t4_trace_lock);
254253691Snp}
255253691Snp
256253691Snpvoid
257253691Snpt4_tracer_port_detach(struct adapter *sc)
258253691Snp{
259253691Snp
260253691Snp	sx_xlock(&t4_trace_lock);
261253691Snp	if (sc->ifp != NULL) {
262253691Snp		mtx_lock(&sc->ifp_lock);
263253691Snp		sc->ifp->if_softc = NULL;
264253691Snp		sc->ifp = NULL;
265253691Snp		mtx_unlock(&sc->ifp_lock);
266253691Snp	}
267253691Snp	ifmedia_removeall(&sc->media);
268253691Snp	sx_xunlock(&t4_trace_lock);
269253691Snp}
270253691Snp
271253691Snpint
272253691Snpt4_get_tracer(struct adapter *sc, struct t4_tracer *t)
273253691Snp{
274253691Snp	int rc, i, enabled;
275253691Snp	struct trace_params tp;
276253691Snp
277253691Snp	if (t->idx >= NTRACE) {
278253691Snp		t->idx = 0xff;
279253691Snp		t->enabled = 0;
280253691Snp		t->valid = 0;
281253691Snp		return (0);
282253691Snp	}
283253691Snp
284253691Snp	rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
285253691Snp	    "t4gett");
286253691Snp	if (rc)
287253691Snp		return (rc);
288253691Snp
289253691Snp	for (i = t->idx; i < NTRACE; i++) {
290253691Snp		if (isset(&sc->tracer_valid, t->idx)) {
291253691Snp			t4_get_trace_filter(sc, &tp, i, &enabled);
292253691Snp			t->idx = i;
293253691Snp			t->enabled = enabled;
294253691Snp			t->valid = 1;
295253691Snp			memcpy(&t->tp.data[0], &tp.data[0], sizeof(t->tp.data));
296253691Snp			memcpy(&t->tp.mask[0], &tp.mask[0], sizeof(t->tp.mask));
297253691Snp			t->tp.snap_len = tp.snap_len;
298253691Snp			t->tp.min_len = tp.min_len;
299253691Snp			t->tp.skip_ofst = tp.skip_ofst;
300253691Snp			t->tp.skip_len = tp.skip_len;
301253691Snp			t->tp.invert = tp.invert;
302253691Snp
303253691Snp			/* convert channel to port iff 0 <= port < 8. */
304253691Snp			if (tp.port < 4)
305253691Snp				t->tp.port = sc->chan_map[tp.port];
306253691Snp			else if (tp.port < 8)
307253691Snp				t->tp.port = sc->chan_map[tp.port - 4] + 4;
308253691Snp			else
309253691Snp				t->tp.port = tp.port;
310253691Snp
311253691Snp			goto done;
312253691Snp		}
313253691Snp	}
314253691Snp
315253691Snp	t->idx = 0xff;
316253691Snp	t->enabled = 0;
317253691Snp	t->valid = 0;
318253691Snpdone:
319253691Snp	end_synchronized_op(sc, LOCK_HELD);
320253691Snp
321253691Snp	return (rc);
322253691Snp}
323253691Snp
324253691Snpint
325253691Snpt4_set_tracer(struct adapter *sc, struct t4_tracer *t)
326253691Snp{
327253691Snp	int rc;
328253691Snp	struct trace_params tp, *tpp;
329253691Snp
330253691Snp	if (t->idx >= NTRACE)
331253691Snp		return (EINVAL);
332253691Snp
333253691Snp	rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
334253691Snp	    "t4sett");
335253691Snp	if (rc)
336253691Snp		return (rc);
337253691Snp
338253691Snp	/*
339253691Snp	 * If no tracing filter is specified this time then check if the filter
340253691Snp	 * at the index is valid anyway because it was set previously.  If so
341253691Snp	 * then this is a legitimate enable/disable operation.
342253691Snp	 */
343253691Snp	if (t->valid == 0) {
344253691Snp		if (isset(&sc->tracer_valid, t->idx))
345253691Snp			tpp = NULL;
346253691Snp		else
347253691Snp			rc = EINVAL;
348253691Snp		goto done;
349253691Snp	}
350253691Snp
351253691Snp	if (t->tp.port > 19 || t->tp.snap_len > 9600 ||
352253691Snp	    t->tp.min_len > M_TFMINPKTSIZE || t->tp.skip_len > M_TFLENGTH ||
353253691Snp	    t->tp.skip_ofst > M_TFOFFSET) {
354253691Snp		rc = EINVAL;
355253691Snp		goto done;
356253691Snp	}
357253691Snp
358253691Snp	memcpy(&tp.data[0], &t->tp.data[0], sizeof(tp.data));
359253691Snp	memcpy(&tp.mask[0], &t->tp.mask[0], sizeof(tp.mask));
360253691Snp	tp.snap_len = t->tp.snap_len;
361253691Snp	tp.min_len = t->tp.min_len;
362253691Snp	tp.skip_ofst = t->tp.skip_ofst;
363253691Snp	tp.skip_len = t->tp.skip_len;
364253691Snp	tp.invert = !!t->tp.invert;
365253691Snp
366253691Snp	/* convert port to channel iff 0 <= port < 8. */
367253691Snp	if (t->tp.port < 4) {
368253691Snp		if (sc->port[t->tp.port] == NULL) {
369253691Snp			rc = EINVAL;
370253691Snp			goto done;
371253691Snp		}
372253691Snp		tp.port = sc->port[t->tp.port]->tx_chan;
373253691Snp	} else if (t->tp.port < 8) {
374253691Snp		if (sc->port[t->tp.port - 4] == NULL) {
375253691Snp			rc = EINVAL;
376253691Snp			goto done;
377253691Snp		}
378253691Snp		tp.port = sc->port[t->tp.port - 4]->tx_chan + 4;
379253691Snp	}
380253691Snp	tpp = &tp;
381253691Snpdone:
382253691Snp	if (rc == 0) {
383253691Snp		rc = -t4_set_trace_filter(sc, tpp, t->idx, t->enabled);
384253691Snp		if (rc == 0) {
385253691Snp			if (t->enabled) {
386253691Snp				setbit(&sc->tracer_valid, t->idx);
387253691Snp				if (sc->tracer_enabled == 0) {
388253691Snp					t4_set_reg_field(sc, A_MPS_TRC_CFG,
389253691Snp					    F_TRCEN, F_TRCEN);
390253691Snp				}
391253691Snp				setbit(&sc->tracer_enabled, t->idx);
392253691Snp			} else {
393253691Snp				clrbit(&sc->tracer_enabled, t->idx);
394253691Snp				if (sc->tracer_enabled == 0) {
395253691Snp					t4_set_reg_field(sc, A_MPS_TRC_CFG,
396253691Snp					    F_TRCEN, 0);
397253691Snp				}
398253691Snp			}
399253691Snp		}
400253691Snp	}
401253691Snp	end_synchronized_op(sc, LOCK_HELD);
402253691Snp
403253691Snp	return (rc);
404253691Snp}
405253691Snp
406253691Snpint
407253691Snpt4_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
408253691Snp{
409253691Snp	struct adapter *sc = iq->adapter;
410253691Snp	struct ifnet *ifp;
411253691Snp
412253691Snp	KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
413253691Snp	    rss->opcode));
414253691Snp
415253691Snp	mtx_lock(&sc->ifp_lock);
416253691Snp	ifp = sc->ifp;
417253691Snp	if (sc->ifp) {
418253691Snp		m_adj(m, sizeof(struct cpl_trace_pkt));
419253691Snp		m->m_pkthdr.rcvif = ifp;
420253691Snp		ETHER_BPF_MTAP(ifp, m);
421253691Snp	}
422253691Snp	mtx_unlock(&sc->ifp_lock);
423253691Snp	m_freem(m);
424253691Snp
425253691Snp	return (0);
426253691Snp}
427253691Snp
428253691Snpint
429253691Snpt5_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
430253691Snp{
431253691Snp	struct adapter *sc = iq->adapter;
432253691Snp	struct ifnet *ifp;
433253691Snp
434253691Snp	KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
435253691Snp	    rss->opcode));
436253691Snp
437253691Snp	mtx_lock(&sc->ifp_lock);
438253691Snp	ifp = sc->ifp;
439253691Snp	if (ifp != NULL) {
440253691Snp		m_adj(m, sizeof(struct cpl_t5_trace_pkt));
441253691Snp		m->m_pkthdr.rcvif = ifp;
442253691Snp		ETHER_BPF_MTAP(ifp, m);
443253691Snp	}
444253691Snp	mtx_unlock(&sc->ifp_lock);
445253691Snp	m_freem(m);
446253691Snp
447253691Snp	return (0);
448253691Snp}
449253691Snp
450253691Snp
451253691Snpstatic void
452253691Snptracer_init(void *arg)
453253691Snp{
454253691Snp
455253691Snp	return;
456253691Snp}
457253691Snp
458253691Snpstatic int
459253691Snptracer_ioctl(struct ifnet *ifp, unsigned long cmd, caddr_t data)
460253691Snp{
461253691Snp	int rc = 0;
462253691Snp	struct adapter *sc;
463253691Snp	struct ifreq *ifr = (struct ifreq *)data;
464253691Snp
465253691Snp	switch (cmd) {
466253691Snp	case SIOCSIFMTU:
467253691Snp	case SIOCSIFFLAGS:
468255011Snp	case SIOCADDMULTI:
469253691Snp	case SIOCDELMULTI:
470253691Snp	case SIOCSIFCAP:
471253691Snp		break;
472253691Snp	case SIOCSIFMEDIA:
473253691Snp	case SIOCGIFMEDIA:
474253691Snp		sx_xlock(&t4_trace_lock);
475253691Snp		sc = ifp->if_softc;
476253691Snp		if (sc == NULL)
477253691Snp			rc = EIO;
478253691Snp		else
479253691Snp			rc = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
480253691Snp		sx_xunlock(&t4_trace_lock);
481253691Snp		break;
482253691Snp	default:
483253691Snp		rc = ether_ioctl(ifp, cmd, data);
484253691Snp	}
485253691Snp
486253691Snp	return (rc);
487253691Snp}
488253691Snp
489253691Snpstatic int
490253691Snptracer_transmit(struct ifnet *ifp, struct mbuf *m)
491253691Snp{
492253691Snp
493253691Snp	m_freem(m);
494253691Snp	return (0);
495253691Snp}
496253691Snp
497253691Snpstatic void
498253691Snptracer_qflush(struct ifnet *ifp)
499253691Snp{
500253691Snp
501253691Snp	return;
502253691Snp}
503253691Snp
504253691Snpstatic int
505253691Snptracer_media_change(struct ifnet *ifp)
506253691Snp{
507253691Snp
508253691Snp	return (EOPNOTSUPP);
509253691Snp}
510253691Snp
511253691Snpstatic void
512253691Snptracer_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
513253691Snp{
514253691Snp
515253691Snp	ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
516253691Snp
517253691Snp	return;
518253691Snp}
519