diff --git a/sys/net/bridgestp.c b/sys/net/bridgestp.c index 93d8f85..8f33303 100644 --- a/sys/net/bridgestp.c +++ b/sys/net/bridgestp.c @@ -221,6 +221,8 @@ bstp_send_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, 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_softc *sc, struct bridge_iflist *bif, 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 *sc) 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 *sc) 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, struct bridge_iflist *bif, struct mbuf *m) 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, struct bridge_iflist *bif, struct mbuf *m) 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, struct bridge_iflist *bif, struct mbuf *m) (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 *sc) 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 *sc) continue; } } + if (mif == NULL) { + BRIDGE_UNLOCK(sc); bstp_stop(sc); return; } @@ -837,6 +862,8 @@ bstp_initialization(struct bridge_softc *sc) (((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 *sc) 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 *sc) 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); } diff --git a/sys/net/if.h b/sys/net/if.h index 5b01a01..58888ef 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -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 () */ pfil_head_t * if_pfil; /* filtering point */ uint64_t if_capabilities; /* interface capabilities */ diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 471acf4..86c93ea 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -218,6 +218,7 @@ static struct bridge_iflist *bridge_lookup_member(struct bridge_softc *, 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 *); @@ -364,6 +365,12 @@ bridge_clone_create(struct if_clone *ifc, int unit) 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; @@ -413,8 +420,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); @@ -431,6 +440,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); @@ -647,7 +660,7 @@ bridge_ioctl(struct ifnet *ifp, u_long cmd, void *data) /* * 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) @@ -655,31 +668,76 @@ bridge_lookup_member(struct bridge_softc *sc, const char *name) 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 } /* @@ -692,30 +750,24 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif) { 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 @@ -764,11 +816,18 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) 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 @@ -786,15 +845,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 @@ -813,6 +909,8 @@ bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg) req->ifbr_path_cost = bif->bif_path_cost; req->ifbr_portno = bif->bif_ifp->if_index & 0xff; + bridge_release_member(sc, bif); + return (0); } @@ -834,12 +932,15 @@ bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg) 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); @@ -875,6 +976,8 @@ bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) struct ifbreq breq; int count, len, error = 0; + BRIDGE_LOCK(sc); + count = 0; LIST_FOREACH(bif, &sc->sc_iflist, bif_next) count++; @@ -905,6 +1008,8 @@ bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) len -= sizeof(breq); } + BRIDGE_UNLOCK(sc); + bifc->ifbic_len = sizeof(breq) * count; return (error); } @@ -920,6 +1025,8 @@ bridge_ioctl_rts(struct bridge_softc *sc, void *arg) 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)) @@ -941,6 +1048,8 @@ bridge_ioctl_rts(struct bridge_softc *sc, void *arg) len -= sizeof(bareq); } out: + mutex_exit(sc->sc_rtlist_lock); + bac->ifbac_len = sizeof(bareq) * count; return (error); } @@ -959,6 +1068,8 @@ bridge_ioctl_saddr(struct bridge_softc *sc, void *arg) error = bridge_rtupdate(sc, req->ifba_dst, bif->bif_ifp, 1, req->ifba_flags); + bridge_release_member(sc, bif); + return (error); } @@ -1113,6 +1224,8 @@ bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg) if (sc->sc_if.if_flags & IFF_RUNNING) bstp_initialization(sc); + bridge_release_member(sc, bif); + return (0); } @@ -1169,6 +1282,8 @@ bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) if (sc->sc_if.if_flags & IFF_RUNNING) bstp_initialization(sc); + bridge_release_member(sc, bif); + return (0); } @@ -1184,6 +1299,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)); @@ -1236,8 +1354,6 @@ bridge_stop(struct ifnet *ifp, int disable) * 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, @@ -1279,7 +1395,9 @@ bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m, 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++; @@ -1317,7 +1435,9 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa, 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); @@ -1328,14 +1448,17 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa, 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; } @@ -1353,6 +1476,8 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa, 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) @@ -1387,9 +1512,14 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa, bridge_enqueue(sc, dst_if, mc, 0); } + + BRIDGE_UNLOCK(sc); + if (used == 0) m_freem(m); +#ifndef BRIDGE_MPSAFE splx(s); +#endif return (0); } @@ -1400,13 +1530,17 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *sa, 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); } @@ -1437,17 +1571,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; @@ -1470,6 +1611,7 @@ bridge_forward(void *v) case BSTP_IFSTATE_LISTENING: case BSTP_IFSTATE_DISABLED: m_freem(m); + bridge_release_member(sc, bif); continue; } } @@ -1496,9 +1638,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. @@ -1542,6 +1687,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?) */ @@ -1554,15 +1700,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 @@ -1600,7 +1751,6 @@ bridge_ourether(struct bridge_iflist *bif, struct ether_header *eh, int src) * * 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) @@ -1609,7 +1759,8 @@ bridge_input(struct ifnet *ifp, struct mbuf *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; } @@ -1638,6 +1789,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *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)) { @@ -1646,21 +1798,28 @@ bridge_input(struct ifnet *ifp, struct mbuf *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; } @@ -1670,9 +1829,12 @@ bridge_input(struct ifnet *ifp, struct mbuf *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); @@ -1696,6 +1858,8 @@ bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, 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) @@ -1733,6 +1897,8 @@ bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, ether_input(src_if, m); else if (!used) m_freem(m); + + BRIDGE_UNLOCK(sc); } /* @@ -1745,15 +1911,19 @@ bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, 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 @@ -1761,8 +1931,10 @@ bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, * 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; @@ -1771,7 +1943,7 @@ bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, if ((error = bridge_rtnode_insert(sc, brt)) != 0) { pool_put(&bridge_rtnode_pool, brt); - return (error); + goto out; } } @@ -1784,7 +1956,10 @@ bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, brt->brt_expire = time_uptime + sc->sc_brttimeout; } - return (0); +out: + mutex_exit(sc->sc_rtlist_lock); + + return error; } /* @@ -1796,11 +1971,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; } /* @@ -1815,23 +1996,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; } /* @@ -1843,15 +2031,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); } /* @@ -1864,6 +2053,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) { @@ -1883,11 +2074,15 @@ bridge_rtflush(struct bridge_softc *sc, int full) { 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); } /* @@ -1899,12 +2094,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; } /* @@ -1917,11 +2121,15 @@ bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp) { 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); } /* @@ -1946,6 +2154,8 @@ bridge_rtable_init(struct bridge_softc *sc) LIST_INIT(&sc->sc_rtlist); + sc->sc_rtlist_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET); + return (0); } @@ -1959,6 +2169,8 @@ bridge_rtable_fini(struct bridge_softc *sc) { free(sc->sc_rthash, M_DEVBUF); + if (sc->sc_rtlist_lock) + mutex_obj_free(sc->sc_rtlist_lock); } /* @@ -2009,6 +2221,8 @@ bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr) 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); @@ -2034,6 +2248,8 @@ bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) 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]); @@ -2076,15 +2292,14 @@ bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) 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) diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h index dfb0b16..9a95a62 100644 --- a/sys/net/if_bridgevar.h +++ b/sys/net/if_bridgevar.h @@ -77,6 +77,8 @@ #include #include +#include +#include /* * 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 *, struct bridge_iflist *, struct mbuf *); 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_ */