> On 22 Apr 2019, at 5:45 am, Martin Pieuchot <m...@openbsd.org> wrote:
>
> Diff below removes the KERNEL_LOCK() from bridge(4)'s output fast-path.
>
> To do so, it redefines the ifp <-> bridge relationship. Currently every
> interface in a bridge(4) contains a pointer to that bridge's port. This
> relationship is guaranteed to be valid as long as the KERNEL_LOCK() is
> held. We cannot use the NET_LOCK() to protect this relation because
> wifi drivers still call bridge_output() in interrupt handlers. So I
> decided to put the bridge's interface index in `struct ifnet' instead.
>
> bridge_rtlookup() is now also returning an interface index for similar
> reasons.
>
> The `interface list' and `span list' are still protected by the
> KERNEL_LOCK() in this diff. Next step will be to move to SMR and remove
> the intermediate queue in the input path.
>
> All of that should improve latency of bridge(4) and allow us to continue
> untangle the various locks in the Network Stack.
>
> This has been quite extensively tested by Hrvoje Popovski. I'm looking
> for more tests, reviews and oks :)
ok by me.
>
> Index: net/bridgectl.c
> ===================================================================
> RCS file: /cvs/src/sys/net/bridgectl.c,v
> retrieving revision 1.17
> diff -u -p -r1.17 bridgectl.c
> --- net/bridgectl.c 8 Mar 2019 17:48:35 -0000 1.17
> +++ net/bridgectl.c 4 Apr 2019 19:58:21 -0000
> @@ -84,8 +84,7 @@ bridgectl_ioctl(struct ifnet *ifp, u_lon
> error = ENOENT;
> break;
> }
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif == NULL || bif->bridge_sc != sc) {
> + if (ifs->if_bridgeidx != ifp->if_index) {
> error = ESRCH;
> break;
> }
> @@ -126,8 +125,7 @@ bridgectl_ioctl(struct ifnet *ifp, u_lon
> error = ENOENT;
> break;
> }
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif == NULL || bif->bridge_sc != sc) {
> + if (ifs->if_bridgeidx != ifp->if_index) {
> error = ESRCH;
> break;
> }
> @@ -137,6 +135,7 @@ bridgectl_ioctl(struct ifnet *ifp, u_lon
> error = EINVAL;
> break;
> }
> + bif = bridge_getbif(ifs);
> if (brlreq->ifbr_flags & BRL_FLAG_IN) {
> error = bridge_addrule(bif, brlreq, 0);
> if (error)
> @@ -154,11 +153,11 @@ bridgectl_ioctl(struct ifnet *ifp, u_lon
> error = ENOENT;
> break;
> }
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif == NULL || bif->bridge_sc != sc) {
> + if (ifs->if_bridgeidx != ifp->if_index) {
> error = ESRCH;
> break;
> }
> + bif = bridge_getbif(ifs);
> bridge_flushrule(bif);
> break;
> case SIOCBRDGGRL:
> @@ -167,11 +166,11 @@ bridgectl_ioctl(struct ifnet *ifp, u_lon
> error = ENOENT;
> break;
> }
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif == NULL || bif->bridge_sc != sc) {
> + if (ifs->if_bridgeidx != ifp->if_index) {
> error = ESRCH;
> break;
> }
> + bif = bridge_getbif(ifs);
> error = bridge_brlconf(bif, bc);
> break;
> default:
> @@ -206,7 +205,7 @@ bridge_rtupdate(struct bridge_softc *sc,
> goto done;
>
> bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
> - p->brt_if = ifp;
> + p->brt_ifidx = ifp->if_index;
> p->brt_age = 1;
> bridge_copytag(brtag, &p->brt_tunnel);
>
> @@ -227,16 +226,14 @@ bridge_rtupdate(struct bridge_softc *sc,
> dir = memcmp(ea, &q->brt_addr, sizeof(q->brt_addr));
> if (dir == 0) {
> if (setflags) {
> - q->brt_if = ifp;
> + q->brt_ifidx = ifp->if_index;
> q->brt_flags = flags;
> } else if (!(q->brt_flags & IFBAF_STATIC))
> - q->brt_if = ifp;
> + q->brt_ifidx = ifp->if_index;
>
> - if (q->brt_if == ifp)
> + if (q->brt_ifidx == ifp->if_index)
> q->brt_age = 1;
> - ifp = q->brt_if;
> bridge_copytag(brtag, &q->brt_tunnel);
> -
> goto want;
> }
>
> @@ -248,7 +245,7 @@ bridge_rtupdate(struct bridge_softc *sc,
> goto done;
>
> bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
> - p->brt_if = ifp;
> + p->brt_ifidx = ifp->if_index;
> p->brt_age = 1;
> bridge_copytag(brtag, &p->brt_tunnel);
>
> @@ -270,7 +267,7 @@ bridge_rtupdate(struct bridge_softc *sc,
> goto done;
>
> bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
> - p->brt_if = ifp;
> + p->brt_ifidx = ifp->if_index;
> p->brt_age = 1;
> bridge_copytag(brtag, &p->brt_tunnel);
>
> @@ -291,11 +288,12 @@ want:
> return (error);
> }
>
> -struct ifnet *
> -bridge_rtlookup(struct bridge_softc *sc, struct ether_addr *ea, struct mbuf
> *m)
> +unsigned int
> +bridge_rtlookup(struct ifnet *brifp, struct ether_addr *ea, struct mbuf *m)
> {
> + struct bridge_softc *sc = brifp->if_softc;
> struct bridge_rtnode *p = NULL;
> - struct ifnet *ifp = NULL;
> + unsigned int ifidx = 0;
> u_int32_t h;
> int dir;
>
> @@ -311,7 +309,7 @@ bridge_rtlookup(struct bridge_softc *sc,
> }
> }
> if (p != NULL) {
> - ifp = p->brt_if;
> + ifidx = p->brt_ifidx;
>
> if (p->brt_family != AF_UNSPEC && m != NULL) {
> struct bridge_tunneltag *brtag;
> @@ -323,7 +321,7 @@ bridge_rtlookup(struct bridge_softc *sc,
> }
> mtx_leave(&sc->sc_mtx);
>
> - return (ifp);
> + return (ifidx);
> }
>
> u_int32_t
> @@ -378,16 +376,14 @@ void
> bridge_rtagenode(struct ifnet *ifp, int age)
> {
> struct bridge_softc *sc;
> - struct bridge_iflist *bif;
> struct bridge_rtnode *n;
> + struct ifnet *bifp;
> int i;
>
> - bif = (struct bridge_iflist *)ifp->if_bridgeport;
> - if (bif == NULL)
> - return;
> - sc = bif->bridge_sc;
> - if (sc == NULL)
> + bifp = if_get(ifp->if_bridgeidx);
> + if (bifp == NULL)
> return;
> + sc = bifp->if_softc;
>
> /*
> * If the age is zero then flush, otherwise set all the expiry times to
> @@ -400,7 +396,7 @@ bridge_rtagenode(struct ifnet *ifp, int
> for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
> LIST_FOREACH(n, &sc->sc_rts[i], brt_next) {
> /* Cap the expiry time to 'age' */
> - if (n->brt_if == ifp &&
> + if (n->brt_ifidx == ifp->if_index &&
> n->brt_age > time_uptime + age &&
> (n->brt_flags & IFBAF_TYPEMASK) ==
> IFBAF_DYNAMIC)
> n->brt_age = time_uptime + age;
> @@ -408,6 +404,8 @@ bridge_rtagenode(struct ifnet *ifp, int
> }
> mtx_leave(&sc->sc_mtx);
> }
> +
> + if_put(bifp);
> }
>
> /*
> @@ -479,7 +477,7 @@ bridge_rtdelete(struct bridge_softc *sc,
> for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
> n = LIST_FIRST(&sc->sc_rts[i]);
> while (n != NULL) {
> - if (n->brt_if != ifp) {
> + if (n->brt_ifidx != ifp->if_index) {
> /* Not ours */
> n = LIST_NEXT(n, brt_next);
> continue;
> @@ -531,13 +529,21 @@ bridge_rtfind(struct bridge_softc *sc, s
> mtx_enter(&sc->sc_mtx);
> for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
> LIST_FOREACH(n, &sc->sc_rts[k], brt_next) {
> + struct ifnet *ifp;
> +
> if (i >= total)
> goto done;
> bareq = &bareqs[i];
> +
> + ifp = if_get(n->brt_ifidx);
> + if (ifp == NULL)
> + continue;
> + bcopy(ifp->if_xname, bareq->ifba_ifsname,
> + sizeof(bareq->ifba_ifsname));
> + if_put(ifp);
> +
> bcopy(sc->sc_if.if_xname, bareq->ifba_name,
> sizeof(bareq->ifba_name));
> - bcopy(n->brt_if->if_xname, bareq->ifba_ifsname,
> - sizeof(bareq->ifba_ifsname));
> bcopy(&n->brt_addr, &bareq->ifba_dst,
> sizeof(bareq->ifba_dst));
> bridge_copyaddr(&n->brt_tunnel.brtag_peer.sa,
> @@ -565,7 +571,7 @@ bridge_update(struct ifnet *ifp, struct
>
> addr = (u_int8_t *)ea;
>
> - bif = (struct bridge_iflist *)ifp->if_bridgeport;
> + bif = bridge_getbif(ifp);
> if (bif == NULL)
> return;
> sc = bif->bridge_sc;
> Index: net/bridgestp.c
> ===================================================================
> RCS file: /cvs/src/sys/net/bridgestp.c,v
> retrieving revision 1.68
> diff -u -p -r1.68 bridgestp.c
> --- net/bridgestp.c 31 Mar 2019 13:56:25 -0000 1.68
> +++ net/bridgestp.c 4 Apr 2019 19:58:21 -0000
> @@ -1616,7 +1616,7 @@ bstp_ifstate(void *arg)
> return;
>
> s = splnet();
> - if ((bif = (struct bridge_iflist *)ifp->if_bridgeport) == NULL)
> + if ((bif = bridge_getbif(ifp)) == NULL)
> goto done;
> if ((bif->bif_flags & IFBIF_STP) == 0)
> goto done;
> @@ -2092,8 +2092,7 @@ bstp_ioctl(struct ifnet *ifp, u_long cmd
> err = ENOENT;
> break;
> }
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif == NULL || bif->bridge_sc != sc) {
> + if (ifs->if_bridgeidx != ifp->if_index) {
> err = ESRCH;
> break;
> }
> Index: net/if.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if.c,v
> retrieving revision 1.578
> diff -u -p -r1.578 if.c
> --- net/if.c 19 Apr 2019 07:38:02 -0000 1.578
> +++ net/if.c 19 Apr 2019 20:45:20 -0000
> @@ -695,12 +695,10 @@ if_enqueue(struct ifnet *ifp, struct mbu
> #endif
>
> #if NBRIDGE > 0
> - if (ifp->if_bridgeport && (m->m_flags & M_PROTO1) == 0) {
> + if (ifp->if_bridgeidx && (m->m_flags & M_PROTO1) == 0) {
> int error;
>
> - KERNEL_LOCK();
> - error = bridge_output(ifp, m, NULL, NULL);
> - KERNEL_UNLOCK();
> + error = bridge_enqueue(ifp, m);
> return (error);
> }
> #endif
> @@ -1162,7 +1160,7 @@ if_isconnected(const struct ifnet *ifp0,
> connected = 1;
>
> #if NBRIDGE > 0
> - if (SAME_BRIDGE(ifp0->if_bridgeport, ifp->if_bridgeport))
> + if (ifp0->if_bridgeidx == ifp->if_bridgeidx)
> connected = 1;
> #endif
> #if NCARP > 0
> Index: net/if_bridge.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_bridge.c,v
> retrieving revision 1.327
> diff -u -p -r1.327 if_bridge.c
> --- net/if_bridge.c 15 Apr 2019 03:26:55 -0000 1.327
> +++ net/if_bridge.c 16 Apr 2019 18:42:52 -0000
> @@ -111,14 +111,14 @@ int bridge_ifremove(struct bridge_iflist
> void bridge_spanremove(struct bridge_iflist *);
> int bridge_input(struct ifnet *, struct mbuf *, void *);
> void bridge_process(struct ifnet *, struct mbuf *);
> -void bridgeintr_frame(struct bridge_softc *, struct ifnet *, struct mbuf *);
> +void bridgeintr_frame(struct ifnet *, struct ifnet *, struct mbuf *);
> void bridge_bifgetstp(struct bridge_softc *, struct bridge_iflist *,
> struct ifbreq *);
> void bridge_broadcast(struct bridge_softc *, struct ifnet *,
> struct ether_header *, struct mbuf *);
> int bridge_localbroadcast(struct ifnet *, struct ether_header *,
> struct mbuf *);
> -void bridge_span(struct bridge_softc *, struct mbuf *);
> +void bridge_span(struct ifnet *, struct mbuf *);
> void bridge_stop(struct bridge_softc *);
> void bridge_init(struct bridge_softc *);
> int bridge_bifconf(struct bridge_softc *, struct ifbifconf *);
> @@ -248,7 +248,7 @@ bridge_delete(struct bridge_softc *sc, s
> if (bif->bif_flags & IFBIF_STP)
> bstp_delete(bif->bif_stp);
>
> - bif->ifp->if_bridgeport = NULL;
> + bif->ifp->if_bridgeidx = 0;
> error = ifpromisc(bif->ifp, 0);
> hook_disestablish(bif->ifp->if_detachhooks, bif->bif_dhcookie);
>
> @@ -290,9 +290,8 @@ bridge_ioctl(struct ifnet *ifp, u_long c
> break;
> }
>
> - if (ifs->if_bridgeport != NULL) {
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif->bridge_sc == sc)
> + if (ifs->if_bridgeidx != 0) {
> + if (ifs->if_bridgeidx == ifp->if_index)
> error = EEXIST;
> else
> error = EBUSY;
> @@ -331,7 +330,7 @@ bridge_ioctl(struct ifnet *ifp, u_long c
> bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
> SIMPLEQ_INIT(&bif->bif_brlin);
> SIMPLEQ_INIT(&bif->bif_brlout);
> - ifs->if_bridgeport = (caddr_t)bif;
> + ifs->if_bridgeidx = ifp->if_index;
> bif->bif_dhcookie = hook_establish(ifs->if_detachhooks, 0,
> bridge_ifdetach, bif);
> if_ih_insert(bif->ifp, bridge_input, NULL);
> @@ -345,11 +344,11 @@ bridge_ioctl(struct ifnet *ifp, u_long c
> error = ENOENT;
> break;
> }
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif == NULL || bif->bridge_sc != sc) {
> + if (ifs->if_bridgeidx != ifp->if_index) {
> error = ESRCH;
> break;
> }
> + bif = bridge_getbif(ifs);
> error = bridge_ifremove(bif);
> break;
> case SIOCBRDGIFS:
> @@ -367,7 +366,7 @@ bridge_ioctl(struct ifnet *ifp, u_long c
> error = EINVAL;
> break;
> }
> - if (ifs->if_bridgeport != NULL) {
> + if (ifs->if_bridgeidx != 0) {
> error = EBUSY;
> break;
> }
> @@ -417,11 +416,11 @@ bridge_ioctl(struct ifnet *ifp, u_long c
> error = ENOENT;
> break;
> }
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif == NULL || bif->bridge_sc != sc) {
> + if (ifs->if_bridgeidx != ifp->if_index) {
> error = ESRCH;
> break;
> }
> + bif = bridge_getbif(ifs);
> req->ifbr_ifsflags = bif->bif_flags;
> req->ifbr_portno = bif->ifp->if_index & 0xfff;
> req->ifbr_protected = bif->bif_protected;
> @@ -436,8 +435,7 @@ bridge_ioctl(struct ifnet *ifp, u_long c
> error = ENOENT;
> break;
> }
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif == NULL || bif->bridge_sc != sc) {
> + if (ifs->if_bridgeidx != ifp->if_index) {
> error = ESRCH;
> break;
> }
> @@ -445,6 +443,7 @@ bridge_ioctl(struct ifnet *ifp, u_long c
> error = EINVAL;
> break;
> }
> + bif = bridge_getbif(ifs);
> if (req->ifbr_ifsflags & IFBIF_STP) {
> if ((bif->bif_flags & IFBIF_STP) == 0) {
> /* Enable STP */
> @@ -495,11 +494,11 @@ bridge_ioctl(struct ifnet *ifp, u_long c
> error = ENOENT;
> break;
> }
> - bif = (struct bridge_iflist *)ifs->if_bridgeport;
> - if (bif == NULL || bif->bridge_sc != sc) {
> + if (ifs->if_bridgeidx != ifp->if_index) {
> error = ESRCH;
> break;
> }
> + bif = bridge_getbif(ifs);
> bif->bif_protected = req->ifbr_protected;
> break;
> case SIOCBRDGRTS:
> @@ -663,6 +662,28 @@ done:
> return (error);
> }
>
> +struct bridge_iflist *
> +bridge_getbif(struct ifnet *ifp)
> +{
> + struct bridge_iflist *bif;
> + struct bridge_softc *sc;
> + struct ifnet *bifp;
> +
> + bifp = if_get(ifp->if_bridgeidx);
> + if (bifp == NULL)
> + return (NULL);
> +
> + sc = bifp->if_softc;
> + SLIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
> + if (bif->ifp == ifp)
> + break;
> + }
> +
> + if_put(bifp);
> +
> + return (bif);
> +}
> +
> void
> bridge_init(struct bridge_softc *sc)
> {
> @@ -702,19 +723,16 @@ bridge_stop(struct bridge_softc *sc)
> * already attached. We must enqueue or free the mbuf before exiting.
> */
> int
> -bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
> - struct rtentry *rt)
> +bridge_enqueue(struct ifnet *ifp, struct mbuf *m)
> {
> - struct bridge_iflist *bif;
> + struct ifnet *brifp;
> struct ether_header *eh;
> struct ifnet *dst_if = NULL;
> - struct bridge_softc *sc;
> + unsigned int dst_ifidx = 0;
> #if NBPFILTER > 0
> caddr_t if_bpf;
> #endif
> - int error;
> -
> - KERNEL_ASSERT_LOCKED();
> + int error = 0;
>
> if (m->m_len < sizeof(*eh)) {
> m = m_pullup(m, sizeof(*eh));
> @@ -723,8 +741,8 @@ bridge_output(struct ifnet *ifp, struct
> }
>
> /* ifp must be a member interface of the bridge. */
> - bif = (struct bridge_iflist *)ifp->if_bridgeport;
> - if (bif == NULL) {
> + brifp = if_get(ifp->if_bridgeidx);
> + if (brifp == NULL) {
> m_freem(m);
> return (EINVAL);
> }
> @@ -734,40 +752,43 @@ bridge_output(struct ifnet *ifp, struct
> * go ahead and send out that interface. Otherwise the packet
> * is dropped below.
> */
> - sc = bif->bridge_sc;
> - if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
> + if (!ISSET(brifp->if_flags, IFF_RUNNING)) {
> /* Loop prevention. */
> m->m_flags |= M_PROTO1;
> error = if_enqueue(ifp, m);
> + if_put(brifp);
> return (error);
> }
>
> #if NBPFILTER > 0
> - if_bpf = sc->sc_if.if_bpf;
> + if_bpf = brifp->if_bpf;
> if (if_bpf)
> bpf_mtap(if_bpf, m, BPF_DIRECTION_OUT);
> #endif
> ifp->if_opackets++;
> ifp->if_obytes += m->m_pkthdr.len;
>
> - bridge_span(sc, m);
> + bridge_span(brifp, m);
>
> eh = mtod(m, struct ether_header *);
> if (!ETHER_IS_MULTICAST(eh->ether_dhost)) {
> struct ether_addr *dst;
>
> dst = (struct ether_addr *)&eh->ether_dhost[0];
> - dst_if = bridge_rtlookup(sc, dst, m);
> + dst_ifidx = bridge_rtlookup(brifp, dst, m);
> }
>
> /*
> * If the packet is a broadcast or we don't know a better way to
> * get there, send to all interfaces.
> */
> - if (dst_if == NULL) {
> + if (dst_ifidx == 0) {
> + struct bridge_softc *sc = brifp->if_softc;
> + struct bridge_iflist *bif;
> struct mbuf *mc;
> int used = 0;
>
> + KERNEL_LOCK();
> SLIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
> dst_if = bif->ifp;
> if ((dst_if->if_flags & IFF_RUNNING) == 0)
> @@ -793,7 +814,7 @@ bridge_output(struct ifnet *ifp, struct
> } else {
> mc = m_dup_pkt(m, ETHER_ALIGN, M_NOWAIT);
> if (mc == NULL) {
> - sc->sc_if.if_oerrors++;
> + brifp->if_oerrors++;
> continue;
> }
> }
> @@ -802,21 +823,26 @@ bridge_output(struct ifnet *ifp, struct
> BRL_ACTION_BLOCK)
> continue;
>
> - error = bridge_ifenqueue(&sc->sc_if, dst_if, mc);
> - if (error)
> - continue;
> + bridge_ifenqueue(brifp, dst_if, mc);
> }
> + KERNEL_UNLOCK();
> if (!used)
> m_freem(m);
> - return (0);
> + goto out;
> }
>
> - if ((dst_if->if_flags & IFF_RUNNING) == 0) {
> + dst_if = if_get(dst_ifidx);
> + if ((dst_if == NULL) || !ISSET(dst_if->if_flags, IFF_RUNNING)) {
> m_freem(m);
> - return (ENETDOWN);
> + error = ENETDOWN;
> + goto out;
> }
> - bridge_ifenqueue(&sc->sc_if, dst_if, m);
> - return (0);
> +
> + bridge_ifenqueue(brifp, dst_if, m);
> + if_put(dst_if);
> +out:
> + if_put(brifp);
> + return (error);
> }
>
> /*
> @@ -853,12 +879,14 @@ bridgeintr(void)
> * Process a single frame. Frame must be freed or queued before returning.
> */
> void
> -bridgeintr_frame(struct bridge_softc *sc, struct ifnet *src_if, struct mbuf
> *m)
> +bridgeintr_frame(struct ifnet *brifp, struct ifnet *src_if, struct mbuf *m)
> {
> + struct bridge_softc *sc = brifp->if_softc;
> struct ifnet *dst_if = NULL;
> struct bridge_iflist *bif;
> struct ether_addr *dst, *src;
> struct ether_header eh;
> + unsigned int dst_ifidx;
> u_int32_t protected;
> int len;
>
> @@ -866,7 +894,7 @@ bridgeintr_frame(struct bridge_softc *sc
> sc->sc_if.if_ipackets++;
> sc->sc_if.if_ibytes += m->m_pkthdr.len;
>
> - bif = (struct bridge_iflist *)src_if->if_bridgeport;
> + bif = bridge_getbif(src_if);
> KASSERT(bif != NULL);
>
> if (m->m_pkthdr.len < sizeof(eh)) {
> @@ -904,8 +932,8 @@ bridgeintr_frame(struct bridge_softc *sc
> * side of the bridge, drop it.
> */
> if (!ETHER_IS_MULTICAST(eh.ether_dhost)) {
> - dst_if = bridge_rtlookup(sc, dst, NULL);
> - if (dst_if == src_if) {
> + dst_ifidx = bridge_rtlookup(brifp, dst, NULL);
> + if (dst_ifidx == src_if->if_index) {
> m_freem(m);
> return;
> }
> @@ -952,10 +980,6 @@ bridgeintr_frame(struct bridge_softc *sc
> return;
> }
>
> - if (bridge_filterrule(&bif->bif_brlin, &eh, m) == BRL_ACTION_BLOCK) {
> - m_freem(m);
> - return;
> - }
> m = bridge_ip(&sc->sc_if, BRIDGE_IN, src_if, &eh, m);
> if (m == NULL)
> return;
> @@ -963,42 +987,38 @@ bridgeintr_frame(struct bridge_softc *sc
> * If the packet is a multicast or broadcast OR if we don't
> * know any better, forward it to all interfaces.
> */
> - if ((m->m_flags & (M_BCAST | M_MCAST)) || dst_if == NULL) {
> + if ((m->m_flags & (M_BCAST | M_MCAST)) || dst_ifidx == 0) {
> sc->sc_if.if_imcasts++;
> bridge_broadcast(sc, src_if, &eh, m);
> return;
> }
> protected = bif->bif_protected;
>
> + dst_if = if_get(dst_ifidx);
> + if (dst_if == NULL)
> + goto bad;
> +
> /*
> * At this point, we're dealing with a unicast frame going to a
> * different interface
> */
> - if ((dst_if->if_flags & IFF_RUNNING) == 0) {
> - m_freem(m);
> - return;
> - }
> - bif = (struct bridge_iflist *)dst_if->if_bridgeport;
> + if ((dst_if->if_flags & IFF_RUNNING) == 0)
> + goto bad;
> + bif = bridge_getbif(dst_if);
> if ((bif->bif_flags & IFBIF_STP) &&
> - (bif->bif_state == BSTP_IFSTATE_DISCARDING)) {
> - m_freem(m);
> - return;
> - }
> + (bif->bif_state == BSTP_IFSTATE_DISCARDING))
> + goto bad;
> /*
> * Do not transmit if both ports are part of the same protected
> * domain.
> */
> - if (protected != 0 && (protected & bif->bif_protected)) {
> - m_freem(m);
> - return;
> - }
> - if (bridge_filterrule(&bif->bif_brlout, &eh, m) == BRL_ACTION_BLOCK) {
> - m_freem(m);
> - return;
> - }
> + if (protected != 0 && (protected & bif->bif_protected))
> + goto bad;
> + if (bridge_filterrule(&bif->bif_brlout, &eh, m) == BRL_ACTION_BLOCK)
> + goto bad;
> m = bridge_ip(&sc->sc_if, BRIDGE_OUT, dst_if, &eh, m);
> if (m == NULL)
> - return;
> + goto bad;
>
> len = m->m_pkthdr.len;
> #if NVLAN > 0
> @@ -1011,6 +1031,10 @@ bridgeintr_frame(struct bridge_softc *sc
> else {
> bridge_ifenqueue(&sc->sc_if, dst_if, m);
> }
> + m = NULL;
> +bad:
> + if_put(dst_if);
> + m_freem(m);
> }
>
> /*
> @@ -1054,6 +1078,7 @@ bridge_input(struct ifnet *ifp, struct m
> void
> bridge_process(struct ifnet *ifp, struct mbuf *m)
> {
> + struct ifnet *brifp;
> struct bridge_softc *sc;
> struct bridge_iflist *bif, *bif0;
> struct ether_header *eh;
> @@ -1064,12 +1089,8 @@ bridge_process(struct ifnet *ifp, struct
>
> KERNEL_ASSERT_LOCKED();
>
> - bif = (struct bridge_iflist *)ifp->if_bridgeport;
> - if (bif == NULL)
> - goto reenqueue;
> -
> - sc = bif->bridge_sc;
> - if ((sc->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
> + brifp = if_get(ifp->if_bridgeidx);
> + if ((brifp == NULL) || !ISSET(brifp->if_flags, IFF_RUNNING))
> goto reenqueue;
>
> #if NVLAN > 0
> @@ -1080,17 +1101,25 @@ bridge_process(struct ifnet *ifp, struct
> if (ISSET(m->m_flags, M_VLANTAG)) {
> m = vlan_inject(m, ETHERTYPE_VLAN, m->m_pkthdr.ether_vtag);
> if (m == NULL)
> - return;
> + goto bad;
> }
> #endif
>
> #if NBPFILTER > 0
> - if_bpf = sc->sc_if.if_bpf;
> + if_bpf = brifp->if_bpf;
> if (if_bpf)
> bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_IN);
> #endif
>
> - bridge_span(sc, m);
> + sc = brifp->if_softc;
> + SLIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
> + if (bif->ifp == ifp)
> + break;
> + }
> + if (bif == NULL)
> + goto reenqueue;
> +
> + bridge_span(brifp, m);
>
> eh = mtod(m, struct ether_header *);
> if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
> @@ -1105,32 +1134,17 @@ bridge_process(struct ifnet *ifp, struct
> ETHER_ADDR_LEN - 1) == 0) {
> if (eh->ether_dhost[ETHER_ADDR_LEN - 1] == 0) {
> /* STP traffic */
> - if ((m = bstp_input(sc->sc_stp, bif->bif_stp,
> - eh, m)) == NULL)
> - return;
> - } else if (eh->ether_dhost[ETHER_ADDR_LEN - 1] <= 0xf) {
> - m_freem(m);
> - return;
> - }
> + m = bstp_input(sc->sc_stp, bif->bif_stp, eh, m);
> + if (m == NULL)
> + goto bad;
> + } else if (eh->ether_dhost[ETHER_ADDR_LEN - 1] <= 0xf)
> + goto bad;
> }
> -
> - /*
> - * No need to process frames for ifs in the discarding state
> - */
> - if ((bif->bif_flags & IFBIF_STP) &&
> - (bif->bif_state == BSTP_IFSTATE_DISCARDING))
> - goto reenqueue;
> -
> - mc = m_dup_pkt(m, ETHER_ALIGN, M_NOWAIT);
> - if (mc == NULL)
> - goto reenqueue;
> -
> - bridge_ifinput(ifp, mc);
> -
> - bridgeintr_frame(sc, ifp, m);
> - return;
> }
>
> + if (bridge_filterrule(&bif->bif_brlin, eh, m) == BRL_ACTION_BLOCK)
> + goto bad;
> +
> /*
> * No need to queue frames for ifs in the discarding state
> */
> @@ -1138,6 +1152,14 @@ bridge_process(struct ifnet *ifp, struct
> (bif->bif_state == BSTP_IFSTATE_DISCARDING))
> goto reenqueue;
>
> + if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
> + mc = m_dup_pkt(m, ETHER_ALIGN, M_NOWAIT);
> + if (mc != NULL)
> + bridgeintr_frame(brifp, ifp, mc);
> +
> + goto reenqueue;
> + }
> +
> /*
> * Unicast, make sure it's not for us.
> */
> @@ -1150,30 +1172,27 @@ bridge_process(struct ifnet *ifp, struct
> bridge_rtupdate(sc,
> (struct ether_addr *)&eh->ether_shost,
> ifp, 0, IFBAF_DYNAMIC, m);
> - if (bridge_filterrule(&bif0->bif_brlin, eh, m) ==
> - BRL_ACTION_BLOCK) {
> - m_freem(m);
> - return;
> - }
> -
> /* Count for the bridge */
> - sc->sc_if.if_ipackets++;
> - sc->sc_if.if_ibytes += m->m_pkthdr.len;
> + brifp->if_ipackets++;
> + brifp->if_ibytes += m->m_pkthdr.len;
>
> - bridge_ifinput(bif->ifp, m);
> - return;
> - }
> - if (bridge_ourether(bif->ifp, eh->ether_shost)) {
> - m_freem(m);
> - return;
> + ifp = bif->ifp;
> + goto reenqueue;
> }
> + if (bridge_ourether(bif->ifp, eh->ether_shost))
> + goto bad;
> }
>
> - bridgeintr_frame(sc, ifp, m);
> + bridgeintr_frame(brifp, ifp, m);
> + if_put(brifp);
> return;
>
> reenqueue:
> bridge_ifinput(ifp, m);
> + m = NULL;
> +bad:
> + m_freem(m);
> + if_put(brifp);
> }
>
> /*
> @@ -1190,7 +1209,7 @@ bridge_broadcast(struct bridge_softc *sc
> int len, used = 0;
> u_int32_t protected;
>
> - bif = (struct bridge_iflist *)ifp->if_bridgeport;
> + bif = bridge_getbif(ifp);
> protected = bif->bif_protected;
>
> SLIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
> @@ -1301,13 +1320,18 @@ bridge_localbroadcast(struct ifnet *ifp,
> }
>
> void
> -bridge_span(struct bridge_softc *sc, struct mbuf *m)
> +bridge_span(struct ifnet *brifp, struct mbuf *m)
> {
> + struct bridge_softc *sc = brifp->if_softc;
> struct bridge_iflist *bif;
> struct ifnet *ifp;
> struct mbuf *mc;
> int error;
>
> + if (SLIST_EMPTY(&sc->sc_spanlist))
> + return;
> +
> + KERNEL_LOCK();
> SLIST_FOREACH(bif, &sc->sc_spanlist, bif_next) {
> ifp = bif->ifp;
>
> @@ -1316,14 +1340,15 @@ bridge_span(struct bridge_softc *sc, str
>
> mc = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
> if (mc == NULL) {
> - sc->sc_if.if_oerrors++;
> + brifp->if_oerrors++;
> continue;
> }
>
> - error = bridge_ifenqueue(&sc->sc_if, ifp, mc);
> + error = bridge_ifenqueue(brifp, ifp, mc);
> if (error)
> continue;
> }
> + KERNEL_UNLOCK();
> }
>
> /*
> @@ -1936,7 +1961,7 @@ bridge_send_icmp_err(struct ifnet *ifp,
> goto dropit;
> bcopy(eh, mtod(m, caddr_t), sizeof(*eh));
>
> - bridge_output(ifp, m, NULL, NULL);
> + bridge_enqueue(ifp, m);
> m_freem(n);
> return;
>
> Index: net/if_bridge.h
> ===================================================================
> RCS file: /cvs/src/sys/net/if_bridge.h,v
> retrieving revision 1.63
> diff -u -p -r1.63 if_bridge.h
> --- net/if_bridge.h 8 Mar 2019 17:48:35 -0000 1.63
> +++ net/if_bridge.h 4 Apr 2019 19:58:21 -0000
> @@ -424,10 +424,6 @@ struct bridge_iflist {
> };
> #define bif_state bif_stp->bp_state
>
> -#define SAME_BRIDGE(_bp1, _bp2)
> \
> - (_bp1 && _bp2 && ((struct bridge_iflist *)_bp1)->bridge_sc == \
> - ((struct bridge_iflist *)_bp2)->bridge_sc)
> -
> /*
> * XXX ip_ipsp.h's sockaddr_union should be converted to sockaddr *
> * passing with correct sa_len, then a good approach for cleaning this
> @@ -453,7 +449,7 @@ struct bridge_tunneltag {
> */
> struct bridge_rtnode {
> LIST_ENTRY(bridge_rtnode) brt_next; /* next in list */
> - struct ifnet *brt_if; /* destination ifs */
> + unsigned int brt_ifidx; /* destination ifs */
> u_int8_t brt_flags; /* address flags */
> u_int8_t brt_age; /* age counter */
> struct ether_addr brt_addr; /* dst addr */
> @@ -470,6 +466,7 @@ struct bridge_rtnode {
> * Locks used to protect struct members in this file:
> * I immutable after creation
> * m per-softc mutex
> + * k kernel lock
> */
> /*
> * Software state for each bridge
> @@ -482,8 +479,8 @@ struct bridge_softc {
> uint64_t sc_hashkey[2]; /* [I] siphash key */
> struct timeout sc_brtimeout; /* timeout state */
> struct bstp_state *sc_stp; /* stp state */
> - SLIST_HEAD(, bridge_iflist) sc_iflist; /* interface list */
> - SLIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports */
> + SLIST_HEAD(, bridge_iflist) sc_iflist; /* [k] interface list */
> + SLIST_HEAD(, bridge_iflist) sc_spanlist; /* [k] span ports */
> struct mutex sc_mtx; /* mutex */
> LIST_HEAD(, bridge_rtnode) sc_rts[BRIDGE_RTABLE_SIZE]; /* [m]
> hash table */
> };
> @@ -491,8 +488,7 @@ struct bridge_softc {
> extern const u_int8_t bstp_etheraddr[];
> struct llc;
>
> -int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *,
> - struct rtentry *);
> +int bridge_enqueue(struct ifnet *, struct mbuf *);
> void bridge_update(struct ifnet *, struct ether_addr *, int);
> void bridge_rtdelete(struct bridge_softc *, struct ifnet *, int);
> void bridge_rtagenode(struct ifnet *, int);
> @@ -517,8 +513,8 @@ void bstp_ifsflags(struct bstp_port *, u
>
> int bridgectl_ioctl(struct ifnet *, u_long, caddr_t);
> int bridge_rtupdate(struct bridge_softc *,
> - struct ether_addr *, struct ifnet *ifp, int, u_int8_t, struct mbuf *);
> -struct ifnet *bridge_rtlookup(struct bridge_softc *,
> + struct ether_addr *, struct ifnet *, int, u_int8_t, struct mbuf *);
> +unsigned int bridge_rtlookup(struct ifnet *,
> struct ether_addr *, struct mbuf *);
> void bridge_rtflush(struct bridge_softc *, int);
> void bridge_rtage(void *);
> @@ -529,6 +525,7 @@ void bridge_flushrule(struct bridge_ifli
>
> void bridge_fragment(struct ifnet *, struct ifnet *, struct ether_header *,
> struct mbuf *);
> +struct bridge_iflist *bridge_getbif(struct ifnet *);
>
> #endif /* _KERNEL */
> #endif /* _NET_IF_BRIDGE_H_ */
> Index: net/if_switch.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_switch.c,v
> retrieving revision 1.25
> diff -u -p -r1.25 if_switch.c
> --- net/if_switch.c 28 Dec 2018 14:32:47 -0000 1.25
> +++ net/if_switch.c 4 Apr 2019 19:58:21 -0000
> @@ -492,7 +492,7 @@ switch_port_add(struct switch_softc *sc,
> if ((ifs = ifunit(req->ifbr_ifsname)) == NULL)
> return (ENOENT);
>
> - if (ifs->if_bridgeport != NULL)
> + if (ifs->if_bridgeidx != 0)
> return (EBUSY);
>
> if (ifs->if_switchport != NULL) {
> Index: net/if_var.h
> ===================================================================
> RCS file: /cvs/src/sys/net/if_var.h,v
> retrieving revision 1.97
> diff -u -p -r1.97 if_var.h
> --- net/if_var.h 19 Apr 2019 07:38:02 -0000 1.97
> +++ net/if_var.h 19 Apr 2019 20:45:20 -0000
> @@ -131,8 +131,8 @@ struct ifnet { /* and the
> entries */
> void (*if_rtrequest)(struct ifnet *, int, struct rtentry *);
> char if_xname[IFNAMSIZ]; /* [I] external name (name + unit) */
> int if_pcount; /* [k] # of promiscuous listeners */
> + unsigned int if_bridgeidx; /* [k] used by bridge ports */
> caddr_t if_bpf; /* packet filter structure */
> - caddr_t if_bridgeport; /* used by bridge ports */
> caddr_t if_switchport; /* used by switch ports */
> caddr_t if_mcast; /* used by multicast code */
> caddr_t if_mcast6; /* used by IPv6 multicast code */
> Index: net/if_vxlan.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_vxlan.c,v
> retrieving revision 1.70
> diff -u -p -r1.70 if_vxlan.c
> --- net/if_vxlan.c 3 Dec 2018 17:25:22 -0000 1.70
> +++ net/if_vxlan.c 4 Apr 2019 19:58:21 -0000
> @@ -711,7 +711,7 @@ vxlan_lookup(struct mbuf *m, struct udph
>
> #if NBRIDGE > 0
> /* Store the tunnel src/dst IP and vni for the bridge or switch */
> - if ((ifp->if_bridgeport != NULL || ifp->if_switchport != NULL) &&
> + if ((ifp->if_bridgeidx != 0 || ifp->if_switchport != NULL) &&
> srcsa->sa_family != AF_UNSPEC &&
> ((brtag = bridge_tunneltag(m)) != NULL)) {
> memcpy(&brtag->brtag_peer.sa, srcsa, srcsa->sa_len);
> Index: net80211/ieee80211_node.c
> ===================================================================
> RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v
> retrieving revision 1.163
> diff -u -p -r1.163 ieee80211_node.c
> --- net80211/ieee80211_node.c 15 Mar 2019 11:05:29 -0000 1.163
> +++ net80211/ieee80211_node.c 4 Apr 2019 19:58:21 -0000
> @@ -2478,10 +2478,10 @@ ieee80211_node_join(struct ieee80211com
>
> #if NBRIDGE > 0
> /*
> - * If the parent interface is a bridgeport, learn
> + * If the parent interface is a bridge port, learn
> * the node's address dynamically on this interface.
> */
> - if (ic->ic_if.if_bridgeport != NULL)
> + if (ic->ic_if.if_bridgeidx != 0)
> bridge_update(&ic->ic_if,
> (struct ether_addr *)ni->ni_macaddr, 0);
> #endif
> @@ -2635,10 +2635,10 @@ ieee80211_node_leave(struct ieee80211com
>
> #if NBRIDGE > 0
> /*
> - * If the parent interface is a bridgeport, delete
> + * If the parent interface is a bridge port, delete
> * any dynamically learned address for this node.
> */
> - if (ic->ic_if.if_bridgeport != NULL)
> + if (ic->ic_if.if_bridgeidx != 0)
> bridge_update(&ic->ic_if,
> (struct ether_addr *)ni->ni_macaddr, 1);
> #endif
> Index: netinet/ip_output.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet/ip_output.c,v
> retrieving revision 1.353
> diff -u -p -r1.353 ip_output.c
> --- netinet/ip_output.c 18 Jan 2019 20:46:03 -0000 1.353
> +++ netinet/ip_output.c 4 Apr 2019 19:58:21 -0000
> @@ -460,7 +460,7 @@ sendit:
> if (ntohs(ip->ip_len) <= mtu) {
> ip->ip_sum = 0;
> if ((ifp->if_capabilities & IFCAP_CSUM_IPv4) &&
> - (ifp->if_bridgeport == NULL))
> + (ifp->if_bridgeidx == 0))
> m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
> else {
> ipstat_inc(ips_outswcsum);
> @@ -719,7 +719,7 @@ ip_fragment(struct mbuf *m, struct ifnet
> mhip->ip_sum = 0;
> if ((ifp != NULL) &&
> (ifp->if_capabilities & IFCAP_CSUM_IPv4) &&
> - (ifp->if_bridgeport == NULL))
> + (ifp->if_bridgeidx == 0))
> m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
> else {
> ipstat_inc(ips_outswcsum);
> @@ -740,7 +740,7 @@ ip_fragment(struct mbuf *m, struct ifnet
> ip->ip_sum = 0;
> if ((ifp != NULL) &&
> (ifp->if_capabilities & IFCAP_CSUM_IPv4) &&
> - (ifp->if_bridgeport == NULL))
> + (ifp->if_bridgeidx == 0))
> m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT;
> else {
> ipstat_inc(ips_outswcsum);
> @@ -1806,14 +1806,14 @@ in_proto_cksum_out(struct mbuf *m, struc
>
> if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) {
> if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_TCPv4) ||
> - ip->ip_hl != 5 || ifp->if_bridgeport != NULL) {
> + ip->ip_hl != 5 || ifp->if_bridgeidx != 0) {
> tcpstat_inc(tcps_outswcsum);
> in_delayed_cksum(m);
> m->m_pkthdr.csum_flags &= ~M_TCP_CSUM_OUT; /* Clear */
> }
> } else if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) {
> if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_UDPv4) ||
> - ip->ip_hl != 5 || ifp->if_bridgeport != NULL) {
> + ip->ip_hl != 5 || ifp->if_bridgeidx != 0) {
> udpstat_inc(udps_outswcsum);
> in_delayed_cksum(m);
> m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_OUT; /* Clear */
> Index: netinet6/ip6_output.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet6/ip6_output.c,v
> retrieving revision 1.241
> diff -u -p -r1.241 ip6_output.c
> --- netinet6/ip6_output.c 3 Dec 2018 17:25:22 -0000 1.241
> +++ netinet6/ip6_output.c 4 Apr 2019 19:58:21 -0000
> @@ -2704,7 +2704,7 @@ in6_proto_cksum_out(struct mbuf *m, stru
> if (m->m_pkthdr.csum_flags & M_TCP_CSUM_OUT) {
> if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_TCPv6) ||
> ip6->ip6_nxt != IPPROTO_TCP ||
> - ifp->if_bridgeport != NULL) {
> + ifp->if_bridgeidx != 0) {
> tcpstat_inc(tcps_outswcsum);
> in6_delayed_cksum(m, IPPROTO_TCP);
> m->m_pkthdr.csum_flags &= ~M_TCP_CSUM_OUT; /* Clear */
> @@ -2712,7 +2712,7 @@ in6_proto_cksum_out(struct mbuf *m, stru
> } else if (m->m_pkthdr.csum_flags & M_UDP_CSUM_OUT) {
> if (!ifp || !(ifp->if_capabilities & IFCAP_CSUM_UDPv6) ||
> ip6->ip6_nxt != IPPROTO_UDP ||
> - ifp->if_bridgeport != NULL) {
> + ifp->if_bridgeidx != 0) {
> udpstat_inc(udps_outswcsum);
> in6_delayed_cksum(m, IPPROTO_UDP);
> m->m_pkthdr.csum_flags &= ~M_UDP_CSUM_OUT; /* Clear */
>