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