On 10/2/19 3:23 PM, Eric Dumazet wrote: > > > On 10/2/19 2:08 PM, Eric Dumazet wrote: >> >> >> On 10/1/19 11:18 AM, Eric Dumazet wrote: >>> >>> >>> On 9/30/19 8:28 PM, David Ahern wrote: >>>> From: David Ahern <dsah...@gmail.com> >>>> >>>> Rajendra reported a kernel panic when a link was taken down: >>>> >>>> [ 6870.263084] BUG: unable to handle kernel NULL pointer dereference at >>>> 00000000000000a8 >>>> [ 6870.271856] IP: [<ffffffff8efc5764>] __ipv6_ifa_notify+0x154/0x290 >>>> >>>> <snip> >>>> >>> >>> Reviewed-by: Eric Dumazet <eduma...@google.com> >>> >>> Thanks ! >>> >> >> Apparently this patch causes problems. I yet have to make an analysis. >> > > It seems we need to allow the code to do some changes if IF_READY is not set. >
That statement was correct. Prior to the patch in question ifp->state is bumped to INET6_IFADDR_STATE_DAD in addrconf_dad_work. When NETDEV_CHANGE event happens, addrconf_notify calls addrconf_dad_run which calls addrconf_dad_kick to restart dad after the dad process (applies even if nodad is set -- yes, odd). With the patch, IF_READY is not set when the bond device is created and dad_work skips bumping the state. When the CHANGE event comes through the state is not INET6_IFADDR_STATE_DAD (and the restart argument is not set), so addrconf_dad_run does not call addrconf_dad_kick. Bottom line, regardless of IF_READY we need the state change to happen in dad_work, we just need to skip the call to addconf_dad_begin. Can you test the change below on your boxes? It applies on current net. Rajendra: can you test as well and make sure your problem is still resolved? Thanks, diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index dd3be06d5a06..fce0d0dca7bb 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4032,12 +4032,6 @@ static void addrconf_dad_work(struct work_struct *w) rtnl_lock(); - /* check if device was taken down before this delayed work - * function could be canceled - */ - if (idev->dead || !(idev->if_flags & IF_READY)) - goto out; - spin_lock_bh(&ifp->lock); if (ifp->state == INET6_IFADDR_STATE_PREDAD) { action = DAD_BEGIN; @@ -4068,6 +4062,12 @@ static void addrconf_dad_work(struct work_struct *w) } spin_unlock_bh(&ifp->lock); + /* check if device was taken down before this delayed work + * function could be canceled + */ + if (idev->dead || !(idev->if_flags & IF_READY)) + goto out; + if (action == DAD_BEGIN) { addrconf_dad_begin(ifp); goto out;