198c230c8SBjoern A. Zeeb /*- 298c230c8SBjoern A. Zeeb * Copyright (c) 2008 The FreeBSD Foundation 398c230c8SBjoern A. Zeeb * All rights reserved. 498c230c8SBjoern A. Zeeb * 598c230c8SBjoern A. Zeeb * This software was developed by CK Software GmbH under sponsorship 698c230c8SBjoern A. Zeeb * from the FreeBSD Foundation. 798c230c8SBjoern A. Zeeb * 898c230c8SBjoern A. Zeeb * Redistribution and use in source and binary forms, with or without 998c230c8SBjoern A. Zeeb * modification, are permitted provided that the following conditions 1098c230c8SBjoern A. Zeeb * are met: 1198c230c8SBjoern A. Zeeb * 1. Redistributions of source code must retain the above copyright 1298c230c8SBjoern A. Zeeb * notice, this list of conditions and the following disclaimer. 1398c230c8SBjoern A. Zeeb * 2. Redistributions in binary form must reproduce the above copyright 1498c230c8SBjoern A. Zeeb * notice, this list of conditions and the following disclaimer in the 1598c230c8SBjoern A. Zeeb * documentation and/or other materials provided with the distribution. 1698c230c8SBjoern A. Zeeb * 1798c230c8SBjoern A. Zeeb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1898c230c8SBjoern A. Zeeb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1998c230c8SBjoern A. Zeeb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2098c230c8SBjoern A. Zeeb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2198c230c8SBjoern A. Zeeb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2298c230c8SBjoern A. Zeeb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2398c230c8SBjoern A. Zeeb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2498c230c8SBjoern A. Zeeb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2598c230c8SBjoern A. Zeeb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2698c230c8SBjoern A. Zeeb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2798c230c8SBjoern A. Zeeb * SUCH DAMAGE. 2898c230c8SBjoern A. Zeeb */ 2998c230c8SBjoern A. Zeeb 3098c230c8SBjoern A. Zeeb /* 3198c230c8SBjoern A. Zeeb * A pair of virtual ethernet interfaces directly connected with 3298c230c8SBjoern A. Zeeb * a virtual cross-over cable. 3398c230c8SBjoern A. Zeeb * This is mostly intended to be used to provide connectivity between 3498c230c8SBjoern A. Zeeb * different virtual network stack instances. 3598c230c8SBjoern A. Zeeb */ 3698c230c8SBjoern A. Zeeb /* 3798c230c8SBjoern A. Zeeb * Things to re-think once we have more experience: 3898c230c8SBjoern A. Zeeb * - ifp->if_reassign function once we can test with vimage. 3998c230c8SBjoern A. Zeeb * - Real random etheraddrs that are checked to be uniquish; 4098c230c8SBjoern A. Zeeb * in case we bridge we may need this or let the user handle that case? 4198c230c8SBjoern A. Zeeb * - netisr and callback logic. 4298c230c8SBjoern A. Zeeb * - netisr queue lengths. 4398c230c8SBjoern A. Zeeb */ 4498c230c8SBjoern A. Zeeb 4598c230c8SBjoern A. Zeeb #include <sys/cdefs.h> 4698c230c8SBjoern A. Zeeb __FBSDID("$FreeBSD$"); 4798c230c8SBjoern A. Zeeb 4898c230c8SBjoern A. Zeeb #include <sys/param.h> 4998c230c8SBjoern A. Zeeb #include <sys/kernel.h> 5098c230c8SBjoern A. Zeeb #include <sys/mbuf.h> 5198c230c8SBjoern A. Zeeb #include <sys/module.h> 5298c230c8SBjoern A. Zeeb #include <sys/refcount.h> 5398c230c8SBjoern A. Zeeb #include <sys/queue.h> 5498c230c8SBjoern A. Zeeb #include <sys/socket.h> 5598c230c8SBjoern A. Zeeb #include <sys/sockio.h> 5698c230c8SBjoern A. Zeeb #include <sys/sysctl.h> 5798c230c8SBjoern A. Zeeb #include <sys/types.h> 5898c230c8SBjoern A. Zeeb #include <sys/vimage.h> 5998c230c8SBjoern A. Zeeb 6098c230c8SBjoern A. Zeeb #include <net/bpf.h> 6198c230c8SBjoern A. Zeeb #include <net/ethernet.h> 6298c230c8SBjoern A. Zeeb #include <net/if.h> 6398c230c8SBjoern A. Zeeb #include <net/if_clone.h> 6498c230c8SBjoern A. Zeeb #include <net/if_var.h> 6598c230c8SBjoern A. Zeeb #include <net/if_types.h> 6698c230c8SBjoern A. Zeeb #include <net/netisr.h> 6798c230c8SBjoern A. Zeeb 6898c230c8SBjoern A. Zeeb #define EPAIRNAME "epair" 6998c230c8SBjoern A. Zeeb 7098c230c8SBjoern A. Zeeb #ifdef DEBUG_EPAIR 7198c230c8SBjoern A. Zeeb static int epair_debug = 0; 7298c230c8SBjoern A. Zeeb SYSCTL_DECL(_net_link); 7398c230c8SBjoern A. Zeeb SYSCTL_NODE(_net_link, OID_AUTO, epair, CTLFLAG_RW, 0, "epair sysctl"); 7498c230c8SBjoern A. Zeeb SYSCTL_XINT(_net_link_epair, OID_AUTO, epair_debug, CTLFLAG_RW, 7598c230c8SBjoern A. Zeeb &epair_debug, 0, "if_epair(4) debugging."); 7698c230c8SBjoern A. Zeeb #define DPRINTF(fmt, arg...) if (epair_debug) \ 7798c230c8SBjoern A. Zeeb printf("[%s:%d] " fmt, __func__, __LINE__, ##arg) 7898c230c8SBjoern A. Zeeb #else 7998c230c8SBjoern A. Zeeb #define DPRINTF(fmt, arg...) 8098c230c8SBjoern A. Zeeb #endif 8198c230c8SBjoern A. Zeeb 8298c230c8SBjoern A. Zeeb struct epair_softc { 8398c230c8SBjoern A. Zeeb struct ifnet *ifp; 8498c230c8SBjoern A. Zeeb struct ifnet *oifp; 8598c230c8SBjoern A. Zeeb u_int refcount; 8698c230c8SBjoern A. Zeeb void (*if_qflush)(struct ifnet *); 8798c230c8SBjoern A. Zeeb }; 8898c230c8SBjoern A. Zeeb 8998c230c8SBjoern A. Zeeb struct epair_ifp_drain { 9098c230c8SBjoern A. Zeeb STAILQ_ENTRY(epair_ifp_drain) ifp_next; 9198c230c8SBjoern A. Zeeb struct ifnet *ifp; 9298c230c8SBjoern A. Zeeb }; 9398c230c8SBjoern A. Zeeb 9498c230c8SBjoern A. Zeeb static STAILQ_HEAD(, epair_ifp_drain) epair_ifp_drain_list = 9598c230c8SBjoern A. Zeeb STAILQ_HEAD_INITIALIZER(epair_ifp_drain_list); 9698c230c8SBjoern A. Zeeb 9798c230c8SBjoern A. Zeeb #define ADD_IFQ_FOR_DRAINING(ifp) \ 9898c230c8SBjoern A. Zeeb do { \ 9998c230c8SBjoern A. Zeeb struct epair_ifp_drain *elm = NULL; \ 10098c230c8SBjoern A. Zeeb \ 10198c230c8SBjoern A. Zeeb STAILQ_FOREACH(elm, &epair_ifp_drain_list, ifp_next) { \ 10298c230c8SBjoern A. Zeeb if (elm->ifp == (ifp)) \ 10398c230c8SBjoern A. Zeeb break; \ 10498c230c8SBjoern A. Zeeb } \ 10598c230c8SBjoern A. Zeeb if (elm == NULL) { \ 10698c230c8SBjoern A. Zeeb elm = malloc(sizeof(struct epair_ifp_drain), \ 10798c230c8SBjoern A. Zeeb M_EPAIR, M_ZERO); \ 10898c230c8SBjoern A. Zeeb if (elm != NULL) { \ 10998c230c8SBjoern A. Zeeb elm->ifp = (ifp); \ 11098c230c8SBjoern A. Zeeb STAILQ_INSERT_TAIL( \ 11198c230c8SBjoern A. Zeeb &epair_ifp_drain_list, \ 11298c230c8SBjoern A. Zeeb elm, ifp_next); \ 11398c230c8SBjoern A. Zeeb } \ 11498c230c8SBjoern A. Zeeb } \ 11598c230c8SBjoern A. Zeeb } while(0) 11698c230c8SBjoern A. Zeeb 11798c230c8SBjoern A. Zeeb /* Our "hw" tx queue. */ 11898c230c8SBjoern A. Zeeb static struct ifqueue epairinq; 11998c230c8SBjoern A. Zeeb static int epair_drv_flags; 12098c230c8SBjoern A. Zeeb 12198c230c8SBjoern A. Zeeb static struct mtx if_epair_mtx; 12298c230c8SBjoern A. Zeeb #define EPAIR_LOCK_INIT() mtx_init(&if_epair_mtx, "if_epair", \ 12398c230c8SBjoern A. Zeeb NULL, MTX_DEF) 12498c230c8SBjoern A. Zeeb #define EPAIR_LOCK_DESTROY() mtx_destroy(&if_epair_mtx) 12598c230c8SBjoern A. Zeeb #define EPAIR_LOCK_ASSERT() mtx_assert(&if_epair_mtx, MA_OWNED) 12698c230c8SBjoern A. Zeeb #define EPAIR_LOCK() mtx_lock(&if_epair_mtx) 12798c230c8SBjoern A. Zeeb #define EPAIR_UNLOCK() mtx_unlock(&if_epair_mtx) 12898c230c8SBjoern A. Zeeb 12998c230c8SBjoern A. Zeeb static MALLOC_DEFINE(M_EPAIR, EPAIRNAME, 13098c230c8SBjoern A. Zeeb "Pair of virtual cross-over connected Ethernet-like interfaces"); 13198c230c8SBjoern A. Zeeb 13298c230c8SBjoern A. Zeeb static int epair_clone_match(struct if_clone *, const char *); 13398c230c8SBjoern A. Zeeb static int epair_clone_create(struct if_clone *, char *, size_t, caddr_t); 13498c230c8SBjoern A. Zeeb static int epair_clone_destroy(struct if_clone *, struct ifnet *); 13598c230c8SBjoern A. Zeeb 13698c230c8SBjoern A. Zeeb static void epair_start_locked(struct ifnet *); 13798c230c8SBjoern A. Zeeb 13898c230c8SBjoern A. Zeeb static struct if_clone epair_cloner = IFC_CLONE_INITIALIZER( 13998c230c8SBjoern A. Zeeb EPAIRNAME, NULL, IF_MAXUNIT, 14098c230c8SBjoern A. Zeeb NULL, epair_clone_match, epair_clone_create, epair_clone_destroy); 14198c230c8SBjoern A. Zeeb 14298c230c8SBjoern A. Zeeb 14398c230c8SBjoern A. Zeeb /* 14498c230c8SBjoern A. Zeeb * Netisr handler functions. 14598c230c8SBjoern A. Zeeb */ 14698c230c8SBjoern A. Zeeb static void 14798c230c8SBjoern A. Zeeb epair_sintr(struct mbuf *m) 14898c230c8SBjoern A. Zeeb { 14998c230c8SBjoern A. Zeeb struct ifnet *ifp; 15098c230c8SBjoern A. Zeeb struct epair_softc *sc; 15198c230c8SBjoern A. Zeeb 15298c230c8SBjoern A. Zeeb ifp = m->m_pkthdr.rcvif; 15398c230c8SBjoern A. Zeeb (*ifp->if_input)(ifp, m); 15498c230c8SBjoern A. Zeeb sc = ifp->if_softc; 15598c230c8SBjoern A. Zeeb refcount_release(&sc->refcount); 15698c230c8SBjoern A. Zeeb DPRINTF("ifp=%p refcount=%u\n", ifp, sc->refcount); 15798c230c8SBjoern A. Zeeb } 15898c230c8SBjoern A. Zeeb 15998c230c8SBjoern A. Zeeb static void 16098c230c8SBjoern A. Zeeb epair_sintr_drained(void) 16198c230c8SBjoern A. Zeeb { 16298c230c8SBjoern A. Zeeb struct epair_ifp_drain *elm, *tvar; 16398c230c8SBjoern A. Zeeb struct ifnet *ifp; 16498c230c8SBjoern A. Zeeb 16598c230c8SBjoern A. Zeeb EPAIR_LOCK(); 16698c230c8SBjoern A. Zeeb /* 16798c230c8SBjoern A. Zeeb * Assume our "hw" queue and possibly ifq will be emptied 16898c230c8SBjoern A. Zeeb * again. In case we will overflow the "hw" queue while 16998c230c8SBjoern A. Zeeb * draining, epair_start_locked will set IFF_DRV_OACTIVE 17098c230c8SBjoern A. Zeeb * again and we will stop and return. 17198c230c8SBjoern A. Zeeb */ 17298c230c8SBjoern A. Zeeb STAILQ_FOREACH_SAFE(elm, &epair_ifp_drain_list, ifp_next, tvar) { 17398c230c8SBjoern A. Zeeb ifp = elm->ifp; 17498c230c8SBjoern A. Zeeb epair_drv_flags &= ~IFF_DRV_OACTIVE; 17598c230c8SBjoern A. Zeeb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 17698c230c8SBjoern A. Zeeb epair_start_locked(ifp); 17798c230c8SBjoern A. Zeeb 17898c230c8SBjoern A. Zeeb IFQ_LOCK(&ifp->if_snd); 17998c230c8SBjoern A. Zeeb if (IFQ_IS_EMPTY(&ifp->if_snd)) { 18098c230c8SBjoern A. Zeeb STAILQ_REMOVE(&epair_ifp_drain_list, elm, 18198c230c8SBjoern A. Zeeb epair_ifp_drain, ifp_next); 18298c230c8SBjoern A. Zeeb free(elm, M_EPAIR); 18398c230c8SBjoern A. Zeeb } 18498c230c8SBjoern A. Zeeb IFQ_UNLOCK(&ifp->if_snd); 18598c230c8SBjoern A. Zeeb 18698c230c8SBjoern A. Zeeb if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { 18798c230c8SBjoern A. Zeeb /* Our "hw"q overflew again. */ 18898c230c8SBjoern A. Zeeb epair_drv_flags |= IFF_DRV_OACTIVE 18998c230c8SBjoern A. Zeeb DPRINTF("hw queue length overflow at %u\n", 19098c230c8SBjoern A. Zeeb epairinq.ifq_maxlen); 19198c230c8SBjoern A. Zeeb #if 0 19298c230c8SBjoern A. Zeeb /* ``Auto-tuning.'' */ 19398c230c8SBjoern A. Zeeb epairinq.ifq_maxlen += ifqmaxlen; 19498c230c8SBjoern A. Zeeb #endif 19598c230c8SBjoern A. Zeeb break; 19698c230c8SBjoern A. Zeeb } 19798c230c8SBjoern A. Zeeb } 19898c230c8SBjoern A. Zeeb EPAIR_UNLOCK(); 19998c230c8SBjoern A. Zeeb } 20098c230c8SBjoern A. Zeeb 20198c230c8SBjoern A. Zeeb /* 20298c230c8SBjoern A. Zeeb * Network interface (`if') related functions. 20398c230c8SBjoern A. Zeeb */ 20498c230c8SBjoern A. Zeeb static void 20598c230c8SBjoern A. Zeeb epair_start_locked(struct ifnet *ifp) 20698c230c8SBjoern A. Zeeb { 20798c230c8SBjoern A. Zeeb struct mbuf *m; 20898c230c8SBjoern A. Zeeb struct epair_softc *sc; 20998c230c8SBjoern A. Zeeb struct ifnet *oifp; 21098c230c8SBjoern A. Zeeb int error; 21198c230c8SBjoern A. Zeeb 21298c230c8SBjoern A. Zeeb EPAIR_LOCK_ASSERT(); 21398c230c8SBjoern A. Zeeb DPRINTF("ifp=%p\n", ifp); 21498c230c8SBjoern A. Zeeb 21598c230c8SBjoern A. Zeeb if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 21698c230c8SBjoern A. Zeeb return; 21798c230c8SBjoern A. Zeeb if ((ifp->if_flags & IFF_UP) == 0) 21898c230c8SBjoern A. Zeeb return; 21998c230c8SBjoern A. Zeeb 22098c230c8SBjoern A. Zeeb /* 22198c230c8SBjoern A. Zeeb * We get patckets here from ether_output via if_handoff() 22298c230c8SBjoern A. Zeeb * and ned to put them into the input queue of the oifp 22398c230c8SBjoern A. Zeeb * and call oifp->if_input() via netisr/epair_sintr(). 22498c230c8SBjoern A. Zeeb */ 22598c230c8SBjoern A. Zeeb sc = ifp->if_softc; 22698c230c8SBjoern A. Zeeb oifp = sc->oifp; 22798c230c8SBjoern A. Zeeb sc = oifp->if_softc; 22898c230c8SBjoern A. Zeeb for (;;) { 22998c230c8SBjoern A. Zeeb IFQ_DEQUEUE(&ifp->if_snd, m); 23098c230c8SBjoern A. Zeeb if (m == NULL) 23198c230c8SBjoern A. Zeeb break; 23298c230c8SBjoern A. Zeeb BPF_MTAP(ifp, m); 23398c230c8SBjoern A. Zeeb 23498c230c8SBjoern A. Zeeb /* 23598c230c8SBjoern A. Zeeb * In case the outgoing interface is not usable, 23698c230c8SBjoern A. Zeeb * drop the packet. 23798c230c8SBjoern A. Zeeb */ 23898c230c8SBjoern A. Zeeb if ((oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 23998c230c8SBjoern A. Zeeb (oifp->if_flags & IFF_UP) ==0) { 24098c230c8SBjoern A. Zeeb ifp->if_oerrors++; 24198c230c8SBjoern A. Zeeb m_freem(m); 24298c230c8SBjoern A. Zeeb continue; 24398c230c8SBjoern A. Zeeb } 24498c230c8SBjoern A. Zeeb DPRINTF("packet %s -> %s\n", ifp->if_xname, oifp->if_xname); 24598c230c8SBjoern A. Zeeb 24698c230c8SBjoern A. Zeeb /* 24798c230c8SBjoern A. Zeeb * Add a reference so the interface cannot go while the 24898c230c8SBjoern A. Zeeb * packet is in transit as we rely on rcvif to stay valid. 24998c230c8SBjoern A. Zeeb */ 25098c230c8SBjoern A. Zeeb refcount_acquire(&sc->refcount); 25198c230c8SBjoern A. Zeeb m->m_pkthdr.rcvif = oifp; 25298c230c8SBjoern A. Zeeb CURVNET_SET_QUIET(oifp->if_vnet); 25398c230c8SBjoern A. Zeeb error = netisr_queue(NETISR_EPAIR, m); 25498c230c8SBjoern A. Zeeb CURVNET_RESTORE(); 25598c230c8SBjoern A. Zeeb if (!error) { 25698c230c8SBjoern A. Zeeb ifp->if_opackets++; 25798c230c8SBjoern A. Zeeb /* Someone else received the packet. */ 25898c230c8SBjoern A. Zeeb oifp->if_ipackets++; 25998c230c8SBjoern A. Zeeb } else { 26098c230c8SBjoern A. Zeeb epair_drv_flags |= IFF_DRV_OACTIVE; 26198c230c8SBjoern A. Zeeb ifp->if_drv_flags |= IFF_DRV_OACTIVE; 26298c230c8SBjoern A. Zeeb ADD_IFQ_FOR_DRAINING(ifp); 26398c230c8SBjoern A. Zeeb refcount_release(&sc->refcount); 26498c230c8SBjoern A. Zeeb } 26598c230c8SBjoern A. Zeeb } 26698c230c8SBjoern A. Zeeb } 26798c230c8SBjoern A. Zeeb 26898c230c8SBjoern A. Zeeb static void 26998c230c8SBjoern A. Zeeb epair_start(struct ifnet *ifp) 27098c230c8SBjoern A. Zeeb { 27198c230c8SBjoern A. Zeeb 27298c230c8SBjoern A. Zeeb EPAIR_LOCK(); 27398c230c8SBjoern A. Zeeb epair_start_locked(ifp); 27498c230c8SBjoern A. Zeeb EPAIR_UNLOCK(); 27598c230c8SBjoern A. Zeeb } 27698c230c8SBjoern A. Zeeb 27798c230c8SBjoern A. Zeeb static int 27898c230c8SBjoern A. Zeeb epair_transmit_locked(struct ifnet *ifp, struct mbuf *m) 27998c230c8SBjoern A. Zeeb { 28098c230c8SBjoern A. Zeeb struct epair_softc *sc; 28198c230c8SBjoern A. Zeeb struct ifnet *oifp; 28298c230c8SBjoern A. Zeeb int error, len; 28398c230c8SBjoern A. Zeeb short mflags; 28498c230c8SBjoern A. Zeeb 28598c230c8SBjoern A. Zeeb EPAIR_LOCK_ASSERT(); 28698c230c8SBjoern A. Zeeb DPRINTF("ifp=%p m=%p\n", ifp, m); 28798c230c8SBjoern A. Zeeb 28898c230c8SBjoern A. Zeeb if (m == NULL) 28998c230c8SBjoern A. Zeeb return (0); 29098c230c8SBjoern A. Zeeb 29198c230c8SBjoern A. Zeeb /* 29298c230c8SBjoern A. Zeeb * We are not going to use the interface en/dequeue mechanism 29398c230c8SBjoern A. Zeeb * on the TX side. We are called from ether_output_frame() 29498c230c8SBjoern A. Zeeb * and will put the packet into the incoming queue of the 29598c230c8SBjoern A. Zeeb * other interface of our pair via the netsir. 29698c230c8SBjoern A. Zeeb */ 29798c230c8SBjoern A. Zeeb if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 29898c230c8SBjoern A. Zeeb m_freem(m); 29998c230c8SBjoern A. Zeeb return (ENXIO); 30098c230c8SBjoern A. Zeeb } 30198c230c8SBjoern A. Zeeb if ((ifp->if_flags & IFF_UP) == 0) { 30298c230c8SBjoern A. Zeeb m_freem(m); 30398c230c8SBjoern A. Zeeb return (ENETDOWN); 30498c230c8SBjoern A. Zeeb } 30598c230c8SBjoern A. Zeeb 30698c230c8SBjoern A. Zeeb BPF_MTAP(ifp, m); 30798c230c8SBjoern A. Zeeb 30898c230c8SBjoern A. Zeeb /* 30998c230c8SBjoern A. Zeeb * In case the outgoing interface is not usable, 31098c230c8SBjoern A. Zeeb * drop the packet. 31198c230c8SBjoern A. Zeeb */ 31298c230c8SBjoern A. Zeeb sc = ifp->if_softc; 31398c230c8SBjoern A. Zeeb oifp = sc->oifp; 31498c230c8SBjoern A. Zeeb if ((oifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 31598c230c8SBjoern A. Zeeb (oifp->if_flags & IFF_UP) ==0) { 31698c230c8SBjoern A. Zeeb ifp->if_oerrors++; 31798c230c8SBjoern A. Zeeb m_freem(m); 31898c230c8SBjoern A. Zeeb return (0); 31998c230c8SBjoern A. Zeeb } 32098c230c8SBjoern A. Zeeb len = m->m_pkthdr.len; 32198c230c8SBjoern A. Zeeb mflags = m->m_flags; 32298c230c8SBjoern A. Zeeb DPRINTF("packet %s -> %s\n", ifp->if_xname, oifp->if_xname); 32398c230c8SBjoern A. Zeeb 32498c230c8SBjoern A. Zeeb #ifdef ALTQ 32598c230c8SBjoern A. Zeeb /* Support ALTQ via the clasic if_start() path. */ 32698c230c8SBjoern A. Zeeb IF_LOCK(&ifp->if_snd); 32798c230c8SBjoern A. Zeeb if (ALTQ_IS_ENABLED(&ifp->if_snd)) { 32898c230c8SBjoern A. Zeeb ALTQ_ENQUEUE(&ifp->if_snd, m, NULL, error); 32998c230c8SBjoern A. Zeeb if (error) 33098c230c8SBjoern A. Zeeb ifp->if_snd.ifq_drops++; 33198c230c8SBjoern A. Zeeb IF_UNLOCK(&ifp->if_snd); 33298c230c8SBjoern A. Zeeb if (!error) { 33398c230c8SBjoern A. Zeeb ifp->if_obytes += len; 33498c230c8SBjoern A. Zeeb if (mflags & (M_BCAST|M_MCAST)) 33598c230c8SBjoern A. Zeeb ifp->if_omcasts++; 33698c230c8SBjoern A. Zeeb 33798c230c8SBjoern A. Zeeb if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) 33898c230c8SBjoern A. Zeeb epair_start_locked(ifp); 33998c230c8SBjoern A. Zeeb else 34098c230c8SBjoern A. Zeeb ADD_IFQ_FOR_DRAINING(ifp); 34198c230c8SBjoern A. Zeeb } 34298c230c8SBjoern A. Zeeb return (error); 34398c230c8SBjoern A. Zeeb } 34498c230c8SBjoern A. Zeeb IF_UNLOCK(&ifp->if_snd); 34598c230c8SBjoern A. Zeeb #endif 34698c230c8SBjoern A. Zeeb 34798c230c8SBjoern A. Zeeb if ((epair_drv_flags & IFF_DRV_OACTIVE) != 0) { 34898c230c8SBjoern A. Zeeb /* 34998c230c8SBjoern A. Zeeb * Our hardware queue is full, try to fall back 35098c230c8SBjoern A. Zeeb * queuing to the ifq but do not call ifp->if_start. 35198c230c8SBjoern A. Zeeb * Either we are lucky or the packet is gone. 35298c230c8SBjoern A. Zeeb */ 35398c230c8SBjoern A. Zeeb IFQ_ENQUEUE(&ifp->if_snd, m, error); 35498c230c8SBjoern A. Zeeb if (!error) 35598c230c8SBjoern A. Zeeb ADD_IFQ_FOR_DRAINING(ifp); 35698c230c8SBjoern A. Zeeb return (error); 35798c230c8SBjoern A. Zeeb } 35898c230c8SBjoern A. Zeeb sc = oifp->if_softc; 35998c230c8SBjoern A. Zeeb /* 36098c230c8SBjoern A. Zeeb * Add a reference so the interface cannot go while the 36198c230c8SBjoern A. Zeeb * packet is in transit as we rely on rcvif to stay valid. 36298c230c8SBjoern A. Zeeb */ 36398c230c8SBjoern A. Zeeb refcount_acquire(&sc->refcount); 36498c230c8SBjoern A. Zeeb m->m_pkthdr.rcvif = oifp; 36598c230c8SBjoern A. Zeeb CURVNET_SET_QUIET(oifp->if_vnet); 36698c230c8SBjoern A. Zeeb error = netisr_queue(NETISR_EPAIR, m); 36798c230c8SBjoern A. Zeeb CURVNET_RESTORE(); 36898c230c8SBjoern A. Zeeb if (!error) { 36998c230c8SBjoern A. Zeeb ifp->if_opackets++; 37098c230c8SBjoern A. Zeeb /* 37198c230c8SBjoern A. Zeeb * IFQ_HANDOFF_ADJ/ip_handoff() update statistics, 37298c230c8SBjoern A. Zeeb * but as we bypass all this we have to duplicate 37398c230c8SBjoern A. Zeeb * the logic another time. 37498c230c8SBjoern A. Zeeb */ 37598c230c8SBjoern A. Zeeb ifp->if_obytes += len; 37698c230c8SBjoern A. Zeeb if (mflags & (M_BCAST|M_MCAST)) 37798c230c8SBjoern A. Zeeb ifp->if_omcasts++; 37898c230c8SBjoern A. Zeeb /* Someone else received the packet. */ 37998c230c8SBjoern A. Zeeb oifp->if_ipackets++; 38098c230c8SBjoern A. Zeeb } else { 38198c230c8SBjoern A. Zeeb /* The packet was freed already. */ 38298c230c8SBjoern A. Zeeb refcount_release(&sc->refcount); 38398c230c8SBjoern A. Zeeb epair_drv_flags |= IFF_DRV_OACTIVE; 38498c230c8SBjoern A. Zeeb ifp->if_drv_flags |= IFF_DRV_OACTIVE; 38598c230c8SBjoern A. Zeeb } 38698c230c8SBjoern A. Zeeb 38798c230c8SBjoern A. Zeeb return (error); 38898c230c8SBjoern A. Zeeb } 38998c230c8SBjoern A. Zeeb 39098c230c8SBjoern A. Zeeb static int 39198c230c8SBjoern A. Zeeb epair_transmit(struct ifnet *ifp, struct mbuf *m) 39298c230c8SBjoern A. Zeeb { 39398c230c8SBjoern A. Zeeb int error; 39498c230c8SBjoern A. Zeeb 39598c230c8SBjoern A. Zeeb EPAIR_LOCK(); 39698c230c8SBjoern A. Zeeb error = epair_transmit_locked(ifp, m); 39798c230c8SBjoern A. Zeeb EPAIR_UNLOCK(); 39898c230c8SBjoern A. Zeeb return (error); 39998c230c8SBjoern A. Zeeb } 40098c230c8SBjoern A. Zeeb 40198c230c8SBjoern A. Zeeb static void 40298c230c8SBjoern A. Zeeb epair_qflush(struct ifnet *ifp) 40398c230c8SBjoern A. Zeeb { 40498c230c8SBjoern A. Zeeb struct epair_softc *sc; 40598c230c8SBjoern A. Zeeb struct ifaltq *ifq; 40698c230c8SBjoern A. Zeeb 40798c230c8SBjoern A. Zeeb EPAIR_LOCK(); 40898c230c8SBjoern A. Zeeb sc = ifp->if_softc; 40998c230c8SBjoern A. Zeeb ifq = &ifp->if_snd; 41098c230c8SBjoern A. Zeeb DPRINTF("ifp=%p sc refcnt=%u ifq_len=%u\n", 41198c230c8SBjoern A. Zeeb ifp, sc->refcount, ifq->ifq_len); 41298c230c8SBjoern A. Zeeb /* 41398c230c8SBjoern A. Zeeb * Instead of calling refcount_release(&sc->refcount); 41498c230c8SBjoern A. Zeeb * n times, just subtract for the cleanup. 41598c230c8SBjoern A. Zeeb */ 41698c230c8SBjoern A. Zeeb sc->refcount -= ifq->ifq_len; 41798c230c8SBjoern A. Zeeb EPAIR_UNLOCK(); 41898c230c8SBjoern A. Zeeb if (sc->if_qflush) 41998c230c8SBjoern A. Zeeb sc->if_qflush(ifp); 42098c230c8SBjoern A. Zeeb } 42198c230c8SBjoern A. Zeeb 42298c230c8SBjoern A. Zeeb static int 42398c230c8SBjoern A. Zeeb epair_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 42498c230c8SBjoern A. Zeeb { 42598c230c8SBjoern A. Zeeb struct ifreq *ifr; 42698c230c8SBjoern A. Zeeb int error; 42798c230c8SBjoern A. Zeeb 42898c230c8SBjoern A. Zeeb ifr = (struct ifreq *)data; 42998c230c8SBjoern A. Zeeb switch (cmd) { 43098c230c8SBjoern A. Zeeb case SIOCSIFFLAGS: 43198c230c8SBjoern A. Zeeb case SIOCADDMULTI: 43298c230c8SBjoern A. Zeeb case SIOCDELMULTI: 43398c230c8SBjoern A. Zeeb error = 0; 43498c230c8SBjoern A. Zeeb break; 43598c230c8SBjoern A. Zeeb 43698c230c8SBjoern A. Zeeb default: 43798c230c8SBjoern A. Zeeb /* Let the common ethernet handler process this. */ 43898c230c8SBjoern A. Zeeb error = ether_ioctl(ifp, cmd, data); 43998c230c8SBjoern A. Zeeb break; 44098c230c8SBjoern A. Zeeb } 44198c230c8SBjoern A. Zeeb 44298c230c8SBjoern A. Zeeb return (error); 44398c230c8SBjoern A. Zeeb } 44498c230c8SBjoern A. Zeeb 44598c230c8SBjoern A. Zeeb static void 44698c230c8SBjoern A. Zeeb epair_init(void *dummy __unused) 44798c230c8SBjoern A. Zeeb { 44898c230c8SBjoern A. Zeeb } 44998c230c8SBjoern A. Zeeb 45098c230c8SBjoern A. Zeeb 45198c230c8SBjoern A. Zeeb /* 45298c230c8SBjoern A. Zeeb * Interface cloning functions. 45398c230c8SBjoern A. Zeeb * We use our private ones so that we can create/destroy our secondary 45498c230c8SBjoern A. Zeeb * device along with the primary one. 45598c230c8SBjoern A. Zeeb */ 45698c230c8SBjoern A. Zeeb static int 45798c230c8SBjoern A. Zeeb epair_clone_match(struct if_clone *ifc, const char *name) 45898c230c8SBjoern A. Zeeb { 45998c230c8SBjoern A. Zeeb const char *cp; 46098c230c8SBjoern A. Zeeb 46198c230c8SBjoern A. Zeeb DPRINTF("name='%s'\n", name); 46298c230c8SBjoern A. Zeeb 46398c230c8SBjoern A. Zeeb /* 46498c230c8SBjoern A. Zeeb * Our base name is epair. 46598c230c8SBjoern A. Zeeb * Our interfaces will be named epair<n>[ab]. 46698c230c8SBjoern A. Zeeb * So accept anything of the following list: 46798c230c8SBjoern A. Zeeb * - epair 46898c230c8SBjoern A. Zeeb * - epair<n> 46998c230c8SBjoern A. Zeeb * but not the epair<n>[ab] versions. 47098c230c8SBjoern A. Zeeb */ 47198c230c8SBjoern A. Zeeb if (strncmp(EPAIRNAME, name, sizeof(EPAIRNAME)-1) != 0) 47298c230c8SBjoern A. Zeeb return (0); 47398c230c8SBjoern A. Zeeb 47498c230c8SBjoern A. Zeeb for (cp = name + sizeof(EPAIRNAME) - 1; *cp != '\0'; cp++) { 47598c230c8SBjoern A. Zeeb if (*cp < '0' || *cp > '9') 47698c230c8SBjoern A. Zeeb return (0); 47798c230c8SBjoern A. Zeeb } 47898c230c8SBjoern A. Zeeb 47998c230c8SBjoern A. Zeeb return (1); 48098c230c8SBjoern A. Zeeb } 48198c230c8SBjoern A. Zeeb 48298c230c8SBjoern A. Zeeb static int 48398c230c8SBjoern A. Zeeb epair_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) 48498c230c8SBjoern A. Zeeb { 48598c230c8SBjoern A. Zeeb struct epair_softc *sca, *scb; 48698c230c8SBjoern A. Zeeb struct ifnet *ifp; 48798c230c8SBjoern A. Zeeb char *dp; 48898c230c8SBjoern A. Zeeb int error, unit, wildcard; 48998c230c8SBjoern A. Zeeb uint8_t eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ 49098c230c8SBjoern A. Zeeb 49198c230c8SBjoern A. Zeeb /* 49298c230c8SBjoern A. Zeeb * We are abusing params to create our second interface. 49398c230c8SBjoern A. Zeeb * Actually we already created it and called if_clone_createif() 49498c230c8SBjoern A. Zeeb * for it to do the official insertion procedure the moment we knew 49598c230c8SBjoern A. Zeeb * it cannot fail anymore. So just do attach it here. 49698c230c8SBjoern A. Zeeb */ 49798c230c8SBjoern A. Zeeb if (params) { 49898c230c8SBjoern A. Zeeb scb = (struct epair_softc *)params; 49998c230c8SBjoern A. Zeeb ifp = scb->ifp; 50098c230c8SBjoern A. Zeeb /* Assign a hopefully unique, locally administered etheraddr. */ 50198c230c8SBjoern A. Zeeb eaddr[0] = 0x02; 50298c230c8SBjoern A. Zeeb eaddr[3] = (ifp->if_index >> 8) & 0xff; 50398c230c8SBjoern A. Zeeb eaddr[4] = ifp->if_index & 0xff; 50498c230c8SBjoern A. Zeeb eaddr[5] = 0x0b; 50598c230c8SBjoern A. Zeeb ether_ifattach(ifp, eaddr); 50698c230c8SBjoern A. Zeeb /* Correctly set the name for the cloner list. */ 50798c230c8SBjoern A. Zeeb strlcpy(name, scb->ifp->if_xname, len); 50898c230c8SBjoern A. Zeeb return (0); 50998c230c8SBjoern A. Zeeb } 51098c230c8SBjoern A. Zeeb 51198c230c8SBjoern A. Zeeb /* Try to see if a special unit was requested. */ 51298c230c8SBjoern A. Zeeb error = ifc_name2unit(name, &unit); 51398c230c8SBjoern A. Zeeb if (error != 0) 51498c230c8SBjoern A. Zeeb return (error); 51598c230c8SBjoern A. Zeeb wildcard = (unit < 0); 51698c230c8SBjoern A. Zeeb 51798c230c8SBjoern A. Zeeb error = ifc_alloc_unit(ifc, &unit); 51898c230c8SBjoern A. Zeeb if (error != 0) 51998c230c8SBjoern A. Zeeb return (error); 52098c230c8SBjoern A. Zeeb 52198c230c8SBjoern A. Zeeb /* 52298c230c8SBjoern A. Zeeb * If no unit had been given, we need to adjust the ifName. 52398c230c8SBjoern A. Zeeb * Also make sure there is space for our extra [ab] suffix. 52498c230c8SBjoern A. Zeeb */ 52598c230c8SBjoern A. Zeeb for (dp = name; *dp != '\0'; dp++); 52698c230c8SBjoern A. Zeeb if (wildcard) { 52798c230c8SBjoern A. Zeeb error = snprintf(dp, len - (dp - name), "%d", unit); 52898c230c8SBjoern A. Zeeb if (error > len - (dp - name) - 1) { 52998c230c8SBjoern A. Zeeb /* ifName too long. */ 53098c230c8SBjoern A. Zeeb ifc_free_unit(ifc, unit); 53198c230c8SBjoern A. Zeeb return (ENOSPC); 53298c230c8SBjoern A. Zeeb } 53398c230c8SBjoern A. Zeeb dp += error; 53498c230c8SBjoern A. Zeeb } 53598c230c8SBjoern A. Zeeb if (len - (dp - name) - 1 < 1) { 53698c230c8SBjoern A. Zeeb /* No space left for our [ab] suffix. */ 53798c230c8SBjoern A. Zeeb ifc_free_unit(ifc, unit); 53898c230c8SBjoern A. Zeeb return (ENOSPC); 53998c230c8SBjoern A. Zeeb } 54098c230c8SBjoern A. Zeeb *dp = 'a'; 54198c230c8SBjoern A. Zeeb /* Must not change dp so we can replace 'a' by 'b' later. */ 54298c230c8SBjoern A. Zeeb *(dp+1) = '\0'; 54398c230c8SBjoern A. Zeeb 54498c230c8SBjoern A. Zeeb /* Allocate memory for both [ab] interfaces */ 54598c230c8SBjoern A. Zeeb sca = malloc(sizeof(struct epair_softc), M_EPAIR, M_WAITOK | M_ZERO); 54698c230c8SBjoern A. Zeeb refcount_init(&sca->refcount, 1); 54798c230c8SBjoern A. Zeeb sca->ifp = if_alloc(IFT_ETHER); 54898c230c8SBjoern A. Zeeb if (sca->ifp == NULL) { 54998c230c8SBjoern A. Zeeb free(sca, M_EPAIR); 55098c230c8SBjoern A. Zeeb ifc_free_unit(ifc, unit); 55198c230c8SBjoern A. Zeeb return (ENOSPC); 55298c230c8SBjoern A. Zeeb } 55398c230c8SBjoern A. Zeeb 55498c230c8SBjoern A. Zeeb scb = malloc(sizeof(struct epair_softc), M_EPAIR, M_WAITOK | M_ZERO); 55598c230c8SBjoern A. Zeeb refcount_init(&scb->refcount, 1); 55698c230c8SBjoern A. Zeeb scb->ifp = if_alloc(IFT_ETHER); 55798c230c8SBjoern A. Zeeb if (scb->ifp == NULL) { 55898c230c8SBjoern A. Zeeb free(scb, M_EPAIR); 55998c230c8SBjoern A. Zeeb if_free(sca->ifp); 56098c230c8SBjoern A. Zeeb free(sca, M_EPAIR); 56198c230c8SBjoern A. Zeeb ifc_free_unit(ifc, unit); 56298c230c8SBjoern A. Zeeb return (ENOSPC); 56398c230c8SBjoern A. Zeeb } 56498c230c8SBjoern A. Zeeb 56598c230c8SBjoern A. Zeeb /* 56698c230c8SBjoern A. Zeeb * Cross-reference the interfaces so we will be able to free both. 56798c230c8SBjoern A. Zeeb */ 56898c230c8SBjoern A. Zeeb sca->oifp = scb->ifp; 56998c230c8SBjoern A. Zeeb scb->oifp = sca->ifp; 57098c230c8SBjoern A. Zeeb 57198c230c8SBjoern A. Zeeb /* Finish initialization of interface <n>a. */ 57298c230c8SBjoern A. Zeeb ifp = sca->ifp; 57398c230c8SBjoern A. Zeeb ifp->if_softc = sca; 57498c230c8SBjoern A. Zeeb strlcpy(ifp->if_xname, name, IFNAMSIZ); 57598c230c8SBjoern A. Zeeb ifp->if_dname = ifc->ifc_name; 57698c230c8SBjoern A. Zeeb ifp->if_dunit = unit; 57798c230c8SBjoern A. Zeeb ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 57898c230c8SBjoern A. Zeeb ifp->if_start = epair_start; 57998c230c8SBjoern A. Zeeb ifp->if_ioctl = epair_ioctl; 58098c230c8SBjoern A. Zeeb ifp->if_init = epair_init; 58198c230c8SBjoern A. Zeeb ifp->if_snd.ifq_maxlen = ifqmaxlen; 58298c230c8SBjoern A. Zeeb /* Assign a hopefully unique, locally administered etheraddr. */ 58398c230c8SBjoern A. Zeeb eaddr[0] = 0x02; 58498c230c8SBjoern A. Zeeb eaddr[3] = (ifp->if_index >> 8) & 0xff; 58598c230c8SBjoern A. Zeeb eaddr[4] = ifp->if_index & 0xff; 58698c230c8SBjoern A. Zeeb eaddr[5] = 0x0a; 58798c230c8SBjoern A. Zeeb ether_ifattach(ifp, eaddr); 58898c230c8SBjoern A. Zeeb sca->if_qflush = ifp->if_qflush; 58998c230c8SBjoern A. Zeeb ifp->if_qflush = epair_qflush; 59098c230c8SBjoern A. Zeeb ifp->if_transmit = epair_transmit; 59198c230c8SBjoern A. Zeeb ifp->if_baudrate = IF_Gbps(10UL); /* arbitrary maximum */ 59298c230c8SBjoern A. Zeeb 59398c230c8SBjoern A. Zeeb /* Swap the name and finish initialization of interface <n>b. */ 59498c230c8SBjoern A. Zeeb *dp = 'b'; 59598c230c8SBjoern A. Zeeb 59698c230c8SBjoern A. Zeeb ifp = scb->ifp; 59798c230c8SBjoern A. Zeeb ifp->if_softc = scb; 59898c230c8SBjoern A. Zeeb strlcpy(ifp->if_xname, name, IFNAMSIZ); 59998c230c8SBjoern A. Zeeb ifp->if_dname = ifc->ifc_name; 60098c230c8SBjoern A. Zeeb ifp->if_dunit = unit; 60198c230c8SBjoern A. Zeeb ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 60298c230c8SBjoern A. Zeeb ifp->if_start = epair_start; 60398c230c8SBjoern A. Zeeb ifp->if_ioctl = epair_ioctl; 60498c230c8SBjoern A. Zeeb ifp->if_init = epair_init; 60598c230c8SBjoern A. Zeeb ifp->if_snd.ifq_maxlen = ifqmaxlen; 60698c230c8SBjoern A. Zeeb /* We need to play some tricks here for the second interface. */ 60798c230c8SBjoern A. Zeeb strlcpy(name, EPAIRNAME, len); 60898c230c8SBjoern A. Zeeb error = if_clone_create(name, len, (caddr_t)scb); 60998c230c8SBjoern A. Zeeb if (error) 61098c230c8SBjoern A. Zeeb panic("%s: if_clone_createif() for our 2nd iface failed: %d", 61198c230c8SBjoern A. Zeeb __func__, error); 61298c230c8SBjoern A. Zeeb scb->if_qflush = ifp->if_qflush; 61398c230c8SBjoern A. Zeeb ifp->if_qflush = epair_qflush; 61498c230c8SBjoern A. Zeeb ifp->if_transmit = epair_transmit; 61598c230c8SBjoern A. Zeeb ifp->if_baudrate = IF_Gbps(10UL); /* arbitrary maximum */ 61698c230c8SBjoern A. Zeeb 61798c230c8SBjoern A. Zeeb /* 61898c230c8SBjoern A. Zeeb * Restore name to <n>a as the ifp for this will go into the 61998c230c8SBjoern A. Zeeb * cloner list for the initial call. 62098c230c8SBjoern A. Zeeb */ 62198c230c8SBjoern A. Zeeb strlcpy(name, sca->ifp->if_xname, len); 62298c230c8SBjoern A. Zeeb DPRINTF("name='%s/%db' created sca=%p scb=%p\n", name, unit, sca, scb); 62398c230c8SBjoern A. Zeeb 62498c230c8SBjoern A. Zeeb /* Tell the world, that we are ready to rock. */ 62598c230c8SBjoern A. Zeeb sca->ifp->if_drv_flags |= IFF_DRV_RUNNING; 62698c230c8SBjoern A. Zeeb scb->ifp->if_drv_flags |= IFF_DRV_RUNNING; 62798c230c8SBjoern A. Zeeb 62898c230c8SBjoern A. Zeeb return (0); 62998c230c8SBjoern A. Zeeb } 63098c230c8SBjoern A. Zeeb 63198c230c8SBjoern A. Zeeb static int 63298c230c8SBjoern A. Zeeb epair_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) 63398c230c8SBjoern A. Zeeb { 63498c230c8SBjoern A. Zeeb struct ifnet *oifp; 63598c230c8SBjoern A. Zeeb struct epair_softc *sca, *scb; 63698c230c8SBjoern A. Zeeb int unit, error; 63798c230c8SBjoern A. Zeeb 63898c230c8SBjoern A. Zeeb DPRINTF("ifp=%p\n", ifp); 63998c230c8SBjoern A. Zeeb 64098c230c8SBjoern A. Zeeb /* 64198c230c8SBjoern A. Zeeb * In case we called into if_clone_destroyif() ourselves 64298c230c8SBjoern A. Zeeb * again to remove the second interface, the softc will be 64398c230c8SBjoern A. Zeeb * NULL. In that case so not do anything but return success. 64498c230c8SBjoern A. Zeeb */ 64598c230c8SBjoern A. Zeeb if (ifp->if_softc == NULL) 64698c230c8SBjoern A. Zeeb return (0); 64798c230c8SBjoern A. Zeeb 64898c230c8SBjoern A. Zeeb unit = ifp->if_dunit; 64998c230c8SBjoern A. Zeeb sca = ifp->if_softc; 65098c230c8SBjoern A. Zeeb oifp = sca->oifp; 65198c230c8SBjoern A. Zeeb scb = oifp->if_softc; 65298c230c8SBjoern A. Zeeb 65398c230c8SBjoern A. Zeeb DPRINTF("ifp=%p oifp=%p\n", ifp, oifp); 65498c230c8SBjoern A. Zeeb ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 65598c230c8SBjoern A. Zeeb oifp->if_drv_flags &= ~IFF_DRV_RUNNING; 65698c230c8SBjoern A. Zeeb ether_ifdetach(oifp); 65798c230c8SBjoern A. Zeeb ether_ifdetach(ifp); 65898c230c8SBjoern A. Zeeb /* 65998c230c8SBjoern A. Zeeb * Wait for all packets to be dispatched to if_input. 66098c230c8SBjoern A. Zeeb * The numbers can only go down as the interfaces are 66198c230c8SBjoern A. Zeeb * detached so there is no need to use atomics. 66298c230c8SBjoern A. Zeeb */ 66398c230c8SBjoern A. Zeeb DPRINTF("sca refcnt=%u scb refcnt=%u\n", sca->refcount, scb->refcount); 66498c230c8SBjoern A. Zeeb KASSERT(sca->refcount == 1 && scb->refcount == 1, 66598c230c8SBjoern A. Zeeb ("%s: sca->refcount!=1: %d || scb->refcount!=1: %d", 66698c230c8SBjoern A. Zeeb __func__, sca->refcount, scb->refcount)); 66798c230c8SBjoern A. Zeeb 66898c230c8SBjoern A. Zeeb /* 66998c230c8SBjoern A. Zeeb * Get rid of our second half. 67098c230c8SBjoern A. Zeeb */ 67198c230c8SBjoern A. Zeeb oifp->if_softc = NULL; 67298c230c8SBjoern A. Zeeb error = if_clone_destroyif(ifc, oifp); 67398c230c8SBjoern A. Zeeb if (error) 67498c230c8SBjoern A. Zeeb panic("%s: if_clone_destroyif() for our 2nd iface failed: %d", 67598c230c8SBjoern A. Zeeb __func__, error); 67698c230c8SBjoern A. Zeeb 67798c230c8SBjoern A. Zeeb /* Finish cleaning up. Free them and release the unit. */ 67898c230c8SBjoern A. Zeeb if_free_type(oifp, IFT_ETHER); 67998c230c8SBjoern A. Zeeb if_free_type(ifp, IFT_ETHER); 68098c230c8SBjoern A. Zeeb free(scb, M_EPAIR); 68198c230c8SBjoern A. Zeeb free(sca, M_EPAIR); 68298c230c8SBjoern A. Zeeb ifc_free_unit(ifc, unit); 68398c230c8SBjoern A. Zeeb 68498c230c8SBjoern A. Zeeb return (0); 68598c230c8SBjoern A. Zeeb } 68698c230c8SBjoern A. Zeeb 68798c230c8SBjoern A. Zeeb static int 68898c230c8SBjoern A. Zeeb epair_modevent(module_t mod, int type, void *data) 68998c230c8SBjoern A. Zeeb { 69098c230c8SBjoern A. Zeeb int tmp; 69198c230c8SBjoern A. Zeeb 69298c230c8SBjoern A. Zeeb switch (type) { 69398c230c8SBjoern A. Zeeb case MOD_LOAD: 69498c230c8SBjoern A. Zeeb /* For now limit us to one global mutex and one inq. */ 69598c230c8SBjoern A. Zeeb EPAIR_LOCK_INIT(); 69698c230c8SBjoern A. Zeeb epair_drv_flags = 0; 69798c230c8SBjoern A. Zeeb epairinq.ifq_maxlen = 16 * ifqmaxlen; /* What is a good 16? */ 69898c230c8SBjoern A. Zeeb if (TUNABLE_INT_FETCH("net.link.epair.netisr_maxqlen", &tmp)) 69998c230c8SBjoern A. Zeeb epairinq.ifq_maxlen = tmp; 70098c230c8SBjoern A. Zeeb mtx_init(&epairinq.ifq_mtx, "epair_inq", NULL, MTX_DEF); 70198c230c8SBjoern A. Zeeb netisr_register2(NETISR_EPAIR, (netisr_t *)epair_sintr, 70298c230c8SBjoern A. Zeeb epair_sintr_drained, &epairinq, 0); 70398c230c8SBjoern A. Zeeb if_clone_attach(&epair_cloner); 70498c230c8SBjoern A. Zeeb if (bootverbose) 70598c230c8SBjoern A. Zeeb printf("%s initialized.\n", EPAIRNAME); 70698c230c8SBjoern A. Zeeb break; 70798c230c8SBjoern A. Zeeb case MOD_UNLOAD: 70898c230c8SBjoern A. Zeeb if_clone_detach(&epair_cloner); 70998c230c8SBjoern A. Zeeb netisr_unregister(NETISR_EPAIR); 71098c230c8SBjoern A. Zeeb mtx_destroy(&epairinq.ifq_mtx); 71198c230c8SBjoern A. Zeeb EPAIR_LOCK_DESTROY(); 71298c230c8SBjoern A. Zeeb if (bootverbose) 71398c230c8SBjoern A. Zeeb printf("%s unloaded.\n", EPAIRNAME); 71498c230c8SBjoern A. Zeeb break; 71598c230c8SBjoern A. Zeeb default: 71698c230c8SBjoern A. Zeeb return (EOPNOTSUPP); 71798c230c8SBjoern A. Zeeb } 71898c230c8SBjoern A. Zeeb return (0); 71998c230c8SBjoern A. Zeeb } 72098c230c8SBjoern A. Zeeb 72198c230c8SBjoern A. Zeeb static moduledata_t epair_mod = { 72298c230c8SBjoern A. Zeeb "if_epair", 72398c230c8SBjoern A. Zeeb epair_modevent, 72498c230c8SBjoern A. Zeeb 0 72598c230c8SBjoern A. Zeeb }; 72698c230c8SBjoern A. Zeeb 72798c230c8SBjoern A. Zeeb DECLARE_MODULE(if_epair, epair_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 72898c230c8SBjoern A. Zeeb MODULE_VERSION(if_epair, 1); 729