Module Name: src Committed By: ozaki-r Date: Mon Jul 14 02:34:36 UTC 2014
Modified Files: src/sys/net: bridgestp.c if.h if_bridge.c if_bridgevar.h Log Message: Make bridge MPSAFE - Introduce BRIDGE_MPSAFE - It's enabled only when NET_MPSAFE is defined in if.h or the kernel config - Add iflist and rtlist mutex locks - Locking iflist is performance sensitive, so it's not used when !BRIDGE_MPSAFE - Add bif object reference counting - It enables fine-grain locking for bridge member lists by allowing to not hold a lock during touching a bif - bridge_release_member is added to decrement the reference count - A condition variable is added to do bridge_delete_member gracefully - Add if_bridgeif to ifnet - It's a shortcut to a bif object of a bridge member - It reduces a bif lookup cost and so lock contention on iflist - Make bridgestp MPSAFE too To generate a diff of this commit: cvs rdiff -u -r1.16 -r1.17 src/sys/net/bridgestp.c cvs rdiff -u -r1.170 -r1.171 src/sys/net/if.h cvs rdiff -u -r1.86 -r1.87 src/sys/net/if_bridge.c cvs rdiff -u -r1.19 -r1.20 src/sys/net/if_bridgevar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/bridgestp.c diff -u src/sys/net/bridgestp.c:1.16 src/sys/net/bridgestp.c:1.17 --- src/sys/net/bridgestp.c:1.16 Wed Jun 18 01:19:19 2014 +++ src/sys/net/bridgestp.c Mon Jul 14 02:34:36 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: bridgestp.c,v 1.16 2014/06/18 01:19:19 ozaki-r Exp $ */ +/* $NetBSD: bridgestp.c,v 1.17 2014/07/14 02:34:36 ozaki-r Exp $ */ /* * Copyright (c) 2000 Jason L. Wright (ja...@thought.net) @@ -40,7 +40,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: bridgestp.c,v 1.16 2014/06/18 01:19:19 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bridgestp.c,v 1.17 2014/07/14 02:34:36 ozaki-r Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -221,6 +221,8 @@ bstp_send_config_bpdu(struct bridge_soft struct bstp_cbpdu bpdu; int s; + KASSERT(BRIDGE_LOCKED(sc)); + ifp = bif->bif_ifp; if ((ifp->if_flags & IFF_RUNNING) == 0) @@ -274,9 +276,11 @@ bstp_send_config_bpdu(struct bridge_soft memcpy(mtod(m, char *) + sizeof(*eh), &bpdu, sizeof(bpdu)); + BRIDGE_UNLOCK(sc); s = splnet(); bridge_enqueue(sc, ifp, m, 0); splx(s); + BRIDGE_LOCK(sc); } static int @@ -363,6 +367,8 @@ bstp_transmit_tcn(struct bridge_softc *s struct mbuf *m; int s; + KASSERT(BRIDGE_LOCKED(sc)); + KASSERT(bif != NULL); ifp = bif->bif_ifp; if ((ifp->if_flags & IFF_RUNNING) == 0) @@ -390,9 +396,11 @@ bstp_transmit_tcn(struct bridge_softc *s memcpy(mtod(m, char *) + sizeof(*eh), &bpdu, sizeof(bpdu)); + BRIDGE_UNLOCK(sc); s = splnet(); bridge_enqueue(sc, ifp, m, 0); splx(s); + BRIDGE_LOCK(sc); } static void @@ -592,6 +600,10 @@ bstp_input(struct bridge_softc *sc, stru struct bstp_tcn_unit tu; uint16_t len; +#ifdef BRIDGE_MPSAFE + KASSERT(bif->bif_refs > 0); +#endif + eh = mtod(m, struct ether_header *); if ((bif->bif_flags & IFBIF_STP) == 0) @@ -621,7 +633,11 @@ bstp_input(struct bridge_softc *sc, stru switch (tpdu.tbu_bpdutype) { case BSTP_MSGTYPE_TCN: tu.tu_message_type = tpdu.tbu_bpdutype; + + BRIDGE_LOCK(sc); bstp_received_tcn_bpdu(sc, bif, &tu); + BRIDGE_UNLOCK(sc); + break; case BSTP_MSGTYPE_CFG: if (m->m_len < sizeof(cpdu) && @@ -658,7 +674,11 @@ bstp_input(struct bridge_softc *sc, stru (cpdu.cbu_flags & BSTP_FLAG_TCA) ? 1 : 0; cu.cu_topology_change = (cpdu.cbu_flags & BSTP_FLAG_TC) ? 1 : 0; + + BRIDGE_LOCK(sc); bstp_received_config_bpdu(sc, bif, &cu); + BRIDGE_UNLOCK(sc); + break; default: goto out; @@ -805,6 +825,9 @@ bstp_initialization(struct bridge_softc struct bridge_iflist *bif, *mif; mif = NULL; + + BRIDGE_LOCK(sc); + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if ((bif->bif_flags & IFBIF_STP) == 0) continue; @@ -823,7 +846,9 @@ bstp_initialization(struct bridge_softc continue; } } + if (mif == NULL) { + BRIDGE_UNLOCK(sc); bstp_stop(sc); return; } @@ -837,6 +862,8 @@ bstp_initialization(struct bridge_softc (((uint64_t)(uint8_t)CLLADDR(mif->bif_ifp->if_sadl)[4]) << 8) | (((uint64_t)(uint8_t)CLLADDR(mif->bif_ifp->if_sadl)[5]) << 0); + BRIDGE_UNLOCK(sc); + sc->sc_designated_root = sc->sc_bridge_id; sc->sc_root_path_cost = 0; sc->sc_root_port = NULL; @@ -853,6 +880,8 @@ bstp_initialization(struct bridge_softc callout_reset(&sc->sc_bstpcallout, hz, bstp_tick, sc); + BRIDGE_LOCK(sc); + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (bif->bif_flags & IFBIF_STP) bstp_enable_port(sc, bif); @@ -863,6 +892,8 @@ bstp_initialization(struct bridge_softc bstp_port_state_selection(sc); bstp_config_bpdu_generation(sc); bstp_timer_start(&sc->sc_hello_timer, 0); + + BRIDGE_UNLOCK(sc); } void @@ -870,12 +901,14 @@ bstp_stop(struct bridge_softc *sc) { struct bridge_iflist *bif; + BRIDGE_LOCK(sc); LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED); bstp_timer_stop(&bif->bif_hold_timer); bstp_timer_stop(&bif->bif_message_age_timer); bstp_timer_stop(&bif->bif_forward_delay_timer); } + BRIDGE_UNLOCK(sc); callout_stop(&sc->sc_bstpcallout); @@ -1032,6 +1065,7 @@ bstp_tick(void *arg) int s; s = splnet(); + BRIDGE_LOCK(sc); LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if ((bif->bif_flags & IFBIF_STP) == 0) @@ -1079,6 +1113,7 @@ bstp_tick(void *arg) if (sc->sc_if.if_flags & IFF_RUNNING) callout_reset(&sc->sc_bstpcallout, hz, bstp_tick, sc); + BRIDGE_UNLOCK(sc); splx(s); } Index: src/sys/net/if.h diff -u src/sys/net/if.h:1.170 src/sys/net/if.h:1.171 --- src/sys/net/if.h:1.170 Tue Jul 1 14:04:40 2014 +++ src/sys/net/if.h Mon Jul 14 02:34:36 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: if.h,v 1.170 2014/07/01 14:04:40 ozaki-r Exp $ */ +/* $NetBSD: if.h,v 1.171 2014/07/14 02:34:36 ozaki-r Exp $ */ /*- * Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc. @@ -290,6 +290,7 @@ typedef struct ifnet { struct ifaddr *if_hwdl; const uint8_t *if_broadcastaddr;/* linklevel broadcast bytestring */ void *if_bridge; /* bridge glue */ + void *if_bridgeif; /* shortcut to interface list entry */ int if_dlt; /* data link type (<net/dlt.h>) */ pfil_head_t * if_pfil; /* filtering point */ uint64_t if_capabilities; /* interface capabilities */ Index: src/sys/net/if_bridge.c diff -u src/sys/net/if_bridge.c:1.86 src/sys/net/if_bridge.c:1.87 --- src/sys/net/if_bridge.c:1.86 Wed Jul 2 09:51:08 2014 +++ src/sys/net/if_bridge.c Mon Jul 14 02:34:36 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bridge.c,v 1.86 2014/07/02 09:51:08 ozaki-r Exp $ */ +/* $NetBSD: if_bridge.c,v 1.87 2014/07/14 02:34:36 ozaki-r Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -80,7 +80,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.86 2014/07/02 09:51:08 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.87 2014/07/14 02:34:36 ozaki-r Exp $"); #ifdef _KERNEL_OPT #include "opt_bridge_ipf.h" @@ -219,6 +219,7 @@ static struct bridge_iflist *bridge_look const char *name); static struct bridge_iflist *bridge_lookup_member_if(struct bridge_softc *, struct ifnet *ifp); +static void bridge_release_member(struct bridge_softc *, struct bridge_iflist *); static void bridge_delete_member(struct bridge_softc *, struct bridge_iflist *); @@ -365,6 +366,12 @@ bridge_clone_create(struct if_clone *ifc callout_init(&sc->sc_bstpcallout, 0); LIST_INIT(&sc->sc_iflist); +#ifdef BRIDGE_MPSAFE + sc->sc_iflist_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET); +#else + sc->sc_iflist_lock = NULL; +#endif + cv_init(&sc->sc_iflist_cv, "if_bridge_cv"); if_initname(ifp, ifc->ifc_name, unit); ifp->if_softc = sc; @@ -414,8 +421,10 @@ bridge_clone_destroy(struct ifnet *ifp) bridge_stop(ifp, 1); + BRIDGE_LOCK(sc); while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL) bridge_delete_member(sc, bif); + BRIDGE_UNLOCK(sc); mutex_enter(&bridge_list_lock); LIST_REMOVE(sc, sc_list); @@ -432,6 +441,10 @@ bridge_clone_destroy(struct ifnet *ifp) /* Tear down the routing table. */ bridge_rtable_fini(sc); + cv_destroy(&sc->sc_iflist_cv); + if (sc->sc_iflist_lock) + mutex_obj_free(sc->sc_iflist_lock); + free(sc, M_DEVBUF); return (0); @@ -648,7 +661,7 @@ bridge_ioctl(struct ifnet *ifp, u_long c /* * bridge_lookup_member: * - * Lookup a bridge member interface. Must be called at splnet(). + * Lookup a bridge member interface. */ static struct bridge_iflist * bridge_lookup_member(struct bridge_softc *sc, const char *name) @@ -656,31 +669,76 @@ bridge_lookup_member(struct bridge_softc struct bridge_iflist *bif; struct ifnet *ifp; + BRIDGE_LOCK(sc); + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { ifp = bif->bif_ifp; if (strcmp(ifp->if_xname, name) == 0) - return (bif); + break; } - return (NULL); +#ifdef BRIDGE_MPSAFE + if (bif != NULL) { + if (bif->bif_waiting) + bif = NULL; + else + atomic_inc_32(&bif->bif_refs); + } +#endif + + BRIDGE_UNLOCK(sc); + + return bif; } /* * bridge_lookup_member_if: * - * Lookup a bridge member interface by ifnet*. Must be called at splnet(). + * Lookup a bridge member interface by ifnet*. */ static struct bridge_iflist * bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp) { struct bridge_iflist *bif; - LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { - if (bif->bif_ifp == member_ifp) - return (bif); + BRIDGE_LOCK(sc); + + bif = member_ifp->if_bridgeif; + +#ifdef BRIDGE_MPSAFE + if (bif != NULL) { + if (bif->bif_waiting) + bif = NULL; + else + atomic_inc_32(&bif->bif_refs); } +#endif - return (NULL); + BRIDGE_UNLOCK(sc); + + return bif; +} + +/* + * bridge_release_member: + * + * Release the specified member interface. + */ +static void +bridge_release_member(struct bridge_softc *sc, struct bridge_iflist *bif) +{ +#ifdef BRIDGE_MPSAFE + atomic_dec_32(&bif->bif_refs); + membar_sync(); + if (__predict_false(bif->bif_waiting && bif->bif_refs == 0)) { + BRIDGE_LOCK(sc); + cv_broadcast(&sc->sc_iflist_cv); + BRIDGE_UNLOCK(sc); + } +#else + (void)sc; + (void)bif; +#endif } /* @@ -693,30 +751,24 @@ bridge_delete_member(struct bridge_softc { struct ifnet *ifs = bif->bif_ifp; - switch (ifs->if_type) { - case IFT_ETHER: - /* - * Take the interface out of promiscuous mode. - */ - (void) ifpromisc(ifs, 0); - break; - default: -#ifdef DIAGNOSTIC - panic("bridge_delete_member: impossible"); -#endif - break; - } + KASSERT(BRIDGE_LOCKED(sc)); ifs->if_input = ether_input; ifs->if_bridge = NULL; + ifs->if_bridgeif = NULL; + LIST_REMOVE(bif, bif_next); - bridge_rtdelete(sc, ifs); +#ifdef BRIDGE_MPSAFE + bif->bif_waiting = true; + membar_sync(); + while (bif->bif_refs > 0) { + aprint_debug("%s: cv_wait on iflist\n", __func__); + cv_wait(&sc->sc_iflist_cv, sc->sc_iflist_lock); + } +#endif free(bif, M_DEVBUF); - - if (sc->sc_if.if_flags & IFF_RUNNING) - bstp_initialization(sc); } static int @@ -765,11 +817,18 @@ bridge_ioctl_add(struct bridge_softc *sc bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; bif->bif_priority = BSTP_DEFAULT_PORT_PRIORITY; bif->bif_path_cost = BSTP_DEFAULT_PATH_COST; + bif->bif_refs = 0; + bif->bif_waiting = false; + + BRIDGE_LOCK(sc); ifs->if_bridge = sc; + ifs->if_bridgeif = bif; LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next); ifs->if_input = bridge_input; + BRIDGE_UNLOCK(sc); + if (sc->sc_if.if_flags & IFF_RUNNING) bstp_initialization(sc); else @@ -787,15 +846,52 @@ static int bridge_ioctl_del(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; + const char *name = req->ifbr_ifsname; struct bridge_iflist *bif; + struct ifnet *ifs; - bif = bridge_lookup_member(sc, req->ifbr_ifsname); - if (bif == NULL) - return (ENOENT); + BRIDGE_LOCK(sc); + + /* + * Don't use bridge_lookup_member. We want to get a member + * with bif_refs == 0. + */ + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + ifs = bif->bif_ifp; + if (strcmp(ifs->if_xname, name) == 0) + break; + } + + if (bif == NULL) { + BRIDGE_UNLOCK(sc); + return ENOENT; + } bridge_delete_member(sc, bif); - return (0); + BRIDGE_UNLOCK(sc); + + switch (ifs->if_type) { + case IFT_ETHER: + /* + * Take the interface out of promiscuous mode. + * Don't call it with holding sc_iflist_lock. + */ + (void) ifpromisc(ifs, 0); + break; + default: +#ifdef DIAGNOSTIC + panic("bridge_delete_member: impossible"); +#endif + break; + } + + bridge_rtdelete(sc, ifs); + + if (sc->sc_if.if_flags & IFF_RUNNING) + bstp_initialization(sc); + + return 0; } static int @@ -814,6 +910,8 @@ bridge_ioctl_gifflags(struct bridge_soft req->ifbr_path_cost = bif->bif_path_cost; req->ifbr_portno = bif->bif_ifp->if_index & 0xff; + bridge_release_member(sc, bif); + return (0); } @@ -835,12 +933,15 @@ bridge_ioctl_sifflags(struct bridge_soft default: /* Nothing else can. */ + bridge_release_member(sc, bif); return (EINVAL); } } bif->bif_flags = req->ifbr_ifsflags; + bridge_release_member(sc, bif); + if (sc->sc_if.if_flags & IFF_RUNNING) bstp_initialization(sc); @@ -876,6 +977,8 @@ bridge_ioctl_gifs(struct bridge_softc *s struct ifbreq breq; int count, len, error = 0; + BRIDGE_LOCK(sc); + count = 0; LIST_FOREACH(bif, &sc->sc_iflist, bif_next) count++; @@ -906,6 +1009,8 @@ bridge_ioctl_gifs(struct bridge_softc *s len -= sizeof(breq); } + BRIDGE_UNLOCK(sc); + bifc->ifbic_len = sizeof(breq) * count; return (error); } @@ -921,6 +1026,8 @@ bridge_ioctl_rts(struct bridge_softc *sc if (bac->ifbac_len == 0) return (0); + mutex_enter(sc->sc_rtlist_lock); + len = bac->ifbac_len; LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { if (len < sizeof(bareq)) @@ -942,6 +1049,8 @@ bridge_ioctl_rts(struct bridge_softc *sc len -= sizeof(bareq); } out: + mutex_exit(sc->sc_rtlist_lock); + bac->ifbac_len = sizeof(bareq) * count; return (error); } @@ -960,6 +1069,8 @@ bridge_ioctl_saddr(struct bridge_softc * error = bridge_rtupdate(sc, req->ifba_dst, bif->bif_ifp, 1, req->ifba_flags); + bridge_release_member(sc, bif); + return (error); } @@ -1114,6 +1225,8 @@ bridge_ioctl_sifprio(struct bridge_softc if (sc->sc_if.if_flags & IFF_RUNNING) bstp_initialization(sc); + bridge_release_member(sc, bif); + return (0); } @@ -1170,6 +1283,8 @@ bridge_ioctl_sifcost(struct bridge_softc if (sc->sc_if.if_flags & IFF_RUNNING) bstp_initialization(sc); + bridge_release_member(sc, bif); + return (0); } @@ -1185,6 +1300,9 @@ bridge_ifdetach(struct ifnet *ifp) struct bridge_softc *sc = ifp->if_bridge; struct ifbreq breq; + /* ioctl_lock should prevent this from happening */ + KASSERT(sc != NULL); + memset(&breq, 0, sizeof(breq)); strlcpy(breq.ifbr_ifsname, ifp->if_xname, sizeof(breq.ifbr_ifsname)); @@ -1237,8 +1355,6 @@ bridge_stop(struct ifnet *ifp, int disab * bridge_enqueue: * * Enqueue a packet on a bridge member interface. - * - * NOTE: must be called at splnet(). */ void bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m, @@ -1280,7 +1396,9 @@ bridge_enqueue(struct bridge_softc *sc, len = m->m_pkthdr.len; m->m_flags |= M_PROTO1; mflags = m->m_flags; + IFQ_ENQUEUE(&dst_ifp->if_snd, m, &pktattr, error); + if (error) { /* mbuf is already freed */ sc->sc_if.if_oerrors++; @@ -1318,7 +1436,9 @@ bridge_output(struct ifnet *ifp, struct struct ether_header *eh; struct ifnet *dst_if; struct bridge_softc *sc; +#ifndef BRIDGE_MPSAFE int s; +#endif if (m->m_len < ETHER_HDR_LEN) { m = m_pullup(m, ETHER_HDR_LEN); @@ -1329,14 +1449,17 @@ bridge_output(struct ifnet *ifp, struct eh = mtod(m, struct ether_header *); sc = ifp->if_bridge; +#ifndef BRIDGE_MPSAFE s = splnet(); +#endif /* * If bridge is down, but the original output interface is up, * go ahead and send out that interface. Otherwise, the packet * is dropped below. */ - if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) { + if (__predict_false(sc == NULL) || + (sc->sc_if.if_flags & IFF_RUNNING) == 0) { dst_if = ifp; goto sendunicast; } @@ -1354,6 +1477,8 @@ bridge_output(struct ifnet *ifp, struct struct mbuf *mc; int used = 0; + BRIDGE_LOCK(sc); + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { dst_if = bif->bif_ifp; if ((dst_if->if_flags & IFF_RUNNING) == 0) @@ -1388,9 +1513,14 @@ bridge_output(struct ifnet *ifp, struct bridge_enqueue(sc, dst_if, mc, 0); } + + BRIDGE_UNLOCK(sc); + if (used == 0) m_freem(m); +#ifndef BRIDGE_MPSAFE splx(s); +#endif return (0); } @@ -1401,13 +1531,17 @@ bridge_output(struct ifnet *ifp, struct if ((dst_if->if_flags & IFF_RUNNING) == 0) { m_freem(m); +#ifndef BRIDGE_MPSAFE splx(s); +#endif return (0); } bridge_enqueue(sc, dst_if, m, 0); +#ifndef BRIDGE_MPSAFE splx(s); +#endif return (0); } @@ -1438,17 +1572,24 @@ bridge_forward(void *v) struct bridge_iflist *bif; struct ifnet *src_if, *dst_if; struct ether_header *eh; +#ifndef BRIDGE_MPSAFE int s; KERNEL_LOCK(1, NULL); mutex_enter(softnet_lock); +#endif + if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) { +#ifndef BRIDGE_MPSAFE mutex_exit(softnet_lock); KERNEL_UNLOCK_ONE(NULL); +#endif return; } +#ifndef BRIDGE_MPSAFE s = splnet(); +#endif while ((m = pktq_dequeue(sc->sc_fwd_pktq)) != NULL) { src_if = m->m_pkthdr.rcvif; @@ -1471,6 +1612,7 @@ bridge_forward(void *v) case BSTP_IFSTATE_LISTENING: case BSTP_IFSTATE_DISABLED: m_freem(m); + bridge_release_member(sc, bif); continue; } } @@ -1497,9 +1639,12 @@ bridge_forward(void *v) if ((bif->bif_flags & IFBIF_STP) != 0 && bif->bif_state == BSTP_IFSTATE_LEARNING) { m_freem(m); + bridge_release_member(sc, bif); continue; } + bridge_release_member(sc, bif); + /* * At this point, the port either doesn't participate * in spanning tree or it is in the forwarding state. @@ -1543,6 +1688,7 @@ bridge_forward(void *v) m_freem(m); continue; } + bif = bridge_lookup_member_if(sc, dst_if); if (bif == NULL) { /* Not a member of the bridge (anymore?) */ @@ -1555,15 +1701,20 @@ bridge_forward(void *v) case BSTP_IFSTATE_DISABLED: case BSTP_IFSTATE_BLOCKING: m_freem(m); + bridge_release_member(sc, bif); continue; } } + bridge_release_member(sc, bif); + bridge_enqueue(sc, dst_if, m, 1); } +#ifndef BRIDGE_MPSAFE splx(s); mutex_exit(softnet_lock); KERNEL_UNLOCK_ONE(NULL); +#endif } static bool @@ -1601,7 +1752,6 @@ bridge_ourether(struct bridge_iflist *bi * * Receive input from a member interface. Queue the packet for * bridging if it is not for us. - * should be called at splnet() */ static void bridge_input(struct ifnet *ifp, struct mbuf *m) @@ -1610,7 +1760,8 @@ bridge_input(struct ifnet *ifp, struct m struct bridge_iflist *bif; struct ether_header *eh; - if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) { + if (__predict_false(sc == NULL) || + (sc->sc_if.if_flags & IFF_RUNNING) == 0) { ether_input(ifp, m); return; } @@ -1639,6 +1790,7 @@ bridge_input(struct ifnet *ifp, struct m !bstp_state_before_learning(bif)) { struct bridge_iflist *_bif; + BRIDGE_LOCK(sc); LIST_FOREACH(_bif, &sc->sc_iflist, bif_next) { /* It is destined for us. */ if (bridge_ourether(_bif, eh, 0)) { @@ -1647,21 +1799,28 @@ bridge_input(struct ifnet *ifp, struct m eh->ether_shost, ifp, 0, IFBAF_DYNAMIC); m->m_pkthdr.rcvif = _bif->bif_ifp; ether_input(_bif->bif_ifp, m); - return; + break; } /* We just received a packet that we sent out. */ if (bridge_ourether(_bif, eh, 1)) { m_freem(m); - return; + break; } } + BRIDGE_UNLOCK(sc); + + if (_bif != NULL) { + bridge_release_member(sc, bif); + return; + } } /* Tap off 802.1D packets; they do not get forwarded. */ if (bif->bif_flags & IFBIF_STP && memcmp(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN) == 0) { bstp_input(sc, bif, m); + bridge_release_member(sc, bif); return; } @@ -1671,9 +1830,12 @@ bridge_input(struct ifnet *ifp, struct m */ if (bstp_state_before_learning(bif)) { ether_input(ifp, m); + bridge_release_member(sc, bif); return; } + bridge_release_member(sc, bif); + /* Queue the packet for bridge forwarding. */ if (__predict_false(!pktq_enqueue(sc->sc_fwd_pktq, m, 0))) m_freem(m); @@ -1697,6 +1859,8 @@ bridge_broadcast(struct bridge_softc *sc used = bmcast = m->m_flags & (M_BCAST|M_MCAST); + BRIDGE_LOCK(sc); + LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { dst_if = bif->bif_ifp; if (dst_if == src_if) @@ -1734,6 +1898,8 @@ bridge_broadcast(struct bridge_softc *sc ether_input(src_if, m); else if (!used) m_freem(m); + + BRIDGE_UNLOCK(sc); } /* @@ -1746,15 +1912,19 @@ bridge_rtupdate(struct bridge_softc *sc, struct ifnet *dst_if, int setflags, uint8_t flags) { struct bridge_rtnode *brt; - int error; + int error = 0; + + mutex_enter(sc->sc_rtlist_lock); /* * A route for this destination might already exist. If so, * update it, otherwise create a new one. */ if ((brt = bridge_rtnode_lookup(sc, dst)) == NULL) { - if (sc->sc_brtcnt >= sc->sc_brtmax) - return (ENOSPC); + if (sc->sc_brtcnt >= sc->sc_brtmax) { + error = ENOSPC; + goto out; + } /* * Allocate a new bridge forwarding node, and @@ -1762,8 +1932,10 @@ bridge_rtupdate(struct bridge_softc *sc, * address. */ brt = pool_get(&bridge_rtnode_pool, PR_NOWAIT); - if (brt == NULL) - return (ENOMEM); + if (brt == NULL) { + error = ENOMEM; + goto out; + } memset(brt, 0, sizeof(*brt)); brt->brt_expire = time_uptime + sc->sc_brttimeout; @@ -1772,7 +1944,7 @@ bridge_rtupdate(struct bridge_softc *sc, if ((error = bridge_rtnode_insert(sc, brt)) != 0) { pool_put(&bridge_rtnode_pool, brt); - return (error); + goto out; } } @@ -1785,7 +1957,10 @@ bridge_rtupdate(struct bridge_softc *sc, brt->brt_expire = time_uptime + sc->sc_brttimeout; } - return (0); +out: + mutex_exit(sc->sc_rtlist_lock); + + return error; } /* @@ -1797,11 +1972,17 @@ static struct ifnet * bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr) { struct bridge_rtnode *brt; + struct ifnet *ifs = NULL; + + mutex_enter(sc->sc_rtlist_lock); + + brt = bridge_rtnode_lookup(sc, addr); + if (brt != NULL) + ifs = brt->brt_ifp; - if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL) - return (NULL); + mutex_exit(sc->sc_rtlist_lock); - return (brt->brt_ifp); + return ifs; } /* @@ -1816,23 +1997,30 @@ bridge_rttrim(struct bridge_softc *sc) { struct bridge_rtnode *brt, *nbrt; + mutex_enter(sc->sc_rtlist_lock); + /* Make sure we actually need to do this. */ if (sc->sc_brtcnt <= sc->sc_brtmax) - return; + goto out; /* Force an aging cycle; this might trim enough addresses. */ bridge_rtage(sc); if (sc->sc_brtcnt <= sc->sc_brtmax) - return; + goto out; for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { nbrt = LIST_NEXT(brt, brt_list); if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { bridge_rtnode_destroy(sc, brt); if (sc->sc_brtcnt <= sc->sc_brtmax) - return; + goto out; } } + +out: + mutex_exit(sc->sc_rtlist_lock); + + return; } /* @@ -1844,15 +2032,16 @@ static void bridge_timer(void *arg) { struct bridge_softc *sc = arg; - int s; - s = splnet(); + mutex_enter(sc->sc_rtlist_lock); + bridge_rtage(sc); - splx(s); if (sc->sc_if.if_flags & IFF_RUNNING) callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, bridge_timer, sc); + + mutex_exit(sc->sc_rtlist_lock); } /* @@ -1865,6 +2054,8 @@ bridge_rtage(struct bridge_softc *sc) { struct bridge_rtnode *brt, *nbrt; + KASSERT(mutex_owned(sc->sc_rtlist_lock)); + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { nbrt = LIST_NEXT(brt, brt_list); if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { @@ -1884,11 +2075,15 @@ bridge_rtflush(struct bridge_softc *sc, { struct bridge_rtnode *brt, *nbrt; + mutex_enter(sc->sc_rtlist_lock); + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { nbrt = LIST_NEXT(brt, brt_list); if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) bridge_rtnode_destroy(sc, brt); } + + mutex_exit(sc->sc_rtlist_lock); } /* @@ -1900,12 +2095,21 @@ static int bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr) { struct bridge_rtnode *brt; + int error = 0; - if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL) - return (ENOENT); + mutex_enter(sc->sc_rtlist_lock); + + if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL) { + error = ENOENT; + goto out; + } bridge_rtnode_destroy(sc, brt); - return (0); + +out: + mutex_exit(sc->sc_rtlist_lock); + + return error; } /* @@ -1918,11 +2122,15 @@ bridge_rtdelete(struct bridge_softc *sc, { struct bridge_rtnode *brt, *nbrt; + mutex_enter(sc->sc_rtlist_lock); + for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) { nbrt = LIST_NEXT(brt, brt_list); if (brt->brt_ifp == ifp) bridge_rtnode_destroy(sc, brt); } + + mutex_exit(sc->sc_rtlist_lock); } /* @@ -1947,6 +2155,8 @@ bridge_rtable_init(struct bridge_softc * LIST_INIT(&sc->sc_rtlist); + sc->sc_rtlist_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET); + return (0); } @@ -1960,6 +2170,8 @@ bridge_rtable_fini(struct bridge_softc * { free(sc->sc_rthash, M_DEVBUF); + if (sc->sc_rtlist_lock) + mutex_obj_free(sc->sc_rtlist_lock); } /* @@ -2010,6 +2222,8 @@ bridge_rtnode_lookup(struct bridge_softc uint32_t hash; int dir; + KASSERT(mutex_owned(sc->sc_rtlist_lock)); + hash = bridge_rthash(sc, addr); LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) { dir = memcmp(addr, brt->brt_addr, ETHER_ADDR_LEN); @@ -2035,6 +2249,8 @@ bridge_rtnode_insert(struct bridge_softc uint32_t hash; int dir; + KASSERT(mutex_owned(sc->sc_rtlist_lock)); + hash = bridge_rthash(sc, brt->brt_addr); lbrt = LIST_FIRST(&sc->sc_rthash[hash]); @@ -2077,15 +2293,14 @@ bridge_rtnode_insert(struct bridge_softc static void bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt) { - int s = splnet(); + + KASSERT(mutex_owned(sc->sc_rtlist_lock)); LIST_REMOVE(brt, brt_hash); LIST_REMOVE(brt, brt_list); sc->sc_brtcnt--; pool_put(&bridge_rtnode_pool, brt); - - splx(s); } #if defined(BRIDGE_IPF) Index: src/sys/net/if_bridgevar.h diff -u src/sys/net/if_bridgevar.h:1.19 src/sys/net/if_bridgevar.h:1.20 --- src/sys/net/if_bridgevar.h:1.19 Fri Jun 20 14:22:48 2014 +++ src/sys/net/if_bridgevar.h Mon Jul 14 02:34:36 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bridgevar.h,v 1.19 2014/06/20 14:22:48 ozaki-r Exp $ */ +/* $NetBSD: if_bridgevar.h,v 1.20 2014/07/14 02:34:36 ozaki-r Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -77,6 +77,8 @@ #include <sys/callout.h> #include <sys/queue.h> +#include <sys/mutex.h> +#include <sys/condvar.h> /* * Commands used in the SIOCSDRVSPEC ioctl. Note the lookup of the @@ -255,6 +257,8 @@ struct bridge_iflist { uint8_t bif_priority; struct ifnet *bif_ifp; /* member if */ uint32_t bif_flags; /* member if flags */ + uint32_t bif_refs; /* reference count */ + bool bif_waiting; /* waiting for released */ }; /* @@ -299,8 +303,11 @@ struct bridge_softc { callout_t sc_brcallout; /* bridge callout */ callout_t sc_bstpcallout; /* STP callout */ LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */ + kmutex_t *sc_iflist_lock; + kcondvar_t sc_iflist_cv; LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ + kmutex_t *sc_rtlist_lock; uint32_t sc_rthash_key; /* key for hash */ uint32_t sc_filter_flags; /* ipf and flags */ pktqueue_t * sc_fwd_pktq; @@ -320,5 +327,16 @@ void bstp_input(struct bridge_softc *, s void bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *, int); +#ifdef NET_MPSAFE +#define BRIDGE_MPSAFE 1 +#endif + +#define BRIDGE_LOCK(_sc) if ((_sc)->sc_iflist_lock) \ + mutex_enter((_sc)->sc_iflist_lock) +#define BRIDGE_UNLOCK(_sc) if ((_sc)->sc_iflist_lock) \ + mutex_exit((_sc)->sc_iflist_lock) +#define BRIDGE_LOCKED(_sc) (!(_sc)->sc_iflist_lock || \ + mutex_owned((_sc)->sc_iflist_lock)) + #endif /* _KERNEL */ #endif /* !_NET_IF_BRIDGEVAR_H_ */