Module Name: src Committed By: ozaki-r Date: Wed Apr 18 04:01:58 UTC 2018
Modified Files: src/sys/net: if_bridge.c if_bridgevar.h Log Message: bridge: use pslist(9) for rtlist and rthash The change fixes race conditions on list operations. One example is that a reader may see invalid pointers on a looking item in a list due to lack of membar_producer. To generate a diff of this commit: cvs rdiff -u -r1.151 -r1.152 src/sys/net/if_bridge.c cvs rdiff -u -r1.31 -r1.32 src/sys/net/if_bridgevar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/if_bridge.c diff -u src/sys/net/if_bridge.c:1.151 src/sys/net/if_bridge.c:1.152 --- src/sys/net/if_bridge.c:1.151 Wed Apr 18 03:49:44 2018 +++ src/sys/net/if_bridge.c Wed Apr 18 04:01:58 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bridge.c,v 1.151 2018/04/18 03:49:44 ozaki-r Exp $ */ +/* $NetBSD: if_bridge.c,v 1.152 2018/04/18 04:01:58 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.151 2018/04/18 03:49:44 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.152 2018/04/18 04:01:58 ozaki-r Exp $"); #ifdef _KERNEL_OPT #include "opt_bridge_ipf.h" @@ -191,6 +191,29 @@ __CTASSERT(offsetof(struct ifbifconf, if #define BRIDGE_RT_RENTER(__s) do { __s = pserialize_read_enter(); } while (0) #define BRIDGE_RT_REXIT(__s) do { pserialize_read_exit(__s); } while (0) +#define BRIDGE_RTLIST_READER_FOREACH(_brt, _sc) \ + PSLIST_READER_FOREACH((_brt), &((_sc)->sc_rtlist), \ + struct bridge_rtnode, brt_list) +#define BRIDGE_RTLIST_WRITER_FOREACH(_brt, _sc) \ + PSLIST_WRITER_FOREACH((_brt), &((_sc)->sc_rtlist), \ + struct bridge_rtnode, brt_list) +#define BRIDGE_RTLIST_WRITER_INSERT_HEAD(_sc, _brt) \ + PSLIST_WRITER_INSERT_HEAD(&(_sc)->sc_rtlist, brt, brt_list) +#define BRIDGE_RTLIST_WRITER_REMOVE(_brt) \ + PSLIST_WRITER_REMOVE((_brt), brt_list) + +#define BRIDGE_RTHASH_READER_FOREACH(_brt, _sc, _hash) \ + PSLIST_READER_FOREACH((_brt), &(_sc)->sc_rthash[(_hash)], \ + struct bridge_rtnode, brt_hash) +#define BRIDGE_RTHASH_WRITER_FOREACH(_brt, _sc, _hash) \ + PSLIST_WRITER_FOREACH((_brt), &(_sc)->sc_rthash[(_hash)], \ + struct bridge_rtnode, brt_hash) +#define BRIDGE_RTHASH_WRITER_INSERT_HEAD(_sc, _hash, _brt) \ + PSLIST_WRITER_INSERT_HEAD(&(_sc)->sc_rthash[(_hash)], brt, brt_hash) +#define BRIDGE_RTHASH_WRITER_INSERT_AFTER(_brt, _new) \ + PSLIST_WRITER_INSERT_AFTER((_brt), (_new), brt_hash) +#define BRIDGE_RTHASH_WRITER_REMOVE(_brt) \ + PSLIST_WRITER_REMOVE((_brt), brt_hash) #ifdef NET_MPSAFE #define DECLARE_LOCK_VARIABLE @@ -1039,7 +1062,7 @@ bridge_ioctl_rts(struct bridge_softc *sc BRIDGE_RT_LOCK(sc); len = bac->ifbac_len; - LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { + BRIDGE_RTLIST_WRITER_FOREACH(brt, sc) { if (len < sizeof(bareq)) goto out; memset(&bareq, 0, sizeof(bareq)); @@ -2105,7 +2128,7 @@ typedef bool (*bridge_iterate_cb_t) static void bridge_rtlist_iterate_remove(struct bridge_softc *sc, bridge_iterate_cb_t func, void *arg) { - struct bridge_rtnode *brt, *nbrt; + struct bridge_rtnode *brt; struct bridge_rtnode **brt_list; int i, count; @@ -2124,7 +2147,12 @@ retry: } i = 0; - LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { + /* + * We don't need to use a _SAFE variant here because we know + * that a removed item keeps its next pointer as-is thanks to + * pslist(9) and isn't freed in the loop. + */ + BRIDGE_RTLIST_WRITER_FOREACH(brt, sc) { bool need_break = false; if (func(sc, brt, &need_break, arg)) { bridge_rtnode_remove(sc, brt); @@ -2298,7 +2326,7 @@ bridge_rtdelete(struct bridge_softc *sc, /* XXX pserialize_perform for each entry is slow */ again: BRIDGE_RT_LOCK(sc); - LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { + BRIDGE_RTLIST_WRITER_FOREACH(brt, sc) { if (brt->brt_ifp == ifp) break; } @@ -2329,11 +2357,11 @@ bridge_rtable_init(struct bridge_softc * KM_SLEEP); for (i = 0; i < BRIDGE_RTHASH_SIZE; i++) - LIST_INIT(&sc->sc_rthash[i]); + PSLIST_INIT(&sc->sc_rthash[i]); sc->sc_rthash_key = cprng_fast32(); - LIST_INIT(&sc->sc_rtlist); + PSLIST_INIT(&sc->sc_rtlist); sc->sc_rtlist_psz = pserialize_create(); sc->sc_rtlist_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET); @@ -2402,7 +2430,7 @@ bridge_rtnode_lookup(struct bridge_softc int dir; hash = bridge_rthash(sc, addr); - LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) { + BRIDGE_RTHASH_READER_FOREACH(brt, sc, hash) { dir = memcmp(addr, brt->brt_addr, ETHER_ADDR_LEN); if (dir == 0) return brt; @@ -2428,7 +2456,7 @@ bridge_rtnode_insert(struct bridge_softc KASSERT(BRIDGE_RT_LOCKED(sc)); hash = bridge_rthash(sc, brt->brt_addr); - LIST_FOREACH(lbrt, &sc->sc_rthash[hash], brt_hash) { + BRIDGE_RTHASH_WRITER_FOREACH(lbrt, sc, hash) { int dir = memcmp(brt->brt_addr, lbrt->brt_addr, ETHER_ADDR_LEN); if (dir == 0) return EEXIST; @@ -2437,11 +2465,11 @@ bridge_rtnode_insert(struct bridge_softc prev = lbrt; } if (prev == NULL) - LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash); + BRIDGE_RTHASH_WRITER_INSERT_HEAD(sc, hash, brt); else - LIST_INSERT_AFTER(prev, brt, brt_hash); + BRIDGE_RTHASH_WRITER_INSERT_AFTER(prev, brt); - LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list); + BRIDGE_RTLIST_WRITER_INSERT_HEAD(sc, brt); sc->sc_brtcnt++; return 0; @@ -2458,8 +2486,8 @@ bridge_rtnode_remove(struct bridge_softc KASSERT(BRIDGE_RT_LOCKED(sc)); - LIST_REMOVE(brt, brt_hash); - LIST_REMOVE(brt, brt_list); + BRIDGE_RTHASH_WRITER_REMOVE(brt); + BRIDGE_RTLIST_WRITER_REMOVE(brt); sc->sc_brtcnt--; } Index: src/sys/net/if_bridgevar.h diff -u src/sys/net/if_bridgevar.h:1.31 src/sys/net/if_bridgevar.h:1.32 --- src/sys/net/if_bridgevar.h:1.31 Thu Apr 28 00:16:56 2016 +++ src/sys/net/if_bridgevar.h Wed Apr 18 04:01:58 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bridgevar.h,v 1.31 2016/04/28 00:16:56 ozaki-r Exp $ */ +/* $NetBSD: if_bridgevar.h,v 1.32 2018/04/18 04:01:58 ozaki-r Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -275,8 +275,8 @@ struct bridge_iflist { * Bridge route node. */ struct bridge_rtnode { - LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */ - LIST_ENTRY(bridge_rtnode) brt_list; /* list linkage */ + struct pslist_entry brt_hash; /* hash table linkage */ + struct pslist_entry brt_list; /* list linkage */ struct ifnet *brt_ifp; /* destination if */ time_t brt_expire; /* expiration time */ uint8_t brt_flags; /* address flags */ @@ -319,8 +319,8 @@ struct bridge_softc { callout_t sc_brcallout; /* bridge callout */ callout_t sc_bstpcallout; /* STP callout */ struct bridge_iflist_psref sc_iflist_psref; - LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ - LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ + struct pslist_head *sc_rthash; /* our forwarding table */ + struct pslist_head sc_rtlist; /* list version of above */ kmutex_t *sc_rtlist_lock; pserialize_t sc_rtlist_psz; struct workqueue *sc_rtage_wq;