diff options
author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2013-10-21 08:17:15 +0400 |
---|---|---|
committer | Zefan Li <lizefan@huawei.com> | 2016-03-21 04:17:56 +0300 |
commit | bea46f475c5c07022e69b61db190c470bf862f1f (patch) | |
tree | f04af062e8b43f92c7eb589b742b3d8895c7106c /net | |
parent | d865a115933e9edbd86aa36dbfe8d42e2fc5f2a5 (diff) | |
download | linux-bea46f475c5c07022e69b61db190c470bf862f1f.tar.xz |
ipv6: probe routes asynchronous in rt6_probe
commit c2f17e827b419918c856131f592df9521e1a38e3 upstream.
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>
[lizf: Backported to 3.4: adjust context]
Signed-off-by: Zefan Li <lizefan@huawei.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/route.c | 37 |
1 files changed, 31 insertions, 6 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 23b33048ea98..c9092178e3d8 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -404,6 +404,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; @@ -422,15 +440,22 @@ static void rt6_probe(struct rt6_info *rt) read_lock_bh(&neigh->lock); if (!(neigh->nud_state & NUD_VALID) && 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; + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + + if (work) + neigh->updated = jiffies; - neigh->updated = jiffies; read_unlock_bh(&neigh->lock); - target = (struct in6_addr *)&neigh->primary_key; - 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 { read_unlock_bh(&neigh->lock); } |