diff --git a/sys/netgraph/bridge/ng_bridge.c b/sys/netgraph/bridge/ng_bridge.c --- a/sys/netgraph/bridge/ng_bridge.c +++ b/sys/netgraph/bridge/ng_bridge.c @@ -99,6 +99,7 @@ u_int hashMask; /* numBuckets - 1 */ int numLinks; /* num connected links */ struct callout timer; /* one second periodic timer */ + struct lock lock; /* SMP serialization: protects tab, numHosts, numBuckets, hashMask */ }; typedef struct ng_bridge_private *priv_p; @@ -305,6 +306,7 @@ if (priv == NULL) return (ENOMEM); callout_init(&priv->timer); + lockinit(&priv->lock, "ng_bridge", 0, LK_CANRECURSE); /* Allocate and initialize hash table, etc. */ priv->tab = kmalloc(MIN_BUCKETS * sizeof(*priv->tab), M_NETGRAPH, @@ -784,10 +786,14 @@ const int bucket = HASH(addr, priv->hashMask); struct ng_bridge_hent *hent; + lockmgr(&priv->lock, LK_SHARED); SLIST_FOREACH(hent, &priv->tab[bucket], next) { - if (ETHER_EQUAL(hent->host.addr, addr)) + if (ETHER_EQUAL(hent->host.addr, addr)) { + lockmgr(&priv->lock, LK_RELEASE); return (&hent->host); + } } + lockmgr(&priv->lock, LK_RELEASE); return (NULL); } @@ -802,6 +808,7 @@ const int bucket = HASH(addr, priv->hashMask); struct ng_bridge_hent *hent; + lockmgr(&priv->lock, LK_EXCLUSIVE); #ifdef INVARIANTS char ethstr[ETHER_ADDRSTRLEN + 1]; @@ -814,8 +821,10 @@ /* Allocate and initialize new hashtable entry */ hent = kmalloc(sizeof(*hent), M_NETGRAPH, M_NOWAIT); - if (hent == NULL) + if (hent == NULL) { + lockmgr(&priv->lock, LK_RELEASE); return (0); + } bcopy(addr, hent->host.addr, ETHER_ADDR_LEN); hent->host.linkNum = linkNum; hent->host.staleness = 0; @@ -827,6 +836,7 @@ /* Resize table if necessary */ ng_bridge_rehash(priv); + lockmgr(&priv->lock, LK_RELEASE); return (1); } @@ -949,6 +959,10 @@ /* Register a new timeout, keeping the existing node reference */ callout_reset(&priv->timer, hz, ng_bridge_timeout, node); + /* Hold the per-node lock across the sweep + rehash so concurrent + * rcvdata -> ng_bridge_put/get cannot mutate the table mid-sweep. */ + lockmgr(&priv->lock, LK_EXCLUSIVE); + /* Update host time counters and remove stale entries */ for (bucket = 0; bucket < priv->numBuckets; bucket++) { struct ng_bridge_hent **hptr = &SLIST_FIRST(&priv->tab[bucket]); @@ -981,6 +995,8 @@ /* Decrease table size if necessary */ ng_bridge_rehash(priv); + lockmgr(&priv->lock, LK_RELEASE); + /* Decrease loop counter on muted looped back links */ for (counter = linkNum = 0; linkNum < NG_BRIDGE_MAX_LINKS; linkNum++) { struct ng_bridge_link *const link = priv->links[linkNum];