aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2005-10-23 03:18:00 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2005-10-23 03:18:00 -0400
commit49636bb12892786e4a7b207b37ca7b0c5ca1cae0 (patch)
treeb7a29d9344d0e6ee41d5e28f5a7b6fda5da05fa3 /net
parent6fb9974f49f7a6032118c5b6caa6e08e7097913e (diff)
[NEIGH] Fix timer leak in neigh_changeaddr
neigh_changeaddr attempts to delete neighbour timers without setting nud_state. This doesn't work because the timer may have already fired when we acquire the write lock in neigh_changeaddr. The result is that the timer may keep firing for quite a while until the entry reaches NEIGH_FAILED. It should be setting the nud_state straight away so that if the timer has already fired it can simply exit once we relinquish the lock. In fact, this whole function is simply duplicating the logic in neigh_ifdown which in turn is already doing the right thing when it comes to deleting timers and setting nud_state. So all we have to do is take that code out and put it into a common function and make both neigh_changeaddr and neigh_ifdown call it. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'net')
-rw-r--r--net/core/neighbour.c43
1 files changed, 13 insertions, 30 deletions
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 37d8d8c29522..1dcf7fa1f0fe 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -175,39 +175,10 @@ static void pneigh_queue_purge(struct sk_buff_head *list)
175 } 175 }
176} 176}
177 177
178void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) 178static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev)
179{
180 int i;
181
182 write_lock_bh(&tbl->lock);
183
184 for (i=0; i <= tbl->hash_mask; i++) {
185 struct neighbour *n, **np;
186
187 np = &tbl->hash_buckets[i];
188 while ((n = *np) != NULL) {
189 if (dev && n->dev != dev) {
190 np = &n->next;
191 continue;
192 }
193 *np = n->next;
194 write_lock_bh(&n->lock);
195 n->dead = 1;
196 neigh_del_timer(n);
197 write_unlock_bh(&n->lock);
198 neigh_release(n);
199 }
200 }
201
202 write_unlock_bh(&tbl->lock);
203}
204
205int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
206{ 179{
207 int i; 180 int i;
208 181
209 write_lock_bh(&tbl->lock);
210
211 for (i = 0; i <= tbl->hash_mask; i++) { 182 for (i = 0; i <= tbl->hash_mask; i++) {
212 struct neighbour *n, **np = &tbl->hash_buckets[i]; 183 struct neighbour *n, **np = &tbl->hash_buckets[i];
213 184
@@ -243,7 +214,19 @@ int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
243 neigh_release(n); 214 neigh_release(n);
244 } 215 }
245 } 216 }
217}
246 218
219void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
220{
221 write_lock_bh(&tbl->lock);
222 neigh_flush_dev(tbl, dev);
223 write_unlock_bh(&tbl->lock);
224}
225
226int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
227{
228 write_lock_bh(&tbl->lock);
229 neigh_flush_dev(tbl, dev);
247 pneigh_ifdown(tbl, dev); 230 pneigh_ifdown(tbl, dev);
248 write_unlock_bh(&tbl->lock); 231 write_unlock_bh(&tbl->lock);
249 232