diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2004-07-29 00:41:06 -0700 |
---|---|---|
committer | David S. Miller <davem@nuts.davemloft.net> | 2004-07-29 00:41:06 -0700 |
commit | 2f7b3472996d11ad495e688c6faea305a93e61d2 (patch) | |
tree | 20be208b938f0c6e940eabb0fa63256ab7017779 /net | |
parent | 87dd39edb1d4537f13562b18da0f34cab711633e (diff) | |
download | history-2f7b3472996d11ad495e688c6faea305a93e61d2.tar.gz |
[BRIDGE]: forwarding table RCU
Convert the bridge forwarding database over to using RCU.
This avoids a read_lock and atomic_inc/dec in the fast path
of output.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: David S. Miller <davem@redhat.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_device.c | 5 | ||||
-rw-r--r-- | net/bridge/br_fdb.c | 63 | ||||
-rw-r--r-- | net/bridge/br_if.c | 2 | ||||
-rw-r--r-- | net/bridge/br_input.c | 4 | ||||
-rw-r--r-- | net/bridge/br_private.h | 6 |
5 files changed, 44 insertions, 36 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 2d0838456dc46a..44a976221318f4 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -43,10 +43,9 @@ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev) rcu_read_lock(); if (dest[0] & 1) br_flood_deliver(br, skb, 0); - else if ((dst = br_fdb_get(br, dest)) != NULL) { + else if ((dst = __br_fdb_get(br, dest)) != NULL) br_deliver(dst->dst, skb); - br_fdb_put(dst); - } else + else br_flood_deliver(br, skb, 0); rcu_read_unlock(); diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 324489dda89a5c..60b9697d18614d 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -73,7 +73,7 @@ static __inline__ int br_mac_hash(const unsigned char *mac) static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f) { - hlist_del(&f->hlist); + hlist_del_rcu(&f->hlist); if (!f->is_static) list_del(&f->age_list); @@ -85,7 +85,7 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) struct net_bridge *br = p->br; int i; - write_lock_bh(&br->hash_lock); + spin_lock_bh(&br->hash_lock); /* Search all chains since old address/hash is unknown */ for (i = 0; i < BR_HASH_SIZE; i++) { @@ -117,7 +117,7 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) fdb_insert(br, p, newaddr, 1); - write_unlock_bh(&br->hash_lock); + spin_unlock_bh(&br->hash_lock); } void br_fdb_cleanup(unsigned long _data) @@ -126,7 +126,7 @@ void br_fdb_cleanup(unsigned long _data) struct list_head *l, *n; unsigned long delay; - write_lock_bh(&br->hash_lock); + spin_lock_bh(&br->hash_lock); delay = hold_time(br); list_for_each_safe(l, n, &br->age_list) { @@ -144,14 +144,14 @@ void br_fdb_cleanup(unsigned long _data) break; } } - write_unlock_bh(&br->hash_lock); + spin_unlock_bh(&br->hash_lock); } void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p) { int i; - write_lock_bh(&br->hash_lock); + spin_lock_bh(&br->hash_lock); for (i = 0; i < BR_HASH_SIZE; i++) { struct hlist_node *h, *g; @@ -182,33 +182,42 @@ void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p) skip_delete: ; } } - write_unlock_bh(&br->hash_lock); + spin_unlock_bh(&br->hash_lock); } -struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, unsigned char *addr) +/* No locking or refcounting, assumes caller has no preempt (rcu_read_lock) */ +struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, + const unsigned char *addr) { struct hlist_node *h; + struct net_bridge_fdb_entry *fdb; - read_lock_bh(&br->hash_lock); - - hlist_for_each(h, &br->hash[br_mac_hash(addr)]) { - struct net_bridge_fdb_entry *fdb - = hlist_entry(h, struct net_bridge_fdb_entry, hlist); - + hlist_for_each_entry_rcu(fdb, h, &br->hash[br_mac_hash(addr)], hlist) { if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) { - if (has_expired(br, fdb)) - goto ret_null; - - atomic_inc(&fdb->use_count); - read_unlock_bh(&br->hash_lock); + if (unlikely(has_expired(br, fdb))) + break; return fdb; } } - ret_null: - read_unlock_bh(&br->hash_lock); + return NULL; } +/* Interface used by ATM hook that keeps a ref count */ +struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, + unsigned char *addr) +{ + struct net_bridge_fdb_entry *fdb; + + rcu_read_lock(); + fdb = __br_fdb_get(br, addr); + if (fdb) + atomic_inc(&fdb->use_count); + rcu_read_unlock(); + return fdb; +} + + void br_fdb_put(struct net_bridge_fdb_entry *ent) { if (atomic_dec_and_test(&ent->use_count)) @@ -229,9 +238,9 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf, memset(buf, 0, maxnum*sizeof(struct __fdb_entry)); - read_lock_bh(&br->hash_lock); + rcu_read_lock(); for (i = 0; i < BR_HASH_SIZE; i++) { - hlist_for_each_entry(f, h, &br->hash[i], hlist) { + hlist_for_each_entry_rcu(f, h, &br->hash[i], hlist) { if (num >= maxnum) goto out; @@ -255,7 +264,7 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf, } out: - read_unlock_bh(&br->hash_lock); + rcu_read_unlock(); return num; } @@ -309,7 +318,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, memcpy(fdb->addr.addr, addr, ETH_ALEN); atomic_set(&fdb->use_count, 1); - hlist_add_head(&fdb->hlist, &br->hash[hash]); + hlist_add_head_rcu(&fdb->hlist, &br->hash[hash]); if (!timer_pending(&br->gc_timer)) { br->gc_timer.expires = jiffies + hold_time(br); @@ -332,8 +341,8 @@ int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, { int ret; - write_lock_bh(&br->hash_lock); + spin_lock_bh(&br->hash_lock); ret = fdb_insert(br, source, addr, is_local); - write_unlock_bh(&br->hash_lock); + spin_unlock_bh(&br->hash_lock); return ret; } diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 588ead0731b0fd..43d03734e48b2b 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -149,7 +149,7 @@ static struct net_device *new_bridge_dev(const char *name) br->lock = SPIN_LOCK_UNLOCKED; INIT_LIST_HEAD(&br->port_list); - br->hash_lock = RW_LOCK_UNLOCKED; + br->hash_lock = SPIN_LOCK_UNLOCKED; br->bridge_id.prio[0] = 0x80; br->bridge_id.prio[1] = 0x00; diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 1c714c6860e759..fdf8f9eb6554e0 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -83,19 +83,17 @@ int br_handle_frame_finish(struct sk_buff *skb) goto out; } - dst = br_fdb_get(br, dest); + dst = __br_fdb_get(br, dest); if (dst != NULL && dst->is_local) { if (!passedup) br_pass_frame_up(br, skb); else kfree_skb(skb); - br_fdb_put(dst); goto out; } if (dst != NULL) { br_forward(dst->dst, skb); - br_fdb_put(dst); goto out; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1064d4c68c2412..b9ce6a40a30153 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -86,7 +86,7 @@ struct net_bridge struct list_head port_list; struct net_device *dev; struct net_device_stats statistics; - rwlock_t hash_lock; + spinlock_t hash_lock; struct hlist_head hash[BR_HASH_SIZE]; struct list_head age_list; @@ -136,8 +136,10 @@ extern void br_fdb_changeaddr(struct net_bridge_port *p, extern void br_fdb_cleanup(unsigned long arg); extern void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p); +extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, + const unsigned char *addr); extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, - unsigned char *addr); + unsigned char *addr); extern void br_fdb_put(struct net_bridge_fdb_entry *ent); extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, unsigned long off); |