diff options
Diffstat (limited to 'net/bridge/br_stp_timer.c')
-rw-r--r-- | net/bridge/br_stp_timer.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c new file mode 100644 index 000000000000..9bef55f56425 --- /dev/null +++ b/net/bridge/br_stp_timer.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * Spanning tree protocol; timer-related code | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_stp_timer.c,v 1.3 2000/05/05 02:17:17 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/times.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | |||
20 | #include "br_private.h" | ||
21 | #include "br_private_stp.h" | ||
22 | |||
23 | /* called under bridge lock */ | ||
24 | static int br_is_designated_for_some_port(const struct net_bridge *br) | ||
25 | { | ||
26 | struct net_bridge_port *p; | ||
27 | |||
28 | list_for_each_entry(p, &br->port_list, list) { | ||
29 | if (p->state != BR_STATE_DISABLED && | ||
30 | !memcmp(&p->designated_bridge, &br->bridge_id, 8)) | ||
31 | return 1; | ||
32 | } | ||
33 | |||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | static void br_hello_timer_expired(unsigned long arg) | ||
38 | { | ||
39 | struct net_bridge *br = (struct net_bridge *)arg; | ||
40 | |||
41 | pr_debug("%s: hello timer expired\n", br->dev->name); | ||
42 | spin_lock_bh(&br->lock); | ||
43 | if (br->dev->flags & IFF_UP) { | ||
44 | br_config_bpdu_generation(br); | ||
45 | |||
46 | mod_timer(&br->hello_timer, jiffies + br->hello_time); | ||
47 | } | ||
48 | spin_unlock_bh(&br->lock); | ||
49 | } | ||
50 | |||
51 | static void br_message_age_timer_expired(unsigned long arg) | ||
52 | { | ||
53 | struct net_bridge_port *p = (struct net_bridge_port *) arg; | ||
54 | struct net_bridge *br = p->br; | ||
55 | const bridge_id *id = &p->designated_bridge; | ||
56 | int was_root; | ||
57 | |||
58 | if (p->state == BR_STATE_DISABLED) | ||
59 | return; | ||
60 | |||
61 | |||
62 | pr_info("%s: neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)\n", | ||
63 | br->dev->name, | ||
64 | id->prio[0], id->prio[1], | ||
65 | id->addr[0], id->addr[1], id->addr[2], | ||
66 | id->addr[3], id->addr[4], id->addr[5], | ||
67 | p->port_no, p->dev->name); | ||
68 | |||
69 | /* | ||
70 | * According to the spec, the message age timer cannot be | ||
71 | * running when we are the root bridge. So.. this was_root | ||
72 | * check is redundant. I'm leaving it in for now, though. | ||
73 | */ | ||
74 | spin_lock_bh(&br->lock); | ||
75 | if (p->state == BR_STATE_DISABLED) | ||
76 | goto unlock; | ||
77 | was_root = br_is_root_bridge(br); | ||
78 | |||
79 | br_become_designated_port(p); | ||
80 | br_configuration_update(br); | ||
81 | br_port_state_selection(br); | ||
82 | if (br_is_root_bridge(br) && !was_root) | ||
83 | br_become_root_bridge(br); | ||
84 | unlock: | ||
85 | spin_unlock_bh(&br->lock); | ||
86 | } | ||
87 | |||
88 | static void br_forward_delay_timer_expired(unsigned long arg) | ||
89 | { | ||
90 | struct net_bridge_port *p = (struct net_bridge_port *) arg; | ||
91 | struct net_bridge *br = p->br; | ||
92 | |||
93 | pr_debug("%s: %d(%s) forward delay timer\n", | ||
94 | br->dev->name, p->port_no, p->dev->name); | ||
95 | spin_lock_bh(&br->lock); | ||
96 | if (p->state == BR_STATE_LISTENING) { | ||
97 | p->state = BR_STATE_LEARNING; | ||
98 | mod_timer(&p->forward_delay_timer, | ||
99 | jiffies + br->forward_delay); | ||
100 | } else if (p->state == BR_STATE_LEARNING) { | ||
101 | p->state = BR_STATE_FORWARDING; | ||
102 | if (br_is_designated_for_some_port(br)) | ||
103 | br_topology_change_detection(br); | ||
104 | } | ||
105 | br_log_state(p); | ||
106 | spin_unlock_bh(&br->lock); | ||
107 | } | ||
108 | |||
109 | static void br_tcn_timer_expired(unsigned long arg) | ||
110 | { | ||
111 | struct net_bridge *br = (struct net_bridge *) arg; | ||
112 | |||
113 | pr_debug("%s: tcn timer expired\n", br->dev->name); | ||
114 | spin_lock_bh(&br->lock); | ||
115 | if (br->dev->flags & IFF_UP) { | ||
116 | br_transmit_tcn(br); | ||
117 | |||
118 | mod_timer(&br->tcn_timer,jiffies + br->bridge_hello_time); | ||
119 | } | ||
120 | spin_unlock_bh(&br->lock); | ||
121 | } | ||
122 | |||
123 | static void br_topology_change_timer_expired(unsigned long arg) | ||
124 | { | ||
125 | struct net_bridge *br = (struct net_bridge *) arg; | ||
126 | |||
127 | pr_debug("%s: topo change timer expired\n", br->dev->name); | ||
128 | spin_lock_bh(&br->lock); | ||
129 | br->topology_change_detected = 0; | ||
130 | br->topology_change = 0; | ||
131 | spin_unlock_bh(&br->lock); | ||
132 | } | ||
133 | |||
134 | static void br_hold_timer_expired(unsigned long arg) | ||
135 | { | ||
136 | struct net_bridge_port *p = (struct net_bridge_port *) arg; | ||
137 | |||
138 | pr_debug("%s: %d(%s) hold timer expired\n", | ||
139 | p->br->dev->name, p->port_no, p->dev->name); | ||
140 | |||
141 | spin_lock_bh(&p->br->lock); | ||
142 | if (p->config_pending) | ||
143 | br_transmit_config(p); | ||
144 | spin_unlock_bh(&p->br->lock); | ||
145 | } | ||
146 | |||
147 | static inline void br_timer_init(struct timer_list *timer, | ||
148 | void (*_function)(unsigned long), | ||
149 | unsigned long _data) | ||
150 | { | ||
151 | init_timer(timer); | ||
152 | timer->function = _function; | ||
153 | timer->data = _data; | ||
154 | } | ||
155 | |||
156 | void br_stp_timer_init(struct net_bridge *br) | ||
157 | { | ||
158 | br_timer_init(&br->hello_timer, br_hello_timer_expired, | ||
159 | (unsigned long) br); | ||
160 | |||
161 | br_timer_init(&br->tcn_timer, br_tcn_timer_expired, | ||
162 | (unsigned long) br); | ||
163 | |||
164 | br_timer_init(&br->topology_change_timer, | ||
165 | br_topology_change_timer_expired, | ||
166 | (unsigned long) br); | ||
167 | |||
168 | br_timer_init(&br->gc_timer, br_fdb_cleanup, (unsigned long) br); | ||
169 | } | ||
170 | |||
171 | void br_stp_port_timer_init(struct net_bridge_port *p) | ||
172 | { | ||
173 | br_timer_init(&p->message_age_timer, br_message_age_timer_expired, | ||
174 | (unsigned long) p); | ||
175 | |||
176 | br_timer_init(&p->forward_delay_timer, br_forward_delay_timer_expired, | ||
177 | (unsigned long) p); | ||
178 | |||
179 | br_timer_init(&p->hold_timer, br_hold_timer_expired, | ||
180 | (unsigned long) p); | ||
181 | } | ||
182 | |||
183 | /* Report ticks left (in USER_HZ) used for API */ | ||
184 | unsigned long br_timer_value(const struct timer_list *timer) | ||
185 | { | ||
186 | return timer_pending(timer) | ||
187 | ? jiffies_to_clock_t(timer->expires - jiffies) : 0; | ||
188 | } | ||