Parallelize bridge_input step 1.8/2:
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 21 Nov 2008 11:11:03 +0000 (11:11 +0000)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 21 Nov 2008 11:11:03 +0000 (11:11 +0000)
Parallelize bridge member interfaces list --
o  Split bridge_iflist into percpu part and shared part (bridge_ifinfo).
   The shared part contains STP related information.
o  Put create bridge_iflist on each CPU and put it onto percpu member
   interface list.
o  All of the STP operation is still serialized by bridge's serializer,
   except testing member interface's STP state.
o  Span interfaces no longer have unused STP information.

sys/net/bridge/bridgestp.c
sys/net/bridge/if_bridge.c
sys/net/bridge/if_bridgevar.h

index 26715b7..22adfce 100644 (file)
@@ -31,7 +31,7 @@
  * $OpenBSD: bridgestp.c,v 1.5 2001/03/22 03:48:29 jason Exp $
  * $NetBSD: bridgestp.c,v 1.5 2003/11/28 08:56:48 keihan Exp $
  * $FreeBSD: src/sys/net/bridgestp.c,v 1.7 2005/10/11 02:58:32 thompsa Exp $
- * $DragonFly: src/sys/net/bridge/bridgestp.c,v 1.7 2008/11/15 11:46:37 sephe Exp $
+ * $DragonFly: src/sys/net/bridge/bridgestp.c,v 1.8 2008/11/21 11:11:03 sephe Exp $
  */
 
 /*
@@ -206,12 +206,13 @@ bstp_transmit_config(struct bridge_softc *sc, struct bridge_iflist *bif)
        bif->bif_config_bpdu.cu_bridge_id = sc->sc_bridge_id;
        bif->bif_config_bpdu.cu_port_id = bif->bif_port_id;
 
-       if (bstp_root_bridge(sc))
+       if (bstp_root_bridge(sc)) {
                bif->bif_config_bpdu.cu_message_age = 0;
-       else
+       } else {
                bif->bif_config_bpdu.cu_message_age =
-                   sc->sc_root_port->bif_message_age_timer.value +
+                   sc->sc_root_port->bifi_message_age_timer.value +
                    BSTP_MESSAGE_AGE_INCR;
+       }
 
        bif->bif_config_bpdu.cu_max_age = sc->sc_max_age;
        bif->bif_config_bpdu.cu_hello_time = sc->sc_hello_time;
@@ -349,14 +350,19 @@ bstp_record_config_timeout_values(struct bridge_softc *sc,
 static void
 bstp_config_bpdu_generation(struct bridge_softc *sc)
 {
-       struct bridge_iflist *bif;
+       struct bridge_iflist *bif, *nbif;
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
                if (bstp_designated_port(sc, bif) &&
                    (bif->bif_state != BSTP_IFSTATE_DISABLED))
                        bstp_transmit_config(sc, bif);
+
+               if (nbif != NULL && !nbif->bif_onlist) {
+                       KKASSERT(bif->bif_onlist);
+                       nbif = LIST_NEXT(bif, bif_next);
+               }
        }
 }
 
@@ -371,8 +377,7 @@ static void
 bstp_transmit_tcn(struct bridge_softc *sc)
 {
        struct bstp_tbpdu bpdu;
-       struct bridge_iflist *bif = sc->sc_root_port;
-       struct ifnet *ifp = bif->bif_ifp;
+       struct ifnet *ifp = sc->sc_root_port->bifi_ifp;
        struct ether_header *eh;
        struct mbuf *m;
 
@@ -416,7 +421,7 @@ bstp_root_selection(struct bridge_softc *sc)
 {
        struct bridge_iflist *root_port = NULL, *bif;
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
                if (bstp_designated_port(sc, bif))
@@ -458,11 +463,12 @@ set_port:
                root_port = bif;
        }
 
-       sc->sc_root_port = root_port;
        if (root_port == NULL) {
+               sc->sc_root_port = NULL;
                sc->sc_designated_root = sc->sc_bridge_id;
                sc->sc_root_path_cost = 0;
        } else {
+               sc->sc_root_port = root_port->bif_info;
                sc->sc_designated_root = root_port->bif_designated_root;
                sc->sc_root_path_cost = root_port->bif_designated_cost +
                    root_port->bif_path_cost;
@@ -474,7 +480,7 @@ bstp_designated_port_selection(struct bridge_softc *sc)
 {
        struct bridge_iflist *bif;
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
                if (bstp_designated_port(sc, bif))
@@ -511,12 +517,12 @@ bstp_become_designated_port(struct bridge_softc *sc, struct bridge_iflist *bif)
 static void
 bstp_port_state_selection(struct bridge_softc *sc)
 {
-       struct bridge_iflist *bif;
+       struct bridge_iflist *bif, *nbif;
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
-               if (bif == sc->sc_root_port) {
+               if (bif->bif_info == sc->sc_root_port) {
                        bif->bif_config_pending = 0;
                        bif->bif_topology_change_acknowledge = 0;
                        bstp_make_forwarding(sc, bif);
@@ -528,6 +534,11 @@ bstp_port_state_selection(struct bridge_softc *sc)
                        bif->bif_topology_change_acknowledge = 0;
                        bstp_make_blocking(sc, bif);
                }
+
+               if (nbif != NULL && !nbif->bif_onlist) {
+                       KKASSERT(bif->bif_onlist);
+                       nbif = LIST_NEXT(bif, bif_next);
+               }
        }
 }
 
@@ -711,7 +722,7 @@ bstp_received_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif,
                                }
                        }
 
-                       if (bif == sc->sc_root_port) {
+                       if (bif->bif_info == sc->sc_root_port) {
                                bstp_record_config_timeout_values(sc, cu);
                                bstp_config_bpdu_generation(sc);
 
@@ -785,7 +796,7 @@ bstp_designated_for_some_port(struct bridge_softc *sc)
 
        struct bridge_iflist *bif;
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
                if (bif->bif_designated_bridge == sc->sc_bridge_id)
@@ -830,13 +841,13 @@ bstp_addr_cmp(const uint8_t *a, const uint8_t *b)
 void
 bstp_initialization(struct bridge_softc *sc)
 {
-       struct bridge_iflist *bif, *mif;
+       struct bridge_iflist *bif, *mif, *nbif;
        u_char *e_addr;
 
        KKASSERT(&curthread->td_msgport == BRIDGE_CFGPORT);
 
        mif = NULL;
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
                if (bif->bif_ifp->if_type != IFT_ETHER)
@@ -885,11 +896,16 @@ bstp_initialization(struct bridge_softc *sc)
                callout_reset(&sc->sc_bstpcallout, hz,
                    bstp_tick, sc);
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) {
                if (bif->bif_flags & IFBIF_STP)
                        bstp_ifupdstatus(sc, bif);
                else
                        bstp_disable_port(sc, bif);
+
+               if (nbif != NULL && !nbif->bif_onlist) {
+                       KKASSERT(bif->bif_onlist);
+                       nbif = LIST_NEXT(bif, bif_next);
+               }
        }
 
        bstp_port_state_selection(sc);
@@ -905,7 +921,7 @@ bstp_stop(struct bridge_softc *sc)
 
        KKASSERT(&curthread->td_msgport == BRIDGE_CFGPORT);
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], 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);
@@ -989,7 +1005,7 @@ bstp_set_bridge_priority(struct bridge_softc *sc, uint64_t new_bridge_id)
 
        root = bstp_root_bridge(sc);
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
                if (bstp_designated_port(sc, bif))
@@ -1060,7 +1076,13 @@ bstp_linkstate(struct ifnet *ifp, int state)
        sc = ifp->if_bridge;
        lwkt_serialize_enter(sc->sc_ifp->if_serializer);
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       /*
+        * bstp_ifupdstatus() may block, but it is the last
+        * operation of the member iface iteration, so we
+        * don't need to use LIST_FOREACH_MUTABLE()+bif_onlist
+        * check here.
+        */
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
 
@@ -1138,7 +1160,15 @@ bstp_tick_handler(struct netmsg *nmsg)
 
        lwkt_serialize_enter(sc->sc_ifp->if_serializer);
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       /*
+        * NOTE:
+        * We don't need to worry that member iface is ripped
+        * from the per-cpu list during the blocking operation
+        * in the loop body, since deletion is serialized by
+        * BRIDGE_CFGPORT
+        */
+
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
                /*
@@ -1161,7 +1191,7 @@ bstp_tick_handler(struct netmsg *nmsg)
            sc->sc_topology_change_time))
                bstp_topology_change_timer_expiry(sc);
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
                if (bstp_timer_expired(&bif->bif_message_age_timer,
@@ -1169,7 +1199,7 @@ bstp_tick_handler(struct netmsg *nmsg)
                        bstp_message_age_timer_expiry(sc, bif);
        }
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if ((bif->bif_flags & IFBIF_STP) == 0)
                        continue;
                if (bstp_timer_expired(&bif->bif_forward_delay_timer,
index ba74999..64669e3 100644 (file)
@@ -66,7 +66,7 @@
  * $OpenBSD: if_bridge.c,v 1.60 2001/06/15 03:38:33 itojun Exp $
  * $NetBSD: if_bridge.c,v 1.31 2005/06/01 19:45:34 jdc Exp $
  * $FreeBSD: src/sys/net/if_bridge.c,v 1.26 2005/10/13 23:05:55 thompsa Exp $
- * $DragonFly: src/sys/net/bridge/if_bridge.c,v 1.51 2008/11/15 12:34:38 sephe Exp $
+ * $DragonFly: src/sys/net/bridge/if_bridge.c,v 1.52 2008/11/21 11:11:03 sephe Exp $
  */
 
 /*
@@ -301,6 +301,27 @@ struct netmsg_brsaddr {
        uint8_t                 br_flags;
 };
 
+struct netmsg_braddbif {
+       struct netmsg           br_nmsg;
+       struct bridge_softc     *br_softc;
+       struct bridge_ifinfo    *br_bif_info;
+       struct ifnet            *br_bif_ifp;
+};
+
+struct netmsg_brdelbif {
+       struct netmsg           br_nmsg;
+       struct bridge_softc     *br_softc;
+       struct bridge_ifinfo    *br_bif_info;
+       struct bridge_iflist_head *br_bif_list;
+};
+
+struct netmsg_brsflags {
+       struct netmsg           br_nmsg;
+       struct bridge_softc     *br_softc;
+       struct bridge_ifinfo    *br_bif_info;
+       uint32_t                br_bif_flags;
+};
+
 eventhandler_tag       bridge_detach_cookie = NULL;
 
 extern struct mbuf *(*bridge_input_p)(struct ifnet *, struct mbuf *);
@@ -313,8 +334,8 @@ static int  bridge_clone_create(struct if_clone *, int);
 static void    bridge_clone_destroy(struct ifnet *);
 
 static int     bridge_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
-static void    bridge_mutecaps(struct bridge_iflist *, int);
-static void    bridge_ifdetach(void *arg __unused, struct ifnet *);
+static void    bridge_mutecaps(struct bridge_ifinfo *, struct ifnet *, int);
+static void    bridge_ifdetach(void *, struct ifnet *);
 static void    bridge_init(void *);
 static void    bridge_stop(struct ifnet *);
 static void    bridge_start(struct ifnet *);
@@ -362,6 +383,8 @@ 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 struct bridge_iflist *bridge_lookup_member_ifinfo(struct bridge_softc *,
+                   struct bridge_ifinfo *);
 static void    bridge_delete_member(struct bridge_softc *,
                    struct bridge_iflist *, int);
 static void    bridge_delete_span(struct bridge_softc *,
@@ -411,6 +434,16 @@ static void        bridge_pfil_enqueue_handler(struct netmsg *);
 static void    bridge_pfil_enqueue(struct ifnet *, struct mbuf *, int);
 static void    bridge_handoff(struct ifnet *, struct mbuf *);
 
+static void    bridge_del_bif_handler(struct netmsg *);
+static void    bridge_add_bif_handler(struct netmsg *);
+static void    bridge_set_bifflags_handler(struct netmsg *);
+static void    bridge_del_bif(struct bridge_softc *, struct bridge_ifinfo *,
+                   struct bridge_iflist_head *);
+static void    bridge_add_bif(struct bridge_softc *, struct bridge_ifinfo *,
+                   struct ifnet *);
+static void    bridge_set_bifflags(struct bridge_softc *,
+                   struct bridge_ifinfo *, uint32_t);
+
 SYSCTL_DECL(_net_link);
 SYSCTL_NODE(_net_link, IFT_BRIDGE, bridge, CTLFLAG_RW, 0, "Bridge");
 
@@ -576,8 +609,9 @@ bridge_clone_create(struct if_clone *ifc, int unit)
        struct bridge_softc *sc;
        struct ifnet *ifp;
        u_char eaddr[6];
+       int cpu;
 
-       sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
+       sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
        ifp = sc->sc_ifp = &sc->sc_if;
 
        sc->sc_brtmax = BRIDGE_RTABLE_MAX;
@@ -601,7 +635,12 @@ bridge_clone_create(struct if_clone *ifc, int unit)
                    MSGF_DROPABLE | MSGF_PRIORITY, bstp_tick_handler);
        sc->sc_bstptimemsg.nm_lmsg.u.ms_resultp = sc;
 
-       LIST_INIT(&sc->sc_iflist);
+       /* Initialize per-cpu member iface lists */
+       sc->sc_iflists = kmalloc(sizeof(*sc->sc_iflists) * ncpus,
+                                M_DEVBUF, M_WAITOK);
+       for (cpu = 0; cpu < ncpus; ++cpu)
+               LIST_INIT(&sc->sc_iflists[cpu]);
+
        LIST_INIT(&sc->sc_spanlist);
 
        ifp->if_softc = sc;
@@ -652,7 +691,7 @@ bridge_delete_dispatch(struct netmsg *nmsg)
 
        lwkt_serialize_enter(bifp->if_serializer);
 
-       while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL)
+       while ((bif = LIST_FIRST(&sc->sc_iflists[mycpuid])) != NULL)
                bridge_delete_member(sc, bif, 0);
 
        while ((bif = LIST_FIRST(&sc->sc_spanlist)) != NULL)
@@ -696,6 +735,9 @@ bridge_clone_destroy(struct ifnet *ifp)
        /* Tear down the routing table. */
        bridge_rtable_fini(sc);
 
+       /* Free per-cpu member iface lists */
+       kfree(sc->sc_iflists, M_DEVBUF);
+
        kfree(sc, M_DEVBUF);
 }
 
@@ -817,9 +859,8 @@ bridge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
  *     Clear or restore unwanted capabilities on the member interface
  */
 static void
-bridge_mutecaps(struct bridge_iflist *bif, int mute)
+bridge_mutecaps(struct bridge_ifinfo *bif_info, struct ifnet *ifp, int mute)
 {
-       struct ifnet *ifp = bif->bif_ifp;
        struct ifreq ifr;
        int error;
 
@@ -831,15 +872,15 @@ bridge_mutecaps(struct bridge_iflist *bif, int mute)
 
        if (mute) {
                /* mask off and save capabilities */
-               bif->bif_mutecap = ifr.ifr_reqcap & BRIDGE_IFCAPS_MASK;
-               if (bif->bif_mutecap != 0)
+               bif_info->bifi_mutecap = ifr.ifr_reqcap & BRIDGE_IFCAPS_MASK;
+               if (bif_info->bifi_mutecap != 0)
                        ifr.ifr_reqcap &= ~BRIDGE_IFCAPS_MASK;
        } else {
                /* restore muted capabilities */
-               ifr.ifr_reqcap |= bif->bif_mutecap;
+               ifr.ifr_reqcap |= bif_info->bifi_mutecap;
        }
 
-       if (bif->bif_mutecap != 0) {
+       if (bif_info->bifi_mutecap != 0) {
                lwkt_serialize_enter(ifp->if_serializer);
                error = ifp->if_ioctl(ifp, SIOCSIFCAP, (caddr_t)&ifr, NULL);
                lwkt_serialize_exit(ifp->if_serializer);
@@ -855,14 +896,11 @@ static struct bridge_iflist *
 bridge_lookup_member(struct bridge_softc *sc, const char *name)
 {
        struct bridge_iflist *bif;
-       struct ifnet *ifp;
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
-               ifp = bif->bif_ifp;
-               if (strcmp(ifp->if_xname, name) == 0)
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
+               if (strcmp(bif->bif_ifp->if_xname, name) == 0)
                        return (bif);
        }
-
        return (NULL);
 }
 
@@ -876,11 +914,28 @@ bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp)
 {
        struct bridge_iflist *bif;
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if (bif->bif_ifp == member_ifp)
                        return (bif);
        }
+       return (NULL);
+}
 
+/*
+ * bridge_lookup_member_ifinfo:
+ *
+ *     Lookup a bridge member interface by bridge_ifinfo.
+ */
+static struct bridge_iflist *
+bridge_lookup_member_ifinfo(struct bridge_softc *sc,
+                           struct bridge_ifinfo *bif_info)
+{
+       struct bridge_iflist *bif;
+
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
+               if (bif->bif_info == bif_info)
+                       return (bif);
+       }
        return (NULL);
 }
 
@@ -895,23 +950,21 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif,
 {
        struct ifnet *ifs = bif->bif_ifp;
        struct ifnet *bifp = sc->sc_ifp;
+       struct bridge_ifinfo *bif_info = bif->bif_info;
+       struct bridge_iflist_head saved_bifs;
 
        ASSERT_SERIALIZED(bifp->if_serializer);
+       KKASSERT(bif_info != NULL);
 
        ifs->if_bridge = NULL;
 
        /*
         * Release bridge interface's serializer:
         * - To avoid possible dead lock.
-        * - netmsg_service_sync will block current thread.
+        * - Various sync operation will block the current thread.
         */
        lwkt_serialize_exit(bifp->if_serializer);
 
-       /*
-        * Make sure that all protocol threads see 'ifs' if_bridge change.
-        */
-       netmsg_service_sync();
-
        if (!gone) {
                switch (ifs->if_type) {
                case IFT_ETHER:
@@ -920,7 +973,7 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif,
                         * Take the interface out of promiscuous mode.
                         */
                        ifpromisc(ifs, 0);
-                       bridge_mutecaps(bif, 0);
+                       bridge_mutecaps(bif_info, ifs, 0);
                        break;
 
                case IFT_GIF:
@@ -932,19 +985,48 @@ bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif,
                }
        }
 
+       /*
+        * Remove bifs from percpu linked list.
+        *
+        * Removed bifs are not freed immediately, instead,
+        * they are saved in saved_bifs.  They will be freed
+        * after we make sure that no one is accessing them,
+        * i.e. after following netmsg_service_sync()
+        */
+       LIST_INIT(&saved_bifs);
+       bridge_del_bif(sc, bif_info, &saved_bifs);
+
+       /*
+        * Make sure that all protocol threads:
+        * o  see 'ifs' if_bridge is changed
+        * o  know that bif is removed from the percpu linked list
+        */
+       netmsg_service_sync();
+
+       /*
+        * Free the removed bifs
+        */
+       KKASSERT(!LIST_EMPTY(&saved_bifs));
+       while ((bif = LIST_FIRST(&saved_bifs)) != NULL) {
+               LIST_REMOVE(bif, bif_next);
+               kfree(bif, M_DEVBUF);
+       }
+
        /* See the comment in bridge_ioctl_stop() */
        bridge_rtmsg_sync(sc);
-
        bridge_rtdelete(sc, ifs, IFBF_FLUSHALL);
 
        lwkt_serialize_enter(bifp->if_serializer);
 
-       LIST_REMOVE(bif, bif_next);
-
-       kfree(bif, M_DEVBUF);
-
-       if (sc->sc_ifp->if_flags & IFF_RUNNING)
+       if (bifp->if_flags & IFF_RUNNING)
                bstp_initialization(sc);
+
+       /*
+        * Free the bif_info after bstp_initialization(), so that
+        * bridge_softc.sc_root_port will not reference a dangling
+        * pointer.
+        */
+       kfree(bif_info, M_DEVBUF);
 }
 
 /*
@@ -1013,7 +1095,6 @@ bridge_ioctl_stop(struct bridge_softc *sc, void *arg __unused)
         * during above netmsg_service_sync() are flushed.
         */
        bridge_rtmsg_sync(sc);
-
        bridge_rtflush(sc, IFBF_FLUSHDYN);
 
        lwkt_serialize_enter(ifp->if_serializer);
@@ -1024,7 +1105,8 @@ static int
 bridge_ioctl_add(struct bridge_softc *sc, void *arg)
 {
        struct ifbreq *req = arg;
-       struct bridge_iflist *bif = NULL;
+       struct bridge_iflist *bif;
+       struct bridge_ifinfo *bif_info;
        struct ifnet *ifs, *bifp;
        int error = 0;
 
@@ -1042,7 +1124,7 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
 
        /* Allow the first Ethernet member to define the MTU */
        if (ifs->if_type != IFT_GIF) {
-               if (LIST_EMPTY(&sc->sc_iflist)) {
+               if (LIST_EMPTY(&sc->sc_iflists[mycpuid])) {
                        bifp->if_mtu = ifs->if_mtu;
                } else if (bifp->if_mtu != ifs->if_mtu) {
                        if_printf(bifp, "invalid MTU for %s\n", ifs->if_xname);
@@ -1056,21 +1138,21 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
        if (ifs->if_bridge != NULL)
                return (EBUSY);
 
-       bif = kmalloc(sizeof(*bif), M_DEVBUF, M_WAITOK|M_ZERO);
-       bif->bif_ifp = ifs;
-       bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
-       bif->bif_priority = BSTP_DEFAULT_PORT_PRIORITY;
-       bif->bif_path_cost = BSTP_DEFAULT_PATH_COST;
+       bif_info = kmalloc(sizeof(*bif_info), M_DEVBUF, M_WAITOK | M_ZERO);
+       bif_info->bifi_priority = BSTP_DEFAULT_PORT_PRIORITY;
+       bif_info->bifi_path_cost = BSTP_DEFAULT_PATH_COST;
+       bif_info->bifi_ifp = ifs;
+
+       /*
+        * Release bridge interface's serializer:
+        * - To avoid possible dead lock.
+        * - Various sync operation will block the current thread.
+        */
+       lwkt_serialize_exit(bifp->if_serializer);
 
        switch (ifs->if_type) {
        case IFT_ETHER:
        case IFT_L2VLAN:
-               /*
-                * Release bridge interface's serializer to
-                * avoid possible dead lock.
-                */
-               lwkt_serialize_exit(bifp->if_serializer);
-
                /*
                 * Place the interface into promiscuous mode.
                 */
@@ -1079,10 +1161,7 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
                        lwkt_serialize_enter(bifp->if_serializer);
                        goto out;
                }
-
-               bridge_mutecaps(bif, 1);
-
-               lwkt_serialize_enter(bifp->if_serializer);
+               bridge_mutecaps(bif_info, ifs, 1);
                break;
 
        case IFT_GIF: /* :^) */
@@ -1090,10 +1169,16 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
 
        default:
                error = EINVAL;
+               lwkt_serialize_enter(bifp->if_serializer);
                goto out;
        }
 
-       LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next);
+       /*
+        * Add bifs to percpu linked lists
+        */
+       bridge_add_bif(sc, bif_info, ifs);
+
+       lwkt_serialize_enter(bifp->if_serializer);
 
        if (bifp->if_flags & IFF_RUNNING)
                bstp_initialization(sc);
@@ -1107,8 +1192,8 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg)
        ifs->if_bridge = sc;
 out:
        if (error) {
-               if (bif != NULL)
-                       kfree(bif, M_DEVBUF);
+               if (bif_info != NULL)
+                       kfree(bif_info, M_DEVBUF);
        }
        return (error);
 }
@@ -1152,14 +1237,16 @@ bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg)
 {
        struct ifbreq *req = arg;
        struct bridge_iflist *bif;
+       struct ifnet *bifp = sc->sc_ifp;
 
        bif = bridge_lookup_member(sc, req->ifbr_ifsname);
        if (bif == NULL)
                return (ENOENT);
 
-       if (req->ifbr_ifsflags & IFBIF_SPAN)
+       if (req->ifbr_ifsflags & IFBIF_SPAN) {
                /* SPAN is readonly */
                return (EINVAL);
+       }
 
        if (req->ifbr_ifsflags & IFBIF_STP) {
                switch (bif->bif_ifp->if_type) {
@@ -1173,9 +1260,11 @@ bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg)
                }
        }
 
-       bif->bif_flags = req->ifbr_ifsflags;
+       lwkt_serialize_exit(bifp->if_serializer);
+       bridge_set_bifflags(sc, bif->bif_info, req->ifbr_ifsflags);
+       lwkt_serialize_enter(bifp->if_serializer);
 
-       if (sc->sc_ifp->if_flags & IFF_RUNNING)
+       if (bifp->if_flags & IFF_RUNNING)
                bstp_initialization(sc);
 
        return (0);
@@ -1216,7 +1305,7 @@ bridge_ioctl_gifs(struct bridge_softc *sc, void *arg)
        int count, len;
 
        count = 0;
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next)
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next)
                count++;
        LIST_FOREACH(bif, &sc->sc_spanlist, bif_next)
                count++;
@@ -1240,7 +1329,7 @@ bridge_ioctl_gifs(struct bridge_softc *sc, void *arg)
        bc_arg->bca_kptr = breq;
 
        count = 0;
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if (len < sizeof(*breq))
                        break;
 
@@ -1262,9 +1351,6 @@ bridge_ioctl_gifs(struct bridge_softc *sc, void *arg)
                strlcpy(breq->ifbr_ifsname, bif->bif_ifp->if_xname,
                        sizeof(breq->ifbr_ifsname));
                breq->ifbr_ifsflags = bif->bif_flags;
-               breq->ifbr_state = bif->bif_state;
-               breq->ifbr_priority = bif->bif_priority;
-               breq->ifbr_path_cost = bif->bif_path_cost;
                breq->ifbr_portno = bif->bif_ifp->if_index & 0xff;
                breq++;
                count++;
@@ -1546,7 +1632,7 @@ static int
 bridge_ioctl_addspan(struct bridge_softc *sc, void *arg)
 {
        struct ifbreq *req = arg;
-       struct bridge_iflist *bif = NULL;
+       struct bridge_iflist *bif;
        struct ifnet *ifs;
 
        ifs = ifunit(req->ifbr_ifsname);
@@ -1569,10 +1655,10 @@ bridge_ioctl_addspan(struct bridge_softc *sc, void *arg)
                        return (EINVAL);
        }
 
-       bif = kmalloc(sizeof(*bif), M_DEVBUF, M_WAITOK|M_ZERO);
-
+       bif = kmalloc(sizeof(*bif), M_DEVBUF, M_WAITOK | M_ZERO);
        bif->bif_ifp = ifs;
        bif->bif_flags = IFBIF_SPAN;
+       /* NOTE: span bif does not need bridge_ifinfo */
 
        LIST_INSERT_HEAD(&sc->sc_spanlist, bif, bif_next);
 
@@ -1804,7 +1890,11 @@ bridge_output(struct ifnet *ifp, struct mbuf *m)
 
                bridge_span(sc, m);
 
-               LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+               /*
+                * Following loop is MPSAFE; nothing is blocking
+                * in the loop body.
+                */
+               LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                        dst_if = bif->bif_ifp;
                        if ((dst_if->if_flags & IFF_RUNNING) == 0)
                                continue;
@@ -2184,24 +2274,26 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
 
        /*
         * Unicast.  Make sure it's not for us.
+        *
+        * This loop is MPSAFE; the only blocking operation (bridge_rtupdate)
+        * is followed by breaking out of the loop.
         */
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                if (bif->bif_ifp->if_type != IFT_ETHER)
                        continue;
 
                /* It is destined for us. */
                if (memcmp(IF_LLADDR(bif->bif_ifp), eh->ether_dhost,
                    ETHER_ADDR_LEN) == 0) {
-                       if (bif->bif_flags & IFBIF_LEARNING) {
-                               bridge_rtupdate(sc, eh->ether_shost,
-                                               ifp, IFBAF_DYNAMIC);
-                       }
-
                        if (bif->bif_ifp != ifp) {
                                /* XXX loop prevention */
                                m->m_flags |= M_PROTO1;
                                new_ifp = bif->bif_ifp;
                        }
+                       if (bif->bif_flags & IFBIF_LEARNING) {
+                               bridge_rtupdate(sc, eh->ether_shost,
+                                               ifp, IFBAF_DYNAMIC);
+                       }
                        goto out;
                }
 
@@ -2276,7 +2368,11 @@ filt:
                        return;
        }
 
-       LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+       /*
+        * Following loop is MPSAFE; nothing is blocking
+        * in the loop body.
+        */
+       LIST_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
                dst_if = bif->bif_ifp;
                if (dst_if == src_if)
                        continue;
@@ -3558,3 +3654,121 @@ bridge_control(struct bridge_softc *sc, u_long cmd,
        lwkt_serialize_enter(bifp->if_serializer);
        return error;
 }
+
+static void
+bridge_add_bif_handler(struct netmsg *nmsg)
+{
+       struct netmsg_braddbif *amsg = (struct netmsg_braddbif *)nmsg;
+       struct bridge_softc *sc;
+       struct bridge_iflist *bif;
+
+       sc = amsg->br_softc;
+
+       bif = kmalloc(sizeof(*bif), M_DEVBUF, M_WAITOK | M_ZERO);
+       bif->bif_ifp = amsg->br_bif_ifp;
+       bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
+       bif->bif_onlist = 1;
+       bif->bif_info = amsg->br_bif_info;
+
+       LIST_INSERT_HEAD(&sc->sc_iflists[mycpuid], bif, bif_next);
+
+       ifnet_forwardmsg(&nmsg->nm_lmsg, mycpuid + 1);
+}
+
+static void
+bridge_add_bif(struct bridge_softc *sc, struct bridge_ifinfo *bif_info,
+              struct ifnet *ifp)
+{
+       struct netmsg_braddbif amsg;
+
+       ASSERT_NOT_SERIALIZED(sc->sc_ifp->if_serializer);
+
+       netmsg_init(&amsg.br_nmsg, &curthread->td_msgport, 0,
+                   bridge_add_bif_handler);
+       amsg.br_softc = sc;
+       amsg.br_bif_info = bif_info;
+       amsg.br_bif_ifp = ifp;
+
+       ifnet_domsg(&amsg.br_nmsg.nm_lmsg, 0);
+}
+
+static void
+bridge_del_bif_handler(struct netmsg *nmsg)
+{
+       struct netmsg_brdelbif *dmsg = (struct netmsg_brdelbif *)nmsg;
+       struct bridge_softc *sc;
+       struct bridge_iflist *bif;
+
+       sc = dmsg->br_softc;
+
+       /*
+        * Locate the bif associated with the br_bif_info
+        * on the current CPU
+        */
+       bif = bridge_lookup_member_ifinfo(sc, dmsg->br_bif_info);
+       KKASSERT(bif != NULL && bif->bif_onlist);
+
+       /* Remove the bif from the current CPU's iflist */
+       bif->bif_onlist = 0;
+       LIST_REMOVE(bif, bif_next);
+
+       /* Save the removed bif for later freeing */
+       LIST_INSERT_HEAD(dmsg->br_bif_list, bif, bif_next);
+
+       ifnet_forwardmsg(&nmsg->nm_lmsg, mycpuid + 1);
+}
+
+static void
+bridge_del_bif(struct bridge_softc *sc, struct bridge_ifinfo *bif_info,
+              struct bridge_iflist_head *saved_bifs)
+{
+       struct netmsg_brdelbif dmsg;
+
+       ASSERT_NOT_SERIALIZED(sc->sc_ifp->if_serializer);
+
+       netmsg_init(&dmsg.br_nmsg, &curthread->td_msgport, 0,
+                   bridge_del_bif_handler);
+       dmsg.br_softc = sc;
+       dmsg.br_bif_info = bif_info;
+       dmsg.br_bif_list = saved_bifs;
+
+       ifnet_domsg(&dmsg.br_nmsg.nm_lmsg, 0);
+}
+
+static void
+bridge_set_bifflags_handler(struct netmsg *nmsg)
+{
+       struct netmsg_brsflags *smsg = (struct netmsg_brsflags *)nmsg;
+       struct bridge_softc *sc;
+       struct bridge_iflist *bif;
+
+       sc = smsg->br_softc;
+
+       /*
+        * Locate the bif associated with the br_bif_info
+        * on the current CPU
+        */
+       bif = bridge_lookup_member_ifinfo(sc, smsg->br_bif_info);
+       KKASSERT(bif != NULL && bif->bif_onlist);
+
+       bif->bif_flags = smsg->br_bif_flags;
+
+       ifnet_forwardmsg(&nmsg->nm_lmsg, mycpuid + 1);
+}
+
+static void
+bridge_set_bifflags(struct bridge_softc *sc, struct bridge_ifinfo *bif_info,
+                   uint32_t bif_flags)
+{
+       struct netmsg_brsflags smsg;
+
+       ASSERT_NOT_SERIALIZED(sc->sc_ifp->if_serializer);
+
+       netmsg_init(&smsg.br_nmsg, &curthread->td_msgport, 0,
+                   bridge_set_bifflags_handler);
+       smsg.br_softc = sc;
+       smsg.br_bif_info = bif_info;
+       smsg.br_bif_flags = bif_flags;
+
+       ifnet_domsg(&smsg.br_nmsg.nm_lmsg, 0);
+}
index 91314fd..c6386ca 100644 (file)
  * $OpenBSD: if_bridge.h,v 1.14 2001/03/22 03:48:29 jason Exp $
  * $NetBSD: if_bridgevar.h,v 1.4 2003/07/08 07:13:50 itojun Exp $
  * $FreeBSD: src/sys/net/if_bridgevar.h,v 1.4 2005/07/06 01:24:45 thompsa Exp $
- * $DragonFly: src/sys/net/bridge/if_bridgevar.h,v 1.6 2008/11/15 11:46:37 sephe Exp $
+ * $DragonFly: src/sys/net/bridge/if_bridgevar.h,v 1.7 2008/11/21 11:11:03 sephe Exp $
  */
 
+#ifndef _NET_IF_BRIDGEVAR_H
+#define _NET_IF_BRIDGEVAR_H
+
 /*
  * Data structure and control definitions for bridge interfaces.
  */
@@ -224,30 +227,57 @@ struct bstp_tcn_unit {
        uint8_t         tu_message_type;
 };
 
+/*
+ * Bridge interface entry.
+ */
+struct bridge_ifinfo {
+       uint64_t                bifi_designated_root;
+       uint64_t                bifi_designated_bridge;
+       uint32_t                bifi_path_cost;
+       uint32_t                bifi_designated_cost;
+       struct bridge_timer     bifi_hold_timer;
+       struct bridge_timer     bifi_message_age_timer;
+       struct bridge_timer     bifi_forward_delay_timer;
+       struct bstp_config_unit bifi_config_bpdu;
+       uint16_t                bifi_port_id;
+       uint16_t                bifi_designated_port;
+       uint8_t                 bifi_state;
+       uint8_t                 bifi_topology_change_acknowledge;
+       uint8_t                 bifi_config_pending;
+       uint8_t                 bifi_change_detection_enabled;
+       uint8_t                 bifi_priority;
+       struct ifnet            *bifi_ifp;      /* member if */
+       int                     bifi_mutecap;   /* member muted caps */
+};
+
+#define bif_designated_root            bif_info->bifi_designated_root
+#define bif_designated_bridge          bif_info->bifi_designated_bridge
+#define bif_path_cost                  bif_info->bifi_path_cost
+#define bif_designated_cost            bif_info->bifi_designated_cost
+#define bif_hold_timer                 bif_info->bifi_hold_timer
+#define bif_message_age_timer          bif_info->bifi_message_age_timer
+#define bif_forward_delay_timer                bif_info->bifi_forward_delay_timer
+#define bif_config_bpdu                        bif_info->bifi_config_bpdu
+#define bif_port_id                    bif_info->bifi_port_id
+#define bif_designated_port            bif_info->bifi_designated_port
+#define bif_state                      bif_info->bifi_state
+#define bif_topology_change_acknowledge        \
+       bif_info->bifi_topology_change_acknowledge
+#define bif_config_pending             bif_info->bifi_config_pending
+#define bif_change_detection_enabled   bif_info->bifi_change_detection_enabled
+#define bif_priority                   bif_info->bifi_priority
+
 /*
  * Bridge interface list entry.
  */
 struct bridge_iflist {
        LIST_ENTRY(bridge_iflist) bif_next;
-       uint64_t                bif_designated_root;
-       uint64_t                bif_designated_bridge;
-       uint32_t                bif_path_cost;
-       uint32_t                bif_designated_cost;
-       struct bridge_timer     bif_hold_timer;
-       struct bridge_timer     bif_message_age_timer;
-       struct bridge_timer     bif_forward_delay_timer;
-       struct bstp_config_unit bif_config_bpdu;
-       uint16_t                bif_port_id;
-       uint16_t                bif_designated_port;
-       uint8_t                 bif_state;
-       uint8_t                 bif_topology_change_acknowledge;
-       uint8_t                 bif_config_pending;
-       uint8_t                 bif_change_detection_enabled;
-       uint8_t                 bif_priority;
        struct ifnet            *bif_ifp;       /* member if */
        uint32_t                bif_flags;      /* member if flags */
-       int                     bif_mutecap;    /* member muted caps */
+       int                     bif_onlist;
+       struct bridge_ifinfo    *bif_info;
 };
+LIST_HEAD(bridge_iflist_head, bridge_iflist);
 
 /*
  * Bridge route info.
@@ -281,7 +311,7 @@ struct bridge_softc {
        LIST_ENTRY(bridge_softc) sc_list;
        uint64_t                sc_designated_root;
        uint64_t                sc_bridge_id;
-       struct bridge_iflist    *sc_root_port;
+       struct bridge_ifinfo    *sc_root_port;
        uint32_t                sc_root_path_cost;
        uint16_t                sc_max_age;
        uint16_t                sc_hello_time;
@@ -304,11 +334,11 @@ struct bridge_softc {
        struct netmsg           sc_brtimemsg;   /* bridge callout msg */
        struct callout          sc_bstpcallout; /* STP callout */
        struct netmsg           sc_bstptimemsg; /* STP callout msg */
-       LIST_HEAD(, bridge_iflist) sc_iflist;   /* member interface list */
-       struct bridge_rtnode_head **sc_rthashs; /* percpu forwarding table */
-       struct bridge_rtnode_head *sc_rtlists;  /* percpu list of the above */
+       struct bridge_iflist_head *sc_iflists;  /* percpu member if lists */
+       struct bridge_rtnode_head **sc_rthashs; /* percpu forwarding tables */
+       struct bridge_rtnode_head *sc_rtlists;  /* percpu lists of the above */
        uint32_t                sc_rthash_key;  /* key for hash */
-       LIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports list */
+       struct bridge_iflist_head sc_spanlist;  /* span ports list */
        struct bridge_timer     sc_link_timer;
 };
 #define sc_if                   sc_arp.ac_if
@@ -332,3 +362,5 @@ void        bstp_tick_handler(struct netmsg *);
 void   bridge_enqueue(struct ifnet *, struct mbuf *);
 
 #endif /* _KERNEL */
+
+#endif /* !_NET_IF_BRIDGEVAR_H */