1168404Spjd/*- 2168404Spjd * Copyright (c) 2013 Chelsio Communications, Inc. 3168404Spjd * All rights reserved. 4168404Spjd * Written by: Navdeep Parhar <np@FreeBSD.org> 5168404Spjd * 6168404Spjd * Redistribution and use in source and binary forms, with or without 7168404Spjd * modification, are permitted provided that the following conditions 8168404Spjd * are met: 9168404Spjd * 1. Redistributions of source code must retain the above copyright 10168404Spjd * notice, this list of conditions and the following disclaimer. 11168404Spjd * 2. Redistributions in binary form must reproduce the above copyright 12168404Spjd * notice, this list of conditions and the following disclaimer in the 13168404Spjd * documentation and/or other materials provided with the distribution. 14168404Spjd * 15168404Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16168404Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17168404Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18168404Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19168404Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20168404Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21168404Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22219089Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23268657Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24168404Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25168404Spjd * SUCH DAMAGE. 26219089Spjd */ 27219089Spjd 28168404Spjd#include <sys/cdefs.h> 29168404Spjd__FBSDID("$FreeBSD: releng/10.3/sys/dev/cxgbe/t4_tracer.c 270297 2014-08-21 19:54:02Z np $"); 30168404Spjd 31168404Spjd#include "opt_inet.h" 32168404Spjd#include "opt_inet6.h" 33168404Spjd 34168404Spjd#include <sys/param.h> 35168404Spjd#include <sys/lock.h> 36168404Spjd#include <sys/types.h> 37168404Spjd#include <sys/mbuf.h> 38219089Spjd#include <sys/socket.h> 39168404Spjd#include <sys/sockio.h> 40219089Spjd#include <sys/sx.h> 41168404Spjd#include <net/bpf.h> 42168404Spjd#include <net/ethernet.h> 43168404Spjd#include <net/if.h> 44168404Spjd#include <net/if_clone.h> 45168404Spjd#include <net/if_types.h> 46168404Spjd 47168404Spjd#include "common/common.h" 48168404Spjd#include "common/t4_msg.h" 49168404Spjd#include "common/t4_regs.h" 50168404Spjd#include "t4_ioctl.h" 51168404Spjd 52168404Spjd/* 53168404Spjd * Locking notes 54168404Spjd * ============= 55168404Spjd * 56168404Spjd * An interface cloner is registered during mod_load and it can be used to 57168404Spjd * create or destroy the tracing ifnet for an adapter at any time. It is 58168404Spjd * possible for the cloned interface to outlive the adapter (adapter disappears 59168404Spjd * in t4_detach but the tracing ifnet may live till mod_unload when removal of 60168404Spjd * the cloner finally destroys any remaining cloned interfaces). When tracing 61168404Spjd * filters are active, this ifnet is also receiving data. There are potential 62168404Spjd * bad races between ifnet create, ifnet destroy, ifnet rx, ifnet ioctl, 63168404Spjd * cxgbe_detach/t4_detach, mod_unload. 64168404Spjd * 65168404Spjd * a) The driver selects an iq for tracing (sc->traceq) inside a synch op. The 66168404Spjd * iq is destroyed inside a synch op too (and sc->traceq updated). 67168404Spjd * b) The cloner looks for an adapter that matches the name of the ifnet it's 68168404Spjd * been asked to create, starts a synch op on that adapter, and proceeds only 69251631Sdelphij * if the adapter has a tracing iq. 70168404Spjd * c) The cloned ifnet and the adapter are coupled to each other via 71251631Sdelphij * ifp->if_softc and sc->ifp. These can be modified only with the global 72168404Spjd * t4_trace_lock sx as well as the sc->ifp_lock mutex held. Holding either 73219089Spjd * of these will prevent any change. 74219089Spjd * 75219089Spjd * The order in which all the locks involved should be acquired are: 76168404Spjd * t4_list_lock 77168404Spjd * adapter lock 78168404Spjd * (begin synch op and let go of the above two) 79168404Spjd * t4_trace_lock 80168404Spjd * sc->ifp_lock 81168404Spjd */ 82168404Spjd 83168404Spjdstatic struct sx t4_trace_lock; 84168404Spjdstatic const char *t4_cloner_name = "tXnex"; 85168404Spjdstatic struct if_clone *t4_cloner; 86249921Ssmh 87249921Ssmh/* tracer ifnet routines. mostly no-ops. */ 88249921Ssmhstatic void tracer_init(void *); 89249921Ssmhstatic int tracer_ioctl(struct ifnet *, unsigned long, caddr_t); 90249921Ssmhstatic int tracer_transmit(struct ifnet *, struct mbuf *); 91168404Spjdstatic void tracer_qflush(struct ifnet *); 92168404Spjdstatic int tracer_media_change(struct ifnet *); 93168404Spjdstatic void tracer_media_status(struct ifnet *, struct ifmediareq *); 94219089Spjd 95219089Spjd/* match name (request/response) */ 96219089Spjdstruct match_rr { 97219089Spjd const char *name; 98219089Spjd int lock; /* set to 1 to returned sc locked. */ 99219089Spjd struct adapter *sc; 100219089Spjd int rc; 101219089Spjd}; 102219089Spjd 103219089Spjdstatic void 104219089Spjdmatch_name(struct adapter *sc, void *arg) 105219089Spjd{ 106219089Spjd struct match_rr *mrr = arg; 107219089Spjd 108219089Spjd if (strcmp(device_get_nameunit(sc->dev), mrr->name) != 0) 109168404Spjd return; 110219089Spjd 111168404Spjd KASSERT(mrr->sc == NULL, ("%s: multiple matches (%p, %p) for %s", 112219089Spjd __func__, mrr->sc, sc, mrr->name)); 113219089Spjd 114168404Spjd mrr->sc = sc; 115168404Spjd if (mrr->lock) 116168404Spjd mrr->rc = begin_synchronized_op(mrr->sc, NULL, 0, "t4clon"); 117168404Spjd else 118168404Spjd mrr->rc = 0; 119168404Spjd} 120168404Spjd 121168404Spjdstatic int 122168404Spjdt4_cloner_match(struct if_clone *ifc, const char *name) 123168404Spjd{ 124168404Spjd 125168404Spjd if (strncmp(name, "t4nex", 5) != 0 && 126168404Spjd strncmp(name, "t5nex", 5) != 0) 127168404Spjd return (0); 128168404Spjd if (name[5] < '0' || name[5] > '9') 129219089Spjd return (0); 130168404Spjd return (1); 131219089Spjd} 132219089Spjd 133168404Spjdstatic int 134168404Spjdt4_cloner_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) 135168404Spjd{ 136219089Spjd struct match_rr mrr; 137168404Spjd struct adapter *sc; 138219089Spjd struct ifnet *ifp; 139219089Spjd int rc, unit; 140168404Spjd const uint8_t lla[ETHER_ADDR_LEN] = {0, 0, 0, 0, 0, 0}; 141168404Spjd 142168404Spjd mrr.name = name; 143219089Spjd mrr.lock = 1; 144168404Spjd mrr.sc = NULL; 145168404Spjd mrr.rc = ENOENT; 146168404Spjd t4_iterate(match_name, &mrr); 147168404Spjd 148219089Spjd if (mrr.rc != 0) 149219089Spjd return (mrr.rc); 150168404Spjd sc = mrr.sc; 151219089Spjd 152268649Sdelphij KASSERT(sc != NULL, ("%s: name (%s) matched but softc is NULL", 153219089Spjd __func__, name)); 154168404Spjd ASSERT_SYNCHRONIZED_OP(sc); 155168404Spjd 156268649Sdelphij sx_xlock(&t4_trace_lock); 157268649Sdelphij 158268649Sdelphij if (sc->ifp != NULL) { 159268649Sdelphij rc = EEXIST; 160268649Sdelphij goto done; 161168404Spjd } 162249195Smm if (sc->traceq < 0) { 163168404Spjd rc = EAGAIN; 164219089Spjd goto done; 165168404Spjd } 166168404Spjd 167168404Spjd 168168404Spjd unit = -1; 169168404Spjd rc = ifc_alloc_unit(ifc, &unit); 170168404Spjd if (rc != 0) 171168404Spjd goto done; 172168404Spjd 173168404Spjd ifp = if_alloc(IFT_ETHER); 174168404Spjd if (ifp == NULL) { 175168404Spjd ifc_free_unit(ifc, unit); 176168404Spjd rc = ENOMEM; 177168404Spjd goto done; 178168404Spjd } 179168404Spjd 180168404Spjd /* Note that if_xname is not <if_dname><if_dunit>. */ 181168404Spjd strlcpy(ifp->if_xname, name, sizeof(ifp->if_xname)); 182168404Spjd ifp->if_dname = t4_cloner_name; 183168404Spjd ifp->if_dunit = unit; 184168404Spjd ifp->if_init = tracer_init; 185168404Spjd ifp->if_flags = IFF_SIMPLEX | IFF_DRV_RUNNING; 186168404Spjd ifp->if_ioctl = tracer_ioctl; 187168404Spjd ifp->if_transmit = tracer_transmit; 188168404Spjd ifp->if_qflush = tracer_qflush; 189219089Spjd ifp->if_capabilities = IFCAP_JUMBO_MTU | IFCAP_VLAN_MTU; 190168404Spjd ifmedia_init(&sc->media, IFM_IMASK, tracer_media_change, 191168404Spjd tracer_media_status); 192219089Spjd ifmedia_add(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE, 0, NULL); 193219089Spjd ifmedia_set(&sc->media, IFM_ETHER | IFM_FDX | IFM_NONE); 194168404Spjd ether_ifattach(ifp, lla); 195219089Spjd 196219089Spjd mtx_lock(&sc->ifp_lock); 197219089Spjd ifp->if_softc = sc; 198268657Sdelphij sc->ifp = ifp; 199168404Spjd mtx_unlock(&sc->ifp_lock); 200168404Spjddone: 201219089Spjd sx_xunlock(&t4_trace_lock); 202219089Spjd end_synchronized_op(sc, 0); 203168404Spjd return (rc); 204219089Spjd} 205219089Spjd 206168404Spjdstatic int 207219089Spjdt4_cloner_destroy(struct if_clone *ifc, struct ifnet *ifp) 208219089Spjd{ 209168404Spjd struct adapter *sc; 210246666Smm int unit = ifp->if_dunit; 211219089Spjd 212219089Spjd sx_xlock(&t4_trace_lock); 213168404Spjd sc = ifp->if_softc; 214168404Spjd if (sc != NULL) { 215168404Spjd mtx_lock(&sc->ifp_lock); 216168404Spjd sc->ifp = NULL; 217185029Spjd ifp->if_softc = NULL; 218185029Spjd mtx_unlock(&sc->ifp_lock); 219168404Spjd ifmedia_removeall(&sc->media); 220168404Spjd } 221185029Spjd ether_ifdetach(ifp); 222185029Spjd if_free(ifp); 223168404Spjd ifc_free_unit(ifc, unit); 224168404Spjd sx_xunlock(&t4_trace_lock); 225168404Spjd 226219089Spjd return (0); 227219089Spjd} 228219089Spjd 229219089Spjdvoid 230219089Spjdt4_tracer_modload() 231219089Spjd{ 232219089Spjd 233249195Smm sx_init(&t4_trace_lock, "T4/T5 tracer lock"); 234219089Spjd t4_cloner = if_clone_advanced(t4_cloner_name, 0, t4_cloner_match, 235219089Spjd t4_cloner_create, t4_cloner_destroy); 236219089Spjd} 237219089Spjd 238219089Spjdvoid 239219089Spjdt4_tracer_modunload() 240219089Spjd{ 241219089Spjd 242219089Spjd if (t4_cloner != NULL) { 243219089Spjd /* 244219089Spjd * The module is being unloaded so the nexus drivers have 245219089Spjd * detached. The tracing interfaces can not outlive the nexus 246219089Spjd * (ifp->if_softc is the nexus) and must have been destroyed 247249195Smm * already. XXX: but if_clone is opaque to us and we can't 248219089Spjd * assert LIST_EMPTY(&t4_cloner->ifc_iflist) at this time. 249219089Spjd */ 250219089Spjd if_clone_detach(t4_cloner); 251219089Spjd } 252219089Spjd sx_destroy(&t4_trace_lock); 253185029Spjd} 254168404Spjd 255248571Smmvoid 256168404Spjdt4_tracer_port_detach(struct adapter *sc) 257168404Spjd{ 258219089Spjd 259219089Spjd sx_xlock(&t4_trace_lock); 260168404Spjd if (sc->ifp != NULL) { 261219089Spjd mtx_lock(&sc->ifp_lock); 262219089Spjd sc->ifp->if_softc = NULL; 263219089Spjd sc->ifp = NULL; 264219089Spjd mtx_unlock(&sc->ifp_lock); 265219089Spjd } 266219089Spjd ifmedia_removeall(&sc->media); 267219089Spjd sx_xunlock(&t4_trace_lock); 268219089Spjd} 269219089Spjd 270219089Spjdint 271268657Sdelphijt4_get_tracer(struct adapter *sc, struct t4_tracer *t) 272219089Spjd{ 273219089Spjd int rc, i, enabled; 274219089Spjd struct trace_params tp; 275219089Spjd 276219089Spjd if (t->idx >= NTRACE) { 277219089Spjd t->idx = 0xff; 278219089Spjd t->enabled = 0; 279219089Spjd t->valid = 0; 280219089Spjd return (0); 281219089Spjd } 282219089Spjd 283219089Spjd rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK, 284219089Spjd "t4gett"); 285219089Spjd if (rc) 286246666Smm return (rc); 287219089Spjd 288219089Spjd for (i = t->idx; i < NTRACE; i++) { 289219089Spjd if (isset(&sc->tracer_valid, t->idx)) { 290219089Spjd t4_get_trace_filter(sc, &tp, i, &enabled); 291219089Spjd t->idx = i; 292219089Spjd t->enabled = enabled; 293219089Spjd t->valid = 1; 294219089Spjd memcpy(&t->tp.data[0], &tp.data[0], sizeof(t->tp.data)); 295168404Spjd memcpy(&t->tp.mask[0], &tp.mask[0], sizeof(t->tp.mask)); 296168404Spjd t->tp.snap_len = tp.snap_len; 297168404Spjd t->tp.min_len = tp.min_len; 298168404Spjd t->tp.skip_ofst = tp.skip_ofst; 299168404Spjd t->tp.skip_len = tp.skip_len; 300168404Spjd t->tp.invert = tp.invert; 301219089Spjd 302168404Spjd /* convert channel to port iff 0 <= port < 8. */ 303168404Spjd if (tp.port < 4) 304168404Spjd t->tp.port = sc->chan_map[tp.port]; 305168404Spjd else if (tp.port < 8) 306219089Spjd t->tp.port = sc->chan_map[tp.port - 4] + 4; 307219089Spjd else 308219089Spjd t->tp.port = tp.port; 309219089Spjd 310219089Spjd goto done; 311219089Spjd } 312219089Spjd } 313219089Spjd 314168404Spjd t->idx = 0xff; 315219089Spjd t->enabled = 0; 316168404Spjd t->valid = 0; 317219089Spjddone: 318219089Spjd end_synchronized_op(sc, LOCK_HELD); 319219089Spjd 320219089Spjd return (rc); 321219089Spjd} 322168404Spjd 323168404Spjdint 324168404Spjdt4_set_tracer(struct adapter *sc, struct t4_tracer *t) 325168404Spjd{ 326168404Spjd int rc; 327168404Spjd struct trace_params tp, *tpp; 328168404Spjd 329168404Spjd if (t->idx >= NTRACE) 330168404Spjd return (EINVAL); 331168404Spjd 332219089Spjd rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK, 333219089Spjd "t4sett"); 334168404Spjd if (rc) 335219089Spjd return (rc); 336219089Spjd 337219089Spjd /* 338219089Spjd * If no tracing filter is specified this time then check if the filter 339219089Spjd * at the index is valid anyway because it was set previously. If so 340219089Spjd * then this is a legitimate enable/disable operation. 341168404Spjd */ 342219089Spjd if (t->valid == 0) { 343219089Spjd if (isset(&sc->tracer_valid, t->idx)) 344219089Spjd tpp = NULL; 345219089Spjd else 346219089Spjd rc = EINVAL; 347168404Spjd goto done; 348219089Spjd } 349219089Spjd 350168404Spjd if (t->tp.port > 19 || t->tp.snap_len > 9600 || 351219089Spjd t->tp.min_len > M_TFMINPKTSIZE || t->tp.skip_len > M_TFLENGTH || 352248571Smm t->tp.skip_ofst > M_TFOFFSET) { 353168404Spjd rc = EINVAL; 354168404Spjd goto done; 355219089Spjd } 356168404Spjd 357168404Spjd memcpy(&tp.data[0], &t->tp.data[0], sizeof(tp.data)); 358168404Spjd memcpy(&tp.mask[0], &t->tp.mask[0], sizeof(tp.mask)); 359219089Spjd tp.snap_len = t->tp.snap_len; 360219089Spjd tp.min_len = t->tp.min_len; 361219089Spjd tp.skip_ofst = t->tp.skip_ofst; 362219089Spjd tp.skip_len = t->tp.skip_len; 363219089Spjd tp.invert = !!t->tp.invert; 364219089Spjd 365219089Spjd /* convert port to channel iff 0 <= port < 8. */ 366168404Spjd if (t->tp.port < 4) { 367168404Spjd if (sc->port[t->tp.port] == NULL) { 368219089Spjd rc = EINVAL; 369219089Spjd goto done; 370219089Spjd } 371219089Spjd tp.port = sc->port[t->tp.port]->tx_chan; 372219089Spjd } else if (t->tp.port < 8) { 373219089Spjd if (sc->port[t->tp.port - 4] == NULL) { 374168404Spjd rc = EINVAL; 375219089Spjd goto done; 376219089Spjd } 377219089Spjd tp.port = sc->port[t->tp.port - 4]->tx_chan + 4; 378219089Spjd } 379219089Spjd tpp = &tp; 380219089Spjddone: 381219089Spjd if (rc == 0) { 382168404Spjd rc = -t4_set_trace_filter(sc, tpp, t->idx, t->enabled); 383168404Spjd if (rc == 0) { 384219089Spjd if (t->enabled) { 385168404Spjd setbit(&sc->tracer_valid, t->idx); 386168404Spjd if (sc->tracer_enabled == 0) { 387168404Spjd t4_set_reg_field(sc, A_MPS_TRC_CFG, 388168404Spjd F_TRCEN, F_TRCEN); 389219089Spjd } 390168404Spjd setbit(&sc->tracer_enabled, t->idx); 391263397Sdelphij } else { 392263397Sdelphij clrbit(&sc->tracer_enabled, t->idx); 393219089Spjd if (sc->tracer_enabled == 0) { 394219089Spjd t4_set_reg_field(sc, A_MPS_TRC_CFG, 395219089Spjd F_TRCEN, 0); 396219089Spjd } 397219089Spjd } 398168404Spjd } 399168404Spjd } 400219089Spjd end_synchronized_op(sc, LOCK_HELD); 401168404Spjd 402168404Spjd return (rc); 403219089Spjd} 404219089Spjd 405219089Spjdint 406219089Spjdt4_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) 407219089Spjd{ 408219089Spjd struct adapter *sc = iq->adapter; 409219089Spjd struct ifnet *ifp; 410219089Spjd 411219089Spjd KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__, 412219089Spjd rss->opcode)); 413219089Spjd 414219089Spjd mtx_lock(&sc->ifp_lock); 415219089Spjd ifp = sc->ifp; 416219089Spjd if (sc->ifp) { 417219089Spjd m_adj(m, sizeof(struct cpl_trace_pkt)); 418219089Spjd m->m_pkthdr.rcvif = ifp; 419219089Spjd ETHER_BPF_MTAP(ifp, m); 420219089Spjd } 421168404Spjd mtx_unlock(&sc->ifp_lock); 422168404Spjd m_freem(m); 423168404Spjd 424219089Spjd return (0); 425168404Spjd} 426168404Spjd 427219089Spjdint 428219089Spjdt5_trace_pkt(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m) 429219089Spjd{ 430168404Spjd struct adapter *sc = iq->adapter; 431168404Spjd struct ifnet *ifp; 432219089Spjd 433168404Spjd KASSERT(m != NULL, ("%s: no payload with opcode %02x", __func__, 434168404Spjd rss->opcode)); 435219089Spjd 436219089Spjd mtx_lock(&sc->ifp_lock); 437219089Spjd ifp = sc->ifp; 438168404Spjd if (ifp != NULL) { 439168404Spjd m_adj(m, sizeof(struct cpl_t5_trace_pkt)); 440168404Spjd m->m_pkthdr.rcvif = ifp; 441219089Spjd ETHER_BPF_MTAP(ifp, m); 442263397Sdelphij } 443263397Sdelphij mtx_unlock(&sc->ifp_lock); 444219089Spjd m_freem(m); 445219089Spjd 446219089Spjd return (0); 447219089Spjd} 448219089Spjd 449219089Spjd 450219089Spjdstatic void 451219089Spjdtracer_init(void *arg) 452219089Spjd{ 453219089Spjd 454219089Spjd return; 455219089Spjd} 456219089Spjd 457219089Spjdstatic int 458219089Spjdtracer_ioctl(struct ifnet *ifp, unsigned long cmd, caddr_t data) 459219089Spjd{ 460219089Spjd int rc = 0; 461219089Spjd struct adapter *sc; 462219089Spjd struct ifreq *ifr = (struct ifreq *)data; 463219089Spjd 464219089Spjd switch (cmd) { 465219089Spjd case SIOCSIFMTU: 466219089Spjd case SIOCSIFFLAGS: 467168404Spjd case SIOCADDMULTI: 468219089Spjd case SIOCDELMULTI: 469219089Spjd case SIOCSIFCAP: 470219089Spjd break; 471219089Spjd case SIOCSIFMEDIA: 472219089Spjd case SIOCGIFMEDIA: 473219089Spjd sx_xlock(&t4_trace_lock); 474168404Spjd sc = ifp->if_softc; 475168404Spjd if (sc == NULL) 476168404Spjd rc = EIO; 477239620Smm else 478239620Smm rc = ifmedia_ioctl(ifp, ifr, &sc->media, cmd); 479239620Smm sx_xunlock(&t4_trace_lock); 480239620Smm break; 481239620Smm default: 482239620Smm rc = ether_ioctl(ifp, cmd, data); 483239620Smm } 484239620Smm 485239620Smm return (rc); 486239620Smm} 487239620Smm 488239620Smmstatic int 489248571Smmtracer_transmit(struct ifnet *ifp, struct mbuf *m) 490239620Smm{ 491239620Smm 492239620Smm m_freem(m); 493239620Smm return (0); 494239620Smm} 495239620Smm 496239620Smmstatic void 497239620Smmtracer_qflush(struct ifnet *ifp) 498239620Smm{ 499239620Smm 500239620Smm return; 501239620Smm} 502239620Smm 503239620Smmstatic int 504239620Smmtracer_media_change(struct ifnet *ifp) 505239620Smm{ 506239620Smm 507239620Smm return (EOPNOTSUPP); 508168404Spjd} 509168404Spjd 510219089Spjdstatic void 511168404Spjdtracer_media_status(struct ifnet *ifp, struct ifmediareq *ifmr) 512168404Spjd{ 513168404Spjd 514219089Spjd ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 515168404Spjd 516168404Spjd return; 517168404Spjd} 518168404Spjd