br_netfilter_rtable_init(br);
        br_stp_timer_init(br);
        br_multicast_init(br);
+       INIT_DELAYED_WORK(&br->gc_work, br_fdb_cleanup);
 }
 
        if (f->added_by_external_learn)
                fdb_del_external_learn(f);
 
-       hlist_del_rcu(&f->hlist);
+       hlist_del_init_rcu(&f->hlist);
        fdb_notify(br, f, RTM_DELNEIGH);
        call_rcu(&f->rcu, fdb_rcu_free);
 }
        spin_unlock_bh(&br->hash_lock);
 }
 
-void br_fdb_cleanup(unsigned long _data)
+void br_fdb_cleanup(struct work_struct *work)
 {
-       struct net_bridge *br = (struct net_bridge *)_data;
+       struct net_bridge *br = container_of(work, struct net_bridge,
+                                            gc_work.work);
        unsigned long delay = hold_time(br);
-       unsigned long next_timer = jiffies + br->ageing_time;
+       unsigned long work_delay = delay;
+       unsigned long now = jiffies;
        int i;
 
-       spin_lock(&br->hash_lock);
        for (i = 0; i < BR_HASH_SIZE; i++) {
                struct net_bridge_fdb_entry *f;
                struct hlist_node *n;
 
+               if (!br->hash[i].first)
+                       continue;
+
+               spin_lock_bh(&br->hash_lock);
                hlist_for_each_entry_safe(f, n, &br->hash[i], hlist) {
                        unsigned long this_timer;
+
                        if (f->is_static)
                                continue;
                        if (f->added_by_external_learn)
                                continue;
                        this_timer = f->updated + delay;
-                       if (time_before_eq(this_timer, jiffies))
+                       if (time_after(this_timer, now))
+                               work_delay = min(work_delay, this_timer - now);
+                       else
                                fdb_delete(br, f);
-                       else if (time_before(this_timer, next_timer))
-                               next_timer = this_timer;
                }
+               spin_unlock_bh(&br->hash_lock);
+               cond_resched();
        }
-       spin_unlock(&br->hash_lock);
 
-       mod_timer(&br->gc_timer, round_jiffies_up(next_timer));
+       /* Cleanup minimum 10 milliseconds apart */
+       work_delay = max_t(unsigned long, work_delay, msecs_to_jiffies(10));
+       mod_delayed_work(system_long_wq, &br->gc_work, work_delay);
 }
 
 /* Completely flush all dynamic entries in forwarding database.*/
                                &br->hash[br_mac_hash(addr, vid)], hlist) {
                if (ether_addr_equal(fdb->addr.addr, addr) &&
                    fdb->vlan_id == vid) {
-                       if (unlikely(has_expired(br, fdb)))
-                               break;
                        return fdb;
                }
        }
 
 
        br_vlan_flush(br);
        br_multicast_dev_del(br);
-       del_timer_sync(&br->gc_timer);
+       cancel_delayed_work_sync(&br->gc_work);
 
        br_sysfs_delbr(br->dev);
        unregister_netdevice_queue(br->dev, head);
 
                b.hello_timer_value = br_timer_value(&br->hello_timer);
                b.tcn_timer_value = br_timer_value(&br->tcn_timer);
                b.topology_change_timer_value = br_timer_value(&br->topology_change_timer);
-               b.gc_timer_value = br_timer_value(&br->gc_timer);
+               b.gc_timer_value = br_timer_value(&br->gc_work.timer);
                rcu_read_unlock();
 
                if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
 
        if (nla_put_u64_64bit(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER, clockval,
                              IFLA_BR_PAD))
                return -EMSGSIZE;
-       clockval = br_timer_value(&br->gc_timer);
+       clockval = br_timer_value(&br->gc_work.timer);
        if (nla_put_u64_64bit(skb, IFLA_BR_GC_TIMER, clockval, IFLA_BR_PAD))
                return -EMSGSIZE;
 
 
        struct timer_list               hello_timer;
        struct timer_list               tcn_timer;
        struct timer_list               topology_change_timer;
-       struct timer_list               gc_timer;
+       struct delayed_work             gc_work;
        struct kobject                  *ifobj;
        u32                             auto_cnt;
 
                              const unsigned char *addr, u16 vid);
 void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr);
 void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr);
-void br_fdb_cleanup(unsigned long arg);
+void br_fdb_cleanup(struct work_struct *work);
 void br_fdb_delete_by_port(struct net_bridge *br,
                           const struct net_bridge_port *p, u16 vid, int do_all);
 struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
 
        br->ageing_time = t;
        spin_unlock_bh(&br->lock);
 
-       mod_timer(&br->gc_timer, jiffies);
+       mod_delayed_work(system_long_wq, &br->gc_work, 0);
 
        return 0;
 }
 
        spin_lock_bh(&br->lock);
        if (br->stp_enabled == BR_KERNEL_STP)
                mod_timer(&br->hello_timer, jiffies + br->hello_time);
-       mod_timer(&br->gc_timer, jiffies + HZ/10);
+       mod_delayed_work(system_long_wq, &br->gc_work, HZ / 10);
 
        br_config_bpdu_generation(br);
 
        del_timer_sync(&br->hello_timer);
        del_timer_sync(&br->topology_change_timer);
        del_timer_sync(&br->tcn_timer);
-       del_timer_sync(&br->gc_timer);
+       cancel_delayed_work_sync(&br->gc_work);
 }
 
 /* called under bridge lock */
 
        setup_timer(&br->topology_change_timer,
                      br_topology_change_timer_expired,
                      (unsigned long) br);
-
-       setup_timer(&br->gc_timer, br_fdb_cleanup, (unsigned long) br);
 }
 
 void br_stp_port_timer_init(struct net_bridge_port *p)
 
                             char *buf)
 {
        struct net_bridge *br = to_bridge(d);
-       return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer));
+       return sprintf(buf, "%ld\n", br_timer_value(&br->gc_work.timer));
 }
 static DEVICE_ATTR_RO(gc_timer);