Diff below uses a mutex to serialize accesses to the hash table.
It doesn't guarantee that pointers returned by a lookup on this table
are still valid. This will be addressed in a later diff.
I also annotated Immutable fields ([I]) in the bridge_softc structure.
Ok?
Index: net/bridgectl.c
===================================================================
RCS file: /cvs/src/sys/net/bridgectl.c,v
retrieving revision 1.13
diff -u -p -r1.13 bridgectl.c
--- net/bridgectl.c 12 Dec 2018 14:19:15 -0000 1.13
+++ net/bridgectl.c 13 Feb 2019 13:41:17 -0000
@@ -102,7 +102,9 @@ bridgectl_ioctl(struct ifnet *ifp, u_lon
bparam->ifbrp_csize = sc->sc_brtmax;
break;
case SIOCBRDGSCACHE:
+ mtx_enter(&sc->sc_mtx);
sc->sc_brtmax = bparam->ifbrp_csize;
+ mtx_leave(&sc->sc_mtx);
break;
case SIOCBRDGSTO:
if (bparam->ifbrp_ctime < 0 ||
@@ -195,6 +197,7 @@ bridge_rtupdate(struct bridge_softc *sc,
}
h = bridge_hash(sc, ea);
+ mtx_enter(&sc->sc_mtx);
p = LIST_FIRST(&sc->sc_rts[h]);
if (p == NULL) {
if (sc->sc_brtcnt >= sc->sc_brtmax)
@@ -285,26 +288,31 @@ bridge_rtupdate(struct bridge_softc *sc,
done:
ifp = NULL;
want:
+ mtx_leave(&sc->sc_mtx);
return (ifp);
}
struct bridge_rtnode *
bridge_rtlookup(struct bridge_softc *sc, struct ether_addr *ea)
{
- struct bridge_rtnode *p;
+ struct bridge_rtnode *p = NULL;
u_int32_t h;
int dir;
h = bridge_hash(sc, ea);
+ mtx_enter(&sc->sc_mtx);
LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
dir = memcmp(ea, &p->brt_addr, sizeof(p->brt_addr));
if (dir == 0)
- return (p);
- if (dir > 0)
- goto fail;
+ break;
+ if (dir > 0) {
+ p = NULL;
+ break;
+ }
}
-fail:
- return (NULL);
+ mtx_leave(&sc->sc_mtx);
+
+ return (p);
}
u_int32_t
@@ -324,8 +332,7 @@ bridge_rtage(void *vsc)
struct bridge_rtnode *n, *p;
int i;
- KERNEL_ASSERT_LOCKED();
-
+ mtx_enter(&sc->sc_mtx);
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
n = LIST_FIRST(&sc->sc_rts[i]);
while (n != NULL) {
@@ -346,6 +353,7 @@ bridge_rtage(void *vsc)
}
}
}
+ mtx_leave(&sc->sc_mtx);
if (sc->sc_brttimeout != 0)
timeout_add_sec(&sc->sc_brtimeout, sc->sc_brttimeout);
@@ -373,6 +381,7 @@ bridge_rtagenode(struct ifnet *ifp, int
if (age == 0)
bridge_rtdelete(sc, ifp, 1);
else {
+ mtx_enter(&sc->sc_mtx);
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
LIST_FOREACH(n, &sc->sc_rts[i], brt_next) {
/* Cap the expiry time to 'age' */
@@ -382,6 +391,7 @@ bridge_rtagenode(struct ifnet *ifp, int
n->brt_age = time_uptime + age;
}
}
+ mtx_leave(&sc->sc_mtx);
}
}
@@ -394,6 +404,7 @@ bridge_rtflush(struct bridge_softc *sc,
int i;
struct bridge_rtnode *p, *n;
+ mtx_enter(&sc->sc_mtx);
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
n = LIST_FIRST(&sc->sc_rts[i]);
while (n != NULL) {
@@ -408,6 +419,7 @@ bridge_rtflush(struct bridge_softc *sc,
n = LIST_NEXT(n, brt_next);
}
}
+ mtx_leave(&sc->sc_mtx);
}
/*
@@ -420,14 +432,17 @@ bridge_rtdaddr(struct bridge_softc *sc,
struct bridge_rtnode *p;
h = bridge_hash(sc, ea);
+ mtx_enter(&sc->sc_mtx);
LIST_FOREACH(p, &sc->sc_rts[h], brt_next) {
if (memcmp(ea, &p->brt_addr, sizeof(p->brt_addr)) == 0) {
LIST_REMOVE(p, brt_next);
sc->sc_brtcnt--;
+ mtx_leave(&sc->sc_mtx);
free(p, M_DEVBUF, sizeof *p);
return (0);
}
}
+ mtx_leave(&sc->sc_mtx);
return (ENOENT);
}
@@ -445,6 +460,7 @@ bridge_rtdelete(struct bridge_softc *sc,
* Loop through all of the hash buckets and traverse each
* chain looking for routes to this interface.
*/
+ mtx_enter(&sc->sc_mtx);
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++) {
n = LIST_FIRST(&sc->sc_rts[i]);
while (n != NULL) {
@@ -466,6 +482,7 @@ bridge_rtdelete(struct bridge_softc *sc,
n = p;
}
}
+ mtx_leave(&sc->sc_mtx);
}
/*
@@ -479,10 +496,12 @@ bridge_rtfind(struct bridge_softc *sc, s
u_int32_t i = 0, total = 0;
int k, error = 0;
+ mtx_enter(&sc->sc_mtx);
for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
LIST_FOREACH(n, &sc->sc_rts[k], brt_next)
total++;
}
+ mtx_leave(&sc->sc_mtx);
if (baconf->ifbac_len == 0) {
i = total;
@@ -493,6 +512,7 @@ bridge_rtfind(struct bridge_softc *sc, s
if (bareqs == NULL)
goto done;
+ mtx_enter(&sc->sc_mtx);
for (k = 0; k < BRIDGE_RTABLE_SIZE; k++) {
LIST_FOREACH(n, &sc->sc_rts[k], brt_next) {
if (baconf->ifbac_len < (i + 1) * sizeof(*bareqs))
@@ -511,6 +531,7 @@ bridge_rtfind(struct bridge_softc *sc, s
i++;
}
}
+ mtx_leave(&sc->sc_mtx);
error = copyout(bareqs, baconf->ifbac_req, i * sizeof(*bareqs));
done:
Index: net/if_bridge.c
===================================================================
RCS file: /cvs/src/sys/net/if_bridge.c,v
retrieving revision 1.319
diff -u -p -r1.319 if_bridge.c
--- net/if_bridge.c 29 Jan 2019 17:47:35 -0000 1.319
+++ net/if_bridge.c 13 Feb 2019 13:48:36 -0000
@@ -174,6 +174,7 @@ bridge_clone_create(struct if_clone *ifc
timeout_set(&sc->sc_brtimeout, bridge_rtage, sc);
SLIST_INIT(&sc->sc_iflist);
SLIST_INIT(&sc->sc_spanlist);
+ mtx_init(&sc->sc_mtx, IPL_MPFLOOR);
for (i = 0; i < BRIDGE_RTABLE_SIZE; i++)
LIST_INIT(&sc->sc_rts[i]);
arc4random_buf(&sc->sc_hashkey, sizeof(sc->sc_hashkey));
Index: net/if_bridge.h
===================================================================
RCS file: /cvs/src/sys/net/if_bridge.h,v
retrieving revision 1.60
diff -u -p -r1.60 if_bridge.h
--- net/if_bridge.h 29 Jan 2019 17:47:35 -0000 1.60
+++ net/if_bridge.h 13 Feb 2019 13:41:17 -0000
@@ -242,6 +242,9 @@ struct ifbrlconf {
};
#ifdef _KERNEL
+
+#include <sys/mutex.h>
+
/* STP port flags */
#define BSTP_PORT_CANMIGRATE 0x0001
#define BSTP_PORT_NEWINFO 0x0002
@@ -464,19 +467,25 @@ struct bridge_rtnode {
#define BRIDGE_RTABLE_MASK (BRIDGE_RTABLE_SIZE - 1)
/*
+ * Locks used to protect struct members in this file:
+ * I immutable after creation
+ * m per-softc mutex
+ */
+/*
* Software state for each bridge
*/
struct bridge_softc {
struct ifnet sc_if; /* the interface */
- u_int32_t sc_brtmax; /* max # addresses */
- u_int32_t sc_brtcnt; /* current # addrs */
+ uint32_t sc_brtmax; /* [m] max # addresses
*/
+ uint32_t sc_brtcnt; /* [m] current # addrs
*/
int sc_brttimeout; /* timeout ticks */
- u_int64_t sc_hashkey[2]; /* siphash key */
+ uint64_t sc_hashkey[2]; /* [I] siphash key */
struct timeout sc_brtimeout; /* timeout state */
struct bstp_state *sc_stp; /* stp state */
SLIST_HEAD(, bridge_iflist) sc_iflist; /* interface list */
SLIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports */
- LIST_HEAD(, bridge_rtnode) sc_rts[BRIDGE_RTABLE_SIZE]; /* hash
table */
+ struct mutex sc_mtx; /* mutex */
+ LIST_HEAD(, bridge_rtnode) sc_rts[BRIDGE_RTABLE_SIZE]; /* [m]
hash table */
};
extern const u_int8_t bstp_etheraddr[];