Commit a8c505ff authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller

[BRIDGE}: Change bridge forwarding table to use hlist.

parent 07211edd
...@@ -437,6 +437,10 @@ static __inline__ void hlist_add_before(struct hlist_node *n, struct hlist_node ...@@ -437,6 +437,10 @@ static __inline__ void hlist_add_before(struct hlist_node *n, struct hlist_node
for (pos = (head)->first; pos; \ for (pos = (head)->first; pos; \
pos = pos->next) pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; n = pos ? pos->next : 0, pos; \
pos = n)
#else #else
#warning "don't include kernel headers in userspace" #warning "don't include kernel headers in userspace"
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -68,28 +68,6 @@ static __inline__ int br_mac_hash(unsigned char *mac) ...@@ -68,28 +68,6 @@ static __inline__ int br_mac_hash(unsigned char *mac)
return x & (BR_HASH_SIZE - 1); return x & (BR_HASH_SIZE - 1);
} }
static __inline__ void __hash_link(struct net_bridge *br,
struct net_bridge_fdb_entry *ent,
int hash)
{
ent->next_hash = br->hash[hash];
if (ent->next_hash != NULL)
ent->next_hash->pprev_hash = &ent->next_hash;
br->hash[hash] = ent;
ent->pprev_hash = &br->hash[hash];
}
static __inline__ void __hash_unlink(struct net_bridge_fdb_entry *ent)
{
*(ent->pprev_hash) = ent->next_hash;
if (ent->next_hash != NULL)
ent->next_hash->pprev_hash = ent->pprev_hash;
ent->next_hash = NULL;
ent->pprev_hash = NULL;
}
void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr) void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr)
{ {
struct net_bridge *br; struct net_bridge *br;
...@@ -99,22 +77,24 @@ void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr) ...@@ -99,22 +77,24 @@ void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr)
br = p->br; br = p->br;
write_lock_bh(&br->hash_lock); write_lock_bh(&br->hash_lock);
for (i=0;i<BR_HASH_SIZE;i++) { for (i=0;i<BR_HASH_SIZE;i++) {
struct net_bridge_fdb_entry *f; struct hlist_node *h;
hlist_for_each(h, &br->hash[i]) {
struct net_bridge_fdb_entry *f
= hlist_entry(h, struct net_bridge_fdb_entry, hlist);
f = br->hash[i];
while (f != NULL) {
if (f->dst == p && f->is_local) { if (f->dst == p && f->is_local) {
memcpy(f->addr.addr, newaddr, ETH_ALEN); memcpy(f->addr.addr, newaddr, ETH_ALEN);
if (newhash != i) { if (newhash != i) {
__hash_unlink(f); hlist_del(&f->hlist);
__hash_link(br, f, newhash); hlist_add_head(&f->hlist,
&br->hash[newhash]);
} }
write_unlock_bh(&br->hash_lock); goto out;
return;
} }
f = f->next_hash;
} }
} }
out:
write_unlock_bh(&br->hash_lock); write_unlock_bh(&br->hash_lock);
} }
...@@ -127,19 +107,16 @@ void br_fdb_cleanup(struct net_bridge *br) ...@@ -127,19 +107,16 @@ void br_fdb_cleanup(struct net_bridge *br)
write_lock_bh(&br->hash_lock); write_lock_bh(&br->hash_lock);
for (i=0;i<BR_HASH_SIZE;i++) { for (i=0;i<BR_HASH_SIZE;i++) {
struct net_bridge_fdb_entry *f; struct hlist_node *h, *g;
f = br->hash[i];
while (f != NULL) {
struct net_bridge_fdb_entry *g;
g = f->next_hash; hlist_for_each_safe(h, g, &br->hash[i]) {
struct net_bridge_fdb_entry *f
= hlist_entry(h, struct net_bridge_fdb_entry, hlist);
if (!f->is_static && if (!f->is_static &&
time_before_eq(f->ageing_timer, timeout)) { time_before_eq(f->ageing_timer, timeout)) {
__hash_unlink(f); hlist_del(&f->hlist);
br_fdb_put(f); br_fdb_put(f);
} }
f = g;
} }
} }
write_unlock_bh(&br->hash_lock); write_unlock_bh(&br->hash_lock);
...@@ -151,18 +128,15 @@ void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p) ...@@ -151,18 +128,15 @@ void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
write_lock_bh(&br->hash_lock); write_lock_bh(&br->hash_lock);
for (i=0;i<BR_HASH_SIZE;i++) { for (i=0;i<BR_HASH_SIZE;i++) {
struct net_bridge_fdb_entry *f; struct hlist_node *h, *g;
f = br->hash[i]; hlist_for_each_safe(h, g, &br->hash[i]) {
while (f != NULL) { struct net_bridge_fdb_entry *f
struct net_bridge_fdb_entry *g; = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
g = f->next_hash;
if (f->dst == p) { if (f->dst == p) {
__hash_unlink(f); hlist_del(&f->hlist);
br_fdb_put(f); br_fdb_put(f);
} }
f = g;
} }
} }
write_unlock_bh(&br->hash_lock); write_unlock_bh(&br->hash_lock);
...@@ -170,25 +144,24 @@ void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p) ...@@ -170,25 +144,24 @@ void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, unsigned char *addr) struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, unsigned char *addr)
{ {
struct net_bridge_fdb_entry *fdb; struct hlist_node *h;
read_lock_bh(&br->hash_lock); read_lock_bh(&br->hash_lock);
fdb = br->hash[br_mac_hash(addr)];
while (fdb != NULL) { 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);
if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) { if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
if (!has_expired(br, fdb)) { if (has_expired(br, fdb))
goto ret_null;
atomic_inc(&fdb->use_count); atomic_inc(&fdb->use_count);
read_unlock_bh(&br->hash_lock); read_unlock_bh(&br->hash_lock);
return fdb; return fdb;
} }
read_unlock_bh(&br->hash_lock);
return NULL;
}
fdb = fdb->next_hash;
} }
ret_null:
read_unlock_bh(&br->hash_lock); read_unlock_bh(&br->hash_lock);
return NULL; return NULL;
} }
...@@ -213,12 +186,16 @@ int br_fdb_get_entries(struct net_bridge *br, ...@@ -213,12 +186,16 @@ int br_fdb_get_entries(struct net_bridge *br,
read_lock_bh(&br->hash_lock); read_lock_bh(&br->hash_lock);
for (i=0;i<BR_HASH_SIZE;i++) { for (i=0;i<BR_HASH_SIZE;i++) {
struct net_bridge_fdb_entry *f; struct hlist_node *h;
for (f = br->hash[i]; f != NULL && num < maxnum; hlist_for_each(h, &br->hash[i]) {
f = f->next_hash) { struct net_bridge_fdb_entry *f
= hlist_entry(h, struct net_bridge_fdb_entry, hlist);
struct __fdb_entry ent; struct __fdb_entry ent;
if (num >= maxnum)
goto out;
if (has_expired(br, f)) if (has_expired(br, f))
continue; continue;
...@@ -277,14 +254,15 @@ void br_fdb_insert(struct net_bridge *br, ...@@ -277,14 +254,15 @@ void br_fdb_insert(struct net_bridge *br,
unsigned char *addr, unsigned char *addr,
int is_local) int is_local)
{ {
struct hlist_node *h;
struct net_bridge_fdb_entry *fdb; struct net_bridge_fdb_entry *fdb;
int hash; int hash;
hash = br_mac_hash(addr); hash = br_mac_hash(addr);
write_lock_bh(&br->hash_lock); write_lock_bh(&br->hash_lock);
fdb = br->hash[hash]; hlist_for_each(h, &br->hash[hash]) {
while (fdb != NULL) { fdb = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
if (!fdb->is_local && if (!fdb->is_local &&
!memcmp(fdb->addr.addr, addr, ETH_ALEN)) { !memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
__fdb_possibly_replace(fdb, source, is_local); __fdb_possibly_replace(fdb, source, is_local);
...@@ -292,7 +270,6 @@ void br_fdb_insert(struct net_bridge *br, ...@@ -292,7 +270,6 @@ void br_fdb_insert(struct net_bridge *br,
return; return;
} }
fdb = fdb->next_hash;
} }
fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC); fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);
...@@ -308,7 +285,7 @@ void br_fdb_insert(struct net_bridge *br, ...@@ -308,7 +285,7 @@ void br_fdb_insert(struct net_bridge *br,
fdb->is_static = is_local; fdb->is_static = is_local;
fdb->ageing_timer = jiffies; fdb->ageing_timer = jiffies;
__hash_link(br, fdb, hash); hlist_add_head(&fdb->hlist, &br->hash[hash]);
write_unlock_bh(&br->hash_lock); write_unlock_bh(&br->hash_lock);
} }
...@@ -43,8 +43,7 @@ struct mac_addr ...@@ -43,8 +43,7 @@ struct mac_addr
struct net_bridge_fdb_entry struct net_bridge_fdb_entry
{ {
struct net_bridge_fdb_entry *next_hash; struct hlist_node hlist;
struct net_bridge_fdb_entry **pprev_hash;
atomic_t use_count; atomic_t use_count;
mac_addr addr; mac_addr addr;
struct net_bridge_port *dst; struct net_bridge_port *dst;
...@@ -86,7 +85,7 @@ struct net_bridge ...@@ -86,7 +85,7 @@ struct net_bridge
struct net_device dev; struct net_device dev;
struct net_device_stats statistics; struct net_device_stats statistics;
rwlock_t hash_lock; rwlock_t hash_lock;
struct net_bridge_fdb_entry *hash[BR_HASH_SIZE]; struct hlist_head hash[BR_HASH_SIZE];
struct timer_list tick; struct timer_list tick;
/* STP */ /* STP */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment