Module Name: src Committed By: martin Date: Wed Apr 18 14:11:43 UTC 2018
Modified Files: src/sys/net [netbsd-8]: if_bridge.c if_bridgevar.h src/tests/net/if_bridge [netbsd-8]: t_rtable.sh Log Message: Pull up following revision(s) (requested by ozaki-r in ticket #777): tests/net/if_bridge/t_rtable.sh: revision 1.3 sys/net/if_bridge.c: revision 1.150-1.154 sys/net/if_bridgevar.h: revision 1.32 Remove obsolete NULL checks Simplify bridge_rtnode_insert (NFC) 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. Add a test that checks if brconfig flush surely removes all entries Get rid of a unnecessary semicolon Pointed out by kamil@ Add missing PSLIST_ENTRY_INIT and PSLIST_ENTRY_DESTROY To generate a diff of this commit: cvs rdiff -u -r1.134.6.8 -r1.134.6.9 src/sys/net/if_bridge.c cvs rdiff -u -r1.31 -r1.31.10.1 src/sys/net/if_bridgevar.h cvs rdiff -u -r1.1.8.1 -r1.1.8.2 src/tests/net/if_bridge/t_rtable.sh 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.134.6.8 src/sys/net/if_bridge.c:1.134.6.9 --- src/sys/net/if_bridge.c:1.134.6.8 Tue Apr 10 11:48:29 2018 +++ src/sys/net/if_bridge.c Wed Apr 18 14:11:42 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bridge.c,v 1.134.6.8 2018/04/10 11:48:29 martin Exp $ */ +/* $NetBSD: if_bridge.c,v 1.134.6.9 2018/04/18 14:11:42 martin Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -80,7 +80,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.134.6.8 2018/04/10 11:48:29 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_bridge.c,v 1.134.6.9 2018/04/18 14:11:42 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_bridge_ipf.h" @@ -181,20 +181,39 @@ __CTASSERT(offsetof(struct ifbifconf, if #define BRIDGE_RTABLE_PRUNE_PERIOD (5 * 60) #endif -#define BRIDGE_RT_LOCK(_sc) if ((_sc)->sc_rtlist_lock) \ - mutex_enter((_sc)->sc_rtlist_lock) -#define BRIDGE_RT_UNLOCK(_sc) if ((_sc)->sc_rtlist_lock) \ - mutex_exit((_sc)->sc_rtlist_lock) -#define BRIDGE_RT_LOCKED(_sc) (!(_sc)->sc_rtlist_lock || \ - mutex_owned((_sc)->sc_rtlist_lock)) +#define BRIDGE_RT_LOCK(_sc) mutex_enter((_sc)->sc_rtlist_lock) +#define BRIDGE_RT_UNLOCK(_sc) mutex_exit((_sc)->sc_rtlist_lock) +#define BRIDGE_RT_LOCKED(_sc) mutex_owned((_sc)->sc_rtlist_lock) #define BRIDGE_RT_PSZ_PERFORM(_sc) \ - if ((_sc)->sc_rtlist_psz != NULL) \ - pserialize_perform((_sc)->sc_rtlist_psz); + pserialize_perform((_sc)->sc_rtlist_psz) #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 @@ -1043,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)); @@ -2013,6 +2032,8 @@ bridge_rtalloc(struct bridge_softc *sc, brt->brt_expire = time_uptime + sc->sc_brttimeout; brt->brt_flags = IFBAF_DYNAMIC; memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN); + PSLIST_ENTRY_INIT(brt, brt_list); + PSLIST_ENTRY_INIT(brt, brt_hash); BRIDGE_RT_LOCK(sc); error = bridge_rtnode_insert(sc, brt); @@ -2109,7 +2130,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; @@ -2128,7 +2149,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); @@ -2302,7 +2328,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; } @@ -2333,11 +2359,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); @@ -2353,10 +2379,8 @@ bridge_rtable_fini(struct bridge_softc * { kmem_free(sc->sc_rthash, sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE); - if (sc->sc_rtlist_lock) - mutex_obj_free(sc->sc_rtlist_lock); - if (sc->sc_rtlist_psz) - pserialize_destroy(sc->sc_rtlist_psz); + mutex_obj_free(sc->sc_rtlist_lock); + pserialize_destroy(sc->sc_rtlist_psz); } /* @@ -2408,7 +2432,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,41 +2452,26 @@ bridge_rtnode_lookup(struct bridge_softc static int bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) { - struct bridge_rtnode *lbrt; + struct bridge_rtnode *lbrt, *prev = NULL; uint32_t hash; - int dir; KASSERT(BRIDGE_RT_LOCKED(sc)); hash = bridge_rthash(sc, brt->brt_addr); - - lbrt = LIST_FIRST(&sc->sc_rthash[hash]); - if (lbrt == NULL) { - LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash); - goto out; - } - - do { - dir = memcmp(brt->brt_addr, lbrt->brt_addr, ETHER_ADDR_LEN); + BRIDGE_RTHASH_WRITER_FOREACH(lbrt, sc, hash) { + int dir = memcmp(brt->brt_addr, lbrt->brt_addr, ETHER_ADDR_LEN); if (dir == 0) return EEXIST; - if (dir > 0) { - LIST_INSERT_BEFORE(lbrt, brt, brt_hash); - goto out; - } - if (LIST_NEXT(lbrt, brt_hash) == NULL) { - LIST_INSERT_AFTER(lbrt, brt, brt_hash); - goto out; - } - lbrt = LIST_NEXT(lbrt, brt_hash); - } while (lbrt != NULL); - -#ifdef DIAGNOSTIC - panic("bridge_rtnode_insert: impossible"); -#endif + if (dir > 0) + break; + prev = lbrt; + } + if (prev == NULL) + BRIDGE_RTHASH_WRITER_INSERT_HEAD(sc, hash, brt); + else + BRIDGE_RTHASH_WRITER_INSERT_AFTER(prev, brt); - out: - LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list); + BRIDGE_RTLIST_WRITER_INSERT_HEAD(sc, brt); sc->sc_brtcnt++; return 0; @@ -2479,8 +2488,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--; } @@ -2493,6 +2502,8 @@ static void bridge_rtnode_destroy(struct bridge_rtnode *brt) { + PSLIST_ENTRY_DESTROY(brt, brt_list); + PSLIST_ENTRY_DESTROY(brt, brt_hash); pool_put(&bridge_rtnode_pool, brt); } Index: src/sys/net/if_bridgevar.h diff -u src/sys/net/if_bridgevar.h:1.31 src/sys/net/if_bridgevar.h:1.31.10.1 --- src/sys/net/if_bridgevar.h:1.31 Thu Apr 28 00:16:56 2016 +++ src/sys/net/if_bridgevar.h Wed Apr 18 14:11:42 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.31.10.1 2018/04/18 14:11:42 martin 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; Index: src/tests/net/if_bridge/t_rtable.sh diff -u src/tests/net/if_bridge/t_rtable.sh:1.1.8.1 src/tests/net/if_bridge/t_rtable.sh:1.1.8.2 --- src/tests/net/if_bridge/t_rtable.sh:1.1.8.1 Tue Apr 10 11:48:28 2018 +++ src/tests/net/if_bridge/t_rtable.sh Wed Apr 18 14:11:42 2018 @@ -1,4 +1,4 @@ -# $NetBSD: t_rtable.sh,v 1.1.8.1 2018/04/10 11:48:28 martin Exp $ +# $NetBSD: t_rtable.sh,v 1.1.8.2 2018/04/18 14:11:42 martin Exp $ # # Copyright (c) 2017 Internet Initiative Japan Inc. # All rights reserved. @@ -169,6 +169,7 @@ bridge_rtable_flush_head() bridge_rtable_flush_body() { local addr1= addr3= + local n= setup setup_bridge @@ -195,6 +196,34 @@ bridge_rtable_flush_body() atf_check -s exit:0 -o not-match:"$addr3 shmif1" /sbin/brconfig bridge0 unset LD_PRELOAD + # Add extra interfaces and addresses + export RUMP_SERVER=$SOCK1 + rump_server_add_iface $SOCK1 shmif1 bus1 + atf_check -s exit:0 rump.ifconfig shmif1 10.0.0.11/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + export RUMP_SERVER=$SOCK3 + rump_server_add_iface $SOCK3 shmif1 bus2 + atf_check -s exit:0 rump.ifconfig shmif1 10.0.0.12/24 + atf_check -s exit:0 rump.ifconfig -w 10 + + # Let cache entries + export RUMP_SERVER=$SOCK1 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 10.0.0.12 + export RUMP_SERVER=$SOCK3 + atf_check -s exit:0 -o ignore rump.ping -n -w $TIMEOUT -c 1 10.0.0.11 + + export RUMP_SERVER=$SOCK2 + export LD_PRELOAD=/usr/lib/librumphijack.so + $DEBUG && /sbin/brconfig bridge0 + n=$(get_number_of_caches) + atf_check_equal $n 4 + + atf_check -s exit:0 -o ignore /sbin/brconfig bridge0 flush + n=$(get_number_of_caches) + atf_check_equal $n 0 + unset LD_PRELOAD + rump_server_destroy_ifaces }