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

[BRIDGE]: Bridge timer performance enhancement.

parent 217ccfa0
...@@ -20,25 +20,19 @@ ...@@ -20,25 +20,19 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "br_private.h" #include "br_private.h"
static __inline__ unsigned long __timeout(struct net_bridge *br) /* if topology_changing then use forward_delay (default 15 sec)
* otherwise keep longer (default 5 minutes)
*/
static __inline__ unsigned long hold_time(const struct net_bridge *br)
{ {
unsigned long timeout; return br->topology_change ? br->forward_delay : br->ageing_time;
timeout = jiffies - br->ageing_time;
if (br->topology_change)
timeout = jiffies - br->forward_delay;
return timeout;
} }
static __inline__ int has_expired(struct net_bridge *br, static __inline__ int has_expired(const struct net_bridge *br,
struct net_bridge_fdb_entry *fdb) const struct net_bridge_fdb_entry *fdb)
{ {
if (!fdb->is_static && return !fdb->is_static
time_before_eq(fdb->ageing_timer, __timeout(br))) && time_before_eq(fdb->ageing_timer + hold_time(br), jiffies);
return 1;
return 0;
} }
static __inline__ void copy_fdb(struct __fdb_entry *ent, static __inline__ void copy_fdb(struct __fdb_entry *ent,
...@@ -52,7 +46,7 @@ static __inline__ void copy_fdb(struct __fdb_entry *ent, ...@@ -52,7 +46,7 @@ static __inline__ void copy_fdb(struct __fdb_entry *ent,
: ((jiffies - f->ageing_timer) * USER_HZ) / HZ; : ((jiffies - f->ageing_timer) * USER_HZ) / HZ;
} }
static __inline__ int br_mac_hash(unsigned char *mac) static __inline__ int br_mac_hash(const unsigned char *mac)
{ {
unsigned long x; unsigned long x;
...@@ -68,7 +62,14 @@ static __inline__ int br_mac_hash(unsigned char *mac) ...@@ -68,7 +62,14 @@ static __inline__ int br_mac_hash(unsigned char *mac)
return x & (BR_HASH_SIZE - 1); return x & (BR_HASH_SIZE - 1);
} }
void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr) static __inline__ void fdb_delete(struct net_bridge_fdb_entry *f)
{
hlist_del(&f->hlist);
list_del(&f->age_list);
br_fdb_put(f);
}
void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
{ {
struct net_bridge *br; struct net_bridge *br;
int i; int i;
...@@ -98,25 +99,29 @@ void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr) ...@@ -98,25 +99,29 @@ void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr)
write_unlock_bh(&br->hash_lock); write_unlock_bh(&br->hash_lock);
} }
void br_fdb_cleanup(struct net_bridge *br) void br_fdb_cleanup(unsigned long _data)
{ {
int i; struct net_bridge *br = (struct net_bridge *)_data;
unsigned long timeout; struct list_head *l, *n;
unsigned long delay;
timeout = __timeout(br);
write_lock_bh(&br->hash_lock); write_lock_bh(&br->hash_lock);
for (i=0;i<BR_HASH_SIZE;i++) { delay = hold_time(br);
struct hlist_node *h, *g;
list_for_each_safe(l, n, &br->age_list) {
hlist_for_each_safe(h, g, &br->hash[i]) { struct net_bridge_fdb_entry *f
struct net_bridge_fdb_entry *f = list_entry(l, struct net_bridge_fdb_entry, age_list);
= hlist_entry(h, struct net_bridge_fdb_entry, hlist); unsigned long expires = f->ageing_timer + delay;
if (!f->is_static &&
time_before_eq(f->ageing_timer, timeout)) { if (time_before_eq(expires, jiffies)) {
hlist_del(&f->hlist); if (!f->is_static) {
br_fdb_put(f); pr_debug("expire age %lu jiffies %lu\n",
f->ageing_timer, jiffies);
fdb_delete(f);
} }
} else {
mod_timer(&br->gc_timer, expires);
break;
} }
} }
write_unlock_bh(&br->hash_lock); write_unlock_bh(&br->hash_lock);
...@@ -134,8 +139,7 @@ void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p) ...@@ -134,8 +139,7 @@ void br_fdb_delete_by_port(struct net_bridge *br, struct net_bridge_port *p)
struct net_bridge_fdb_entry *f struct net_bridge_fdb_entry *f
= hlist_entry(h, struct net_bridge_fdb_entry, hlist); = hlist_entry(h, struct net_bridge_fdb_entry, hlist);
if (f->dst == p) { if (f->dst == p) {
hlist_del(&f->hlist); fdb_delete(f);
br_fdb_put(f);
} }
} }
} }
...@@ -237,55 +241,46 @@ int br_fdb_get_entries(struct net_bridge *br, ...@@ -237,55 +241,46 @@ int br_fdb_get_entries(struct net_bridge *br,
return num; return num;
} }
static __inline__ void __fdb_possibly_replace(struct net_bridge_fdb_entry *fdb, void br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
struct net_bridge_port *source, const unsigned char *addr, int is_local)
int is_local)
{
if (!fdb->is_static || is_local) {
fdb->dst = source;
fdb->is_local = is_local;
fdb->is_static = is_local;
fdb->ageing_timer = jiffies;
}
}
void br_fdb_insert(struct net_bridge *br,
struct net_bridge_port *source,
unsigned char *addr,
int is_local)
{ {
struct hlist_node *h; struct hlist_node *h;
struct net_bridge_fdb_entry *fdb; struct net_bridge_fdb_entry *fdb;
int hash; int hash = br_mac_hash(addr);
hash = br_mac_hash(addr);
write_lock_bh(&br->hash_lock); write_lock_bh(&br->hash_lock);
hlist_for_each(h, &br->hash[hash]) { hlist_for_each(h, &br->hash[hash]) {
fdb = hlist_entry(h, struct net_bridge_fdb_entry, hlist); 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); if (likely(!fdb->is_static || is_local)) {
write_unlock_bh(&br->hash_lock); /* move to end of age list */
return; list_del(&fdb->age_list);
goto update;
}
goto out;
} }
} }
fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC); fdb = kmalloc(sizeof(*fdb), GFP_ATOMIC);
if (fdb == NULL) { if (fdb == NULL)
write_unlock_bh(&br->hash_lock); goto out;
return;
}
memcpy(fdb->addr.addr, addr, ETH_ALEN); memcpy(fdb->addr.addr, addr, ETH_ALEN);
atomic_set(&fdb->use_count, 1); atomic_set(&fdb->use_count, 1);
hlist_add_head(&fdb->hlist, &br->hash[hash]);
if (!timer_pending(&br->gc_timer)) {
br->gc_timer.expires = jiffies + hold_time(br);
add_timer(&br->gc_timer);
}
update:
fdb->dst = source; fdb->dst = source;
fdb->is_local = is_local; fdb->is_local = is_local;
fdb->is_static = is_local; fdb->is_static = is_local;
fdb->ageing_timer = jiffies; fdb->ageing_timer = jiffies;
list_add_tail(&fdb->age_list, &br->age_list);
hlist_add_head(&fdb->hlist, &br->hash[hash]); out:
write_unlock_bh(&br->hash_lock); write_unlock_bh(&br->hash_lock);
} }
...@@ -84,8 +84,6 @@ static struct net_bridge *new_nb(const char *name) ...@@ -84,8 +84,6 @@ static struct net_bridge *new_nb(const char *name)
memset(br, 0, sizeof(*br)); memset(br, 0, sizeof(*br));
dev = &br->dev; dev = &br->dev;
init_timer(&br->tick);
strncpy(dev->name, name, IFNAMSIZ); strncpy(dev->name, name, IFNAMSIZ);
dev->priv = br; dev->priv = br;
dev->priv_flags = IFF_EBRIDGE; dev->priv_flags = IFF_EBRIDGE;
...@@ -109,12 +107,10 @@ static struct net_bridge *new_nb(const char *name) ...@@ -109,12 +107,10 @@ static struct net_bridge *new_nb(const char *name)
br->bridge_forward_delay = br->forward_delay = 15 * HZ; br->bridge_forward_delay = br->forward_delay = 15 * HZ;
br->topology_change = 0; br->topology_change = 0;
br->topology_change_detected = 0; br->topology_change_detected = 0;
br_timer_clear(&br->hello_timer);
br_timer_clear(&br->tcn_timer);
br_timer_clear(&br->topology_change_timer);
br->ageing_time = 300 * HZ; br->ageing_time = 300 * HZ;
br->gc_interval = 4 * HZ; INIT_LIST_HEAD(&br->age_list);
br_stp_timer_init(br);
return br; return br;
} }
......
...@@ -32,9 +32,10 @@ static inline unsigned long ticks_to_user(unsigned long tick) ...@@ -32,9 +32,10 @@ static inline unsigned long ticks_to_user(unsigned long tick)
} }
/* Report time remaining in user HZ */ /* Report time remaining in user HZ */
static unsigned long timer_residue(const struct br_timer *timer) static unsigned long timer_residue(const struct timer_list *timer)
{ {
return ticks_to_user(timer->running ? (jiffies - timer->expires) : 0); return ticks_to_user(timer_pending(timer)
? (timer->expires - jiffies) : 0);
} }
static int br_ioctl_device(struct net_bridge *br, static int br_ioctl_device(struct net_bridge *br,
...@@ -87,7 +88,6 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -87,7 +88,6 @@ static int br_ioctl_device(struct net_bridge *br,
b.root_port = br->root_port; b.root_port = br->root_port;
b.stp_enabled = br->stp_enabled; b.stp_enabled = br->stp_enabled;
b.ageing_time = ticks_to_user(br->ageing_time); b.ageing_time = ticks_to_user(br->ageing_time);
b.gc_interval = ticks_to_user(br->gc_interval);
b.hello_timer_value = timer_residue(&br->hello_timer); b.hello_timer_value = timer_residue(&br->hello_timer);
b.tcn_timer_value = timer_residue(&br->tcn_timer); b.tcn_timer_value = timer_residue(&br->tcn_timer);
b.topology_change_timer_value = timer_residue(&br->topology_change_timer); b.topology_change_timer_value = timer_residue(&br->topology_change_timer);
...@@ -146,8 +146,7 @@ static int br_ioctl_device(struct net_bridge *br, ...@@ -146,8 +146,7 @@ static int br_ioctl_device(struct net_bridge *br,
br->ageing_time = user_to_ticks(arg0); br->ageing_time = user_to_ticks(arg0);
return 0; return 0;
case BRCTL_SET_GC_INTERVAL: case BRCTL_SET_GC_INTERVAL: /* no longer used */
br->gc_interval = user_to_ticks(arg0);
return 0; return 0;
case BRCTL_GET_PORT_INFO: case BRCTL_GET_PORT_INFO:
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include "br_private_timer.h"
#define BR_HASH_BITS 8 #define BR_HASH_BITS 8
#define BR_HASH_SIZE (1 << BR_HASH_BITS) #define BR_HASH_SIZE (1 << BR_HASH_BITS)
...@@ -44,10 +43,11 @@ struct mac_addr ...@@ -44,10 +43,11 @@ struct mac_addr
struct net_bridge_fdb_entry struct net_bridge_fdb_entry
{ {
struct hlist_node hlist; struct hlist_node hlist;
atomic_t use_count;
mac_addr addr;
struct net_bridge_port *dst; struct net_bridge_port *dst;
struct list_head age_list;
atomic_t use_count;
unsigned long ageing_timer; unsigned long ageing_timer;
mac_addr addr;
unsigned is_local:1; unsigned is_local:1;
unsigned is_static:1; unsigned is_static:1;
}; };
...@@ -71,10 +71,9 @@ struct net_bridge_port ...@@ -71,10 +71,9 @@ struct net_bridge_port
unsigned config_pending:1; unsigned config_pending:1;
int priority; int priority;
struct br_timer forward_delay_timer; struct timer_list forward_delay_timer;
struct br_timer hold_timer; struct timer_list hold_timer;
struct br_timer message_age_timer; struct timer_list message_age_timer;
struct rcu_head rcu; struct rcu_head rcu;
}; };
...@@ -86,7 +85,7 @@ struct net_bridge ...@@ -86,7 +85,7 @@ struct net_bridge
struct net_device_stats statistics; struct net_device_stats statistics;
rwlock_t hash_lock; rwlock_t hash_lock;
struct hlist_head hash[BR_HASH_SIZE]; struct hlist_head hash[BR_HASH_SIZE];
struct timer_list tick; struct list_head age_list;
/* STP */ /* STP */
bridge_id designated_root; bridge_id designated_root;
...@@ -103,13 +102,12 @@ struct net_bridge ...@@ -103,13 +102,12 @@ struct net_bridge
unsigned topology_change:1; unsigned topology_change:1;
unsigned topology_change_detected:1; unsigned topology_change_detected:1;
struct br_timer hello_timer; struct timer_list hello_timer;
struct br_timer tcn_timer; struct timer_list tcn_timer;
struct br_timer topology_change_timer; struct timer_list topology_change_timer;
struct br_timer gc_timer; struct timer_list gc_timer;
int ageing_time; int ageing_time;
int gc_interval;
}; };
extern struct notifier_block br_device_notifier; extern struct notifier_block br_device_notifier;
...@@ -128,8 +126,8 @@ extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev); ...@@ -128,8 +126,8 @@ extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
/* br_fdb.c */ /* br_fdb.c */
extern void br_fdb_changeaddr(struct net_bridge_port *p, extern void br_fdb_changeaddr(struct net_bridge_port *p,
unsigned char *newaddr); const unsigned char *newaddr);
extern void br_fdb_cleanup(struct net_bridge *br); extern void br_fdb_cleanup(unsigned long arg);
extern void br_fdb_delete_by_port(struct net_bridge *br, extern void br_fdb_delete_by_port(struct net_bridge *br,
struct net_bridge_port *p); struct net_bridge_port *p);
extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br, extern struct net_bridge_fdb_entry *br_fdb_get(struct net_bridge *br,
...@@ -140,9 +138,9 @@ extern int br_fdb_get_entries(struct net_bridge *br, ...@@ -140,9 +138,9 @@ extern int br_fdb_get_entries(struct net_bridge *br,
int maxnum, int maxnum,
int offset); int offset);
extern void br_fdb_insert(struct net_bridge *br, extern void br_fdb_insert(struct net_bridge *br,
struct net_bridge_port *source, struct net_bridge_port *source,
unsigned char *addr, const unsigned char *addr,
int is_local); int is_local);
/* br_forward.c */ /* br_forward.c */
extern void br_deliver(const struct net_bridge_port *to, extern void br_deliver(const struct net_bridge_port *to,
...@@ -188,10 +186,10 @@ extern int br_netfilter_init(void); ...@@ -188,10 +186,10 @@ extern int br_netfilter_init(void);
extern void br_netfilter_fini(void); extern void br_netfilter_fini(void);
/* br_stp.c */ /* br_stp.c */
extern void br_log_state(const struct net_bridge_port *p);
extern struct net_bridge_port *br_get_port(struct net_bridge *br, extern struct net_bridge_port *br_get_port(struct net_bridge *br,
int port_no); int port_no);
extern void br_init_port(struct net_bridge_port *p); extern void br_init_port(struct net_bridge_port *p);
extern port_id br_make_port_id(struct net_bridge_port *p);
extern void br_become_designated_port(struct net_bridge_port *p); extern void br_become_designated_port(struct net_bridge_port *p);
/* br_stp_if.c */ /* br_stp_if.c */
...@@ -210,4 +208,8 @@ extern void br_stp_set_path_cost(struct net_bridge_port *p, ...@@ -210,4 +208,8 @@ extern void br_stp_set_path_cost(struct net_bridge_port *p,
/* br_stp_bpdu.c */ /* br_stp_bpdu.c */
extern void br_stp_handle_bpdu(struct sk_buff *skb); extern void br_stp_handle_bpdu(struct sk_buff *skb);
/* br_stp_timer.c */
extern void br_stp_timer_init(struct net_bridge *br);
extern void br_stp_port_timer_init(struct net_bridge_port *p);
#endif #endif
...@@ -47,7 +47,6 @@ extern void br_configuration_update(struct net_bridge *); ...@@ -47,7 +47,6 @@ extern void br_configuration_update(struct net_bridge *);
extern void br_port_state_selection(struct net_bridge *); extern void br_port_state_selection(struct net_bridge *);
extern void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu); extern void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu);
extern void br_received_tcn_bpdu(struct net_bridge_port *p); extern void br_received_tcn_bpdu(struct net_bridge_port *p);
extern void br_tick(unsigned long __data);
extern void br_transmit_config(struct net_bridge_port *p); extern void br_transmit_config(struct net_bridge_port *p);
extern void br_transmit_tcn(struct net_bridge *br); extern void br_transmit_tcn(struct net_bridge *br);
extern void br_topology_change_detection(struct net_bridge *br); extern void br_topology_change_detection(struct net_bridge *br);
......
/*
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*
* $Id: br_private_timer.h,v 1.1 2000/02/18 16:47:13 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _BR_PRIVATE_TIMER_H
#define _BR_PRIVATE_TIMER_H
struct br_timer
{
int running;
unsigned long expires;
};
extern __inline__ void br_timer_clear(struct br_timer *t)
{
t->running = 0;
}
extern __inline__ unsigned long br_timer_get_residue(struct br_timer *t)
{
if (t->running)
return jiffies - t->expires;
return 0;
}
extern __inline__ void br_timer_set(struct br_timer *t, unsigned long x)
{
t->expires = x;
t->running = 1;
}
extern __inline__ int br_timer_is_running(struct br_timer *t)
{
return t->running;
}
extern __inline__ int br_timer_has_expired(struct br_timer *t, unsigned long to)
{
return t->running && time_after_eq(jiffies, t->expires + to);
}
#endif
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
...@@ -20,6 +19,18 @@ ...@@ -20,6 +19,18 @@
#include "br_private.h" #include "br_private.h"
#include "br_private_stp.h" #include "br_private_stp.h"
static const char *br_port_state_names[] = {
"disabled", "learning", "forwarding", "blocking",
};
void br_log_state(const struct net_bridge_port *p)
{
pr_info("%s: port %d(%s) entering %s state\n",
p->br->dev.name, p->port_no, p->dev->name,
br_port_state_names[p->state]);
}
/* called under bridge lock */ /* called under bridge lock */
struct net_bridge_port *br_get_port(struct net_bridge *br, int port_no) struct net_bridge_port *br_get_port(struct net_bridge *br, int port_no)
{ {
...@@ -34,7 +45,8 @@ struct net_bridge_port *br_get_port(struct net_bridge *br, int port_no) ...@@ -34,7 +45,8 @@ struct net_bridge_port *br_get_port(struct net_bridge *br, int port_no)
} }
/* called under bridge lock */ /* called under bridge lock */
static int br_should_become_root_port(struct net_bridge_port *p, int root_port) static int br_should_become_root_port(const struct net_bridge_port *p,
int root_port)
{ {
struct net_bridge *br; struct net_bridge *br;
struct net_bridge_port *rp; struct net_bridge_port *rp;
...@@ -116,9 +128,12 @@ void br_become_root_bridge(struct net_bridge *br) ...@@ -116,9 +128,12 @@ void br_become_root_bridge(struct net_bridge *br)
br->hello_time = br->bridge_hello_time; br->hello_time = br->bridge_hello_time;
br->forward_delay = br->bridge_forward_delay; br->forward_delay = br->bridge_forward_delay;
br_topology_change_detection(br); br_topology_change_detection(br);
br_timer_clear(&br->tcn_timer); del_timer(&br->tcn_timer);
br_config_bpdu_generation(br);
br_timer_set(&br->hello_timer, jiffies); if (br->dev.flags & IFF_UP) {
br_config_bpdu_generation(br);
mod_timer(&br->hello_timer, jiffies + br->hello_time);
}
} }
/* called under bridge lock */ /* called under bridge lock */
...@@ -127,7 +142,8 @@ void br_transmit_config(struct net_bridge_port *p) ...@@ -127,7 +142,8 @@ void br_transmit_config(struct net_bridge_port *p)
struct br_config_bpdu bpdu; struct br_config_bpdu bpdu;
struct net_bridge *br; struct net_bridge *br;
if (br_timer_is_running(&p->hold_timer)) {
if (timer_pending(&p->hold_timer)) {
p->config_pending = 1; p->config_pending = 1;
return; return;
} }
...@@ -142,12 +158,11 @@ void br_transmit_config(struct net_bridge_port *p) ...@@ -142,12 +158,11 @@ void br_transmit_config(struct net_bridge_port *p)
bpdu.port_id = p->port_id; bpdu.port_id = p->port_id;
bpdu.message_age = 0; bpdu.message_age = 0;
if (!br_is_root_bridge(br)) { if (!br_is_root_bridge(br)) {
struct net_bridge_port *root; struct net_bridge_port *root
unsigned long age; = br_get_port(br, br->root_port);
bpdu.max_age = root->message_age_timer.expires - jiffies;
root = br_get_port(br, br->root_port); if (bpdu.max_age <= 0) bpdu.max_age = 1;
age = br_timer_get_residue(&root->message_age_timer) + 1;
bpdu.message_age = age;
} }
bpdu.max_age = br->max_age; bpdu.max_age = br->max_age;
bpdu.hello_time = br->hello_time; bpdu.hello_time = br->hello_time;
...@@ -157,22 +172,26 @@ void br_transmit_config(struct net_bridge_port *p) ...@@ -157,22 +172,26 @@ void br_transmit_config(struct net_bridge_port *p)
p->topology_change_ack = 0; p->topology_change_ack = 0;
p->config_pending = 0; p->config_pending = 0;
br_timer_set(&p->hold_timer, jiffies);
mod_timer(&p->hold_timer, jiffies + BR_HOLD_TIME);
} }
/* called under bridge lock */ /* called under bridge lock */
static void br_record_config_information(struct net_bridge_port *p, struct br_config_bpdu *bpdu) static inline void br_record_config_information(struct net_bridge_port *p,
const struct br_config_bpdu *bpdu)
{ {
p->designated_root = bpdu->root; p->designated_root = bpdu->root;
p->designated_cost = bpdu->root_path_cost; p->designated_cost = bpdu->root_path_cost;
p->designated_bridge = bpdu->bridge_id; p->designated_bridge = bpdu->bridge_id;
p->designated_port = bpdu->port_id; p->designated_port = bpdu->port_id;
br_timer_set(&p->message_age_timer, jiffies - bpdu->message_age); mod_timer(&p->message_age_timer, jiffies
+ (p->br->max_age - bpdu->message_age));
} }
/* called under bridge lock */ /* called under bridge lock */
static void br_record_config_timeout_values(struct net_bridge *br, struct br_config_bpdu *bpdu) static inline void br_record_config_timeout_values(struct net_bridge *br,
const struct br_config_bpdu *bpdu)
{ {
br->max_age = bpdu->max_age; br->max_age = bpdu->max_age;
br->hello_time = bpdu->hello_time; br->hello_time = bpdu->hello_time;
...@@ -187,7 +206,7 @@ void br_transmit_tcn(struct net_bridge *br) ...@@ -187,7 +206,7 @@ void br_transmit_tcn(struct net_bridge *br)
} }
/* called under bridge lock */ /* called under bridge lock */
static int br_should_become_designated_port(struct net_bridge_port *p) static int br_should_become_designated_port(const struct net_bridge_port *p)
{ {
struct net_bridge *br; struct net_bridge *br;
int t; int t;
...@@ -261,25 +280,28 @@ static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_b ...@@ -261,25 +280,28 @@ static int br_supersedes_port_info(struct net_bridge_port *p, struct br_config_b
} }
/* called under bridge lock */ /* called under bridge lock */
static void br_topology_change_acknowledged(struct net_bridge *br) static inline void br_topology_change_acknowledged(struct net_bridge *br)
{ {
br->topology_change_detected = 0; br->topology_change_detected = 0;
br_timer_clear(&br->tcn_timer); del_timer(&br->tcn_timer);
} }
/* called under bridge lock */ /* called under bridge lock */
void br_topology_change_detection(struct net_bridge *br) void br_topology_change_detection(struct net_bridge *br)
{ {
printk(KERN_INFO "%s: topology change detected", br->dev.name); if (!(br->dev.flags & IFF_UP))
return;
pr_info("%s: topology change detected", br->dev.name);
if (br_is_root_bridge(br)) { if (br_is_root_bridge(br)) {
printk(", propagating"); printk(", propagating");
br->topology_change = 1; br->topology_change = 1;
br_timer_set(&br->topology_change_timer, jiffies); mod_timer(&br->topology_change_timer, jiffies
+ br->bridge_forward_delay + br->bridge_max_age);
} else if (!br->topology_change_detected) { } else if (!br->topology_change_detected) {
printk(", sending tcn bpdu"); printk(", sending tcn bpdu");
br_transmit_tcn(br); br_transmit_tcn(br);
br_timer_set(&br->tcn_timer, jiffies); mod_timer(&br->tcn_timer, jiffies + br->bridge_hello_time);
} }
printk("\n"); printk("\n");
...@@ -299,7 +321,7 @@ void br_config_bpdu_generation(struct net_bridge *br) ...@@ -299,7 +321,7 @@ void br_config_bpdu_generation(struct net_bridge *br)
} }
/* called under bridge lock */ /* called under bridge lock */
static void br_reply(struct net_bridge_port *p) static inline void br_reply(struct net_bridge_port *p)
{ {
br_transmit_config(p); br_transmit_config(p);
} }
...@@ -323,6 +345,7 @@ void br_become_designated_port(struct net_bridge_port *p) ...@@ -323,6 +345,7 @@ void br_become_designated_port(struct net_bridge_port *p)
p->designated_port = p->port_id; p->designated_port = p->port_id;
} }
/* called under bridge lock */ /* called under bridge lock */
static void br_make_blocking(struct net_bridge_port *p) static void br_make_blocking(struct net_bridge_port *p)
{ {
...@@ -332,11 +355,9 @@ static void br_make_blocking(struct net_bridge_port *p) ...@@ -332,11 +355,9 @@ static void br_make_blocking(struct net_bridge_port *p)
p->state == BR_STATE_LEARNING) p->state == BR_STATE_LEARNING)
br_topology_change_detection(p->br); br_topology_change_detection(p->br);
printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
p->br->dev.name, p->port_no, p->dev->name, "blocking");
p->state = BR_STATE_BLOCKING; p->state = BR_STATE_BLOCKING;
br_timer_clear(&p->forward_delay_timer); br_log_state(p);
del_timer(&p->forward_delay_timer);
} }
} }
...@@ -345,20 +366,12 @@ static void br_make_forwarding(struct net_bridge_port *p) ...@@ -345,20 +366,12 @@ static void br_make_forwarding(struct net_bridge_port *p)
{ {
if (p->state == BR_STATE_BLOCKING) { if (p->state == BR_STATE_BLOCKING) {
if (p->br->stp_enabled) { if (p->br->stp_enabled) {
printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
p->br->dev.name, p->port_no, p->dev->name,
"listening");
p->state = BR_STATE_LISTENING; p->state = BR_STATE_LISTENING;
} else { } else {
printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
p->br->dev.name, p->port_no, p->dev->name,
"learning");
p->state = BR_STATE_LEARNING; p->state = BR_STATE_LEARNING;
} }
br_timer_set(&p->forward_delay_timer, jiffies); br_log_state(p);
} mod_timer(&p->forward_delay_timer, jiffies + p->br->forward_delay); }
} }
/* called under bridge lock */ /* called under bridge lock */
...@@ -373,7 +386,7 @@ void br_port_state_selection(struct net_bridge *br) ...@@ -373,7 +386,7 @@ void br_port_state_selection(struct net_bridge *br)
p->topology_change_ack = 0; p->topology_change_ack = 0;
br_make_forwarding(p); br_make_forwarding(p);
} else if (br_is_designated_port(p)) { } else if (br_is_designated_port(p)) {
br_timer_clear(&p->message_age_timer); del_timer(&p->message_age_timer);
br_make_forwarding(p); br_make_forwarding(p);
} else { } else {
p->config_pending = 0; p->config_pending = 0;
...@@ -381,11 +394,12 @@ void br_port_state_selection(struct net_bridge *br) ...@@ -381,11 +394,12 @@ void br_port_state_selection(struct net_bridge *br)
br_make_blocking(p); br_make_blocking(p);
} }
} }
} }
} }
/* called under bridge lock */ /* called under bridge lock */
static void br_topology_change_acknowledge(struct net_bridge_port *p) static inline void br_topology_change_acknowledge(struct net_bridge_port *p)
{ {
p->topology_change_ack = 1; p->topology_change_ack = 1;
br_transmit_config(p); br_transmit_config(p);
...@@ -396,20 +410,23 @@ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *b ...@@ -396,20 +410,23 @@ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *b
{ {
struct net_bridge *br; struct net_bridge *br;
int was_root; int was_root;
br = p->br; br = p->br;
was_root = br_is_root_bridge(br); was_root = br_is_root_bridge(br);
if (br_supersedes_port_info(p, bpdu)) { if (br_supersedes_port_info(p, bpdu)) {
br_record_config_information(p, bpdu); br_record_config_information(p, bpdu);
br_configuration_update(br); br_configuration_update(br);
br_port_state_selection(br); br_port_state_selection(br);
if (!br_is_root_bridge(br) && was_root) { if (!br_is_root_bridge(br) && was_root) {
br_timer_clear(&br->hello_timer); del_timer(&br->hello_timer);
if (br->topology_change_detected) { if (br->topology_change_detected) {
br_timer_clear(&br->topology_change_timer); del_timer(&br->topology_change_timer);
br_transmit_tcn(br); br_transmit_tcn(br);
br_timer_set(&br->tcn_timer, jiffies);
mod_timer(&br->tcn_timer,
jiffies + br->bridge_hello_time);
} }
} }
...@@ -428,7 +445,7 @@ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *b ...@@ -428,7 +445,7 @@ void br_received_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *b
void br_received_tcn_bpdu(struct net_bridge_port *p) void br_received_tcn_bpdu(struct net_bridge_port *p)
{ {
if (br_is_designated_port(p)) { if (br_is_designated_port(p)) {
printk(KERN_INFO "%s: received tcn bpdu on port %i(%s)\n", pr_info("%s: received tcn bpdu on port %i(%s)\n",
p->br->dev.name, p->port_no, p->dev->name); p->br->dev.name, p->port_no, p->dev->name);
br_topology_change_detection(p->br); br_topology_change_detection(p->br);
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include "br_private.h" #include "br_private.h"
#include "br_private_stp.h" #include "br_private_stp.h"
__u16 br_make_port_id(struct net_bridge_port *p) static inline __u16 br_make_port_id(const struct net_bridge_port *p)
{ {
return (p->priority << 8) | p->port_no; return (p->priority << 8) | p->port_no;
} }
...@@ -33,33 +33,25 @@ void br_init_port(struct net_bridge_port *p) ...@@ -33,33 +33,25 @@ void br_init_port(struct net_bridge_port *p)
p->state = BR_STATE_BLOCKING; p->state = BR_STATE_BLOCKING;
p->topology_change_ack = 0; p->topology_change_ack = 0;
p->config_pending = 0; p->config_pending = 0;
br_timer_clear(&p->message_age_timer);
br_timer_clear(&p->forward_delay_timer); br_stp_port_timer_init(p);
br_timer_clear(&p->hold_timer);
} }
/* called under bridge lock */ /* called under bridge lock */
void br_stp_enable_bridge(struct net_bridge *br) void br_stp_enable_bridge(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
struct timer_list *timer = &br->tick;
spin_lock_bh(&br->lock); spin_lock_bh(&br->lock);
init_timer(timer); br->hello_timer.expires = jiffies + br->hello_time;
timer->data = (unsigned long) br; add_timer(&br->hello_timer);
timer->function = br_tick;
timer->expires = jiffies + 1;
add_timer(timer);
br_timer_set(&br->hello_timer, jiffies);
br_config_bpdu_generation(br); br_config_bpdu_generation(br);
list_for_each_entry(p, &br->port_list, list) { list_for_each_entry(p, &br->port_list, list) {
if (p->dev->flags & IFF_UP) if (p->dev->flags & IFF_UP)
br_stp_enable_port(p); br_stp_enable_port(p);
}
br_timer_set(&br->gc_timer, jiffies); }
spin_unlock_bh(&br->lock); spin_unlock_bh(&br->lock);
} }
...@@ -68,22 +60,22 @@ void br_stp_disable_bridge(struct net_bridge *br) ...@@ -68,22 +60,22 @@ void br_stp_disable_bridge(struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
spin_lock_bh(&br->lock); spin_lock(&br->lock);
br->topology_change = 0;
br->topology_change_detected = 0;
br_timer_clear(&br->hello_timer);
br_timer_clear(&br->topology_change_timer);
br_timer_clear(&br->tcn_timer);
br_timer_clear(&br->gc_timer);
br_fdb_cleanup(br);
list_for_each_entry(p, &br->port_list, list) { list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED) if (p->state != BR_STATE_DISABLED)
br_stp_disable_port(p); br_stp_disable_port(p);
} }
spin_unlock_bh(&br->lock);
del_timer_sync(&br->tick); br->topology_change = 0;
br->topology_change_detected = 0;
spin_unlock(&br->lock);
del_timer_sync(&br->hello_timer);
del_timer_sync(&br->topology_change_timer);
del_timer_sync(&br->tcn_timer);
del_timer_sync(&br->gc_timer);
} }
/* called under bridge lock */ /* called under bridge lock */
...@@ -108,10 +100,13 @@ void br_stp_disable_port(struct net_bridge_port *p) ...@@ -108,10 +100,13 @@ void br_stp_disable_port(struct net_bridge_port *p)
p->state = BR_STATE_DISABLED; p->state = BR_STATE_DISABLED;
p->topology_change_ack = 0; p->topology_change_ack = 0;
p->config_pending = 0; p->config_pending = 0;
br_timer_clear(&p->message_age_timer);
br_timer_clear(&p->forward_delay_timer); del_timer(&p->message_age_timer);
br_timer_clear(&p->hold_timer); del_timer(&p->forward_delay_timer);
del_timer(&p->hold_timer);
br_configuration_update(br); br_configuration_update(br);
br_port_state_selection(br); br_port_state_selection(br);
if (br_is_root_bridge(br) && !wasroot) if (br_is_root_bridge(br) && !wasroot)
......
...@@ -20,51 +20,59 @@ ...@@ -20,51 +20,59 @@
#include "br_private.h" #include "br_private.h"
#include "br_private_stp.h" #include "br_private_stp.h"
static void dump_bridge_id(bridge_id *id)
{
printk("%.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", id->prio[0],
id->prio[1], id->addr[0], id->addr[1], id->addr[2], id->addr[3],
id->addr[4], id->addr[5]);
}
/* called under bridge lock */ /* called under bridge lock */
static int br_is_designated_for_some_port(struct net_bridge *br) static int br_is_designated_for_some_port(const struct net_bridge *br)
{ {
struct net_bridge_port *p; struct net_bridge_port *p;
list_for_each_entry(p, &br->port_list, list) { list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED && if (p->state != BR_STATE_DISABLED &&
!memcmp(&p->designated_bridge, &br->bridge_id, 8)) !memcmp(&p->designated_bridge, &br->bridge_id, 8))
return 1; return 1;
} }
return 0; return 0;
} }
/* called under bridge lock */ static void br_hello_timer_expired(unsigned long arg)
static void br_hello_timer_expired(struct net_bridge *br)
{ {
br_config_bpdu_generation(br); struct net_bridge *br = (struct net_bridge *)arg;
br_timer_set(&br->hello_timer, jiffies);
pr_debug("%s: hello timer expired\n", br->dev.name);
spin_lock_bh(&br->lock);
if (br->dev.flags & IFF_UP) {
br_config_bpdu_generation(br);
br->hello_timer.expires = jiffies + br->hello_time;
add_timer(&br->hello_timer);
}
spin_unlock_bh(&br->lock);
} }
/* called under bridge lock */ static void br_message_age_timer_expired(unsigned long arg)
static void br_message_age_timer_expired(struct net_bridge_port *p)
{ {
struct net_bridge *br; struct net_bridge_port *p = (struct net_bridge_port *) arg;
struct net_bridge *br = p->br;
const bridge_id *id = &p->designated_bridge;
int was_root; int was_root;
br = p->br; if (p->state == BR_STATE_DISABLED)
printk(KERN_INFO "%s: ", br->dev.name); return;
printk("neighbour ");
dump_bridge_id(&p->designated_bridge);
printk(" lost on port %i(%s)\n", p->port_no, p->dev->name); pr_info("%s: neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)\n",
br->dev.name,
id->prio[0], id->prio[1],
id->addr[0], id->addr[1], id->addr[2],
id->addr[3], id->addr[4], id->addr[5],
p->port_no, p->dev->name);
/* /*
* According to the spec, the message age timer cannot be * According to the spec, the message age timer cannot be
* running when we are the root bridge. So.. this was_root * running when we are the root bridge. So.. this was_root
* check is redundant. I'm leaving it in for now, though. * check is redundant. I'm leaving it in for now, though.
*/ */
spin_lock_bh(&br->lock);
was_root = br_is_root_bridge(br); was_root = br_is_root_bridge(br);
br_become_designated_port(p); br_become_designated_port(p);
...@@ -72,107 +80,101 @@ static void br_message_age_timer_expired(struct net_bridge_port *p) ...@@ -72,107 +80,101 @@ static void br_message_age_timer_expired(struct net_bridge_port *p)
br_port_state_selection(br); br_port_state_selection(br);
if (br_is_root_bridge(br) && !was_root) if (br_is_root_bridge(br) && !was_root)
br_become_root_bridge(br); br_become_root_bridge(br);
spin_unlock_bh(&br->lock);
} }
/* called under bridge lock */ static void br_forward_delay_timer_expired(unsigned long arg)
static void br_forward_delay_timer_expired(struct net_bridge_port *p)
{ {
if (p->state == BR_STATE_LISTENING) { struct net_bridge_port *p = (struct net_bridge_port *) arg;
printk(KERN_INFO "%s: port %i(%s) entering %s state\n", struct net_bridge *br = p->br;
p->br->dev.name, p->port_no, p->dev->name, "learning");
pr_debug("%s: %d(%s) forward delay timer\n",
br->dev.name, p->port_no, p->dev->name);
spin_lock_bh(&br->lock);
if (p->state == BR_STATE_LISTENING) {
p->state = BR_STATE_LEARNING; p->state = BR_STATE_LEARNING;
br_timer_set(&p->forward_delay_timer, jiffies); p->forward_delay_timer.expires = jiffies + br->forward_delay;
add_timer(&p->forward_delay_timer);
} else if (p->state == BR_STATE_LEARNING) { } else if (p->state == BR_STATE_LEARNING) {
printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
p->br->dev.name, p->port_no, p->dev->name, "forwarding");
p->state = BR_STATE_FORWARDING; p->state = BR_STATE_FORWARDING;
if (br_is_designated_for_some_port(p->br)) if (br_is_designated_for_some_port(br))
br_topology_change_detection(p->br); br_topology_change_detection(br);
} }
br_log_state(p);
spin_unlock_bh(&br->lock);
} }
/* called under bridge lock */ static void br_tcn_timer_expired(unsigned long arg)
static void br_tcn_timer_expired(struct net_bridge *br)
{ {
printk(KERN_INFO "%s: retransmitting tcn bpdu\n", br->dev.name); struct net_bridge *br = (struct net_bridge *) arg;
br_transmit_tcn(br);
br_timer_set(&br->tcn_timer, jiffies); pr_debug("%s: tcn timer expired\n", br->dev.name);
spin_lock_bh(&br->lock);
if (br->dev.flags & IFF_UP) {
br_transmit_tcn(br);
br->tcn_timer.expires = jiffies + br->bridge_hello_time;
add_timer(&br->tcn_timer);
}
spin_unlock_bh(&br->lock);
} }
/* called under bridge lock */ static void br_topology_change_timer_expired(unsigned long arg)
static void br_topology_change_timer_expired(struct net_bridge *br)
{ {
struct net_bridge *br = (struct net_bridge *) arg;
pr_debug("%s: topo change timer expired\n", br->dev.name);
spin_lock_bh(&br->lock);
br->topology_change_detected = 0; br->topology_change_detected = 0;
br->topology_change = 0; br->topology_change = 0;
spin_unlock_bh(&br->lock);
} }
/* called under bridge lock */ static void br_hold_timer_expired(unsigned long arg)
static void br_hold_timer_expired(struct net_bridge_port *p)
{ {
struct net_bridge_port *p = (struct net_bridge_port *) arg;
pr_debug("%s: %d(%s) hold timer expired\n",
p->br->dev.name, p->port_no, p->dev->name);
spin_lock_bh(&p->br->lock);
if (p->config_pending) if (p->config_pending)
br_transmit_config(p); br_transmit_config(p);
spin_unlock_bh(&p->br->lock);
} }
/* called under bridge lock */ static inline void br_timer_init(struct timer_list *timer,
static void br_check_port_timers(struct net_bridge_port *p) void (*_function)(unsigned long),
unsigned long _data)
{ {
if (br_timer_has_expired(&p->message_age_timer, p->br->max_age)) { init_timer(timer);
br_timer_clear(&p->message_age_timer); timer->function = _function;
br_message_age_timer_expired(p); timer->data = _data;
}
if (br_timer_has_expired(&p->forward_delay_timer, p->br->forward_delay)) {
br_timer_clear(&p->forward_delay_timer);
br_forward_delay_timer_expired(p);
}
if (br_timer_has_expired(&p->hold_timer, BR_HOLD_TIME)) {
br_timer_clear(&p->hold_timer);
br_hold_timer_expired(p);
}
} }
/* called under bridge lock */ void br_stp_timer_init(struct net_bridge *br)
static void br_check_timers(struct net_bridge *br)
{ {
struct net_bridge_port *p; br_timer_init(&br->hello_timer, br_hello_timer_expired,
(unsigned long) br);
if (br_timer_has_expired(&br->gc_timer, br->gc_interval)) {
br_timer_set(&br->gc_timer, jiffies);
br_fdb_cleanup(br);
}
if (br_timer_has_expired(&br->hello_timer, br->hello_time)) { br_timer_init(&br->tcn_timer, br_tcn_timer_expired,
br_timer_clear(&br->hello_timer); (unsigned long) br);
br_hello_timer_expired(br);
}
if (br_timer_has_expired(&br->tcn_timer, br->bridge_hello_time)) { br_timer_init(&br->topology_change_timer,
br_timer_clear(&br->tcn_timer); br_topology_change_timer_expired,
br_tcn_timer_expired(br); (unsigned long) br);
}
if (br_timer_has_expired(&br->topology_change_timer, br->bridge_forward_delay + br->bridge_max_age)) { br_timer_init(&br->gc_timer, br_fdb_cleanup, (unsigned long) br);
br_timer_clear(&br->topology_change_timer);
br_topology_change_timer_expired(br);
}
list_for_each_entry(p, &br->port_list, list) {
if (p->state != BR_STATE_DISABLED)
br_check_port_timers(p);
}
} }
void br_tick(unsigned long __data) void br_stp_port_timer_init(struct net_bridge_port *p)
{ {
struct net_bridge *br = (struct net_bridge *)__data; br_timer_init(&p->message_age_timer, br_message_age_timer_expired,
(unsigned long) p);
if (spin_trylock_bh(&br->lock)) {
br_check_timers(br); br_timer_init(&p->forward_delay_timer, br_forward_delay_timer_expired,
spin_unlock_bh(&br->lock); (unsigned long) p);
}
br->tick.expires = jiffies + 1; br_timer_init(&p->hold_timer, br_hold_timer_expired,
add_timer(&br->tick); (unsigned long) p);
} }
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