Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7cdee8a authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller
Browse files

bridge: move to workqueue gc



Move the fdb garbage collector to a workqueue which fires at least 10
milliseconds apart and cleans chain by chain allowing for other tasks
to run in the meantime. When having thousands of fdbs the system is much
more responsive. Most importantly remove the need to check if the
matched entry has expired in __br_fdb_get that causes false-sharing and
is completely unnecessary if we cleanup entries, at worst we'll get 10ms
of traffic for that entry before it gets deleted.

Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1f90c7f3
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -411,4 +411,5 @@ void br_dev_setup(struct net_device *dev)
	br_netfilter_rtable_init(br);
	br_netfilter_rtable_init(br);
	br_stp_timer_init(br);
	br_stp_timer_init(br);
	br_multicast_init(br);
	br_multicast_init(br);
	INIT_DELAYED_WORK(&br->gc_work, br_fdb_cleanup);
}
}
+19 −12
Original line number Original line Diff line number Diff line
@@ -154,7 +154,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
	if (f->added_by_external_learn)
	if (f->added_by_external_learn)
		fdb_del_external_learn(f);
		fdb_del_external_learn(f);


	hlist_del_rcu(&f->hlist);
	hlist_del_init_rcu(&f->hlist);
	fdb_notify(br, f, RTM_DELNEIGH);
	fdb_notify(br, f, RTM_DELNEIGH);
	call_rcu(&f->rcu, fdb_rcu_free);
	call_rcu(&f->rcu, fdb_rcu_free);
}
}
@@ -290,34 +290,43 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
	spin_unlock_bh(&br->hash_lock);
	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 delay = hold_time(br);
	unsigned long next_timer = jiffies + br->ageing_time;
	unsigned long work_delay = delay;
	unsigned long now = jiffies;
	int i;
	int i;


	spin_lock(&br->hash_lock);
	for (i = 0; i < BR_HASH_SIZE; i++) {
	for (i = 0; i < BR_HASH_SIZE; i++) {
		struct net_bridge_fdb_entry *f;
		struct net_bridge_fdb_entry *f;
		struct hlist_node *n;
		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) {
		hlist_for_each_entry_safe(f, n, &br->hash[i], hlist) {
			unsigned long this_timer;
			unsigned long this_timer;

			if (f->is_static)
			if (f->is_static)
				continue;
				continue;
			if (f->added_by_external_learn)
			if (f->added_by_external_learn)
				continue;
				continue;
			this_timer = f->updated + delay;
			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);
				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.*/
/* Completely flush all dynamic entries in forwarding database.*/
@@ -382,8 +391,6 @@ struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br,
				&br->hash[br_mac_hash(addr, vid)], hlist) {
				&br->hash[br_mac_hash(addr, vid)], hlist) {
		if (ether_addr_equal(fdb->addr.addr, addr) &&
		if (ether_addr_equal(fdb->addr.addr, addr) &&
		    fdb->vlan_id == vid) {
		    fdb->vlan_id == vid) {
			if (unlikely(has_expired(br, fdb)))
				break;
			return fdb;
			return fdb;
		}
		}
	}
	}
+1 −1
Original line number Original line Diff line number Diff line
@@ -313,7 +313,7 @@ void br_dev_delete(struct net_device *dev, struct list_head *head)


	br_vlan_flush(br);
	br_vlan_flush(br);
	br_multicast_dev_del(br);
	br_multicast_dev_del(br);
	del_timer_sync(&br->gc_timer);
	cancel_delayed_work_sync(&br->gc_work);


	br_sysfs_delbr(br->dev);
	br_sysfs_delbr(br->dev);
	unregister_netdevice_queue(br->dev, head);
	unregister_netdevice_queue(br->dev, head);
+1 −1
Original line number Original line Diff line number Diff line
@@ -149,7 +149,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
		b.hello_timer_value = br_timer_value(&br->hello_timer);
		b.hello_timer_value = br_timer_value(&br->hello_timer);
		b.tcn_timer_value = br_timer_value(&br->tcn_timer);
		b.tcn_timer_value = br_timer_value(&br->tcn_timer);
		b.topology_change_timer_value = br_timer_value(&br->topology_change_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();
		rcu_read_unlock();


		if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
		if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
+1 −1
Original line number Original line Diff line number Diff line
@@ -1250,7 +1250,7 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
	if (nla_put_u64_64bit(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER, clockval,
	if (nla_put_u64_64bit(skb, IFLA_BR_TOPOLOGY_CHANGE_TIMER, clockval,
			      IFLA_BR_PAD))
			      IFLA_BR_PAD))
		return -EMSGSIZE;
		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))
	if (nla_put_u64_64bit(skb, IFLA_BR_GC_TIMER, clockval, IFLA_BR_PAD))
		return -EMSGSIZE;
		return -EMSGSIZE;


Loading