diff options
author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2013-10-21 00:17:15 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-10-21 18:56:22 -0400 |
commit | c2f17e827b419918c856131f592df9521e1a38e3 (patch) | |
tree | c98a464c94f28df3384615b235f62fc45a9c78a7 /net/ipv6 | |
parent | 3a70417c1d8fb11b38e5a8ae7a268ac581e8c95c (diff) |
ipv6: probe routes asynchronous in rt6_probe
Routes need to be probed asynchronous otherwise the call stack gets
exhausted when the kernel attemps to deliver another skb inline, like
e.g. xt_TEE does, and we probe at the same time.
We update neigh->updated still at once, otherwise we would send to
many probes.
Cc: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/route.c | 38 |
1 files changed, 31 insertions, 7 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c1ee3813e1ae..f54e3a101098 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -476,6 +476,24 @@ out: | |||
476 | } | 476 | } |
477 | 477 | ||
478 | #ifdef CONFIG_IPV6_ROUTER_PREF | 478 | #ifdef CONFIG_IPV6_ROUTER_PREF |
479 | struct __rt6_probe_work { | ||
480 | struct work_struct work; | ||
481 | struct in6_addr target; | ||
482 | struct net_device *dev; | ||
483 | }; | ||
484 | |||
485 | static void rt6_probe_deferred(struct work_struct *w) | ||
486 | { | ||
487 | struct in6_addr mcaddr; | ||
488 | struct __rt6_probe_work *work = | ||
489 | container_of(w, struct __rt6_probe_work, work); | ||
490 | |||
491 | addrconf_addr_solict_mult(&work->target, &mcaddr); | ||
492 | ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL); | ||
493 | dev_put(work->dev); | ||
494 | kfree(w); | ||
495 | } | ||
496 | |||
479 | static void rt6_probe(struct rt6_info *rt) | 497 | static void rt6_probe(struct rt6_info *rt) |
480 | { | 498 | { |
481 | struct neighbour *neigh; | 499 | struct neighbour *neigh; |
@@ -499,17 +517,23 @@ static void rt6_probe(struct rt6_info *rt) | |||
499 | 517 | ||
500 | if (!neigh || | 518 | if (!neigh || |
501 | time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { | 519 | time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { |
502 | struct in6_addr mcaddr; | 520 | struct __rt6_probe_work *work; |
503 | struct in6_addr *target; | 521 | |
522 | work = kmalloc(sizeof(*work), GFP_ATOMIC); | ||
504 | 523 | ||
505 | if (neigh) { | 524 | if (neigh && work) |
506 | neigh->updated = jiffies; | 525 | neigh->updated = jiffies; |
526 | |||
527 | if (neigh) | ||
507 | write_unlock(&neigh->lock); | 528 | write_unlock(&neigh->lock); |
508 | } | ||
509 | 529 | ||
510 | target = (struct in6_addr *)&rt->rt6i_gateway; | 530 | if (work) { |
511 | addrconf_addr_solict_mult(target, &mcaddr); | 531 | INIT_WORK(&work->work, rt6_probe_deferred); |
512 | ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); | 532 | work->target = rt->rt6i_gateway; |
533 | dev_hold(rt->dst.dev); | ||
534 | work->dev = rt->dst.dev; | ||
535 | schedule_work(&work->work); | ||
536 | } | ||
513 | } else { | 537 | } else { |
514 | out: | 538 | out: |
515 | write_unlock(&neigh->lock); | 539 | write_unlock(&neigh->lock); |