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
86static struct sx t4_trace_lock;
87static const char *t4_cloner_name = "tXnex";
88static struct if_clone *t4_cloner;
89
90/* tracer ifnet routines.  mostly no-ops. */
91static void tracer_init(void *);
92static int tracer_ioctl(struct ifnet *, unsigned long, caddr_t);
93static int tracer_transmit(struct ifnet *, struct mbuf *);
94static void tracer_qflush(struct ifnet *);
95static int tracer_media_change(struct ifnet *);
96static void tracer_media_status(struct ifnet *, struct ifmediareq *);
97
98/* match name (request/response) */
99struct 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
106static void
107match_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
124static int
125t4_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
137static int
138t4_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);
204done:
205	sx_xunlock(&t4_trace_lock);
206	end_synchronized_op(sc, 0);
207	return (rc);
208}
209
210static int
211t4_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
233void
234t4_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
242void
243t4_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
259void
260t4_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
274int
275t4_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	for (i = t->idx; i < NTRACE; i++) {
293		if (isset(&sc->tracer_valid, t->idx)) {
294			t4_get_trace_filter(sc, &tp, i, &enabled);
295			t->idx = i;
296			t->enabled = enabled;
297			t->valid = 1;
298			memcpy(&t->tp.data[0], &tp.data[0], sizeof(t->tp.data));
299			memcpy(&t->tp.mask[0], &tp.mask[0], sizeof(t->tp.mask));
300			t->tp.snap_len = tp.snap_len;
301			t->tp.min_len = tp.min_len;
302			t->tp.skip_ofst = tp.skip_ofst;
303			t->tp.skip_len = tp.skip_len;
304			t->tp.invert = tp.invert;
305
306			/* convert channel to port iff 0 <= port < 8. */
307			if (tp.port < 4)
308				t->tp.port = sc->chan_map[tp.port];
309			else if (tp.port < 8)
310				t->tp.port = sc->chan_map[tp.port - 4] + 4;
311			else
312				t->tp.port = tp.port;
313
314			goto done;
315		}
316	}
317
318	t->idx = 0xff;
319	t->enabled = 0;
320	t->valid = 0;
321done:
322	end_synchronized_op(sc, LOCK_HELD);
323
324	return (rc);
325}
326
327int
328t4_set_tracer(struct adapter *sc, struct t4_tracer *t)
329{
330	int rc;
331	struct trace_params tp, *tpp;
332
333	if (t->idx >= NTRACE)
334		return (EINVAL);
335
336	rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK,
337	    "t4sett");
338	if (rc)
339		return (rc);
340
341	/*
342	 * If no tracing filter is specified this time then check if the filter
343	 * at the index is valid anyway because it was set previously.  If so
344	 * then this is a legitimate enable/disable operation.
345	 */
346	if (t->valid == 0) {
347		if (isset(&sc->tracer_valid, t->idx))
348			tpp = NULL;
349		else
350			rc = EINVAL;
351		goto done;
352	}
353
354	if (t->tp.port > 19 || t->tp.snap_len > 9600 ||
355	    t->tp.min_len > M_TFMINPKTSIZE || t->tp.skip_len > M_TFLENGTH ||
356	    t->tp.skip_ofst > M_TFOFFSET) {
357		rc = EINVAL;
358		goto done;
359	}
360
361	memcpy(&tp.data[0], &t->tp.data[0], sizeof(tp.data));
362	memcpy(&tp.mask[0], &t->tp.mask[0], sizeof(tp.mask));
363	tp.snap_len = t->tp.snap_len;
364	tp.min_len = t->tp.min_len;
365	tp.skip_ofst = t->tp.skip_ofst;
366	tp.skip_len = t->tp.skip_len;
367	tp.invert = !!t->tp.invert;
368
369	/* convert port to channel iff 0 <= port < 8. */
370	if (t->tp.port < 4) {
371		if (sc->port[t->tp.port] == NULL) {
372			rc = EINVAL;
373			goto done;
374		}
375		tp.port = sc->port[t->tp.port]->tx_chan;
376	} else if (t->tp.port < 8) {
377		if (sc->port[t->tp.port - 4] == NULL) {
378			rc = EINVAL;
379			goto done;
380		}
381		tp.port = sc->port[t->tp.port - 4]->tx_chan + 4;
382	}
383	tpp = &tp;
384done:
385	if (rc == 0) {
386		rc = -t4_set_trace_filter(sc, tpp, t->idx, t->enabled);
387		if (rc == 0) {
388			if (t->enabled) {
389				setbit(&sc->tracer_valid, t->idx);
390				if (sc->tracer_enabled == 0) {
391					t4_set_reg_field(sc, A_MPS_TRC_CFG,
392					    F_TRCEN, F_TRCEN);
393				}
394				setbit(&sc->tracer_enabled, t->idx);
395			} else {
396				clrbit(&sc->tracer_enabled, t->idx);
397				if (sc->tracer_enabled == 0) {
398					t4_set_reg_field(sc, A_MPS_TRC_CFG,
399					    F_TRCEN, 0);
400				}
401			}
402		}
403	}
404	end_synchronized_op(sc, LOCK_HELD);
405
406	return (rc);
407}
408
409int
410t4_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
411{
412	struct adapter *sc = iq->adapter;
413	struct ifnet *ifp;
414
415	KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
416	    rss->opcode));
417
418	mtx_lock(&sc->ifp_lock);
419	ifp = sc->ifp;
420	if (sc->ifp) {
421		m_adj(m, sizeof(struct cpl_trace_pkt));
422		m->m_pkthdr.rcvif = ifp;
423		ETHER_BPF_MTAP(ifp, m);
424	}
425	mtx_unlock(&sc->ifp_lock);
426	m_freem(m);
427
428	return (0);
429}
430
431int
432t5_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m)
433{
434	struct adapter *sc = iq->adapter;
435	struct ifnet *ifp;
436
437	KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__,
438	    rss->opcode));
439
440	mtx_lock(&sc->ifp_lock);
441	ifp = sc->ifp;
442	if (ifp != NULL) {
443		m_adj(m, sizeof(struct cpl_t5_trace_pkt));
444		m->m_pkthdr.rcvif = ifp;
445		ETHER_BPF_MTAP(ifp, m);
446	}
447	mtx_unlock(&sc->ifp_lock);
448	m_freem(m);
449
450	return (0);
451}
452
453
454static void
455tracer_init(void *arg)
456{
457
458	return;
459}
460
461static int
462tracer_ioctl(struct ifnet *ifp, unsigned long cmd, caddr_t data)
463{
464	int rc = 0;
465	struct adapter *sc;
466	struct ifreq *ifr = (struct ifreq *)data;
467
468	switch (cmd) {
469	case SIOCSIFMTU:
470	case SIOCSIFFLAGS:
471	case SIOCADDMULTI:
472	case SIOCDELMULTI:
473	case SIOCSIFCAP:
474		break;
475	case SIOCSIFMEDIA:
476	case SIOCGIFMEDIA:
477	case SIOCGIFXMEDIA:
478		sx_xlock(&t4_trace_lock);
479		sc = ifp->if_softc;
480		if (sc == NULL)
481			rc = EIO;
482		else
483			rc = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
484		sx_xunlock(&t4_trace_lock);
485		break;
486	default:
487		rc = ether_ioctl(ifp, cmd, data);
488	}
489
490	return (rc);
491}
492
493static int
494tracer_transmit(struct ifnet *ifp, struct mbuf *m)
495{
496
497	m_freem(m);
498	return (0);
499}
500
501static void
502tracer_qflush(struct ifnet *ifp)
503{
504
505	return;
506}
507
508static int
509tracer_media_change(struct ifnet *ifp)
510{
511
512	return (EOPNOTSUPP);
513}
514
515static void
516tracer_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
517{
518
519	ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
520
521	return;
522}
523