| From 769af606834de4263e33410c40f60f60244b2466 Mon Sep 17 00:00:00 2001 |
| From: Hannes Frederic Sowa <hannes@stressinduktion.org> |
| Date: Mon, 21 Oct 2013 06:17:15 +0200 |
| Subject: ipv6: probe routes asynchronous in rt6_probe |
| |
| From: Hannes Frederic Sowa <hannes@stressinduktion.org> |
| |
| [ Upstream commit c2f17e827b419918c856131f592df9521e1a38e3 ] |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv6/route.c | 38 +++++++++++++++++++++++++++++++------- |
| 1 file changed, 31 insertions(+), 7 deletions(-) |
| |
| --- a/net/ipv6/route.c |
| +++ b/net/ipv6/route.c |
| @@ -473,6 +473,24 @@ out: |
| } |
| |
| #ifdef CONFIG_IPV6_ROUTER_PREF |
| +struct __rt6_probe_work { |
| + struct work_struct work; |
| + struct in6_addr target; |
| + struct net_device *dev; |
| +}; |
| + |
| +static void rt6_probe_deferred(struct work_struct *w) |
| +{ |
| + struct in6_addr mcaddr; |
| + struct __rt6_probe_work *work = |
| + container_of(w, struct __rt6_probe_work, work); |
| + |
| + addrconf_addr_solict_mult(&work->target, &mcaddr); |
| + ndisc_send_ns(work->dev, NULL, &work->target, &mcaddr, NULL); |
| + dev_put(work->dev); |
| + kfree(w); |
| +} |
| + |
| static void rt6_probe(struct rt6_info *rt) |
| { |
| struct neighbour *neigh; |
| @@ -496,17 +514,23 @@ static void rt6_probe(struct rt6_info *r |
| |
| if (!neigh || |
| time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) { |
| - struct in6_addr mcaddr; |
| - struct in6_addr *target; |
| + struct __rt6_probe_work *work; |
| |
| - if (neigh) { |
| + work = kmalloc(sizeof(*work), GFP_ATOMIC); |
| + |
| + if (neigh && work) |
| neigh->updated = jiffies; |
| + |
| + if (neigh) |
| write_unlock(&neigh->lock); |
| - } |
| |
| - target = (struct in6_addr *)&rt->rt6i_gateway; |
| - addrconf_addr_solict_mult(target, &mcaddr); |
| - ndisc_send_ns(rt->dst.dev, NULL, target, &mcaddr, NULL); |
| + if (work) { |
| + INIT_WORK(&work->work, rt6_probe_deferred); |
| + work->target = rt->rt6i_gateway; |
| + dev_hold(rt->dst.dev); |
| + work->dev = rt->dst.dev; |
| + schedule_work(&work->work); |
| + } |
| } else { |
| out: |
| write_unlock(&neigh->lock); |