Module Name:    src
Committed By:   ozaki-r
Date:           Mon Jul 14 02:34:36 UTC 2014

Modified Files:
        src/sys/net: bridgestp.c if.h if_bridge.c if_bridgevar.h

Log Message:
Make bridge MPSAFE

- Introduce BRIDGE_MPSAFE
  - It's enabled only when NET_MPSAFE is defined
    in if.h or the kernel config
- Add iflist and rtlist mutex locks
  - Locking iflist is performance sensitive,
    so it's not used when !BRIDGE_MPSAFE
- Add bif object reference counting
  - It enables fine-grain locking for bridge member lists
    by allowing to not hold a lock during touching a bif
  - bridge_release_member is added to decrement the
    reference count
  - A condition variable is added to do bridge_delete_member
    gracefully
- Add if_bridgeif to ifnet
  - It's a shortcut to a bif object of a bridge member
  - It reduces a bif lookup cost and so lock contention on iflist
- Make bridgestp MPSAFE too


To generate a diff of this commit:
cvs rdiff -u -r1.16 -r1.17 src/sys/net/bridgestp.c
cvs rdiff -u -r1.170 -r1.171 src/sys/net/if.h
cvs rdiff -u -r1.86 -r1.87 src/sys/net/if_bridge.c
cvs rdiff -u -r1.19 -r1.20 src/sys/net/if_bridgevar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/net/bridgestp.c
diff -u src/sys/net/bridgestp.c:1.16 src/sys/net/bridgestp.c:1.17
--- src/sys/net/bridgestp.c:1.16	Wed Jun 18 01:19:19 2014
+++ src/sys/net/bridgestp.c	Mon Jul 14 02:34:36 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: bridgestp.c,v 1.16 2014/06/18 01:19:19 ozaki-r Exp $	*/
+/*	$NetBSD: bridgestp.c,v 1.17 2014/07/14 02:34:36 ozaki-r Exp $	*/
 
 /*
  * Copyright (c) 2000 Jason L. Wright (ja...@thought.net)
@@ -40,7 +40,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bridgestp.c,v 1.16 2014/06/18 01:19:19 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bridgestp.c,v 1.17 2014/07/14 02:34:36 ozaki-r Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -221,6 +221,8 @@ bstp_send_config_bpdu(struct bridge_soft
 	struct bstp_cbpdu bpdu;
 	int s;
 
+	KASSERT(BRIDGE_LOCKED(sc));
+
 	ifp = bif->bif_ifp;
 
 	if ((ifp->if_flags & IFF_RUNNING) == 0)
@@ -274,9 +276,11 @@ bstp_send_config_bpdu(struct bridge_soft
 
 	memcpy(mtod(m, char *) + sizeof(*eh), &bpdu, sizeof(bpdu));
 
+	BRIDGE_UNLOCK(sc);
 	s = splnet();
 	bridge_enqueue(sc, ifp, m, 0);
 	splx(s);
+	BRIDGE_LOCK(sc);
 }
 
 static int
@@ -363,6 +367,8 @@ bstp_transmit_tcn(struct bridge_softc *s
 	struct mbuf *m;
 	int s;
 
+	KASSERT(BRIDGE_LOCKED(sc));
+
 	KASSERT(bif != NULL);
 	ifp = bif->bif_ifp;
 	if ((ifp->if_flags & IFF_RUNNING) == 0)
@@ -390,9 +396,11 @@ bstp_transmit_tcn(struct bridge_softc *s
 
 	memcpy(mtod(m, char *) + sizeof(*eh), &bpdu, sizeof(bpdu));
 
+	BRIDGE_UNLOCK(sc);
 	s = splnet();
 	bridge_enqueue(sc, ifp, m, 0);
 	splx(s);
+	BRIDGE_LOCK(sc);
 }
 
 static void
@@ -592,6 +600,10 @@ bstp_input(struct bridge_softc *sc, stru
 	struct bstp_tcn_unit tu;
 	uint16_t len;
 
+#ifdef BRIDGE_MPSAFE
+	KASSERT(bif->bif_refs > 0);
+#endif
+
 	eh = mtod(m, struct ether_header *);
 
 	if ((bif->bif_flags & IFBIF_STP) == 0)
@@ -621,7 +633,11 @@ bstp_input(struct bridge_softc *sc, stru
 	switch (tpdu.tbu_bpdutype) {
 	case BSTP_MSGTYPE_TCN:
 		tu.tu_message_type = tpdu.tbu_bpdutype;
+
+		BRIDGE_LOCK(sc);
 		bstp_received_tcn_bpdu(sc, bif, &tu);
+		BRIDGE_UNLOCK(sc);
+
 		break;
 	case BSTP_MSGTYPE_CFG:
 		if (m->m_len < sizeof(cpdu) &&
@@ -658,7 +674,11 @@ bstp_input(struct bridge_softc *sc, stru
 		    (cpdu.cbu_flags & BSTP_FLAG_TCA) ? 1 : 0;
 		cu.cu_topology_change =
 		    (cpdu.cbu_flags & BSTP_FLAG_TC) ? 1 : 0;
+
+		BRIDGE_LOCK(sc);
 		bstp_received_config_bpdu(sc, bif, &cu);
+		BRIDGE_UNLOCK(sc);
+
 		break;
 	default:
 		goto out;
@@ -805,6 +825,9 @@ bstp_initialization(struct bridge_softc 
 	struct bridge_iflist *bif, *mif;
 
 	mif = NULL;
+
+	BRIDGE_LOCK(sc);
+
 	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
 		if ((bif->bif_flags & IFBIF_STP) == 0)
 			continue;
@@ -823,7 +846,9 @@ bstp_initialization(struct bridge_softc 
 			continue;
 		}
 	}
+
 	if (mif == NULL) {
+		BRIDGE_UNLOCK(sc);
 		bstp_stop(sc);
 		return;
 	}
@@ -837,6 +862,8 @@ bstp_initialization(struct bridge_softc 
 	    (((uint64_t)(uint8_t)CLLADDR(mif->bif_ifp->if_sadl)[4]) << 8) |
 	    (((uint64_t)(uint8_t)CLLADDR(mif->bif_ifp->if_sadl)[5]) << 0);
 
+	BRIDGE_UNLOCK(sc);
+
 	sc->sc_designated_root = sc->sc_bridge_id;
 	sc->sc_root_path_cost = 0;
 	sc->sc_root_port = NULL;
@@ -853,6 +880,8 @@ bstp_initialization(struct bridge_softc 
 		callout_reset(&sc->sc_bstpcallout, hz,
 		    bstp_tick, sc);
 
+	BRIDGE_LOCK(sc);
+
 	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
 		if (bif->bif_flags & IFBIF_STP)
 			bstp_enable_port(sc, bif);
@@ -863,6 +892,8 @@ bstp_initialization(struct bridge_softc 
 	bstp_port_state_selection(sc);
 	bstp_config_bpdu_generation(sc);
 	bstp_timer_start(&sc->sc_hello_timer, 0);
+
+	BRIDGE_UNLOCK(sc);
 }
 
 void
@@ -870,12 +901,14 @@ bstp_stop(struct bridge_softc *sc)
 {
 	struct bridge_iflist *bif;
 
+	BRIDGE_LOCK(sc);
 	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
 		bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED);
 		bstp_timer_stop(&bif->bif_hold_timer);
 		bstp_timer_stop(&bif->bif_message_age_timer);
 		bstp_timer_stop(&bif->bif_forward_delay_timer);
 	}
+	BRIDGE_UNLOCK(sc);
 
 	callout_stop(&sc->sc_bstpcallout);
 
@@ -1032,6 +1065,7 @@ bstp_tick(void *arg)
 	int s;
 
 	s = splnet();
+	BRIDGE_LOCK(sc);
 
 	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
 		if ((bif->bif_flags & IFBIF_STP) == 0)
@@ -1079,6 +1113,7 @@ bstp_tick(void *arg)
 	if (sc->sc_if.if_flags & IFF_RUNNING)
 		callout_reset(&sc->sc_bstpcallout, hz, bstp_tick, sc);
 
+	BRIDGE_UNLOCK(sc);
 	splx(s);
 }
 

Index: src/sys/net/if.h
diff -u src/sys/net/if.h:1.170 src/sys/net/if.h:1.171
--- src/sys/net/if.h:1.170	Tue Jul  1 14:04:40 2014
+++ src/sys/net/if.h	Mon Jul 14 02:34:36 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: if.h,v 1.170 2014/07/01 14:04:40 ozaki-r Exp $	*/
+/*	$NetBSD: if.h,v 1.171 2014/07/14 02:34:36 ozaki-r Exp $	*/
 
 /*-
  * Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@@ -290,6 +290,7 @@ typedef struct ifnet {
 	struct ifaddr	*if_hwdl;
 	const uint8_t *if_broadcastaddr;/* linklevel broadcast bytestring */
 	void	*if_bridge;		/* bridge glue */
+	void	*if_bridgeif;		/* shortcut to interface list entry */
 	int	if_dlt;			/* data link type (<net/dlt.h>) */
 	pfil_head_t *	if_pfil;	/* filtering point */
 	uint64_t if_capabilities;	/* interface capabilities */

Index: src/sys/net/if_bridge.c
diff -u src/sys/net/if_bridge.c:1.86 src/sys/net/if_bridge.c:1.87
--- src/sys/net/if_bridge.c:1.86	Wed Jul  2 09:51:08 2014
+++ src/sys/net/if_bridge.c	Mon Jul 14 02:34:36 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_bridge.c,v 1.86 2014/07/02 09:51:08 ozaki-r Exp $	*/
+/*	$NetBSD: if_bridge.c,v 1.87 2014/07/14 02:34:36 ozaki-r Exp $	*/
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -80,7 +80,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.86 2014/07/02 09:51:08 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.87 2014/07/14 02:34:36 ozaki-r Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_bridge_ipf.h"
@@ -219,6 +219,7 @@ static struct bridge_iflist *bridge_look
 						  const char *name);
 static struct bridge_iflist *bridge_lookup_member_if(struct bridge_softc *,
 						     struct ifnet *ifp);
+static void	bridge_release_member(struct bridge_softc *, struct bridge_iflist *);
 static void	bridge_delete_member(struct bridge_softc *,
 				     struct bridge_iflist *);
 
@@ -365,6 +366,12 @@ bridge_clone_create(struct if_clone *ifc
 	callout_init(&sc->sc_bstpcallout, 0);
 
 	LIST_INIT(&sc->sc_iflist);
+#ifdef BRIDGE_MPSAFE
+	sc->sc_iflist_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
+#else
+	sc->sc_iflist_lock = NULL;
+#endif
+	cv_init(&sc->sc_iflist_cv, "if_bridge_cv");
 
 	if_initname(ifp, ifc->ifc_name, unit);
 	ifp->if_softc = sc;
@@ -414,8 +421,10 @@ bridge_clone_destroy(struct ifnet *ifp)
 
 	bridge_stop(ifp, 1);
 
+	BRIDGE_LOCK(sc);
 	while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL)
 		bridge_delete_member(sc, bif);
+	BRIDGE_UNLOCK(sc);
 
 	mutex_enter(&bridge_list_lock);
 	LIST_REMOVE(sc, sc_list);
@@ -432,6 +441,10 @@ bridge_clone_destroy(struct ifnet *ifp)
 	/* Tear down the routing table. */
 	bridge_rtable_fini(sc);
 
+	cv_destroy(&sc->sc_iflist_cv);
+	if (sc->sc_iflist_lock)
+		mutex_obj_free(sc->sc_iflist_lock);
+
 	free(sc, M_DEVBUF);
 
 	return (0);
@@ -648,7 +661,7 @@ bridge_ioctl(struct ifnet *ifp, u_long c
 /*
  * bridge_lookup_member:
  *
- *	Lookup a bridge member interface.  Must be called at splnet().
+ *	Lookup a bridge member interface.
  */
 static struct bridge_iflist *
 bridge_lookup_member(struct bridge_softc *sc, const char *name)
@@ -656,31 +669,76 @@ bridge_lookup_member(struct bridge_softc
 	struct bridge_iflist *bif;
 	struct ifnet *ifp;
 
+	BRIDGE_LOCK(sc);
+
 	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
 		ifp = bif->bif_ifp;
 		if (strcmp(ifp->if_xname, name) == 0)
-			return (bif);
+			break;
 	}
 
-	return (NULL);
+#ifdef BRIDGE_MPSAFE
+	if (bif != NULL) {
+		if (bif->bif_waiting)
+			bif = NULL;
+		else
+			atomic_inc_32(&bif->bif_refs);
+	}
+#endif
+
+	BRIDGE_UNLOCK(sc);
+
+	return bif;
 }
 
 /*
  * bridge_lookup_member_if:
  *
- *	Lookup a bridge member interface by ifnet*.  Must be called at splnet().
+ *	Lookup a bridge member interface by ifnet*.
  */
 static struct bridge_iflist *
 bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp)
 {
 	struct bridge_iflist *bif;
 
-	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
-		if (bif->bif_ifp == member_ifp)
-			return (bif);
+	BRIDGE_LOCK(sc);
+
+	bif = member_ifp->if_bridgeif;
+
+#ifdef BRIDGE_MPSAFE
+	if (bif != NULL) {
+		if (bif->bif_waiting)
+			bif = NULL;
+		else
+			atomic_inc_32(&bif->bif_refs);
 	}
+#endif
 
-	return (NULL);
+	BRIDGE_UNLOCK(sc);
+
+	return bif;
+}
+
+/*
+ * bridge_release_member:
+ *
+ *	Release the specified member interface.
+ */
+static void
+bridge_release_member(struct bridge_softc *sc, struct bridge_iflist *bif)
+{
+#ifdef BRIDGE_MPSAFE
+	atomic_dec_32(&bif->bif_refs);
+	membar_sync();
+	if (__predict_false(bif->bif_waiting && bif->bif_refs == 0)) {
+		BRIDGE_LOCK(sc);
+		cv_broadcast(&sc->sc_iflist_cv);
+		BRIDGE_UNLOCK(sc);
+	}
+#else
+	(void)sc;
+	(void)bif;
+#endif
 }
 
 /*
@@ -693,30 +751,24 @@ bridge_delete_member(struct bridge_softc
 {
 	struct ifnet *ifs = bif->bif_ifp;
 
-	switch (ifs->if_type) {
-	case IFT_ETHER:
-		/*
-		 * Take the interface out of promiscuous mode.
-		 */
-		(void) ifpromisc(ifs, 0);
-		break;
-	default:
-#ifdef DIAGNOSTIC
-		panic("bridge_delete_member: impossible");
-#endif
-		break;
-	}
+	KASSERT(BRIDGE_LOCKED(sc));
 
 	ifs->if_input = ether_input;
 	ifs->if_bridge = NULL;
+	ifs->if_bridgeif = NULL;
+
 	LIST_REMOVE(bif, bif_next);
 
-	bridge_rtdelete(sc, ifs);
+#ifdef BRIDGE_MPSAFE
+	bif->bif_waiting = true;
+	membar_sync();
+	while (bif->bif_refs > 0) {
+		aprint_debug("%s: cv_wait on iflist\n", __func__);
+		cv_wait(&sc->sc_iflist_cv, sc->sc_iflist_lock);
+	}
+#endif
 
 	free(bif, M_DEVBUF);
-
-	if (sc->sc_if.if_flags & IFF_RUNNING)
-		bstp_initialization(sc);
 }
 
 static int
@@ -765,11 +817,18 @@ bridge_ioctl_add(struct bridge_softc *sc
 	bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
 	bif->bif_priority = BSTP_DEFAULT_PORT_PRIORITY;
 	bif->bif_path_cost = BSTP_DEFAULT_PATH_COST;
+	bif->bif_refs = 0;
+	bif->bif_waiting = false;
+
+	BRIDGE_LOCK(sc);
 
 	ifs->if_bridge = sc;
+	ifs->if_bridgeif = bif;
 	LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next);
 	ifs->if_input = bridge_input;
 
+	BRIDGE_UNLOCK(sc);
+
 	if (sc->sc_if.if_flags & IFF_RUNNING)
 		bstp_initialization(sc);
 	else
@@ -787,15 +846,52 @@ static int
 bridge_ioctl_del(struct bridge_softc *sc, void *arg)
 {
 	struct ifbreq *req = arg;
+	const char *name = req->ifbr_ifsname;
 	struct bridge_iflist *bif;
+	struct ifnet *ifs;
 
-	bif = bridge_lookup_member(sc, req->ifbr_ifsname);
-	if (bif == NULL)
-		return (ENOENT);
+	BRIDGE_LOCK(sc);
+
+	/*
+	 * Don't use bridge_lookup_member. We want to get a member
+	 * with bif_refs == 0.
+	 */
+	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
+		ifs = bif->bif_ifp;
+		if (strcmp(ifs->if_xname, name) == 0)
+			break;
+	}
+
+	if (bif == NULL) {
+		BRIDGE_UNLOCK(sc);
+		return ENOENT;
+	}
 
 	bridge_delete_member(sc, bif);
 
-	return (0);
+	BRIDGE_UNLOCK(sc);
+
+	switch (ifs->if_type) {
+	case IFT_ETHER:
+		/*
+		 * Take the interface out of promiscuous mode.
+		 * Don't call it with holding sc_iflist_lock.
+		 */
+		(void) ifpromisc(ifs, 0);
+		break;
+	default:
+#ifdef DIAGNOSTIC
+		panic("bridge_delete_member: impossible");
+#endif
+		break;
+	}
+
+	bridge_rtdelete(sc, ifs);
+
+	if (sc->sc_if.if_flags & IFF_RUNNING)
+		bstp_initialization(sc);
+
+	return 0;
 }
 
 static int
@@ -814,6 +910,8 @@ bridge_ioctl_gifflags(struct bridge_soft
 	req->ifbr_path_cost = bif->bif_path_cost;
 	req->ifbr_portno = bif->bif_ifp->if_index & 0xff;
 
+	bridge_release_member(sc, bif);
+
 	return (0);
 }
 
@@ -835,12 +933,15 @@ bridge_ioctl_sifflags(struct bridge_soft
 
 		default:
 			/* Nothing else can. */
+			bridge_release_member(sc, bif);
 			return (EINVAL);
 		}
 	}
 
 	bif->bif_flags = req->ifbr_ifsflags;
 
+	bridge_release_member(sc, bif);
+
 	if (sc->sc_if.if_flags & IFF_RUNNING)
 		bstp_initialization(sc);
 
@@ -876,6 +977,8 @@ bridge_ioctl_gifs(struct bridge_softc *s
 	struct ifbreq breq;
 	int count, len, error = 0;
 
+	BRIDGE_LOCK(sc);
+
 	count = 0;
 	LIST_FOREACH(bif, &sc->sc_iflist, bif_next)
 		count++;
@@ -906,6 +1009,8 @@ bridge_ioctl_gifs(struct bridge_softc *s
 		len -= sizeof(breq);
 	}
 
+	BRIDGE_UNLOCK(sc);
+
 	bifc->ifbic_len = sizeof(breq) * count;
 	return (error);
 }
@@ -921,6 +1026,8 @@ bridge_ioctl_rts(struct bridge_softc *sc
 	if (bac->ifbac_len == 0)
 		return (0);
 
+	mutex_enter(sc->sc_rtlist_lock);
+
 	len = bac->ifbac_len;
 	LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) {
 		if (len < sizeof(bareq))
@@ -942,6 +1049,8 @@ bridge_ioctl_rts(struct bridge_softc *sc
 		len -= sizeof(bareq);
 	}
  out:
+	mutex_exit(sc->sc_rtlist_lock);
+
 	bac->ifbac_len = sizeof(bareq) * count;
 	return (error);
 }
@@ -960,6 +1069,8 @@ bridge_ioctl_saddr(struct bridge_softc *
 	error = bridge_rtupdate(sc, req->ifba_dst, bif->bif_ifp, 1,
 	    req->ifba_flags);
 
+	bridge_release_member(sc, bif);
+
 	return (error);
 }
 
@@ -1114,6 +1225,8 @@ bridge_ioctl_sifprio(struct bridge_softc
 	if (sc->sc_if.if_flags & IFF_RUNNING)
 		bstp_initialization(sc);
 
+	bridge_release_member(sc, bif);
+
 	return (0);
 }
 
@@ -1170,6 +1283,8 @@ bridge_ioctl_sifcost(struct bridge_softc
 	if (sc->sc_if.if_flags & IFF_RUNNING)
 		bstp_initialization(sc);
 
+	bridge_release_member(sc, bif);
+
 	return (0);
 }
 
@@ -1185,6 +1300,9 @@ bridge_ifdetach(struct ifnet *ifp)
 	struct bridge_softc *sc = ifp->if_bridge;
 	struct ifbreq breq;
 
+	/* ioctl_lock should prevent this from happening */
+	KASSERT(sc != NULL);
+
 	memset(&breq, 0, sizeof(breq));
 	strlcpy(breq.ifbr_ifsname, ifp->if_xname, sizeof(breq.ifbr_ifsname));
 
@@ -1237,8 +1355,6 @@ bridge_stop(struct ifnet *ifp, int disab
  * bridge_enqueue:
  *
  *	Enqueue a packet on a bridge member interface.
- *
- *	NOTE: must be called at splnet().
  */
 void
 bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m,
@@ -1280,7 +1396,9 @@ bridge_enqueue(struct bridge_softc *sc, 
 	len = m->m_pkthdr.len;
 	m->m_flags |= M_PROTO1;
 	mflags = m->m_flags;
+
 	IFQ_ENQUEUE(&dst_ifp->if_snd, m, &pktattr, error);
+
 	if (error) {
 		/* mbuf is already freed */
 		sc->sc_if.if_oerrors++;
@@ -1318,7 +1436,9 @@ bridge_output(struct ifnet *ifp, struct 
 	struct ether_header *eh;
 	struct ifnet *dst_if;
 	struct bridge_softc *sc;
+#ifndef BRIDGE_MPSAFE
 	int s;
+#endif
 
 	if (m->m_len < ETHER_HDR_LEN) {
 		m = m_pullup(m, ETHER_HDR_LEN);
@@ -1329,14 +1449,17 @@ bridge_output(struct ifnet *ifp, struct 
 	eh = mtod(m, struct ether_header *);
 	sc = ifp->if_bridge;
 
+#ifndef BRIDGE_MPSAFE
 	s = splnet();
+#endif
 
 	/*
 	 * If bridge is down, but the original output interface is up,
 	 * go ahead and send out that interface.  Otherwise, the packet
 	 * is dropped below.
 	 */
-	if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
+	if (__predict_false(sc == NULL) ||
+	    (sc->sc_if.if_flags & IFF_RUNNING) == 0) {
 		dst_if = ifp;
 		goto sendunicast;
 	}
@@ -1354,6 +1477,8 @@ bridge_output(struct ifnet *ifp, struct 
 		struct mbuf *mc;
 		int used = 0;
 
+		BRIDGE_LOCK(sc);
+
 		LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
 			dst_if = bif->bif_ifp;
 			if ((dst_if->if_flags & IFF_RUNNING) == 0)
@@ -1388,9 +1513,14 @@ bridge_output(struct ifnet *ifp, struct 
 
 			bridge_enqueue(sc, dst_if, mc, 0);
 		}
+
+		BRIDGE_UNLOCK(sc);
+
 		if (used == 0)
 			m_freem(m);
+#ifndef BRIDGE_MPSAFE
 		splx(s);
+#endif
 		return (0);
 	}
 
@@ -1401,13 +1531,17 @@ bridge_output(struct ifnet *ifp, struct 
 
 	if ((dst_if->if_flags & IFF_RUNNING) == 0) {
 		m_freem(m);
+#ifndef BRIDGE_MPSAFE
 		splx(s);
+#endif
 		return (0);
 	}
 
 	bridge_enqueue(sc, dst_if, m, 0);
 
+#ifndef BRIDGE_MPSAFE
 	splx(s);
+#endif
 	return (0);
 }
 
@@ -1438,17 +1572,24 @@ bridge_forward(void *v)
 	struct bridge_iflist *bif;
 	struct ifnet *src_if, *dst_if;
 	struct ether_header *eh;
+#ifndef BRIDGE_MPSAFE
 	int s;
 
 	KERNEL_LOCK(1, NULL);
 	mutex_enter(softnet_lock);
+#endif
+
 	if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
+#ifndef BRIDGE_MPSAFE
 		mutex_exit(softnet_lock);
 		KERNEL_UNLOCK_ONE(NULL);
+#endif
 		return;
 	}
 
+#ifndef BRIDGE_MPSAFE
 	s = splnet();
+#endif
 	while ((m = pktq_dequeue(sc->sc_fwd_pktq)) != NULL) {
 		src_if = m->m_pkthdr.rcvif;
 
@@ -1471,6 +1612,7 @@ bridge_forward(void *v)
 			case BSTP_IFSTATE_LISTENING:
 			case BSTP_IFSTATE_DISABLED:
 				m_freem(m);
+				bridge_release_member(sc, bif);
 				continue;
 			}
 		}
@@ -1497,9 +1639,12 @@ bridge_forward(void *v)
 		if ((bif->bif_flags & IFBIF_STP) != 0 &&
 		    bif->bif_state == BSTP_IFSTATE_LEARNING) {
 			m_freem(m);
+			bridge_release_member(sc, bif);
 			continue;
 		}
 
+		bridge_release_member(sc, bif);
+
 		/*
 		 * At this point, the port either doesn't participate
 		 * in spanning tree or it is in the forwarding state.
@@ -1543,6 +1688,7 @@ bridge_forward(void *v)
 			m_freem(m);
 			continue;
 		}
+
 		bif = bridge_lookup_member_if(sc, dst_if);
 		if (bif == NULL) {
 			/* Not a member of the bridge (anymore?) */
@@ -1555,15 +1701,20 @@ bridge_forward(void *v)
 			case BSTP_IFSTATE_DISABLED:
 			case BSTP_IFSTATE_BLOCKING:
 				m_freem(m);
+				bridge_release_member(sc, bif);
 				continue;
 			}
 		}
 
+		bridge_release_member(sc, bif);
+
 		bridge_enqueue(sc, dst_if, m, 1);
 	}
+#ifndef BRIDGE_MPSAFE
 	splx(s);
 	mutex_exit(softnet_lock);
 	KERNEL_UNLOCK_ONE(NULL);
+#endif
 }
 
 static bool
@@ -1601,7 +1752,6 @@ bridge_ourether(struct bridge_iflist *bi
  *
  *	Receive input from a member interface.  Queue the packet for
  *	bridging if it is not for us.
- *	should be called at splnet()
  */
 static void
 bridge_input(struct ifnet *ifp, struct mbuf *m)
@@ -1610,7 +1760,8 @@ bridge_input(struct ifnet *ifp, struct m
 	struct bridge_iflist *bif;
 	struct ether_header *eh;
 
-	if ((sc->sc_if.if_flags & IFF_RUNNING) == 0) {
+	if (__predict_false(sc == NULL) ||
+	    (sc->sc_if.if_flags & IFF_RUNNING) == 0) {
 		ether_input(ifp, m);
 		return;
 	}
@@ -1639,6 +1790,7 @@ bridge_input(struct ifnet *ifp, struct m
 	    !bstp_state_before_learning(bif)) {
 		struct bridge_iflist *_bif;
 
+		BRIDGE_LOCK(sc);
 		LIST_FOREACH(_bif, &sc->sc_iflist, bif_next) {
 			/* It is destined for us. */
 			if (bridge_ourether(_bif, eh, 0)) {
@@ -1647,21 +1799,28 @@ bridge_input(struct ifnet *ifp, struct m
 					    eh->ether_shost, ifp, 0, IFBAF_DYNAMIC);
 				m->m_pkthdr.rcvif = _bif->bif_ifp;
 				ether_input(_bif->bif_ifp, m);
-				return;
+				break;
 			}
 
 			/* We just received a packet that we sent out. */
 			if (bridge_ourether(_bif, eh, 1)) {
 				m_freem(m);
-				return;
+				break;
 			}
 		}
+		BRIDGE_UNLOCK(sc);
+
+		if (_bif != NULL) {
+			bridge_release_member(sc, bif);
+			return;
+		}
 	}
 
 	/* Tap off 802.1D packets; they do not get forwarded. */
 	if (bif->bif_flags & IFBIF_STP &&
 	    memcmp(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN) == 0) {
 		bstp_input(sc, bif, m);
+		bridge_release_member(sc, bif);
 		return;
 	}
 
@@ -1671,9 +1830,12 @@ bridge_input(struct ifnet *ifp, struct m
 	 */
 	if (bstp_state_before_learning(bif)) {
 		ether_input(ifp, m);
+		bridge_release_member(sc, bif);
 		return;
 	}
 
+	bridge_release_member(sc, bif);
+
 	/* Queue the packet for bridge forwarding. */
 	if (__predict_false(!pktq_enqueue(sc->sc_fwd_pktq, m, 0)))
 		m_freem(m);
@@ -1697,6 +1859,8 @@ bridge_broadcast(struct bridge_softc *sc
 
 	used = bmcast = m->m_flags & (M_BCAST|M_MCAST);
 
+	BRIDGE_LOCK(sc);
+
 	LIST_FOREACH(bif, &sc->sc_iflist, bif_next) {
 		dst_if = bif->bif_ifp;
 		if (dst_if == src_if)
@@ -1734,6 +1898,8 @@ bridge_broadcast(struct bridge_softc *sc
 		ether_input(src_if, m);
 	else if (!used)
 		m_freem(m);
+
+	BRIDGE_UNLOCK(sc);
 }
 
 /*
@@ -1746,15 +1912,19 @@ bridge_rtupdate(struct bridge_softc *sc,
     struct ifnet *dst_if, int setflags, uint8_t flags)
 {
 	struct bridge_rtnode *brt;
-	int error;
+	int error = 0;
+
+	mutex_enter(sc->sc_rtlist_lock);
 
 	/*
 	 * A route for this destination might already exist.  If so,
 	 * update it, otherwise create a new one.
 	 */
 	if ((brt = bridge_rtnode_lookup(sc, dst)) == NULL) {
-		if (sc->sc_brtcnt >= sc->sc_brtmax)
-			return (ENOSPC);
+		if (sc->sc_brtcnt >= sc->sc_brtmax) {
+			error = ENOSPC;
+			goto out;
+		}
 
 		/*
 		 * Allocate a new bridge forwarding node, and
@@ -1762,8 +1932,10 @@ bridge_rtupdate(struct bridge_softc *sc,
 		 * address.
 		 */
 		brt = pool_get(&bridge_rtnode_pool, PR_NOWAIT);
-		if (brt == NULL)
-			return (ENOMEM);
+		if (brt == NULL) {
+			error = ENOMEM;
+			goto out;
+		}
 
 		memset(brt, 0, sizeof(*brt));
 		brt->brt_expire = time_uptime + sc->sc_brttimeout;
@@ -1772,7 +1944,7 @@ bridge_rtupdate(struct bridge_softc *sc,
 
 		if ((error = bridge_rtnode_insert(sc, brt)) != 0) {
 			pool_put(&bridge_rtnode_pool, brt);
-			return (error);
+			goto out;
 		}
 	}
 
@@ -1785,7 +1957,10 @@ bridge_rtupdate(struct bridge_softc *sc,
 			brt->brt_expire = time_uptime + sc->sc_brttimeout;
 	}
 
-	return (0);
+out:
+	mutex_exit(sc->sc_rtlist_lock);
+
+	return error;
 }
 
 /*
@@ -1797,11 +1972,17 @@ static struct ifnet *
 bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr)
 {
 	struct bridge_rtnode *brt;
+	struct ifnet *ifs = NULL;
+
+	mutex_enter(sc->sc_rtlist_lock);
+
+	brt = bridge_rtnode_lookup(sc, addr);
+	if (brt != NULL)
+		ifs = brt->brt_ifp;
 
-	if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL)
-		return (NULL);
+	mutex_exit(sc->sc_rtlist_lock);
 
-	return (brt->brt_ifp);
+	return ifs;
 }
 
 /*
@@ -1816,23 +1997,30 @@ bridge_rttrim(struct bridge_softc *sc)
 {
 	struct bridge_rtnode *brt, *nbrt;
 
+	mutex_enter(sc->sc_rtlist_lock);
+
 	/* Make sure we actually need to do this. */
 	if (sc->sc_brtcnt <= sc->sc_brtmax)
-		return;
+		goto out;
 
 	/* Force an aging cycle; this might trim enough addresses. */
 	bridge_rtage(sc);
 	if (sc->sc_brtcnt <= sc->sc_brtmax)
-		return;
+		goto out;
 
 	for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) {
 		nbrt = LIST_NEXT(brt, brt_list);
 		if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
 			bridge_rtnode_destroy(sc, brt);
 			if (sc->sc_brtcnt <= sc->sc_brtmax)
-				return;
+				goto out;
 		}
 	}
+
+out:
+	mutex_exit(sc->sc_rtlist_lock);
+
+	return;
 }
 
 /*
@@ -1844,15 +2032,16 @@ static void
 bridge_timer(void *arg)
 {
 	struct bridge_softc *sc = arg;
-	int s;
 
-	s = splnet();
+	mutex_enter(sc->sc_rtlist_lock);
+
 	bridge_rtage(sc);
-	splx(s);
 
 	if (sc->sc_if.if_flags & IFF_RUNNING)
 		callout_reset(&sc->sc_brcallout,
 		    bridge_rtable_prune_period * hz, bridge_timer, sc);
+
+	mutex_exit(sc->sc_rtlist_lock);
 }
 
 /*
@@ -1865,6 +2054,8 @@ bridge_rtage(struct bridge_softc *sc)
 {
 	struct bridge_rtnode *brt, *nbrt;
 
+	KASSERT(mutex_owned(sc->sc_rtlist_lock));
+
 	for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) {
 		nbrt = LIST_NEXT(brt, brt_list);
 		if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) {
@@ -1884,11 +2075,15 @@ bridge_rtflush(struct bridge_softc *sc, 
 {
 	struct bridge_rtnode *brt, *nbrt;
 
+	mutex_enter(sc->sc_rtlist_lock);
+
 	for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) {
 		nbrt = LIST_NEXT(brt, brt_list);
 		if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)
 			bridge_rtnode_destroy(sc, brt);
 	}
+
+	mutex_exit(sc->sc_rtlist_lock);
 }
 
 /*
@@ -1900,12 +2095,21 @@ static int
 bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr)
 {
 	struct bridge_rtnode *brt;
+	int error = 0;
 
-	if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL)
-		return (ENOENT);
+	mutex_enter(sc->sc_rtlist_lock);
+
+	if ((brt = bridge_rtnode_lookup(sc, addr)) == NULL) {
+		error = ENOENT;
+		goto out;
+	}
 
 	bridge_rtnode_destroy(sc, brt);
-	return (0);
+
+out:
+	mutex_exit(sc->sc_rtlist_lock);
+
+	return error;
 }
 
 /*
@@ -1918,11 +2122,15 @@ bridge_rtdelete(struct bridge_softc *sc,
 {
 	struct bridge_rtnode *brt, *nbrt;
 
+	mutex_enter(sc->sc_rtlist_lock);
+
 	for (brt = LIST_FIRST(&sc->sc_rtlist); brt != NULL; brt = nbrt) {
 		nbrt = LIST_NEXT(brt, brt_list);
 		if (brt->brt_ifp == ifp)
 			bridge_rtnode_destroy(sc, brt);
 	}
+
+	mutex_exit(sc->sc_rtlist_lock);
 }
 
 /*
@@ -1947,6 +2155,8 @@ bridge_rtable_init(struct bridge_softc *
 
 	LIST_INIT(&sc->sc_rtlist);
 
+	sc->sc_rtlist_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
+
 	return (0);
 }
 
@@ -1960,6 +2170,8 @@ bridge_rtable_fini(struct bridge_softc *
 {
 
 	free(sc->sc_rthash, M_DEVBUF);
+	if (sc->sc_rtlist_lock)
+		mutex_obj_free(sc->sc_rtlist_lock);
 }
 
 /*
@@ -2010,6 +2222,8 @@ bridge_rtnode_lookup(struct bridge_softc
 	uint32_t hash;
 	int dir;
 
+	KASSERT(mutex_owned(sc->sc_rtlist_lock));
+
 	hash = bridge_rthash(sc, addr);
 	LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) {
 		dir = memcmp(addr, brt->brt_addr, ETHER_ADDR_LEN);
@@ -2035,6 +2249,8 @@ bridge_rtnode_insert(struct bridge_softc
 	uint32_t hash;
 	int dir;
 
+	KASSERT(mutex_owned(sc->sc_rtlist_lock));
+
 	hash = bridge_rthash(sc, brt->brt_addr);
 
 	lbrt = LIST_FIRST(&sc->sc_rthash[hash]);
@@ -2077,15 +2293,14 @@ bridge_rtnode_insert(struct bridge_softc
 static void
 bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt)
 {
-	int s = splnet();
+
+	KASSERT(mutex_owned(sc->sc_rtlist_lock));
 
 	LIST_REMOVE(brt, brt_hash);
 
 	LIST_REMOVE(brt, brt_list);
 	sc->sc_brtcnt--;
 	pool_put(&bridge_rtnode_pool, brt);
-
-	splx(s);
 }
 
 #if defined(BRIDGE_IPF)

Index: src/sys/net/if_bridgevar.h
diff -u src/sys/net/if_bridgevar.h:1.19 src/sys/net/if_bridgevar.h:1.20
--- src/sys/net/if_bridgevar.h:1.19	Fri Jun 20 14:22:48 2014
+++ src/sys/net/if_bridgevar.h	Mon Jul 14 02:34:36 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: if_bridgevar.h,v 1.19 2014/06/20 14:22:48 ozaki-r Exp $	*/
+/*	$NetBSD: if_bridgevar.h,v 1.20 2014/07/14 02:34:36 ozaki-r Exp $	*/
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -77,6 +77,8 @@
 
 #include <sys/callout.h>
 #include <sys/queue.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
 
 /*
  * Commands used in the SIOCSDRVSPEC ioctl.  Note the lookup of the
@@ -255,6 +257,8 @@ struct bridge_iflist {
 	uint8_t			bif_priority;
 	struct ifnet		*bif_ifp;	/* member if */
 	uint32_t		bif_flags;	/* member if flags */
+	uint32_t		bif_refs;	/* reference count */
+	bool			bif_waiting;	/* waiting for released  */
 };
 
 /*
@@ -299,8 +303,11 @@ struct bridge_softc {
 	callout_t		sc_brcallout;	/* bridge callout */
 	callout_t		sc_bstpcallout;	/* STP callout */
 	LIST_HEAD(, bridge_iflist) sc_iflist;	/* member interface list */
+	kmutex_t		*sc_iflist_lock;
+	kcondvar_t		sc_iflist_cv;
 	LIST_HEAD(, bridge_rtnode) *sc_rthash;	/* our forwarding table */
 	LIST_HEAD(, bridge_rtnode) sc_rtlist;	/* list version of above */
+	kmutex_t		*sc_rtlist_lock;
 	uint32_t		sc_rthash_key;	/* key for hash */
 	uint32_t		sc_filter_flags; /* ipf and flags */
 	pktqueue_t *		sc_fwd_pktq;
@@ -320,5 +327,16 @@ void	bstp_input(struct bridge_softc *, s
 void	bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *,
 	    int);
 
+#ifdef NET_MPSAFE
+#define BRIDGE_MPSAFE	1
+#endif
+
+#define BRIDGE_LOCK(_sc)	if ((_sc)->sc_iflist_lock) \
+					mutex_enter((_sc)->sc_iflist_lock)
+#define BRIDGE_UNLOCK(_sc)	if ((_sc)->sc_iflist_lock) \
+					mutex_exit((_sc)->sc_iflist_lock)
+#define BRIDGE_LOCKED(_sc)	(!(_sc)->sc_iflist_lock || \
+				 mutex_owned((_sc)->sc_iflist_lock))
+
 #endif /* _KERNEL */
 #endif /* !_NET_IF_BRIDGEVAR_H_ */

Reply via email to