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: stable/11/sys/dev/cxgbe/t4_tracer.c 309560 2016-12-05 20:43:25Z jhb $"); 30253691Snp 31253691Snp#include "opt_inet.h" 32253691Snp#include "opt_inet6.h" 33253691Snp 34253691Snp#include <sys/param.h> 35257241Sglebius#include <sys/eventhandler.h> 36253691Snp#include <sys/lock.h> 37253691Snp#include <sys/types.h> 38253691Snp#include <sys/mbuf.h> 39253691Snp#include <sys/socket.h> 40253691Snp#include <sys/sockio.h> 41253691Snp#include <sys/sx.h> 42253691Snp#include <net/bpf.h> 43253691Snp#include <net/ethernet.h> 44253691Snp#include <net/if.h> 45253691Snp#include <net/if_clone.h> 46253691Snp#include <net/if_types.h> 47253691Snp 48253691Snp#include "common/common.h" 49253691Snp#include "common/t4_msg.h" 50253691Snp#include "common/t4_regs.h" 51253691Snp#include "t4_ioctl.h" 52253691Snp 53253691Snp/* 54253691Snp * Locking notes 55253691Snp * ============= 56253691Snp * 57253691Snp * An interface cloner is registered during mod_load and it can be used to 58253691Snp * create or destroy the tracing ifnet for an adapter at any time. It is 59253691Snp * possible for the cloned interface to outlive the adapter (adapter disappears 60253691Snp * in t4_detach but the tracing ifnet may live till mod_unload when removal of 61253691Snp * the cloner finally destroys any remaining cloned interfaces). When tracing 62253691Snp * filters are active, this ifnet is also receiving data. There are potential 63253691Snp * bad races between ifnet create, ifnet destroy, ifnet rx, ifnet ioctl, 64253691Snp * cxgbe_detach/t4_detach, mod_unload. 65253691Snp * 66253691Snp * a) The driver selects an iq for tracing (sc->traceq) inside a synch op. The 67253691Snp * iq is destroyed inside a synch op too (and sc->traceq updated). 68253691Snp * b) The cloner looks for an adapter that matches the name of the ifnet it's 69253691Snp * been asked to create, starts a synch op on that adapter, and proceeds only 70253691Snp * if the adapter has a tracing iq. 71253691Snp * c) The cloned ifnet and the adapter are coupled to each other via 72253691Snp * ifp->if_softc and sc->ifp. These can be modified only with the global 73253691Snp * t4_trace_lock sx as well as the sc->ifp_lock mutex held. Holding either 74253691Snp * of these will prevent any change. 75253691Snp * 76253691Snp * The order in which all the locks involved should be acquired are: 77253691Snp * t4_list_lock 78253691Snp * adapter lock 79253691Snp * (begin synch op and let go of the above two) 80253691Snp * t4_trace_lock 81253691Snp * sc->ifp_lock 82253691Snp */ 83253691Snp 84253691Snpstatic struct sx t4_trace_lock; 85253691Snpstatic const char *t4_cloner_name = "tXnex"; 86253691Snpstatic struct if_clone *t4_cloner; 87253691Snp 88253691Snp/* tracer ifnet routines. mostly no-ops. */ 89253691Snpstatic void tracer_init(void *); 90253691Snpstatic int tracer_ioctl(struct ifnet *, unsigned long, caddr_t); 91253691Snpstatic int tracer_transmit(struct ifnet *, struct mbuf *); 92253691Snpstatic void tracer_qflush(struct ifnet *); 93253691Snpstatic int tracer_media_change(struct ifnet *); 94253691Snpstatic void tracer_media_status(struct ifnet *, struct ifmediareq *); 95253691Snp 96253691Snp/* match name (request/response) */ 97253691Snpstruct match_rr { 98253691Snp const char *name; 99253691Snp int lock; /* set to 1 to returned sc locked. */ 100253691Snp struct adapter *sc; 101253691Snp int rc; 102253691Snp}; 103253691Snp 104253691Snpstatic void 105253691Snpmatch_name(struct adapter *sc, void *arg) 106253691Snp{ 107253691Snp struct match_rr *mrr = arg; 108253691Snp 109253691Snp if (strcmp(device_get_nameunit(sc->dev), mrr->name) != 0) 110253691Snp return; 111253691Snp 112253691Snp KASSERT(mrr->sc == NULL, ("%s: multiple matches (%p, %p) for %s", 113253691Snp __func__, mrr->sc, sc, mrr->name)); 114253691Snp 115253691Snp mrr->sc = sc; 116253691Snp if (mrr->lock) 117253691Snp mrr->rc = begin_synchronized_op(mrr->sc, NULL, 0, "t4clon"); 118253691Snp else 119253691Snp mrr->rc = 0; 120253691Snp} 121253691Snp 122253691Snpstatic int 123253691Snpt4_cloner_match(struct if_clone *ifc, const char *name) 124253691Snp{ 125253691Snp 126255006Snp if (strncmp(name, "t4nex", 5) != 0 && 127309560Sjhb strncmp(name, "t5nex", 5) != 0 && 128309560Sjhb strncmp(name, "t6nex", 5) != 0) 129255006Snp return (0); 130255006Snp if (name[5] < '0' || name[5] > '9') 131255006Snp return (0); 132255006Snp return (1); 133253691Snp} 134253691Snp 135253691Snpstatic int 136253691Snpt4_cloner_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) 137253691Snp{ 138253691Snp struct match_rr mrr; 139253691Snp struct adapter *sc; 140253691Snp struct ifnet *ifp; 141253691Snp int rc, unit; 142253691Snp const uint8_t lla[ETHER_ADDR_LEN] = {0, 0, 0, 0, 0, 0}; 143253691Snp 144253691Snp mrr.name = name; 145253691Snp mrr.lock = 1; 146253691Snp mrr.sc = NULL; 147253691Snp mrr.rc = ENOENT; 148253691Snp t4_iterate(match_name, &mrr); 149253691Snp 150253691Snp if (mrr.rc != 0) 151253691Snp return (mrr.rc); 152253691Snp sc = mrr.sc; 153253691Snp 154253691Snp KASSERT(sc != NULL, ("%s: name (%s) matched but softc is NULL", 155253691Snp __func__, name)); 156253691Snp ASSERT_SYNCHRONIZED_OP(sc); 157253691Snp 158253691Snp sx_xlock(&t4_trace_lock); 159253691Snp 160253691Snp if (sc->ifp != NULL) { 161253691Snp rc = EEXIST; 162253691Snp goto done; 163253691Snp } 164253691Snp if (sc->traceq < 0) { 165253691Snp rc = EAGAIN; 166253691Snp goto done; 167253691Snp } 168253691Snp 169253691Snp 170253691Snp unit = -1; 171253691Snp rc = ifc_alloc_unit(ifc, &unit); 172253691Snp if (rc != 0) 173253691Snp goto done; 174253691Snp 175253691Snp ifp = if_alloc(IFT_ETHER); 176253691Snp if (ifp == NULL) { 177253691Snp ifc_free_unit(ifc, unit); 178253691Snp rc = ENOMEM; 179253691Snp goto done; 180253691Snp } 181253691Snp 182253691Snp /* Note that if_xname is not <if_dname><if_dunit>. */ 183253691Snp strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname)); 184253691Snp ifp->if_dname = t4_cloner_name; 185253691Snp ifp->if_dunit = unit; 186253691Snp ifp->if_init = tracer_init; 187253691Snp ifp->if_flags = IFF_SIMPLEX | IFF_DRV_RUNNING; 188253691Snp ifp->if_ioctl = tracer_ioctl; 189253691Snp ifp->if_transmit = tracer_transmit; 190253691Snp ifp->if_qflush = tracer_qflush; 191253691Snp ifp->if_capabilities = IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU; 192253691Snp ifmedia_init(&sc->media, IFM_IMASK, tracer_media_change, 193253691Snp tracer_media_status); 194253691Snp ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE, 0, NULL); 195253691Snp ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE); 196253691Snp ether_ifattach(ifp, lla); 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: 469255011Snp case SIOCADDMULTI: 470253691Snp case SIOCDELMULTI: 471253691Snp case SIOCSIFCAP: 472253691Snp break; 473253691Snp case SIOCSIFMEDIA: 474253691Snp case SIOCGIFMEDIA: 475309560Sjhb case SIOCGIFXMEDIA: 476253691Snp sx_xlock(&t4_trace_lock); 477253691Snp sc = ifp->if_softc; 478253691Snp if (sc == NULL) 479253691Snp rc = EIO; 480253691Snp else 481253691Snp rc = ifmedia_ioctl(ifp, ifr, &sc->media, cmd); 482253691Snp sx_xunlock(&t4_trace_lock); 483253691Snp break; 484253691Snp default: 485253691Snp rc = ether_ioctl(ifp, cmd, data); 486253691Snp } 487253691Snp 488253691Snp return (rc); 489253691Snp} 490253691Snp 491253691Snpstatic int 492253691Snptracer_transmit(struct ifnet *ifp, struct mbuf *m) 493253691Snp{ 494253691Snp 495253691Snp m_freem(m); 496253691Snp return (0); 497253691Snp} 498253691Snp 499253691Snpstatic void 500253691Snptracer_qflush(struct ifnet *ifp) 501253691Snp{ 502253691Snp 503253691Snp return; 504253691Snp} 505253691Snp 506253691Snpstatic int 507253691Snptracer_media_change(struct ifnet *ifp) 508253691Snp{ 509253691Snp 510253691Snp return (EOPNOTSUPP); 511253691Snp} 512253691Snp 513253691Snpstatic void 514253691Snptracer_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 515253691Snp{ 516253691Snp 517253691Snp ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 518253691Snp 519253691Snp return; 520253691Snp} 521