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 ([email protected])
@@ -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_ */