Module Name: src Committed By: snj Date: Sat Apr 4 18:00:03 UTC 2009
Modified Files: src/sys/net [netbsd-5]: if_bridge.c if_bridgevar.h Log Message: Pull up following revision(s) (requested by bouyer in ticket #660): sys/net/if_bridge.c: revision 1.65 sys/net/if_bridgevar.h: revision 1.14 Fix for if_start() and pfil_hook() being called from hardware interrupt context (reported on various mailing-lists, and part of PR kern/41114, causing panic in pf(4) and possibly ipf(4) when BRIDGE_IPF is used). Defer bridge_forward() to a software interrupt; bridge_input() enqueues mbufs to ifp->if_snd which is handled in bridge_forward(). To generate a diff of this commit: cvs rdiff -u -r1.62 -r1.62.6.1 src/sys/net/if_bridge.c cvs rdiff -u -r1.11 -r1.11.46.1 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/if_bridge.c diff -u src/sys/net/if_bridge.c:1.62 src/sys/net/if_bridge.c:1.62.6.1 --- src/sys/net/if_bridge.c:1.62 Sun Jun 15 16:37:21 2008 +++ src/sys/net/if_bridge.c Sat Apr 4 18:00:03 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bridge.c,v 1.62 2008/06/15 16:37:21 christos Exp $ */ +/* $NetBSD: if_bridge.c,v 1.62.6.1 2009/04/04 18:00:03 snj Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -80,7 +80,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.62 2008/06/15 16:37:21 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.62.6.1 2009/04/04 18:00:03 snj Exp $"); #include "opt_bridge_ipf.h" #include "opt_inet.h" @@ -97,6 +97,7 @@ #include <sys/proc.h> #include <sys/pool.h> #include <sys/kauth.h> +#include <sys/cpu.h> #if NBPFILTER > 0 #include <net/bpf.h> @@ -185,7 +186,7 @@ static void bridge_stop(struct ifnet *, int); static void bridge_start(struct ifnet *); -static void bridge_forward(struct bridge_softc *, struct mbuf *m); +static void bridge_forward(void *); static void bridge_timer(void *); @@ -350,6 +351,13 @@ sc->sc_hold_time = BSTP_DEFAULT_HOLD_TIME; sc->sc_filter_flags = 0; + /* software interrupt to do the work */ + sc->sc_softintr = softint_establish(SOFTINT_NET, bridge_forward, sc); + if (sc->sc_softintr == NULL) { + free(sc, M_DEVBUF); + return ENOMEM; + } + /* Initialize our routing table. */ bridge_rtable_init(sc); @@ -370,6 +378,7 @@ ifp->if_addrlen = 0; ifp->if_dlt = DLT_EN10MB; ifp->if_hdrlen = ETHER_HDR_LEN; + IFQ_SET_READY(&ifp->if_snd); if_attach(ifp); @@ -410,6 +419,10 @@ /* Tear down the routing table. */ bridge_rtable_fini(sc); + + + softint_disestablish(sc->sc_softintr); + free(sc, M_DEVBUF); return (0); @@ -1298,124 +1311,139 @@ * The forwarding function of the bridge. */ static void -bridge_forward(struct bridge_softc *sc, struct mbuf *m) +bridge_forward(void *v) { + struct bridge_softc *sc = v; + struct mbuf *m; struct bridge_iflist *bif; struct ifnet *src_if, *dst_if; struct ether_header *eh; + int s; - src_if = m->m_pkthdr.rcvif; + if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) + return; - sc->sc_if.if_ipackets++; - sc->sc_if.if_ibytes += m->m_pkthdr.len; + s = splbio(); + while (1) { + IFQ_POLL(&sc->sc_if.if_snd, m); + if (m == NULL) + break; + IFQ_DEQUEUE(&sc->sc_if.if_snd, m); - /* - * Look up the bridge_iflist. - */ - bif = bridge_lookup_member_if(sc, src_if); - if (bif == NULL) { - /* Interface is not a bridge member (anymore?) */ - m_freem(m); - return; - } + src_if = m->m_pkthdr.rcvif; - if (bif->bif_flags & IFBIF_STP) { - switch (bif->bif_state) { - case BSTP_IFSTATE_BLOCKING: - case BSTP_IFSTATE_LISTENING: - case BSTP_IFSTATE_DISABLED: + sc->sc_if.if_ipackets++; + sc->sc_if.if_ibytes += m->m_pkthdr.len; + + /* + * Look up the bridge_iflist. + */ + bif = bridge_lookup_member_if(sc, src_if); + if (bif == NULL) { + /* Interface is not a bridge member (anymore?) */ m_freem(m); - return; + continue; } - } - eh = mtod(m, struct ether_header *); - - /* - * If the interface is learning, and the source - * address is valid and not multicast, record - * the address. - */ - if ((bif->bif_flags & IFBIF_LEARNING) != 0 && - ETHER_IS_MULTICAST(eh->ether_shost) == 0 && - (eh->ether_shost[0] == 0 && - eh->ether_shost[1] == 0 && - eh->ether_shost[2] == 0 && - eh->ether_shost[3] == 0 && - eh->ether_shost[4] == 0 && - eh->ether_shost[5] == 0) == 0) { - (void) bridge_rtupdate(sc, eh->ether_shost, - src_if, 0, IFBAF_DYNAMIC); - } + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + m_freem(m); + continue; + } + } - if ((bif->bif_flags & IFBIF_STP) != 0 && - bif->bif_state == BSTP_IFSTATE_LEARNING) { - m_freem(m); - return; - } + eh = mtod(m, struct ether_header *); - /* - * At this point, the port either doesn't participate - * in spanning tree or it is in the forwarding state. - */ + /* + * If the interface is learning, and the source + * address is valid and not multicast, record + * the address. + */ + if ((bif->bif_flags & IFBIF_LEARNING) != 0 && + ETHER_IS_MULTICAST(eh->ether_shost) == 0 && + (eh->ether_shost[0] == 0 && + eh->ether_shost[1] == 0 && + eh->ether_shost[2] == 0 && + eh->ether_shost[3] == 0 && + eh->ether_shost[4] == 0 && + eh->ether_shost[5] == 0) == 0) { + (void) bridge_rtupdate(sc, eh->ether_shost, + src_if, 0, IFBAF_DYNAMIC); + } - /* - * If the packet is unicast, destined for someone on - * "this" side of the bridge, drop it. - */ - if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { - dst_if = bridge_rtlookup(sc, eh->ether_dhost); - if (src_if == dst_if) { + if ((bif->bif_flags & IFBIF_STP) != 0 && + bif->bif_state == BSTP_IFSTATE_LEARNING) { m_freem(m); - return; + continue; + } + + /* + * At this point, the port either doesn't participate + * in spanning tree or it is in the forwarding state. + */ + + /* + * If the packet is unicast, destined for someone on + * "this" side of the bridge, drop it. + */ + if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { + dst_if = bridge_rtlookup(sc, eh->ether_dhost); + if (src_if == dst_if) { + m_freem(m); + continue; + } + } else { + /* ...forward it to all interfaces. */ + sc->sc_if.if_imcasts++; + dst_if = NULL; } - } else { - /* ...forward it to all interfaces. */ - sc->sc_if.if_imcasts++; - dst_if = NULL; - } #ifdef PFIL_HOOKS - if (pfil_run_hooks(&sc->sc_if.if_pfil, &m, - m->m_pkthdr.rcvif, PFIL_IN) != 0) { - if (m != NULL) - m_freem(m); - return; - } - if (m == NULL) - return; + if (pfil_run_hooks(&sc->sc_if.if_pfil, &m, + m->m_pkthdr.rcvif, PFIL_IN) != 0) { + if (m != NULL) + m_freem(m); + continue; + } + if (m == NULL) + continue; #endif /* PFIL_HOOKS */ - if (dst_if == NULL) { - bridge_broadcast(sc, src_if, m); - return; - } - - /* - * 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 = bridge_lookup_member_if(sc, dst_if); - if (bif == NULL) { - /* Not a member of the bridge (anymore?) */ - m_freem(m); - return; - } + if (dst_if == NULL) { + bridge_broadcast(sc, src_if, m); + continue; + } - if (bif->bif_flags & IFBIF_STP) { - switch (bif->bif_state) { - case BSTP_IFSTATE_DISABLED: - case BSTP_IFSTATE_BLOCKING: + /* + * 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; + continue; + } + bif = bridge_lookup_member_if(sc, dst_if); + if (bif == NULL) { + /* Not a member of the bridge (anymore?) */ + m_freem(m); + continue; + } + + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_DISABLED: + case BSTP_IFSTATE_BLOCKING: + m_freem(m); + continue; + } } - } - bridge_enqueue(sc, dst_if, m, 1); + bridge_enqueue(sc, dst_if, m, 1); + } + splx(s); } /* @@ -1423,6 +1451,7 @@ * * Receive input from a member interface. Queue the packet for * bridging if it is not for us. + * should be called at splbio() */ struct mbuf * bridge_input(struct ifnet *ifp, struct mbuf *m) @@ -1464,12 +1493,17 @@ * for bridge processing; return the original packet for * local processing. */ + if (IF_QFULL(&sc->sc_if.if_snd)) { + IF_DROP(&sc->sc_if.if_snd); + return (m); + } mc = m_dup(m, 0, M_COPYALL, M_NOWAIT); if (mc == NULL) return (m); /* Perform the bridge forwarding function with the copy. */ - bridge_forward(sc, mc); + IF_ENQUEUE(&sc->sc_if.if_snd, mc); + softint_schedule(sc->sc_softintr); /* Return the original packet for local processing. */ return (m); @@ -1517,7 +1551,13 @@ } /* Perform the bridge forwarding function. */ - bridge_forward(sc, m); + if (IF_QFULL(&sc->sc_if.if_snd)) { + IF_DROP(&sc->sc_if.if_snd); + m_freem(m); + return (NULL); + } + IF_ENQUEUE(&sc->sc_if.if_snd, m); + softint_schedule(sc->sc_softintr); return (NULL); } @@ -2003,6 +2043,7 @@ /* * Check basic packet sanity and run IPF through pfil. */ + KASSERT(!cpu_intr_p()); switch (ether_type) { case ETHERTYPE_IP : Index: src/sys/net/if_bridgevar.h diff -u src/sys/net/if_bridgevar.h:1.11 src/sys/net/if_bridgevar.h:1.11.46.1 --- src/sys/net/if_bridgevar.h:1.11 Mon Jul 9 21:11:00 2007 +++ src/sys/net/if_bridgevar.h Sat Apr 4 18:00:03 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bridgevar.h,v 1.11 2007/07/09 21:11:00 ad Exp $ */ +/* $NetBSD: if_bridgevar.h,v 1.11.46.1 2009/04/04 18:00:03 snj Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -300,6 +300,7 @@ LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ uint32_t sc_rthash_key; /* key for hash */ uint32_t sc_filter_flags; /* ipf and flags */ + void *sc_softintr; }; extern const uint8_t bstp_etheraddr[];