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