kmem_cache_free(ip_vs_conn_cachep, cp);
 }
 
+/* Try to delete connection while not holding reference */
+static void ip_vs_conn_del(struct ip_vs_conn *cp)
+{
+       if (del_timer(&cp->timer)) {
+               /* Drop cp->control chain too */
+               if (cp->control)
+                       cp->timeout = 0;
+               ip_vs_conn_expire(&cp->timer);
+       }
+}
+
+/* Try to delete connection while holding reference */
+static void ip_vs_conn_del_put(struct ip_vs_conn *cp)
+{
+       if (del_timer(&cp->timer)) {
+               /* Drop cp->control chain too */
+               if (cp->control)
+                       cp->timeout = 0;
+               __ip_vs_conn_put(cp);
+               ip_vs_conn_expire(&cp->timer);
+       } else {
+               __ip_vs_conn_put(cp);
+       }
+}
+
 static void ip_vs_conn_expire(struct timer_list *t)
 {
        struct ip_vs_conn *cp = from_timer(cp, t, timer);
 
                /* does anybody control me? */
                if (ct) {
+                       bool has_ref = !cp->timeout && __ip_vs_conn_get(ct);
+
                        ip_vs_control_del(cp);
                        /* Drop CTL or non-assured TPL if not used anymore */
-                       if (!cp->timeout && !atomic_read(&ct->n_control) &&
+                       if (has_ref && !atomic_read(&ct->n_control) &&
                            (!(ct->flags & IP_VS_CONN_F_TEMPLATE) ||
                             !(ct->state & IP_VS_CTPL_S_ASSURED))) {
                                IP_VS_DBG(4, "drop controlling connection\n");
-                               ct->timeout = 0;
-                               ip_vs_conn_expire_now(ct);
+                               ip_vs_conn_del_put(ct);
+                       } else if (has_ref) {
+                               __ip_vs_conn_put(ct);
                        }
                }
 
 
 drop:
                        IP_VS_DBG(4, "drop connection\n");
-                       cp->timeout = 0;
-                       ip_vs_conn_expire_now(cp);
+                       ip_vs_conn_del(cp);
                }
                cond_resched_rcu();
        }
                hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[idx], c_list) {
                        if (cp->ipvs != ipvs)
                                continue;
-                       /* As timers are expired in LIFO order, restart
-                        * the timer of controlling connection first, so
-                        * that it is expired after us.
-                        */
+                       if (atomic_read(&cp->n_control))
+                               continue;
                        cp_c = cp->control;
-                       /* cp->control is valid only with reference to cp */
-                       if (cp_c && __ip_vs_conn_get(cp)) {
+                       IP_VS_DBG(4, "del connection\n");
+                       ip_vs_conn_del(cp);
+                       if (cp_c && !atomic_read(&cp_c->n_control)) {
                                IP_VS_DBG(4, "del controlling connection\n");
-                               ip_vs_conn_expire_now(cp_c);
-                               __ip_vs_conn_put(cp);
+                               ip_vs_conn_del(cp_c);
                        }
-                       IP_VS_DBG(4, "del connection\n");
-                       ip_vs_conn_expire_now(cp);
                }
                cond_resched_rcu();
        }
 
        update_defense_level(ipvs);
        if (atomic_read(&ipvs->dropentry))
                ip_vs_random_dropentry(ipvs);
-       schedule_delayed_work(&ipvs->defense_work, DEFENSE_TIMER_PERIOD);
+       queue_delayed_work(system_long_wq, &ipvs->defense_work,
+                          DEFENSE_TIMER_PERIOD);
 }
 #endif
 
        ipvs->sysctl_tbl = tbl;
        /* Schedule defense work */
        INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler);
-       schedule_delayed_work(&ipvs->defense_work, DEFENSE_TIMER_PERIOD);
+       queue_delayed_work(system_long_wq, &ipvs->defense_work,
+                          DEFENSE_TIMER_PERIOD);
 
        return 0;
 }