Bug 54731 - Kernel panic in fib6_age
Summary: Kernel panic in fib6_age
Status: RESOLVED CODE_FIX
Alias: None
Product: Networking
Classification: Unclassified
Component: IPV6 (show other bugs)
Hardware: All Linux
: P1 blocking
Assignee: Hideaki YOSHIFUJI
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-03-04 10:28 UTC by Zhouyi Zhou
Modified: 2013-03-15 14:57 UTC (History)
0 users

See Also:
Kernel Version: Linux-3.6.6 and (v3.9-rc1 seen from git-web)
Subsystem:
Regression: No
Bisected commit-id:


Attachments

Description Zhouyi Zhou 2013-03-04 10:28:57 UTC
Kernel panic backtrace:
#0  0xffffffff8176a91a in fib6_age (rt=0xffff8800571c6780, arg=0x0 <irq_stack_union>) at net/ipv6/ip6_fib.c:1566
#1  0xffffffff8176a461 in fib6_clean_node (w=0xffff8800655fb4e8) at net/ipv6/ip6_fib.c:1422
#2  0xffffffff8176a266 in fib6_walk_continue (w=0xffff8800655fb4e8) at net/ipv6/ip6_fib.c:1362
#3  0xffffffff8176a3d5 in fib6_walk (w=0xffff8800655fb4e8) at net/ipv6/ip6_fib.c:1406
#4  0xffffffff8176a59d in fib6_clean_tree (net=0xffffffff81edb300 <init_net>, root=0xffff880036c41950
...
gdb>p neigh
(gdb) p neigh
$18 = (struct neighbour *) 0xffffffffffffff97



Solution:

            neigh = dst_neigh_lookup(&rt->dst, &rt->rt6i_gateway);
+           if (IS_ERR(n))
+             return PTR_ERR(n);
            if (neigh) {
                     neigh_flags = neigh->flags;
                     neigh_release(neigh);
            }
Comment 1 Zhouyi Zhou 2013-03-05 06:36:32 UTC
There are a lot of ipv6 nodes in my link local environment, and the kernel 
report IPv6: Neighbour table overflow constantly.

The panic occurres once every three to four times during the reboots.

dst_neigh_lookup calls ip6_neigh_lookup indirectly, and neigh_create return ENOBUFS in case of neighbour table full.
139 static struct neighbour *ip6_neigh_lookup(const struct dst_entry *dst,
140                                           struct sk_buff *skb,
141                                           const void *daddr)
142 {
143         struct rt6_info *rt = (struct rt6_info *) dst;
144         struct neighbour *n;
145 
146         daddr = choose_neigh_daddr(rt, skb, daddr);
147         n = __ipv6_neigh_lookup(&nd_tbl, dst->dev, daddr);
148         if (n)
149                 return n;
150         return neigh_create(&nd_tbl, daddr, dst->dev);
151 }

Solution should be:

            neigh = dst_neigh_lookup(&rt->dst, &rt->rt6i_gateway);
+           if (IS_ERR(neigh))
+             return PTR_ERR(neigh);
            else{
                     neigh_flags = neigh->flags;
                     neigh_release(neigh);
            }
Comment 2 Zhouyi Zhou 2013-03-11 08:30:11 UTC
according to snakky.zhang
This one is more correct
                        neigh = dst_neigh_lookup(&rt->dst, &rt->rt6i_gateway);
-                       if (neigh) {
+                       if (!IS_ERR(neigh)) {

                                neigh_flags = neigh->flags;
                                neigh_release(neigh);
                        }
Comment 3 Zhouyi Zhou 2013-03-15 14:57:59 UTC
The patch has been committed to davem's net tree.
https://git.kernel.org/cgit/linux/kernel/git/davem/net.git/commit/?id=aaa0c23cb90141309f5076ba5e3bfbd39544b985

diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 565bfb1..a3fde52 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -1575,6 +1575,12 @@ static int c4iw_reconnect(struct c4iw_ep *ep)
neigh = dst_neigh_lookup(ep->dst,
&ep->com.cm_id->remote_addr.sin_addr.s_addr);
+ if (!neigh) {
+ pr_err("%s - cannot alloc neigh.\n", __func__);
+ err = -ENOMEM;
+ goto fail4;
+ }
+
/* get a l2t entry */
if (neigh->dev->flags & IFF_LOOPBACK) {
PDBG("%s LOOPBACK\n", __func__);
@@ -3053,6 +3059,12 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb)
dst = &rt->dst;
neigh = dst_neigh_lookup_skb(dst, skb);
+ if (!neigh) {
+ pr_err("%s - failed to allocate neigh!\n",
+ __func__);
+ goto free_dst;
+ }
+
if (neigh->dev->flags & IFF_LOOPBACK) {
pdev = ip_dev_find(&init_net, iph->daddr);
e = cxgb4_l2t_get(dev->rdev.lldi.l2t, neigh,
diff --git a/include/net/dst.h b/include/net/dst.h
index 853cda1..1f8fd10 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -413,13 +413,15 @@ static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n,
static inline struct neighbour *dst_neigh_lookup(const struct dst_entry *dst, const void *daddr)
{
- return dst->ops->neigh_lookup(dst, NULL, daddr);
+ struct neighbour *n = dst->ops->neigh_lookup(dst, NULL, daddr);
+ return IS_ERR(n) ? NULL : n;
}
static inline struct neighbour *dst_neigh_lookup_skb(const struct dst_entry *dst,
struct sk_buff *skb)
{
- return dst->ops->neigh_lookup(dst, skb, NULL);
+ struct neighbour *n = dst->ops->neigh_lookup(dst, skb, NULL);
+ return IS_ERR(n) ? NULL : n;
}
static inline void dst_link_failure(struct sk_buff *skb)

Note You need to log in before you can comment on or make changes to this bug.