Merge tag 'v3.10.105' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / ipv6 / addrconf.c
index 475a16d7b9557fffdd04732353d5e60d740d3d48..40e6a85eb069b4c484e42eeb5206fee2ce048f94 100644 (file)
@@ -139,10 +139,12 @@ static int ipv6_count_addresses(struct inet6_dev *idev);
 static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE];
 static DEFINE_SPINLOCK(addrconf_hash_lock);
 
-static void addrconf_verify(unsigned long);
+static void addrconf_verify(void);
+static void addrconf_verify_rtnl(void);
+static void addrconf_verify_work(struct work_struct *);
 
-static DEFINE_TIMER(addr_chk_timer, addrconf_verify, 0, 0);
-static DEFINE_SPINLOCK(addrconf_verify_lock);
+static struct workqueue_struct *addrconf_wq;
+static DECLARE_DELAYED_WORK(addr_chk_work, addrconf_verify_work);
 
 static void addrconf_join_anycast(struct inet6_ifaddr *ifp);
 static void addrconf_leave_anycast(struct inet6_ifaddr *ifp);
@@ -157,7 +159,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
                                                  u32 flags, u32 noflags);
 
 static void addrconf_dad_start(struct inet6_ifaddr *ifp);
-static void addrconf_dad_timer(unsigned long data);
+static void addrconf_dad_work(struct work_struct *w);
 static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
 static void addrconf_dad_run(struct inet6_dev *idev);
 static void addrconf_rs_timer(unsigned long data);
@@ -261,37 +263,32 @@ static inline bool addrconf_qdisc_ok(const struct net_device *dev)
        return !qdisc_tx_is_noop(dev);
 }
 
-static void addrconf_del_timer(struct inet6_ifaddr *ifp)
+static void addrconf_del_rs_timer(struct inet6_dev *idev)
 {
-       if (del_timer(&ifp->timer))
+       if (del_timer(&idev->rs_timer))
+               __in6_dev_put(idev);
+}
+
+static void addrconf_del_dad_work(struct inet6_ifaddr *ifp)
+{
+       if (cancel_delayed_work(&ifp->dad_work))
                __in6_ifa_put(ifp);
 }
 
-enum addrconf_timer_t {
-       AC_NONE,
-       AC_DAD,
-       AC_RS,
-};
+static void addrconf_mod_rs_timer(struct inet6_dev *idev,
+                                 unsigned long when)
+{
+       if (!timer_pending(&idev->rs_timer))
+               in6_dev_hold(idev);
+       mod_timer(&idev->rs_timer, jiffies + when);
+}
 
-static void addrconf_mod_timer(struct inet6_ifaddr *ifp,
-                              enum addrconf_timer_t what,
-                              unsigned long when)
+static void addrconf_mod_dad_work(struct inet6_ifaddr *ifp,
+                                  unsigned long delay)
 {
-       if (!del_timer(&ifp->timer))
+       if (!delayed_work_pending(&ifp->dad_work))
                in6_ifa_hold(ifp);
-
-       switch (what) {
-       case AC_DAD:
-               ifp->timer.function = addrconf_dad_timer;
-               break;
-       case AC_RS:
-               ifp->timer.function = addrconf_rs_timer;
-               break;
-       default:
-               break;
-       }
-       ifp->timer.expires = jiffies + when;
-       add_timer(&ifp->timer);
+       mod_delayed_work(addrconf_wq, &ifp->dad_work, delay);
 }
 
 static int snmp6_alloc_dev(struct inet6_dev *idev)
@@ -334,6 +331,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
 
        WARN_ON(!list_empty(&idev->addr_list));
        WARN_ON(idev->mc_list != NULL);
+       WARN_ON(timer_pending(&idev->rs_timer));
 
 #ifdef NET_REFCNT_DEBUG
        pr_debug("%s: %s\n", __func__, dev ? dev->name : "NIL");
@@ -365,7 +363,8 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
        rwlock_init(&ndev->lock);
        ndev->dev = dev;
        INIT_LIST_HEAD(&ndev->addr_list);
-
+       setup_timer(&ndev->rs_timer, addrconf_rs_timer,
+                   (unsigned long)ndev);
        memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
        ndev->cnf.mtu6 = dev->mtu;
        ndev->cnf.sysctl = NULL;
@@ -785,8 +784,9 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
 
        in6_dev_put(ifp->idev);
 
-       if (del_timer(&ifp->timer))
-               pr_notice("Timer is still running, when freeing ifa=%p\n", ifp);
+       if (cancel_delayed_work(&ifp->dad_work))
+               pr_notice("delayed DAD work was pending while freeing ifa=%p\n",
+                         ifp);
 
        if (ifp->state != INET6_IFADDR_STATE_DEAD) {
                pr_warn("Freeing alive inet6 address %p\n", ifp);
@@ -878,9 +878,8 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
        spin_lock_init(&ifa->lock);
        spin_lock_init(&ifa->state_lock);
-       init_timer(&ifa->timer);
+       INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
        INIT_HLIST_NODE(&ifa->addr_lst);
-       ifa->timer.data = (unsigned long) ifa;
        ifa->scope = scope;
        ifa->prefix_len = pfxlen;
        ifa->flags = flags | IFA_F_TENTATIVE;
@@ -939,6 +938,8 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
        int deleted = 0, onlink = 0;
        unsigned long expires = jiffies;
 
+       ASSERT_RTNL();
+
        spin_lock_bh(&ifp->state_lock);
        state = ifp->state;
        ifp->state = INET6_IFADDR_STATE_DEAD;
@@ -1003,7 +1004,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
        }
        write_unlock_bh(&idev->lock);
 
-       addrconf_del_timer(ifp);
+       addrconf_del_dad_work(ifp);
 
        ipv6_ifa_notify(RTM_DELADDR, ifp);
 
@@ -1626,7 +1627,7 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
 {
        if (ifp->flags&IFA_F_PERMANENT) {
                spin_lock_bh(&ifp->lock);
-               addrconf_del_timer(ifp);
+               addrconf_del_dad_work(ifp);
                ifp->flags |= IFA_F_TENTATIVE;
                if (dad_failed)
                        ifp->flags |= IFA_F_DADFAILED;
@@ -1649,20 +1650,21 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
                }
                ipv6_del_addr(ifp);
 #endif
-       } else
+       } else {
                ipv6_del_addr(ifp);
+       }
 }
 
 static int addrconf_dad_end(struct inet6_ifaddr *ifp)
 {
        int err = -ENOENT;
 
-       spin_lock(&ifp->state_lock);
+       spin_lock_bh(&ifp->state_lock);
        if (ifp->state == INET6_IFADDR_STATE_DAD) {
                ifp->state = INET6_IFADDR_STATE_POSTDAD;
                err = 0;
        }
-       spin_unlock(&ifp->state_lock);
+       spin_unlock_bh(&ifp->state_lock);
 
        return err;
 }
@@ -1695,11 +1697,17 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
                }
        }
 
-       addrconf_dad_stop(ifp, 1);
-}
+       spin_lock_bh(&ifp->state_lock);
+       /* transition from _POSTDAD to _ERRDAD */
+       ifp->state = INET6_IFADDR_STATE_ERRDAD;
+       spin_unlock_bh(&ifp->state_lock);
 
-/* Join to solicited addr multicast group. */
+       addrconf_mod_dad_work(ifp, 0);
+       in6_ifa_put(ifp);
+}
 
+/* Join to solicited addr multicast group.
+ * caller must hold RTNL */
 void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr)
 {
        struct in6_addr maddr;
@@ -1711,6 +1719,7 @@ void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr)
        ipv6_dev_mc_inc(dev, &maddr);
 }
 
+/* caller must hold RTNL */
 void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr)
 {
        struct in6_addr maddr;
@@ -1722,9 +1731,11 @@ void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr)
        __ipv6_dev_mc_dec(idev, &maddr);
 }
 
+/* caller must hold RTNL */
 static void addrconf_join_anycast(struct inet6_ifaddr *ifp)
 {
        struct in6_addr addr;
+
        if (ifp->prefix_len == 127) /* RFC 6164 */
                return;
        ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
@@ -1733,9 +1744,11 @@ static void addrconf_join_anycast(struct inet6_ifaddr *ifp)
        ipv6_dev_ac_inc(ifp->idev->dev, &addr);
 }
 
+/* caller must hold RTNL */
 static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
 {
        struct in6_addr addr;
+
        if (ifp->prefix_len == 127) /* RFC 6164 */
                return;
        ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
@@ -2400,7 +2413,7 @@ ok:
                        }
 #endif
                        in6_ifa_put(ifp);
-                       addrconf_verify(0);
+                       addrconf_verify();
                }
        }
        inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
@@ -2543,7 +2556,7 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p
                 */
                addrconf_dad_start(ifp);
                in6_ifa_put(ifp);
-               addrconf_verify(0);
+               addrconf_verify_rtnl();
                return 0;
        }
 
@@ -2735,7 +2748,7 @@ static void init_loopback(struct net_device *dev)
                                 * lo device down, release this obsolete dst and
                                 * reallocate a new router for ifa.
                                 */
-                               if (sp_ifa->rt->dst.obsolete > 0) {
+                               if (!atomic_read(&sp_ifa->rt->rt6i_ref)) {
                                        ip6_rt_put(sp_ifa->rt);
                                        sp_ifa->rt = NULL;
                                } else {
@@ -3124,7 +3137,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
                hlist_for_each_entry_rcu(ifa, h, addr_lst) {
                        if (ifa->idev == idev) {
                                hlist_del_init_rcu(&ifa->addr_lst);
-                               addrconf_del_timer(ifa);
+                               addrconf_del_dad_work(ifa);
                                goto restart;
                        }
                }
@@ -3133,6 +3146,8 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
        write_lock_bh(&idev->lock);
 
+       addrconf_del_rs_timer(idev);
+
        /* Step 2: clear flags for stateless addrconf */
        if (!how)
                idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
@@ -3162,7 +3177,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
        while (!list_empty(&idev->addr_list)) {
                ifa = list_first_entry(&idev->addr_list,
                                       struct inet6_ifaddr, if_list);
-               addrconf_del_timer(ifa);
+               addrconf_del_dad_work(ifa);
 
                list_del(&ifa->if_list);
 
@@ -3204,10 +3219,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 
 static void addrconf_rs_timer(unsigned long data)
 {
-       struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
-       struct inet6_dev *idev = ifp->idev;
+       struct inet6_dev *idev = (struct inet6_dev *)data;
+       struct in6_addr lladdr;
 
-       read_lock(&idev->lock);
+       write_lock(&idev->lock);
        if (idev->dead || !(idev->if_flags & IF_READY))
                goto out;
 
@@ -3218,18 +3233,19 @@ static void addrconf_rs_timer(unsigned long data)
        if (idev->if_flags & IF_RA_RCVD)
                goto out;
 
-       spin_lock(&ifp->lock);
-       if (ifp->probes++ < idev->cnf.rtr_solicits) {
-               /* The wait after the last probe can be shorter */
-               addrconf_mod_timer(ifp, AC_RS,
-                                  (ifp->probes == idev->cnf.rtr_solicits) ?
-                                  idev->cnf.rtr_solicit_delay :
-                                  idev->cnf.rtr_solicit_interval);
-               spin_unlock(&ifp->lock);
+       if (idev->rs_probes++ < idev->cnf.rtr_solicits) {
+               if (!__ipv6_get_lladdr(idev, &lladdr, IFA_F_TENTATIVE))
+                       ndisc_send_rs(idev->dev, &lladdr,
+                                     &in6addr_linklocal_allrouters);
+               else
+                       goto out;
 
-               ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
+               /* The wait after the last probe can be shorter */
+               addrconf_mod_rs_timer(idev, (idev->rs_probes ==
+                                            idev->cnf.rtr_solicits) ?
+                                     idev->cnf.rtr_solicit_delay :
+                                     idev->cnf.rtr_solicit_interval);
        } else {
-               spin_unlock(&ifp->lock);
                /*
                 * Note: we do not support deprecated "all on-link"
                 * assumption any longer.
@@ -3238,8 +3254,8 @@ static void addrconf_rs_timer(unsigned long data)
        }
 
 out:
-       read_unlock(&idev->lock);
-       in6_ifa_put(ifp);
+       write_unlock(&idev->lock);
+       in6_dev_put(idev);
 }
 
 /*
@@ -3255,11 +3271,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
        else
                rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
 
-       ifp->probes = idev->cnf.dad_transmits;
-       addrconf_mod_timer(ifp, AC_DAD, rand_num);
+       ifp->dad_probes = idev->cnf.dad_transmits;
+       addrconf_mod_dad_work(ifp, rand_num);
 }
 
-static void addrconf_dad_start(struct inet6_ifaddr *ifp)
+static void addrconf_dad_begin(struct inet6_ifaddr *ifp)
 {
        struct inet6_dev *idev = ifp->idev;
        struct net_device *dev = idev->dev;
@@ -3311,57 +3327,105 @@ out:
        read_unlock_bh(&idev->lock);
 }
 
-static void addrconf_dad_timer(unsigned long data)
+static void addrconf_dad_start(struct inet6_ifaddr *ifp)
 {
-       struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data;
+       bool begin_dad = false;
+
+       spin_lock_bh(&ifp->state_lock);
+       if (ifp->state != INET6_IFADDR_STATE_DEAD) {
+               ifp->state = INET6_IFADDR_STATE_PREDAD;
+               begin_dad = true;
+       }
+       spin_unlock_bh(&ifp->state_lock);
+
+       if (begin_dad)
+               addrconf_mod_dad_work(ifp, 0);
+}
+
+static void addrconf_dad_work(struct work_struct *w)
+{
+       struct inet6_ifaddr *ifp = container_of(to_delayed_work(w),
+                                               struct inet6_ifaddr,
+                                               dad_work);
        struct inet6_dev *idev = ifp->idev;
        struct in6_addr mcaddr;
 
-       if (!ifp->probes && addrconf_dad_end(ifp))
+       enum {
+               DAD_PROCESS,
+               DAD_BEGIN,
+               DAD_ABORT,
+       } action = DAD_PROCESS;
+
+       rtnl_lock();
+
+       spin_lock_bh(&ifp->state_lock);
+       if (ifp->state == INET6_IFADDR_STATE_PREDAD) {
+               action = DAD_BEGIN;
+               ifp->state = INET6_IFADDR_STATE_DAD;
+       } else if (ifp->state == INET6_IFADDR_STATE_ERRDAD) {
+               action = DAD_ABORT;
+               ifp->state = INET6_IFADDR_STATE_POSTDAD;
+       }
+       spin_unlock_bh(&ifp->state_lock);
+
+       if (action == DAD_BEGIN) {
+               addrconf_dad_begin(ifp);
+               goto out;
+       } else if (action == DAD_ABORT) {
+               in6_ifa_hold(ifp);
+               addrconf_dad_stop(ifp, 1);
                goto out;
+       }
 
-       read_lock(&idev->lock);
+       if (!ifp->dad_probes && addrconf_dad_end(ifp))
+               goto out;
+
+       write_lock_bh(&idev->lock);
        if (idev->dead || !(idev->if_flags & IF_READY)) {
-               read_unlock(&idev->lock);
+               write_unlock_bh(&idev->lock);
                goto out;
        }
 
        spin_lock(&ifp->lock);
        if (ifp->state == INET6_IFADDR_STATE_DEAD) {
                spin_unlock(&ifp->lock);
-               read_unlock(&idev->lock);
+               write_unlock_bh(&idev->lock);
                goto out;
        }
 
-       if (ifp->probes == 0) {
+       if (ifp->dad_probes == 0) {
                /*
                 * DAD was successful
                 */
 
                ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
                spin_unlock(&ifp->lock);
-               read_unlock(&idev->lock);
+               write_unlock_bh(&idev->lock);
 
                addrconf_dad_completed(ifp);
 
                goto out;
        }
 
-       ifp->probes--;
-       addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time);
+       ifp->dad_probes--;
+       addrconf_mod_dad_work(ifp, ifp->idev->nd_parms->retrans_time);
        spin_unlock(&ifp->lock);
-       read_unlock(&idev->lock);
+       write_unlock_bh(&idev->lock);
 
        /* send a neighbour solicitation for our addr */
        addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
        ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &in6addr_any);
 out:
        in6_ifa_put(ifp);
+       rtnl_unlock();
 }
 
 static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 {
        struct net_device *dev = ifp->idev->dev;
+       struct in6_addr lladdr;
+
+       addrconf_del_dad_work(ifp);
 
        /*
         *      Configure the address for reception. Now it is valid.
@@ -3382,13 +3446,20 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
                 *      [...] as part of DAD [...] there is no need
                 *      to delay again before sending the first RS
                 */
-               ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
+               if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
+                       ndisc_send_rs(dev, &lladdr,
+                                     &in6addr_linklocal_allrouters);
+               else
+                       return;
 
-               spin_lock_bh(&ifp->lock);
-               ifp->probes = 1;
+               write_lock_bh(&ifp->idev->lock);
+               spin_lock(&ifp->lock);
+               ifp->idev->rs_probes = 1;
                ifp->idev->if_flags |= IF_RS_SENT;
-               addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval);
-               spin_unlock_bh(&ifp->lock);
+               addrconf_mod_rs_timer(ifp->idev,
+                                     ifp->idev->cnf.rtr_solicit_interval);
+               spin_unlock(&ifp->lock);
+               write_unlock_bh(&ifp->idev->lock);
        }
 }
 
@@ -3586,23 +3657,23 @@ int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr)
  *     Periodic address status verification
  */
 
-static void addrconf_verify(unsigned long foo)
+static void addrconf_verify_rtnl(void)
 {
        unsigned long now, next, next_sec, next_sched;
        struct inet6_ifaddr *ifp;
        int i;
 
+       ASSERT_RTNL();
+
        rcu_read_lock_bh();
-       spin_lock(&addrconf_verify_lock);
        now = jiffies;
        next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
 
-       del_timer(&addr_chk_timer);
+       cancel_delayed_work(&addr_chk_work);
 
        for (i = 0; i < IN6_ADDR_HSIZE; i++) {
 restart:
-               hlist_for_each_entry_rcu_bh(ifp,
-                                        &inet6_addr_lst[i], addr_lst) {
+               hlist_for_each_entry_rcu_bh(ifp, &inet6_addr_lst[i], addr_lst) {
                        unsigned long age;
 
                        if (ifp->flags & IFA_F_PERMANENT)
@@ -3693,13 +3764,22 @@ restart:
 
        ADBG((KERN_DEBUG "now = %lu, schedule = %lu, rounded schedule = %lu => %lu\n",
              now, next, next_sec, next_sched));
-
-       addr_chk_timer.expires = next_sched;
-       add_timer(&addr_chk_timer);
-       spin_unlock(&addrconf_verify_lock);
+       mod_delayed_work(addrconf_wq, &addr_chk_work, next_sched - now);
        rcu_read_unlock_bh();
 }
 
+static void addrconf_verify_work(struct work_struct *w)
+{
+       rtnl_lock();
+       addrconf_verify_rtnl();
+       rtnl_unlock();
+}
+
+static void addrconf_verify(void)
+{
+       mod_delayed_work(addrconf_wq, &addr_chk_work, 0);
+}
+
 static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local)
 {
        struct in6_addr *pfx = NULL;
@@ -3751,6 +3831,8 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
        clock_t expires;
        unsigned long timeout;
 
+       ASSERT_RTNL();
+
        if (!valid_lft || (prefered_lft > valid_lft))
                return -EINVAL;
 
@@ -3784,7 +3866,7 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
 
        addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
                              expires, flags);
-       addrconf_verify(0);
+       addrconf_verify_rtnl();
 
        return 0;
 }
@@ -4397,6 +4479,8 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
        bool update_rs = false;
        struct in6_addr ll_addr;
 
+       ASSERT_RTNL();
+
        if (token == NULL)
                return -EINVAL;
        if (ipv6_addr_any(token))
@@ -4442,6 +4526,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
        }
 
        write_unlock_bh(&idev->lock);
+       addrconf_verify_rtnl();
        return 0;
 }
 
@@ -4643,6 +4728,9 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 {
        struct net *net = dev_net(ifp->idev->dev);
 
+       if (event)
+               ASSERT_RTNL();
+
        inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
 
        switch (event) {
@@ -5187,6 +5275,12 @@ int __init addrconf_init(void)
        if (err < 0)
                goto out_addrlabel;
 
+       addrconf_wq = create_workqueue("ipv6_addrconf");
+       if (!addrconf_wq) {
+               err = -ENOMEM;
+               goto out_nowq;
+       }
+
        /* The addrconf netdev notifier requires that loopback_dev
         * has it's ipv6 private information allocated and setup
         * before it can bring up and give link-local addresses
@@ -5217,7 +5311,7 @@ int __init addrconf_init(void)
 
        register_netdevice_notifier(&ipv6_dev_notf);
 
-       addrconf_verify(0);
+       addrconf_verify();
 
        err = rtnl_af_register(&inet6_ops);
        if (err < 0)
@@ -5248,6 +5342,8 @@ errout:
 errout_af:
        unregister_netdevice_notifier(&ipv6_dev_notf);
 errlo:
+       destroy_workqueue(addrconf_wq);
+out_nowq:
        unregister_pernet_subsys(&addrconf_ops);
 out_addrlabel:
        ipv6_addr_label_cleanup();
@@ -5283,7 +5379,8 @@ void addrconf_cleanup(void)
        for (i = 0; i < IN6_ADDR_HSIZE; i++)
                WARN_ON(!hlist_empty(&inet6_addr_lst[i]));
        spin_unlock_bh(&addrconf_hash_lock);
-
-       del_timer(&addr_chk_timer);
+       cancel_delayed_work(&addr_chk_work);
        rtnl_unlock();
+
+       destroy_workqueue(addrconf_wq);
 }