diff options
-rw-r--r-- | net/sched/cls_route.c | 226 |
1 files changed, 132 insertions, 94 deletions
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index dd9fc2523c76..ba96deacf27c 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c | |||
@@ -29,25 +29,26 @@ | |||
29 | * are mutually exclusive. | 29 | * are mutually exclusive. |
30 | * 3. "to TAG from ANY" has higher priority, than "to ANY from XXX" | 30 | * 3. "to TAG from ANY" has higher priority, than "to ANY from XXX" |
31 | */ | 31 | */ |
32 | |||
33 | struct route4_fastmap { | 32 | struct route4_fastmap { |
34 | struct route4_filter *filter; | 33 | struct route4_filter *filter; |
35 | u32 id; | 34 | u32 id; |
36 | int iif; | 35 | int iif; |
37 | }; | 36 | }; |
38 | 37 | ||
39 | struct route4_head { | 38 | struct route4_head { |
40 | struct route4_fastmap fastmap[16]; | 39 | struct route4_fastmap fastmap[16]; |
41 | struct route4_bucket *table[256 + 1]; | 40 | struct route4_bucket __rcu *table[256 + 1]; |
41 | struct rcu_head rcu; | ||
42 | }; | 42 | }; |
43 | 43 | ||
44 | struct route4_bucket { | 44 | struct route4_bucket { |
45 | /* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */ | 45 | /* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */ |
46 | struct route4_filter *ht[16 + 16 + 1]; | 46 | struct route4_filter __rcu *ht[16 + 16 + 1]; |
47 | struct rcu_head rcu; | ||
47 | }; | 48 | }; |
48 | 49 | ||
49 | struct route4_filter { | 50 | struct route4_filter { |
50 | struct route4_filter *next; | 51 | struct route4_filter __rcu *next; |
51 | u32 id; | 52 | u32 id; |
52 | int iif; | 53 | int iif; |
53 | 54 | ||
@@ -55,6 +56,8 @@ struct route4_filter { | |||
55 | struct tcf_exts exts; | 56 | struct tcf_exts exts; |
56 | u32 handle; | 57 | u32 handle; |
57 | struct route4_bucket *bkt; | 58 | struct route4_bucket *bkt; |
59 | struct tcf_proto *tp; | ||
60 | struct rcu_head rcu; | ||
58 | }; | 61 | }; |
59 | 62 | ||
60 | #define ROUTE4_FAILURE ((struct route4_filter *)(-1L)) | 63 | #define ROUTE4_FAILURE ((struct route4_filter *)(-1L)) |
@@ -64,14 +67,13 @@ static inline int route4_fastmap_hash(u32 id, int iif) | |||
64 | return id & 0xF; | 67 | return id & 0xF; |
65 | } | 68 | } |
66 | 69 | ||
70 | static DEFINE_SPINLOCK(fastmap_lock); | ||
67 | static void | 71 | static void |
68 | route4_reset_fastmap(struct Qdisc *q, struct route4_head *head, u32 id) | 72 | route4_reset_fastmap(struct route4_head *head) |
69 | { | 73 | { |
70 | spinlock_t *root_lock = qdisc_root_sleeping_lock(q); | 74 | spin_lock_bh(&fastmap_lock); |
71 | |||
72 | spin_lock_bh(root_lock); | ||
73 | memset(head->fastmap, 0, sizeof(head->fastmap)); | 75 | memset(head->fastmap, 0, sizeof(head->fastmap)); |
74 | spin_unlock_bh(root_lock); | 76 | spin_unlock_bh(&fastmap_lock); |
75 | } | 77 | } |
76 | 78 | ||
77 | static void | 79 | static void |
@@ -80,9 +82,12 @@ route4_set_fastmap(struct route4_head *head, u32 id, int iif, | |||
80 | { | 82 | { |
81 | int h = route4_fastmap_hash(id, iif); | 83 | int h = route4_fastmap_hash(id, iif); |
82 | 84 | ||
85 | /* fastmap updates must look atomic to aling id, iff, filter */ | ||
86 | spin_lock_bh(&fastmap_lock); | ||
83 | head->fastmap[h].id = id; | 87 | head->fastmap[h].id = id; |
84 | head->fastmap[h].iif = iif; | 88 | head->fastmap[h].iif = iif; |
85 | head->fastmap[h].filter = f; | 89 | head->fastmap[h].filter = f; |
90 | spin_unlock_bh(&fastmap_lock); | ||
86 | } | 91 | } |
87 | 92 | ||
88 | static inline int route4_hash_to(u32 id) | 93 | static inline int route4_hash_to(u32 id) |
@@ -123,7 +128,7 @@ static inline int route4_hash_wild(void) | |||
123 | static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp, | 128 | static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp, |
124 | struct tcf_result *res) | 129 | struct tcf_result *res) |
125 | { | 130 | { |
126 | struct route4_head *head = tp->root; | 131 | struct route4_head *head = rcu_dereference_bh(tp->root); |
127 | struct dst_entry *dst; | 132 | struct dst_entry *dst; |
128 | struct route4_bucket *b; | 133 | struct route4_bucket *b; |
129 | struct route4_filter *f; | 134 | struct route4_filter *f; |
@@ -141,32 +146,43 @@ static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp, | |||
141 | iif = inet_iif(skb); | 146 | iif = inet_iif(skb); |
142 | 147 | ||
143 | h = route4_fastmap_hash(id, iif); | 148 | h = route4_fastmap_hash(id, iif); |
149 | |||
150 | spin_lock(&fastmap_lock); | ||
144 | if (id == head->fastmap[h].id && | 151 | if (id == head->fastmap[h].id && |
145 | iif == head->fastmap[h].iif && | 152 | iif == head->fastmap[h].iif && |
146 | (f = head->fastmap[h].filter) != NULL) { | 153 | (f = head->fastmap[h].filter) != NULL) { |
147 | if (f == ROUTE4_FAILURE) | 154 | if (f == ROUTE4_FAILURE) { |
155 | spin_unlock(&fastmap_lock); | ||
148 | goto failure; | 156 | goto failure; |
157 | } | ||
149 | 158 | ||
150 | *res = f->res; | 159 | *res = f->res; |
160 | spin_unlock(&fastmap_lock); | ||
151 | return 0; | 161 | return 0; |
152 | } | 162 | } |
163 | spin_unlock(&fastmap_lock); | ||
153 | 164 | ||
154 | h = route4_hash_to(id); | 165 | h = route4_hash_to(id); |
155 | 166 | ||
156 | restart: | 167 | restart: |
157 | b = head->table[h]; | 168 | b = rcu_dereference_bh(head->table[h]); |
158 | if (b) { | 169 | if (b) { |
159 | for (f = b->ht[route4_hash_from(id)]; f; f = f->next) | 170 | for (f = rcu_dereference_bh(b->ht[route4_hash_from(id)]); |
171 | f; | ||
172 | f = rcu_dereference_bh(f->next)) | ||
160 | if (f->id == id) | 173 | if (f->id == id) |
161 | ROUTE4_APPLY_RESULT(); | 174 | ROUTE4_APPLY_RESULT(); |
162 | 175 | ||
163 | for (f = b->ht[route4_hash_iif(iif)]; f; f = f->next) | 176 | for (f = rcu_dereference_bh(b->ht[route4_hash_iif(iif)]); |
177 | f; | ||
178 | f = rcu_dereference_bh(f->next)) | ||
164 | if (f->iif == iif) | 179 | if (f->iif == iif) |
165 | ROUTE4_APPLY_RESULT(); | 180 | ROUTE4_APPLY_RESULT(); |
166 | 181 | ||
167 | for (f = b->ht[route4_hash_wild()]; f; f = f->next) | 182 | for (f = rcu_dereference_bh(b->ht[route4_hash_wild()]); |
183 | f; | ||
184 | f = rcu_dereference_bh(f->next)) | ||
168 | ROUTE4_APPLY_RESULT(); | 185 | ROUTE4_APPLY_RESULT(); |
169 | |||
170 | } | 186 | } |
171 | if (h < 256) { | 187 | if (h < 256) { |
172 | h = 256; | 188 | h = 256; |
@@ -213,7 +229,7 @@ static inline u32 from_hash(u32 id) | |||
213 | 229 | ||
214 | static unsigned long route4_get(struct tcf_proto *tp, u32 handle) | 230 | static unsigned long route4_get(struct tcf_proto *tp, u32 handle) |
215 | { | 231 | { |
216 | struct route4_head *head = tp->root; | 232 | struct route4_head *head = rtnl_dereference(tp->root); |
217 | struct route4_bucket *b; | 233 | struct route4_bucket *b; |
218 | struct route4_filter *f; | 234 | struct route4_filter *f; |
219 | unsigned int h1, h2; | 235 | unsigned int h1, h2; |
@@ -229,9 +245,11 @@ static unsigned long route4_get(struct tcf_proto *tp, u32 handle) | |||
229 | if (h2 > 32) | 245 | if (h2 > 32) |
230 | return 0; | 246 | return 0; |
231 | 247 | ||
232 | b = head->table[h1]; | 248 | b = rtnl_dereference(head->table[h1]); |
233 | if (b) { | 249 | if (b) { |
234 | for (f = b->ht[h2]; f; f = f->next) | 250 | for (f = rtnl_dereference(b->ht[h2]); |
251 | f; | ||
252 | f = rtnl_dereference(f->next)) | ||
235 | if (f->handle == handle) | 253 | if (f->handle == handle) |
236 | return (unsigned long)f; | 254 | return (unsigned long)f; |
237 | } | 255 | } |
@@ -248,8 +266,11 @@ static int route4_init(struct tcf_proto *tp) | |||
248 | } | 266 | } |
249 | 267 | ||
250 | static void | 268 | static void |
251 | route4_delete_filter(struct tcf_proto *tp, struct route4_filter *f) | 269 | route4_delete_filter(struct rcu_head *head) |
252 | { | 270 | { |
271 | struct route4_filter *f = container_of(head, struct route4_filter, rcu); | ||
272 | struct tcf_proto *tp = f->tp; | ||
273 | |||
253 | tcf_unbind_filter(tp, &f->res); | 274 | tcf_unbind_filter(tp, &f->res); |
254 | tcf_exts_destroy(tp, &f->exts); | 275 | tcf_exts_destroy(tp, &f->exts); |
255 | kfree(f); | 276 | kfree(f); |
@@ -257,7 +278,7 @@ route4_delete_filter(struct tcf_proto *tp, struct route4_filter *f) | |||
257 | 278 | ||
258 | static void route4_destroy(struct tcf_proto *tp) | 279 | static void route4_destroy(struct tcf_proto *tp) |
259 | { | 280 | { |
260 | struct route4_head *head = tp->root; | 281 | struct route4_head *head = rtnl_dereference(tp->root); |
261 | int h1, h2; | 282 | int h1, h2; |
262 | 283 | ||
263 | if (head == NULL) | 284 | if (head == NULL) |
@@ -266,28 +287,35 @@ static void route4_destroy(struct tcf_proto *tp) | |||
266 | for (h1 = 0; h1 <= 256; h1++) { | 287 | for (h1 = 0; h1 <= 256; h1++) { |
267 | struct route4_bucket *b; | 288 | struct route4_bucket *b; |
268 | 289 | ||
269 | b = head->table[h1]; | 290 | b = rtnl_dereference(head->table[h1]); |
270 | if (b) { | 291 | if (b) { |
271 | for (h2 = 0; h2 <= 32; h2++) { | 292 | for (h2 = 0; h2 <= 32; h2++) { |
272 | struct route4_filter *f; | 293 | struct route4_filter *f; |
273 | 294 | ||
274 | while ((f = b->ht[h2]) != NULL) { | 295 | while ((f = rtnl_dereference(b->ht[h2])) != NULL) { |
275 | b->ht[h2] = f->next; | 296 | struct route4_filter *next; |
276 | route4_delete_filter(tp, f); | 297 | |
298 | next = rtnl_dereference(f->next); | ||
299 | RCU_INIT_POINTER(b->ht[h2], next); | ||
300 | call_rcu(&f->rcu, route4_delete_filter); | ||
277 | } | 301 | } |
278 | } | 302 | } |
279 | kfree(b); | 303 | RCU_INIT_POINTER(head->table[h1], NULL); |
304 | kfree_rcu(b, rcu); | ||
280 | } | 305 | } |
281 | } | 306 | } |
282 | kfree(head); | 307 | RCU_INIT_POINTER(tp->root, NULL); |
308 | kfree_rcu(head, rcu); | ||
283 | } | 309 | } |
284 | 310 | ||
285 | static int route4_delete(struct tcf_proto *tp, unsigned long arg) | 311 | static int route4_delete(struct tcf_proto *tp, unsigned long arg) |
286 | { | 312 | { |
287 | struct route4_head *head = tp->root; | 313 | struct route4_head *head = rtnl_dereference(tp->root); |
288 | struct route4_filter **fp, *f = (struct route4_filter *)arg; | 314 | struct route4_filter *f = (struct route4_filter *)arg; |
289 | unsigned int h = 0; | 315 | struct route4_filter __rcu **fp; |
316 | struct route4_filter *nf; | ||
290 | struct route4_bucket *b; | 317 | struct route4_bucket *b; |
318 | unsigned int h = 0; | ||
291 | int i; | 319 | int i; |
292 | 320 | ||
293 | if (!head || !f) | 321 | if (!head || !f) |
@@ -296,27 +324,35 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg) | |||
296 | h = f->handle; | 324 | h = f->handle; |
297 | b = f->bkt; | 325 | b = f->bkt; |
298 | 326 | ||
299 | for (fp = &b->ht[from_hash(h >> 16)]; *fp; fp = &(*fp)->next) { | 327 | fp = &b->ht[from_hash(h >> 16)]; |
300 | if (*fp == f) { | 328 | for (nf = rtnl_dereference(*fp); nf; |
301 | tcf_tree_lock(tp); | 329 | fp = &nf->next, nf = rtnl_dereference(*fp)) { |
302 | *fp = f->next; | 330 | if (nf == f) { |
303 | tcf_tree_unlock(tp); | 331 | /* unlink it */ |
332 | RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); | ||
304 | 333 | ||
305 | route4_reset_fastmap(tp->q, head, f->id); | 334 | /* Remove any fastmap lookups that might ref filter |
306 | route4_delete_filter(tp, f); | 335 | * notice we unlink'd the filter so we can't get it |
336 | * back in the fastmap. | ||
337 | */ | ||
338 | route4_reset_fastmap(head); | ||
307 | 339 | ||
308 | /* Strip tree */ | 340 | /* Delete it */ |
341 | call_rcu(&f->rcu, route4_delete_filter); | ||
309 | 342 | ||
310 | for (i = 0; i <= 32; i++) | 343 | /* Strip RTNL protected tree */ |
311 | if (b->ht[i]) | 344 | for (i = 0; i <= 32; i++) { |
345 | struct route4_filter *rt; | ||
346 | |||
347 | rt = rtnl_dereference(b->ht[i]); | ||
348 | if (rt) | ||
312 | return 0; | 349 | return 0; |
350 | } | ||
313 | 351 | ||
314 | /* OK, session has no flows */ | 352 | /* OK, session has no flows */ |
315 | tcf_tree_lock(tp); | 353 | RCU_INIT_POINTER(head->table[to_hash(h)], NULL); |
316 | head->table[to_hash(h)] = NULL; | 354 | kfree_rcu(b, rcu); |
317 | tcf_tree_unlock(tp); | ||
318 | 355 | ||
319 | kfree(b); | ||
320 | return 0; | 356 | return 0; |
321 | } | 357 | } |
322 | } | 358 | } |
@@ -380,26 +416,25 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, | |||
380 | } | 416 | } |
381 | 417 | ||
382 | h1 = to_hash(nhandle); | 418 | h1 = to_hash(nhandle); |
383 | b = head->table[h1]; | 419 | b = rtnl_dereference(head->table[h1]); |
384 | if (!b) { | 420 | if (!b) { |
385 | err = -ENOBUFS; | 421 | err = -ENOBUFS; |
386 | b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL); | 422 | b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL); |
387 | if (b == NULL) | 423 | if (b == NULL) |
388 | goto errout; | 424 | goto errout; |
389 | 425 | ||
390 | tcf_tree_lock(tp); | 426 | rcu_assign_pointer(head->table[h1], b); |
391 | head->table[h1] = b; | ||
392 | tcf_tree_unlock(tp); | ||
393 | } else { | 427 | } else { |
394 | unsigned int h2 = from_hash(nhandle >> 16); | 428 | unsigned int h2 = from_hash(nhandle >> 16); |
395 | 429 | ||
396 | err = -EEXIST; | 430 | err = -EEXIST; |
397 | for (fp = b->ht[h2]; fp; fp = fp->next) | 431 | for (fp = rtnl_dereference(b->ht[h2]); |
432 | fp; | ||
433 | fp = rtnl_dereference(fp->next)) | ||
398 | if (fp->handle == f->handle) | 434 | if (fp->handle == f->handle) |
399 | goto errout; | 435 | goto errout; |
400 | } | 436 | } |
401 | 437 | ||
402 | tcf_tree_lock(tp); | ||
403 | if (tb[TCA_ROUTE4_TO]) | 438 | if (tb[TCA_ROUTE4_TO]) |
404 | f->id = to; | 439 | f->id = to; |
405 | 440 | ||
@@ -410,7 +445,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, | |||
410 | 445 | ||
411 | f->handle = nhandle; | 446 | f->handle = nhandle; |
412 | f->bkt = b; | 447 | f->bkt = b; |
413 | tcf_tree_unlock(tp); | 448 | f->tp = tp; |
414 | 449 | ||
415 | if (tb[TCA_ROUTE4_CLASSID]) { | 450 | if (tb[TCA_ROUTE4_CLASSID]) { |
416 | f->res.classid = nla_get_u32(tb[TCA_ROUTE4_CLASSID]); | 451 | f->res.classid = nla_get_u32(tb[TCA_ROUTE4_CLASSID]); |
@@ -431,14 +466,15 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, | |||
431 | struct nlattr **tca, | 466 | struct nlattr **tca, |
432 | unsigned long *arg, bool ovr) | 467 | unsigned long *arg, bool ovr) |
433 | { | 468 | { |
434 | struct route4_head *head = tp->root; | 469 | struct route4_head *head = rtnl_dereference(tp->root); |
435 | struct route4_filter *f, *f1, **fp; | 470 | struct route4_filter __rcu **fp; |
471 | struct route4_filter *fold, *f1, *pfp, *f = NULL; | ||
436 | struct route4_bucket *b; | 472 | struct route4_bucket *b; |
437 | struct nlattr *opt = tca[TCA_OPTIONS]; | 473 | struct nlattr *opt = tca[TCA_OPTIONS]; |
438 | struct nlattr *tb[TCA_ROUTE4_MAX + 1]; | 474 | struct nlattr *tb[TCA_ROUTE4_MAX + 1]; |
439 | unsigned int h, th; | 475 | unsigned int h, th; |
440 | u32 old_handle = 0; | ||
441 | int err; | 476 | int err; |
477 | bool new = true; | ||
442 | 478 | ||
443 | if (opt == NULL) | 479 | if (opt == NULL) |
444 | return handle ? -EINVAL : 0; | 480 | return handle ? -EINVAL : 0; |
@@ -447,70 +483,70 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, | |||
447 | if (err < 0) | 483 | if (err < 0) |
448 | return err; | 484 | return err; |
449 | 485 | ||
450 | f = (struct route4_filter *)*arg; | 486 | fold = (struct route4_filter *)*arg; |
451 | if (f) { | 487 | if (fold && handle && fold->handle != handle) |
452 | if (f->handle != handle && handle) | ||
453 | return -EINVAL; | 488 | return -EINVAL; |
454 | 489 | ||
455 | if (f->bkt) | ||
456 | old_handle = f->handle; | ||
457 | |||
458 | err = route4_set_parms(net, tp, base, f, handle, head, tb, | ||
459 | tca[TCA_RATE], 0, ovr); | ||
460 | if (err < 0) | ||
461 | return err; | ||
462 | |||
463 | goto reinsert; | ||
464 | } | ||
465 | |||
466 | err = -ENOBUFS; | 490 | err = -ENOBUFS; |
467 | if (head == NULL) { | 491 | if (head == NULL) { |
468 | head = kzalloc(sizeof(struct route4_head), GFP_KERNEL); | 492 | head = kzalloc(sizeof(struct route4_head), GFP_KERNEL); |
469 | if (head == NULL) | 493 | if (head == NULL) |
470 | goto errout; | 494 | goto errout; |
471 | 495 | rcu_assign_pointer(tp->root, head); | |
472 | tcf_tree_lock(tp); | ||
473 | tp->root = head; | ||
474 | tcf_tree_unlock(tp); | ||
475 | } | 496 | } |
476 | 497 | ||
477 | f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL); | 498 | f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL); |
478 | if (f == NULL) | 499 | if (!f) |
479 | goto errout; | 500 | goto errout; |
480 | 501 | ||
481 | tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); | 502 | tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); |
503 | if (fold) { | ||
504 | f->id = fold->id; | ||
505 | f->iif = fold->iif; | ||
506 | f->res = fold->res; | ||
507 | f->handle = fold->handle; | ||
508 | |||
509 | f->tp = fold->tp; | ||
510 | f->bkt = fold->bkt; | ||
511 | new = false; | ||
512 | } | ||
513 | |||
482 | err = route4_set_parms(net, tp, base, f, handle, head, tb, | 514 | err = route4_set_parms(net, tp, base, f, handle, head, tb, |
483 | tca[TCA_RATE], 1, ovr); | 515 | tca[TCA_RATE], new, ovr); |
484 | if (err < 0) | 516 | if (err < 0) |
485 | goto errout; | 517 | goto errout; |
486 | 518 | ||
487 | reinsert: | ||
488 | h = from_hash(f->handle >> 16); | 519 | h = from_hash(f->handle >> 16); |
489 | for (fp = &f->bkt->ht[h]; (f1 = *fp) != NULL; fp = &f1->next) | 520 | fp = &f->bkt->ht[h]; |
521 | for (pfp = rtnl_dereference(*fp); | ||
522 | (f1 = rtnl_dereference(*fp)) != NULL; | ||
523 | fp = &f1->next) | ||
490 | if (f->handle < f1->handle) | 524 | if (f->handle < f1->handle) |
491 | break; | 525 | break; |
492 | 526 | ||
493 | f->next = f1; | 527 | rcu_assign_pointer(f->next, f1); |
494 | tcf_tree_lock(tp); | 528 | rcu_assign_pointer(*fp, f); |
495 | *fp = f; | ||
496 | 529 | ||
497 | if (old_handle && f->handle != old_handle) { | 530 | if (fold && fold->handle && f->handle != fold->handle) { |
498 | th = to_hash(old_handle); | 531 | th = to_hash(fold->handle); |
499 | h = from_hash(old_handle >> 16); | 532 | h = from_hash(fold->handle >> 16); |
500 | b = head->table[th]; | 533 | b = rtnl_dereference(head->table[th]); |
501 | if (b) { | 534 | if (b) { |
502 | for (fp = &b->ht[h]; *fp; fp = &(*fp)->next) { | 535 | fp = &b->ht[h]; |
503 | if (*fp == f) { | 536 | for (pfp = rtnl_dereference(*fp); pfp; |
537 | fp = &pfp->next, pfp = rtnl_dereference(*fp)) { | ||
538 | if (pfp == f) { | ||
504 | *fp = f->next; | 539 | *fp = f->next; |
505 | break; | 540 | break; |
506 | } | 541 | } |
507 | } | 542 | } |
508 | } | 543 | } |
509 | } | 544 | } |
510 | tcf_tree_unlock(tp); | ||
511 | 545 | ||
512 | route4_reset_fastmap(tp->q, head, f->id); | 546 | route4_reset_fastmap(head); |
513 | *arg = (unsigned long)f; | 547 | *arg = (unsigned long)f; |
548 | if (fold) | ||
549 | call_rcu(&fold->rcu, route4_delete_filter); | ||
514 | return 0; | 550 | return 0; |
515 | 551 | ||
516 | errout: | 552 | errout: |
@@ -520,7 +556,7 @@ errout: | |||
520 | 556 | ||
521 | static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg) | 557 | static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg) |
522 | { | 558 | { |
523 | struct route4_head *head = tp->root; | 559 | struct route4_head *head = rtnl_dereference(tp->root); |
524 | unsigned int h, h1; | 560 | unsigned int h, h1; |
525 | 561 | ||
526 | if (head == NULL) | 562 | if (head == NULL) |
@@ -530,13 +566,15 @@ static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg) | |||
530 | return; | 566 | return; |
531 | 567 | ||
532 | for (h = 0; h <= 256; h++) { | 568 | for (h = 0; h <= 256; h++) { |
533 | struct route4_bucket *b = head->table[h]; | 569 | struct route4_bucket *b = rtnl_dereference(head->table[h]); |
534 | 570 | ||
535 | if (b) { | 571 | if (b) { |
536 | for (h1 = 0; h1 <= 32; h1++) { | 572 | for (h1 = 0; h1 <= 32; h1++) { |
537 | struct route4_filter *f; | 573 | struct route4_filter *f; |
538 | 574 | ||
539 | for (f = b->ht[h1]; f; f = f->next) { | 575 | for (f = rtnl_dereference(b->ht[h1]); |
576 | f; | ||
577 | f = rtnl_dereference(f->next)) { | ||
540 | if (arg->count < arg->skip) { | 578 | if (arg->count < arg->skip) { |
541 | arg->count++; | 579 | arg->count++; |
542 | continue; | 580 | continue; |